##// END OF EJS Templates
server: move service table and factory from commandserver...
Yuya Nishihara -
r30507:dd539e2d default
parent child Browse files
Show More
@@ -1,643 +1,644 b''
1 # chgserver.py - command server extension for cHg
1 # chgserver.py - command server extension for cHg
2 #
2 #
3 # Copyright 2011 Yuya Nishihara <yuya@tcha.org>
3 # Copyright 2011 Yuya Nishihara <yuya@tcha.org>
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 """command server extension for cHg (EXPERIMENTAL)
8 """command server extension for cHg (EXPERIMENTAL)
9
9
10 'S' channel (read/write)
10 'S' channel (read/write)
11 propagate ui.system() request to client
11 propagate ui.system() request to client
12
12
13 'attachio' command
13 'attachio' command
14 attach client's stdio passed by sendmsg()
14 attach client's stdio passed by sendmsg()
15
15
16 'chdir' command
16 'chdir' command
17 change current directory
17 change current directory
18
18
19 'getpager' command
19 'getpager' command
20 checks if pager is enabled and which pager should be executed
20 checks if pager is enabled and which pager should be executed
21
21
22 'setenv' command
22 'setenv' command
23 replace os.environ completely
23 replace os.environ completely
24
24
25 'setumask' command
25 'setumask' command
26 set umask
26 set umask
27
27
28 'validate' command
28 'validate' command
29 reload the config and check if the server is up to date
29 reload the config and check if the server is up to date
30
30
31 Config
31 Config
32 ------
32 ------
33
33
34 ::
34 ::
35
35
36 [chgserver]
36 [chgserver]
37 idletimeout = 3600 # seconds, after which an idle server will exit
37 idletimeout = 3600 # seconds, after which an idle server will exit
38 skiphash = False # whether to skip config or env change checks
38 skiphash = False # whether to skip config or env change checks
39 """
39 """
40
40
41 from __future__ import absolute_import
41 from __future__ import absolute_import
42
42
43 import errno
43 import errno
44 import hashlib
44 import hashlib
45 import inspect
45 import inspect
46 import os
46 import os
47 import re
47 import re
48 import signal
48 import signal
49 import struct
49 import struct
50 import sys
50 import sys
51 import time
51 import time
52
52
53 from mercurial.i18n import _
53 from mercurial.i18n import _
54
54
55 from mercurial import (
55 from mercurial import (
56 cmdutil,
56 cmdutil,
57 commands,
57 commands,
58 commandserver,
58 commandserver,
59 dispatch,
59 dispatch,
60 error,
60 error,
61 extensions,
61 extensions,
62 osutil,
62 osutil,
63 server,
63 util,
64 util,
64 )
65 )
65
66
66 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
67 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
67 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
68 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
68 # be specifying the version(s) of Mercurial they are tested with, or
69 # be specifying the version(s) of Mercurial they are tested with, or
69 # leave the attribute unspecified.
70 # leave the attribute unspecified.
70 testedwith = 'ships-with-hg-core'
71 testedwith = 'ships-with-hg-core'
71
72
72 _log = commandserver.log
73 _log = commandserver.log
73
74
74 def _hashlist(items):
75 def _hashlist(items):
75 """return sha1 hexdigest for a list"""
76 """return sha1 hexdigest for a list"""
76 return hashlib.sha1(str(items)).hexdigest()
77 return hashlib.sha1(str(items)).hexdigest()
77
78
78 # sensitive config sections affecting confighash
79 # sensitive config sections affecting confighash
79 _configsections = [
80 _configsections = [
80 'alias', # affects global state commands.table
81 'alias', # affects global state commands.table
81 'extdiff', # uisetup will register new commands
82 'extdiff', # uisetup will register new commands
82 'extensions',
83 'extensions',
83 ]
84 ]
84
85
85 # sensitive environment variables affecting confighash
86 # sensitive environment variables affecting confighash
86 _envre = re.compile(r'''\A(?:
87 _envre = re.compile(r'''\A(?:
87 CHGHG
88 CHGHG
88 |HG.*
89 |HG.*
89 |LANG(?:UAGE)?
90 |LANG(?:UAGE)?
90 |LC_.*
91 |LC_.*
91 |LD_.*
92 |LD_.*
92 |PATH
93 |PATH
93 |PYTHON.*
94 |PYTHON.*
94 |TERM(?:INFO)?
95 |TERM(?:INFO)?
95 |TZ
96 |TZ
96 )\Z''', re.X)
97 )\Z''', re.X)
97
98
98 def _confighash(ui):
99 def _confighash(ui):
99 """return a quick hash for detecting config/env changes
100 """return a quick hash for detecting config/env changes
100
101
101 confighash is the hash of sensitive config items and environment variables.
102 confighash is the hash of sensitive config items and environment variables.
102
103
103 for chgserver, it is designed that once confighash changes, the server is
104 for chgserver, it is designed that once confighash changes, the server is
104 not qualified to serve its client and should redirect the client to a new
105 not qualified to serve its client and should redirect the client to a new
105 server. different from mtimehash, confighash change will not mark the
106 server. different from mtimehash, confighash change will not mark the
106 server outdated and exit since the user can have different configs at the
107 server outdated and exit since the user can have different configs at the
107 same time.
108 same time.
108 """
109 """
109 sectionitems = []
110 sectionitems = []
110 for section in _configsections:
111 for section in _configsections:
111 sectionitems.append(ui.configitems(section))
112 sectionitems.append(ui.configitems(section))
112 sectionhash = _hashlist(sectionitems)
113 sectionhash = _hashlist(sectionitems)
113 envitems = [(k, v) for k, v in os.environ.iteritems() if _envre.match(k)]
114 envitems = [(k, v) for k, v in os.environ.iteritems() if _envre.match(k)]
114 envhash = _hashlist(sorted(envitems))
115 envhash = _hashlist(sorted(envitems))
115 return sectionhash[:6] + envhash[:6]
116 return sectionhash[:6] + envhash[:6]
116
117
117 def _getmtimepaths(ui):
118 def _getmtimepaths(ui):
118 """get a list of paths that should be checked to detect change
119 """get a list of paths that should be checked to detect change
119
120
120 The list will include:
121 The list will include:
121 - extensions (will not cover all files for complex extensions)
122 - extensions (will not cover all files for complex extensions)
122 - mercurial/__version__.py
123 - mercurial/__version__.py
123 - python binary
124 - python binary
124 """
125 """
125 modules = [m for n, m in extensions.extensions(ui)]
126 modules = [m for n, m in extensions.extensions(ui)]
126 try:
127 try:
127 from mercurial import __version__
128 from mercurial import __version__
128 modules.append(__version__)
129 modules.append(__version__)
129 except ImportError:
130 except ImportError:
130 pass
131 pass
131 files = [sys.executable]
132 files = [sys.executable]
132 for m in modules:
133 for m in modules:
133 try:
134 try:
134 files.append(inspect.getabsfile(m))
135 files.append(inspect.getabsfile(m))
135 except TypeError:
136 except TypeError:
136 pass
137 pass
137 return sorted(set(files))
138 return sorted(set(files))
138
139
139 def _mtimehash(paths):
140 def _mtimehash(paths):
140 """return a quick hash for detecting file changes
141 """return a quick hash for detecting file changes
141
142
142 mtimehash calls stat on given paths and calculate a hash based on size and
143 mtimehash calls stat on given paths and calculate a hash based on size and
143 mtime of each file. mtimehash does not read file content because reading is
144 mtime of each file. mtimehash does not read file content because reading is
144 expensive. therefore it's not 100% reliable for detecting content changes.
145 expensive. therefore it's not 100% reliable for detecting content changes.
145 it's possible to return different hashes for same file contents.
146 it's possible to return different hashes for same file contents.
146 it's also possible to return a same hash for different file contents for
147 it's also possible to return a same hash for different file contents for
147 some carefully crafted situation.
148 some carefully crafted situation.
148
149
149 for chgserver, it is designed that once mtimehash changes, the server is
150 for chgserver, it is designed that once mtimehash changes, the server is
150 considered outdated immediately and should no longer provide service.
151 considered outdated immediately and should no longer provide service.
151
152
152 mtimehash is not included in confighash because we only know the paths of
153 mtimehash is not included in confighash because we only know the paths of
153 extensions after importing them (there is imp.find_module but that faces
154 extensions after importing them (there is imp.find_module but that faces
154 race conditions). We need to calculate confighash without importing.
155 race conditions). We need to calculate confighash without importing.
155 """
156 """
156 def trystat(path):
157 def trystat(path):
157 try:
158 try:
158 st = os.stat(path)
159 st = os.stat(path)
159 return (st.st_mtime, st.st_size)
160 return (st.st_mtime, st.st_size)
160 except OSError:
161 except OSError:
161 # could be ENOENT, EPERM etc. not fatal in any case
162 # could be ENOENT, EPERM etc. not fatal in any case
162 pass
163 pass
163 return _hashlist(map(trystat, paths))[:12]
164 return _hashlist(map(trystat, paths))[:12]
164
165
165 class hashstate(object):
166 class hashstate(object):
166 """a structure storing confighash, mtimehash, paths used for mtimehash"""
167 """a structure storing confighash, mtimehash, paths used for mtimehash"""
167 def __init__(self, confighash, mtimehash, mtimepaths):
168 def __init__(self, confighash, mtimehash, mtimepaths):
168 self.confighash = confighash
169 self.confighash = confighash
169 self.mtimehash = mtimehash
170 self.mtimehash = mtimehash
170 self.mtimepaths = mtimepaths
171 self.mtimepaths = mtimepaths
171
172
172 @staticmethod
173 @staticmethod
173 def fromui(ui, mtimepaths=None):
174 def fromui(ui, mtimepaths=None):
174 if mtimepaths is None:
175 if mtimepaths is None:
175 mtimepaths = _getmtimepaths(ui)
176 mtimepaths = _getmtimepaths(ui)
176 confighash = _confighash(ui)
177 confighash = _confighash(ui)
177 mtimehash = _mtimehash(mtimepaths)
178 mtimehash = _mtimehash(mtimepaths)
178 _log('confighash = %s mtimehash = %s\n' % (confighash, mtimehash))
179 _log('confighash = %s mtimehash = %s\n' % (confighash, mtimehash))
179 return hashstate(confighash, mtimehash, mtimepaths)
180 return hashstate(confighash, mtimehash, mtimepaths)
180
181
181 # copied from hgext/pager.py:uisetup()
182 # copied from hgext/pager.py:uisetup()
182 def _setuppagercmd(ui, options, cmd):
183 def _setuppagercmd(ui, options, cmd):
183 if not ui.formatted():
184 if not ui.formatted():
184 return
185 return
185
186
186 p = ui.config("pager", "pager", os.environ.get("PAGER"))
187 p = ui.config("pager", "pager", os.environ.get("PAGER"))
187 usepager = False
188 usepager = False
188 always = util.parsebool(options['pager'])
189 always = util.parsebool(options['pager'])
189 auto = options['pager'] == 'auto'
190 auto = options['pager'] == 'auto'
190
191
191 if not p:
192 if not p:
192 pass
193 pass
193 elif always:
194 elif always:
194 usepager = True
195 usepager = True
195 elif not auto:
196 elif not auto:
196 usepager = False
197 usepager = False
197 else:
198 else:
198 attended = ['annotate', 'cat', 'diff', 'export', 'glog', 'log', 'qdiff']
199 attended = ['annotate', 'cat', 'diff', 'export', 'glog', 'log', 'qdiff']
199 attend = ui.configlist('pager', 'attend', attended)
200 attend = ui.configlist('pager', 'attend', attended)
200 ignore = ui.configlist('pager', 'ignore')
201 ignore = ui.configlist('pager', 'ignore')
201 cmds, _ = cmdutil.findcmd(cmd, commands.table)
202 cmds, _ = cmdutil.findcmd(cmd, commands.table)
202
203
203 for cmd in cmds:
204 for cmd in cmds:
204 var = 'attend-%s' % cmd
205 var = 'attend-%s' % cmd
205 if ui.config('pager', var):
206 if ui.config('pager', var):
206 usepager = ui.configbool('pager', var)
207 usepager = ui.configbool('pager', var)
207 break
208 break
208 if (cmd in attend or
209 if (cmd in attend or
209 (cmd not in ignore and not attend)):
210 (cmd not in ignore and not attend)):
210 usepager = True
211 usepager = True
211 break
212 break
212
213
213 if usepager:
214 if usepager:
214 ui.setconfig('ui', 'formatted', ui.formatted(), 'pager')
215 ui.setconfig('ui', 'formatted', ui.formatted(), 'pager')
215 ui.setconfig('ui', 'interactive', False, 'pager')
216 ui.setconfig('ui', 'interactive', False, 'pager')
216 return p
217 return p
217
218
218 def _newchgui(srcui, csystem):
219 def _newchgui(srcui, csystem):
219 class chgui(srcui.__class__):
220 class chgui(srcui.__class__):
220 def __init__(self, src=None):
221 def __init__(self, src=None):
221 super(chgui, self).__init__(src)
222 super(chgui, self).__init__(src)
222 if src:
223 if src:
223 self._csystem = getattr(src, '_csystem', csystem)
224 self._csystem = getattr(src, '_csystem', csystem)
224 else:
225 else:
225 self._csystem = csystem
226 self._csystem = csystem
226
227
227 def system(self, cmd, environ=None, cwd=None, onerr=None,
228 def system(self, cmd, environ=None, cwd=None, onerr=None,
228 errprefix=None):
229 errprefix=None):
229 # fallback to the original system method if the output needs to be
230 # fallback to the original system method if the output needs to be
230 # captured (to self._buffers), or the output stream is not stdout
231 # captured (to self._buffers), or the output stream is not stdout
231 # (e.g. stderr, cStringIO), because the chg client is not aware of
232 # (e.g. stderr, cStringIO), because the chg client is not aware of
232 # these situations and will behave differently (write to stdout).
233 # these situations and will behave differently (write to stdout).
233 if (any(s[1] for s in self._bufferstates)
234 if (any(s[1] for s in self._bufferstates)
234 or not util.safehasattr(self.fout, 'fileno')
235 or not util.safehasattr(self.fout, 'fileno')
235 or self.fout.fileno() != util.stdout.fileno()):
236 or self.fout.fileno() != util.stdout.fileno()):
236 return super(chgui, self).system(cmd, environ, cwd, onerr,
237 return super(chgui, self).system(cmd, environ, cwd, onerr,
237 errprefix)
238 errprefix)
238 # copied from mercurial/util.py:system()
239 # copied from mercurial/util.py:system()
239 self.flush()
240 self.flush()
240 def py2shell(val):
241 def py2shell(val):
241 if val is None or val is False:
242 if val is None or val is False:
242 return '0'
243 return '0'
243 if val is True:
244 if val is True:
244 return '1'
245 return '1'
245 return str(val)
246 return str(val)
246 env = os.environ.copy()
247 env = os.environ.copy()
247 if environ:
248 if environ:
248 env.update((k, py2shell(v)) for k, v in environ.iteritems())
249 env.update((k, py2shell(v)) for k, v in environ.iteritems())
249 env['HG'] = util.hgexecutable()
250 env['HG'] = util.hgexecutable()
250 rc = self._csystem(cmd, env, cwd)
251 rc = self._csystem(cmd, env, cwd)
251 if rc and onerr:
252 if rc and onerr:
252 errmsg = '%s %s' % (os.path.basename(cmd.split(None, 1)[0]),
253 errmsg = '%s %s' % (os.path.basename(cmd.split(None, 1)[0]),
253 util.explainexit(rc)[0])
254 util.explainexit(rc)[0])
254 if errprefix:
255 if errprefix:
255 errmsg = '%s: %s' % (errprefix, errmsg)
256 errmsg = '%s: %s' % (errprefix, errmsg)
256 raise onerr(errmsg)
257 raise onerr(errmsg)
257 return rc
258 return rc
258
259
259 return chgui(srcui)
260 return chgui(srcui)
260
261
261 def _loadnewui(srcui, args):
262 def _loadnewui(srcui, args):
262 newui = srcui.__class__()
263 newui = srcui.__class__()
263 for a in ['fin', 'fout', 'ferr', 'environ']:
264 for a in ['fin', 'fout', 'ferr', 'environ']:
264 setattr(newui, a, getattr(srcui, a))
265 setattr(newui, a, getattr(srcui, a))
265 if util.safehasattr(srcui, '_csystem'):
266 if util.safehasattr(srcui, '_csystem'):
266 newui._csystem = srcui._csystem
267 newui._csystem = srcui._csystem
267
268
268 # internal config: extensions.chgserver
269 # internal config: extensions.chgserver
269 newui.setconfig('extensions', 'chgserver',
270 newui.setconfig('extensions', 'chgserver',
270 srcui.config('extensions', 'chgserver'), '--config')
271 srcui.config('extensions', 'chgserver'), '--config')
271
272
272 # command line args
273 # command line args
273 args = args[:]
274 args = args[:]
274 dispatch._parseconfig(newui, dispatch._earlygetopt(['--config'], args))
275 dispatch._parseconfig(newui, dispatch._earlygetopt(['--config'], args))
275
276
276 # stolen from tortoisehg.util.copydynamicconfig()
277 # stolen from tortoisehg.util.copydynamicconfig()
277 for section, name, value in srcui.walkconfig():
278 for section, name, value in srcui.walkconfig():
278 source = srcui.configsource(section, name)
279 source = srcui.configsource(section, name)
279 if ':' in source or source == '--config':
280 if ':' in source or source == '--config':
280 # path:line or command line
281 # path:line or command line
281 continue
282 continue
282 if source == 'none':
283 if source == 'none':
283 # ui.configsource returns 'none' by default
284 # ui.configsource returns 'none' by default
284 source = ''
285 source = ''
285 newui.setconfig(section, name, value, source)
286 newui.setconfig(section, name, value, source)
286
287
287 # load wd and repo config, copied from dispatch.py
288 # load wd and repo config, copied from dispatch.py
288 cwds = dispatch._earlygetopt(['--cwd'], args)
289 cwds = dispatch._earlygetopt(['--cwd'], args)
289 cwd = cwds and os.path.realpath(cwds[-1]) or None
290 cwd = cwds and os.path.realpath(cwds[-1]) or None
290 rpath = dispatch._earlygetopt(["-R", "--repository", "--repo"], args)
291 rpath = dispatch._earlygetopt(["-R", "--repository", "--repo"], args)
291 path, newlui = dispatch._getlocal(newui, rpath, wd=cwd)
292 path, newlui = dispatch._getlocal(newui, rpath, wd=cwd)
292
293
293 return (newui, newlui)
294 return (newui, newlui)
294
295
295 class channeledsystem(object):
296 class channeledsystem(object):
296 """Propagate ui.system() request in the following format:
297 """Propagate ui.system() request in the following format:
297
298
298 payload length (unsigned int),
299 payload length (unsigned int),
299 cmd, '\0',
300 cmd, '\0',
300 cwd, '\0',
301 cwd, '\0',
301 envkey, '=', val, '\0',
302 envkey, '=', val, '\0',
302 ...
303 ...
303 envkey, '=', val
304 envkey, '=', val
304
305
305 and waits:
306 and waits:
306
307
307 exitcode length (unsigned int),
308 exitcode length (unsigned int),
308 exitcode (int)
309 exitcode (int)
309 """
310 """
310 def __init__(self, in_, out, channel):
311 def __init__(self, in_, out, channel):
311 self.in_ = in_
312 self.in_ = in_
312 self.out = out
313 self.out = out
313 self.channel = channel
314 self.channel = channel
314
315
315 def __call__(self, cmd, environ, cwd):
316 def __call__(self, cmd, environ, cwd):
316 args = [util.quotecommand(cmd), os.path.abspath(cwd or '.')]
317 args = [util.quotecommand(cmd), os.path.abspath(cwd or '.')]
317 args.extend('%s=%s' % (k, v) for k, v in environ.iteritems())
318 args.extend('%s=%s' % (k, v) for k, v in environ.iteritems())
318 data = '\0'.join(args)
319 data = '\0'.join(args)
319 self.out.write(struct.pack('>cI', self.channel, len(data)))
320 self.out.write(struct.pack('>cI', self.channel, len(data)))
320 self.out.write(data)
321 self.out.write(data)
321 self.out.flush()
322 self.out.flush()
322
323
323 length = self.in_.read(4)
324 length = self.in_.read(4)
324 length, = struct.unpack('>I', length)
325 length, = struct.unpack('>I', length)
325 if length != 4:
326 if length != 4:
326 raise error.Abort(_('invalid response'))
327 raise error.Abort(_('invalid response'))
327 rc, = struct.unpack('>i', self.in_.read(4))
328 rc, = struct.unpack('>i', self.in_.read(4))
328 return rc
329 return rc
329
330
330 _iochannels = [
331 _iochannels = [
331 # server.ch, ui.fp, mode
332 # server.ch, ui.fp, mode
332 ('cin', 'fin', 'rb'),
333 ('cin', 'fin', 'rb'),
333 ('cout', 'fout', 'wb'),
334 ('cout', 'fout', 'wb'),
334 ('cerr', 'ferr', 'wb'),
335 ('cerr', 'ferr', 'wb'),
335 ]
336 ]
336
337
337 class chgcmdserver(commandserver.server):
338 class chgcmdserver(commandserver.server):
338 def __init__(self, ui, repo, fin, fout, sock, hashstate, baseaddress):
339 def __init__(self, ui, repo, fin, fout, sock, hashstate, baseaddress):
339 super(chgcmdserver, self).__init__(
340 super(chgcmdserver, self).__init__(
340 _newchgui(ui, channeledsystem(fin, fout, 'S')), repo, fin, fout)
341 _newchgui(ui, channeledsystem(fin, fout, 'S')), repo, fin, fout)
341 self.clientsock = sock
342 self.clientsock = sock
342 self._oldios = [] # original (self.ch, ui.fp, fd) before "attachio"
343 self._oldios = [] # original (self.ch, ui.fp, fd) before "attachio"
343 self.hashstate = hashstate
344 self.hashstate = hashstate
344 self.baseaddress = baseaddress
345 self.baseaddress = baseaddress
345 if hashstate is not None:
346 if hashstate is not None:
346 self.capabilities = self.capabilities.copy()
347 self.capabilities = self.capabilities.copy()
347 self.capabilities['validate'] = chgcmdserver.validate
348 self.capabilities['validate'] = chgcmdserver.validate
348
349
349 def cleanup(self):
350 def cleanup(self):
350 super(chgcmdserver, self).cleanup()
351 super(chgcmdserver, self).cleanup()
351 # dispatch._runcatch() does not flush outputs if exception is not
352 # dispatch._runcatch() does not flush outputs if exception is not
352 # handled by dispatch._dispatch()
353 # handled by dispatch._dispatch()
353 self.ui.flush()
354 self.ui.flush()
354 self._restoreio()
355 self._restoreio()
355
356
356 def attachio(self):
357 def attachio(self):
357 """Attach to client's stdio passed via unix domain socket; all
358 """Attach to client's stdio passed via unix domain socket; all
358 channels except cresult will no longer be used
359 channels except cresult will no longer be used
359 """
360 """
360 # tell client to sendmsg() with 1-byte payload, which makes it
361 # tell client to sendmsg() with 1-byte payload, which makes it
361 # distinctive from "attachio\n" command consumed by client.read()
362 # distinctive from "attachio\n" command consumed by client.read()
362 self.clientsock.sendall(struct.pack('>cI', 'I', 1))
363 self.clientsock.sendall(struct.pack('>cI', 'I', 1))
363 clientfds = osutil.recvfds(self.clientsock.fileno())
364 clientfds = osutil.recvfds(self.clientsock.fileno())
364 _log('received fds: %r\n' % clientfds)
365 _log('received fds: %r\n' % clientfds)
365
366
366 ui = self.ui
367 ui = self.ui
367 ui.flush()
368 ui.flush()
368 first = self._saveio()
369 first = self._saveio()
369 for fd, (cn, fn, mode) in zip(clientfds, _iochannels):
370 for fd, (cn, fn, mode) in zip(clientfds, _iochannels):
370 assert fd > 0
371 assert fd > 0
371 fp = getattr(ui, fn)
372 fp = getattr(ui, fn)
372 os.dup2(fd, fp.fileno())
373 os.dup2(fd, fp.fileno())
373 os.close(fd)
374 os.close(fd)
374 if not first:
375 if not first:
375 continue
376 continue
376 # reset buffering mode when client is first attached. as we want
377 # reset buffering mode when client is first attached. as we want
377 # to see output immediately on pager, the mode stays unchanged
378 # to see output immediately on pager, the mode stays unchanged
378 # when client re-attached. ferr is unchanged because it should
379 # when client re-attached. ferr is unchanged because it should
379 # be unbuffered no matter if it is a tty or not.
380 # be unbuffered no matter if it is a tty or not.
380 if fn == 'ferr':
381 if fn == 'ferr':
381 newfp = fp
382 newfp = fp
382 else:
383 else:
383 # make it line buffered explicitly because the default is
384 # make it line buffered explicitly because the default is
384 # decided on first write(), where fout could be a pager.
385 # decided on first write(), where fout could be a pager.
385 if fp.isatty():
386 if fp.isatty():
386 bufsize = 1 # line buffered
387 bufsize = 1 # line buffered
387 else:
388 else:
388 bufsize = -1 # system default
389 bufsize = -1 # system default
389 newfp = os.fdopen(fp.fileno(), mode, bufsize)
390 newfp = os.fdopen(fp.fileno(), mode, bufsize)
390 setattr(ui, fn, newfp)
391 setattr(ui, fn, newfp)
391 setattr(self, cn, newfp)
392 setattr(self, cn, newfp)
392
393
393 self.cresult.write(struct.pack('>i', len(clientfds)))
394 self.cresult.write(struct.pack('>i', len(clientfds)))
394
395
395 def _saveio(self):
396 def _saveio(self):
396 if self._oldios:
397 if self._oldios:
397 return False
398 return False
398 ui = self.ui
399 ui = self.ui
399 for cn, fn, _mode in _iochannels:
400 for cn, fn, _mode in _iochannels:
400 ch = getattr(self, cn)
401 ch = getattr(self, cn)
401 fp = getattr(ui, fn)
402 fp = getattr(ui, fn)
402 fd = os.dup(fp.fileno())
403 fd = os.dup(fp.fileno())
403 self._oldios.append((ch, fp, fd))
404 self._oldios.append((ch, fp, fd))
404 return True
405 return True
405
406
406 def _restoreio(self):
407 def _restoreio(self):
407 ui = self.ui
408 ui = self.ui
408 for (ch, fp, fd), (cn, fn, _mode) in zip(self._oldios, _iochannels):
409 for (ch, fp, fd), (cn, fn, _mode) in zip(self._oldios, _iochannels):
409 newfp = getattr(ui, fn)
410 newfp = getattr(ui, fn)
410 # close newfp while it's associated with client; otherwise it
411 # close newfp while it's associated with client; otherwise it
411 # would be closed when newfp is deleted
412 # would be closed when newfp is deleted
412 if newfp is not fp:
413 if newfp is not fp:
413 newfp.close()
414 newfp.close()
414 # restore original fd: fp is open again
415 # restore original fd: fp is open again
415 os.dup2(fd, fp.fileno())
416 os.dup2(fd, fp.fileno())
416 os.close(fd)
417 os.close(fd)
417 setattr(self, cn, ch)
418 setattr(self, cn, ch)
418 setattr(ui, fn, fp)
419 setattr(ui, fn, fp)
419 del self._oldios[:]
420 del self._oldios[:]
420
421
421 def validate(self):
422 def validate(self):
422 """Reload the config and check if the server is up to date
423 """Reload the config and check if the server is up to date
423
424
424 Read a list of '\0' separated arguments.
425 Read a list of '\0' separated arguments.
425 Write a non-empty list of '\0' separated instruction strings or '\0'
426 Write a non-empty list of '\0' separated instruction strings or '\0'
426 if the list is empty.
427 if the list is empty.
427 An instruction string could be either:
428 An instruction string could be either:
428 - "unlink $path", the client should unlink the path to stop the
429 - "unlink $path", the client should unlink the path to stop the
429 outdated server.
430 outdated server.
430 - "redirect $path", the client should attempt to connect to $path
431 - "redirect $path", the client should attempt to connect to $path
431 first. If it does not work, start a new server. It implies
432 first. If it does not work, start a new server. It implies
432 "reconnect".
433 "reconnect".
433 - "exit $n", the client should exit directly with code n.
434 - "exit $n", the client should exit directly with code n.
434 This may happen if we cannot parse the config.
435 This may happen if we cannot parse the config.
435 - "reconnect", the client should close the connection and
436 - "reconnect", the client should close the connection and
436 reconnect.
437 reconnect.
437 If neither "reconnect" nor "redirect" is included in the instruction
438 If neither "reconnect" nor "redirect" is included in the instruction
438 list, the client can continue with this server after completing all
439 list, the client can continue with this server after completing all
439 the instructions.
440 the instructions.
440 """
441 """
441 args = self._readlist()
442 args = self._readlist()
442 try:
443 try:
443 self.ui, lui = _loadnewui(self.ui, args)
444 self.ui, lui = _loadnewui(self.ui, args)
444 except error.ParseError as inst:
445 except error.ParseError as inst:
445 dispatch._formatparse(self.ui.warn, inst)
446 dispatch._formatparse(self.ui.warn, inst)
446 self.ui.flush()
447 self.ui.flush()
447 self.cresult.write('exit 255')
448 self.cresult.write('exit 255')
448 return
449 return
449 newhash = hashstate.fromui(lui, self.hashstate.mtimepaths)
450 newhash = hashstate.fromui(lui, self.hashstate.mtimepaths)
450 insts = []
451 insts = []
451 if newhash.mtimehash != self.hashstate.mtimehash:
452 if newhash.mtimehash != self.hashstate.mtimehash:
452 addr = _hashaddress(self.baseaddress, self.hashstate.confighash)
453 addr = _hashaddress(self.baseaddress, self.hashstate.confighash)
453 insts.append('unlink %s' % addr)
454 insts.append('unlink %s' % addr)
454 # mtimehash is empty if one or more extensions fail to load.
455 # mtimehash is empty if one or more extensions fail to load.
455 # to be compatible with hg, still serve the client this time.
456 # to be compatible with hg, still serve the client this time.
456 if self.hashstate.mtimehash:
457 if self.hashstate.mtimehash:
457 insts.append('reconnect')
458 insts.append('reconnect')
458 if newhash.confighash != self.hashstate.confighash:
459 if newhash.confighash != self.hashstate.confighash:
459 addr = _hashaddress(self.baseaddress, newhash.confighash)
460 addr = _hashaddress(self.baseaddress, newhash.confighash)
460 insts.append('redirect %s' % addr)
461 insts.append('redirect %s' % addr)
461 _log('validate: %s\n' % insts)
462 _log('validate: %s\n' % insts)
462 self.cresult.write('\0'.join(insts) or '\0')
463 self.cresult.write('\0'.join(insts) or '\0')
463
464
464 def chdir(self):
465 def chdir(self):
465 """Change current directory
466 """Change current directory
466
467
467 Note that the behavior of --cwd option is bit different from this.
468 Note that the behavior of --cwd option is bit different from this.
468 It does not affect --config parameter.
469 It does not affect --config parameter.
469 """
470 """
470 path = self._readstr()
471 path = self._readstr()
471 if not path:
472 if not path:
472 return
473 return
473 _log('chdir to %r\n' % path)
474 _log('chdir to %r\n' % path)
474 os.chdir(path)
475 os.chdir(path)
475
476
476 def setumask(self):
477 def setumask(self):
477 """Change umask"""
478 """Change umask"""
478 mask = struct.unpack('>I', self._read(4))[0]
479 mask = struct.unpack('>I', self._read(4))[0]
479 _log('setumask %r\n' % mask)
480 _log('setumask %r\n' % mask)
480 os.umask(mask)
481 os.umask(mask)
481
482
482 def getpager(self):
483 def getpager(self):
483 """Read cmdargs and write pager command to r-channel if enabled
484 """Read cmdargs and write pager command to r-channel if enabled
484
485
485 If pager isn't enabled, this writes '\0' because channeledoutput
486 If pager isn't enabled, this writes '\0' because channeledoutput
486 does not allow to write empty data.
487 does not allow to write empty data.
487 """
488 """
488 args = self._readlist()
489 args = self._readlist()
489 try:
490 try:
490 cmd, _func, args, options, _cmdoptions = dispatch._parse(self.ui,
491 cmd, _func, args, options, _cmdoptions = dispatch._parse(self.ui,
491 args)
492 args)
492 except (error.Abort, error.AmbiguousCommand, error.CommandError,
493 except (error.Abort, error.AmbiguousCommand, error.CommandError,
493 error.UnknownCommand):
494 error.UnknownCommand):
494 cmd = None
495 cmd = None
495 options = {}
496 options = {}
496 if not cmd or 'pager' not in options:
497 if not cmd or 'pager' not in options:
497 self.cresult.write('\0')
498 self.cresult.write('\0')
498 return
499 return
499
500
500 pagercmd = _setuppagercmd(self.ui, options, cmd)
501 pagercmd = _setuppagercmd(self.ui, options, cmd)
501 if pagercmd:
502 if pagercmd:
502 # Python's SIGPIPE is SIG_IGN by default. change to SIG_DFL so
503 # Python's SIGPIPE is SIG_IGN by default. change to SIG_DFL so
503 # we can exit if the pipe to the pager is closed
504 # we can exit if the pipe to the pager is closed
504 if util.safehasattr(signal, 'SIGPIPE') and \
505 if util.safehasattr(signal, 'SIGPIPE') and \
505 signal.getsignal(signal.SIGPIPE) == signal.SIG_IGN:
506 signal.getsignal(signal.SIGPIPE) == signal.SIG_IGN:
506 signal.signal(signal.SIGPIPE, signal.SIG_DFL)
507 signal.signal(signal.SIGPIPE, signal.SIG_DFL)
507 self.cresult.write(pagercmd)
508 self.cresult.write(pagercmd)
508 else:
509 else:
509 self.cresult.write('\0')
510 self.cresult.write('\0')
510
511
511 def setenv(self):
512 def setenv(self):
512 """Clear and update os.environ
513 """Clear and update os.environ
513
514
514 Note that not all variables can make an effect on the running process.
515 Note that not all variables can make an effect on the running process.
515 """
516 """
516 l = self._readlist()
517 l = self._readlist()
517 try:
518 try:
518 newenv = dict(s.split('=', 1) for s in l)
519 newenv = dict(s.split('=', 1) for s in l)
519 except ValueError:
520 except ValueError:
520 raise ValueError('unexpected value in setenv request')
521 raise ValueError('unexpected value in setenv request')
521 _log('setenv: %r\n' % sorted(newenv.keys()))
522 _log('setenv: %r\n' % sorted(newenv.keys()))
522 os.environ.clear()
523 os.environ.clear()
523 os.environ.update(newenv)
524 os.environ.update(newenv)
524
525
525 capabilities = commandserver.server.capabilities.copy()
526 capabilities = commandserver.server.capabilities.copy()
526 capabilities.update({'attachio': attachio,
527 capabilities.update({'attachio': attachio,
527 'chdir': chdir,
528 'chdir': chdir,
528 'getpager': getpager,
529 'getpager': getpager,
529 'setenv': setenv,
530 'setenv': setenv,
530 'setumask': setumask})
531 'setumask': setumask})
531
532
532 def _tempaddress(address):
533 def _tempaddress(address):
533 return '%s.%d.tmp' % (address, os.getpid())
534 return '%s.%d.tmp' % (address, os.getpid())
534
535
535 def _hashaddress(address, hashstr):
536 def _hashaddress(address, hashstr):
536 return '%s-%s' % (address, hashstr)
537 return '%s-%s' % (address, hashstr)
537
538
538 class chgunixservicehandler(object):
539 class chgunixservicehandler(object):
539 """Set of operations for chg services"""
540 """Set of operations for chg services"""
540
541
541 pollinterval = 1 # [sec]
542 pollinterval = 1 # [sec]
542
543
543 def __init__(self, ui):
544 def __init__(self, ui):
544 self.ui = ui
545 self.ui = ui
545 self._idletimeout = ui.configint('chgserver', 'idletimeout', 3600)
546 self._idletimeout = ui.configint('chgserver', 'idletimeout', 3600)
546 self._lastactive = time.time()
547 self._lastactive = time.time()
547
548
548 def bindsocket(self, sock, address):
549 def bindsocket(self, sock, address):
549 self._inithashstate(address)
550 self._inithashstate(address)
550 self._checkextensions()
551 self._checkextensions()
551 self._bind(sock)
552 self._bind(sock)
552 self._createsymlink()
553 self._createsymlink()
553
554
554 def _inithashstate(self, address):
555 def _inithashstate(self, address):
555 self._baseaddress = address
556 self._baseaddress = address
556 if self.ui.configbool('chgserver', 'skiphash', False):
557 if self.ui.configbool('chgserver', 'skiphash', False):
557 self._hashstate = None
558 self._hashstate = None
558 self._realaddress = address
559 self._realaddress = address
559 return
560 return
560 self._hashstate = hashstate.fromui(self.ui)
561 self._hashstate = hashstate.fromui(self.ui)
561 self._realaddress = _hashaddress(address, self._hashstate.confighash)
562 self._realaddress = _hashaddress(address, self._hashstate.confighash)
562
563
563 def _checkextensions(self):
564 def _checkextensions(self):
564 if not self._hashstate:
565 if not self._hashstate:
565 return
566 return
566 if extensions.notloaded():
567 if extensions.notloaded():
567 # one or more extensions failed to load. mtimehash becomes
568 # one or more extensions failed to load. mtimehash becomes
568 # meaningless because we do not know the paths of those extensions.
569 # meaningless because we do not know the paths of those extensions.
569 # set mtimehash to an illegal hash value to invalidate the server.
570 # set mtimehash to an illegal hash value to invalidate the server.
570 self._hashstate.mtimehash = ''
571 self._hashstate.mtimehash = ''
571
572
572 def _bind(self, sock):
573 def _bind(self, sock):
573 # use a unique temp address so we can stat the file and do ownership
574 # use a unique temp address so we can stat the file and do ownership
574 # check later
575 # check later
575 tempaddress = _tempaddress(self._realaddress)
576 tempaddress = _tempaddress(self._realaddress)
576 util.bindunixsocket(sock, tempaddress)
577 util.bindunixsocket(sock, tempaddress)
577 self._socketstat = os.stat(tempaddress)
578 self._socketstat = os.stat(tempaddress)
578 # rename will replace the old socket file if exists atomically. the
579 # rename will replace the old socket file if exists atomically. the
579 # old server will detect ownership change and exit.
580 # old server will detect ownership change and exit.
580 util.rename(tempaddress, self._realaddress)
581 util.rename(tempaddress, self._realaddress)
581
582
582 def _createsymlink(self):
583 def _createsymlink(self):
583 if self._baseaddress == self._realaddress:
584 if self._baseaddress == self._realaddress:
584 return
585 return
585 tempaddress = _tempaddress(self._baseaddress)
586 tempaddress = _tempaddress(self._baseaddress)
586 os.symlink(os.path.basename(self._realaddress), tempaddress)
587 os.symlink(os.path.basename(self._realaddress), tempaddress)
587 util.rename(tempaddress, self._baseaddress)
588 util.rename(tempaddress, self._baseaddress)
588
589
589 def _issocketowner(self):
590 def _issocketowner(self):
590 try:
591 try:
591 stat = os.stat(self._realaddress)
592 stat = os.stat(self._realaddress)
592 return (stat.st_ino == self._socketstat.st_ino and
593 return (stat.st_ino == self._socketstat.st_ino and
593 stat.st_mtime == self._socketstat.st_mtime)
594 stat.st_mtime == self._socketstat.st_mtime)
594 except OSError:
595 except OSError:
595 return False
596 return False
596
597
597 def unlinksocket(self, address):
598 def unlinksocket(self, address):
598 if not self._issocketowner():
599 if not self._issocketowner():
599 return
600 return
600 # it is possible to have a race condition here that we may
601 # it is possible to have a race condition here that we may
601 # remove another server's socket file. but that's okay
602 # remove another server's socket file. but that's okay
602 # since that server will detect and exit automatically and
603 # since that server will detect and exit automatically and
603 # the client will start a new server on demand.
604 # the client will start a new server on demand.
604 try:
605 try:
605 os.unlink(self._realaddress)
606 os.unlink(self._realaddress)
606 except OSError as exc:
607 except OSError as exc:
607 if exc.errno != errno.ENOENT:
608 if exc.errno != errno.ENOENT:
608 raise
609 raise
609
610
610 def printbanner(self, address):
611 def printbanner(self, address):
611 # no "listening at" message should be printed to simulate hg behavior
612 # no "listening at" message should be printed to simulate hg behavior
612 pass
613 pass
613
614
614 def shouldexit(self):
615 def shouldexit(self):
615 if not self._issocketowner():
616 if not self._issocketowner():
616 self.ui.debug('%s is not owned, exiting.\n' % self._realaddress)
617 self.ui.debug('%s is not owned, exiting.\n' % self._realaddress)
617 return True
618 return True
618 if time.time() - self._lastactive > self._idletimeout:
619 if time.time() - self._lastactive > self._idletimeout:
619 self.ui.debug('being idle too long. exiting.\n')
620 self.ui.debug('being idle too long. exiting.\n')
620 return True
621 return True
621 return False
622 return False
622
623
623 def newconnection(self):
624 def newconnection(self):
624 self._lastactive = time.time()
625 self._lastactive = time.time()
625
626
626 def createcmdserver(self, repo, conn, fin, fout):
627 def createcmdserver(self, repo, conn, fin, fout):
627 return chgcmdserver(self.ui, repo, fin, fout, conn,
628 return chgcmdserver(self.ui, repo, fin, fout, conn,
628 self._hashstate, self._baseaddress)
629 self._hashstate, self._baseaddress)
629
630
630 def chgunixservice(ui, repo, opts):
631 def chgunixservice(ui, repo, opts):
631 if repo:
632 if repo:
632 # one chgserver can serve multiple repos. drop repo information
633 # one chgserver can serve multiple repos. drop repo information
633 ui.setconfig('bundle', 'mainreporoot', '', 'repo')
634 ui.setconfig('bundle', 'mainreporoot', '', 'repo')
634 h = chgunixservicehandler(ui)
635 h = chgunixservicehandler(ui)
635 return commandserver.unixforkingservice(ui, repo=None, opts=opts, handler=h)
636 return commandserver.unixforkingservice(ui, repo=None, opts=opts, handler=h)
636
637
637 def uisetup(ui):
638 def uisetup(ui):
638 commandserver._servicemap['chgunix'] = chgunixservice
639 server._cmdservicemap['chgunix'] = chgunixservice
639
640
640 # CHGINTERNALMARK is temporarily set by chg client to detect if chg will
641 # CHGINTERNALMARK is temporarily set by chg client to detect if chg will
641 # start another chg. drop it to avoid possible side effects.
642 # start another chg. drop it to avoid possible side effects.
642 if 'CHGINTERNALMARK' in os.environ:
643 if 'CHGINTERNALMARK' in os.environ:
643 del os.environ['CHGINTERNALMARK']
644 del os.environ['CHGINTERNALMARK']
@@ -1,7093 +1,7092 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 operator
12 import operator
13 import os
13 import os
14 import random
14 import random
15 import re
15 import re
16 import shlex
16 import shlex
17 import socket
17 import socket
18 import string
18 import string
19 import sys
19 import sys
20 import tempfile
20 import tempfile
21 import time
21 import time
22
22
23 from .i18n import _
23 from .i18n import _
24 from .node import (
24 from .node import (
25 bin,
25 bin,
26 hex,
26 hex,
27 nullhex,
27 nullhex,
28 nullid,
28 nullid,
29 nullrev,
29 nullrev,
30 short,
30 short,
31 )
31 )
32 from . import (
32 from . import (
33 archival,
33 archival,
34 bookmarks,
34 bookmarks,
35 bundle2,
35 bundle2,
36 changegroup,
36 changegroup,
37 cmdutil,
37 cmdutil,
38 commandserver,
39 copies,
38 copies,
40 dagparser,
39 dagparser,
41 dagutil,
40 dagutil,
42 destutil,
41 destutil,
43 dirstateguard,
42 dirstateguard,
44 discovery,
43 discovery,
45 encoding,
44 encoding,
46 error,
45 error,
47 exchange,
46 exchange,
48 extensions,
47 extensions,
49 fileset,
48 fileset,
50 formatter,
49 formatter,
51 graphmod,
50 graphmod,
52 hbisect,
51 hbisect,
53 help,
52 help,
54 hg,
53 hg,
55 hgweb,
54 hgweb,
56 localrepo,
55 localrepo,
57 lock as lockmod,
56 lock as lockmod,
58 merge as mergemod,
57 merge as mergemod,
59 minirst,
58 minirst,
60 obsolete,
59 obsolete,
61 patch,
60 patch,
62 phases,
61 phases,
63 policy,
62 policy,
64 pvec,
63 pvec,
65 pycompat,
64 pycompat,
66 repair,
65 repair,
67 revlog,
66 revlog,
68 revset,
67 revset,
69 scmutil,
68 scmutil,
70 server,
69 server,
71 setdiscovery,
70 setdiscovery,
72 sshserver,
71 sshserver,
73 sslutil,
72 sslutil,
74 streamclone,
73 streamclone,
75 templatekw,
74 templatekw,
76 templater,
75 templater,
77 treediscovery,
76 treediscovery,
78 ui as uimod,
77 ui as uimod,
79 util,
78 util,
80 )
79 )
81
80
82 release = lockmod.release
81 release = lockmod.release
83
82
84 table = {}
83 table = {}
85
84
86 command = cmdutil.command(table)
85 command = cmdutil.command(table)
87
86
88 # label constants
87 # label constants
89 # until 3.5, bookmarks.current was the advertised name, not
88 # until 3.5, bookmarks.current was the advertised name, not
90 # bookmarks.active, so we must use both to avoid breaking old
89 # bookmarks.active, so we must use both to avoid breaking old
91 # custom styles
90 # custom styles
92 activebookmarklabel = 'bookmarks.active bookmarks.current'
91 activebookmarklabel = 'bookmarks.active bookmarks.current'
93
92
94 # common command options
93 # common command options
95
94
96 globalopts = [
95 globalopts = [
97 ('R', 'repository', '',
96 ('R', 'repository', '',
98 _('repository root directory or name of overlay bundle file'),
97 _('repository root directory or name of overlay bundle file'),
99 _('REPO')),
98 _('REPO')),
100 ('', 'cwd', '',
99 ('', 'cwd', '',
101 _('change working directory'), _('DIR')),
100 _('change working directory'), _('DIR')),
102 ('y', 'noninteractive', None,
101 ('y', 'noninteractive', None,
103 _('do not prompt, automatically pick the first choice for all prompts')),
102 _('do not prompt, automatically pick the first choice for all prompts')),
104 ('q', 'quiet', None, _('suppress output')),
103 ('q', 'quiet', None, _('suppress output')),
105 ('v', 'verbose', None, _('enable additional output')),
104 ('v', 'verbose', None, _('enable additional output')),
106 ('', 'config', [],
105 ('', 'config', [],
107 _('set/override config option (use \'section.name=value\')'),
106 _('set/override config option (use \'section.name=value\')'),
108 _('CONFIG')),
107 _('CONFIG')),
109 ('', 'debug', None, _('enable debugging output')),
108 ('', 'debug', None, _('enable debugging output')),
110 ('', 'debugger', None, _('start debugger')),
109 ('', 'debugger', None, _('start debugger')),
111 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
110 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
112 _('ENCODE')),
111 _('ENCODE')),
113 ('', 'encodingmode', encoding.encodingmode,
112 ('', 'encodingmode', encoding.encodingmode,
114 _('set the charset encoding mode'), _('MODE')),
113 _('set the charset encoding mode'), _('MODE')),
115 ('', 'traceback', None, _('always print a traceback on exception')),
114 ('', 'traceback', None, _('always print a traceback on exception')),
116 ('', 'time', None, _('time how long the command takes')),
115 ('', 'time', None, _('time how long the command takes')),
117 ('', 'profile', None, _('print command execution profile')),
116 ('', 'profile', None, _('print command execution profile')),
118 ('', 'version', None, _('output version information and exit')),
117 ('', 'version', None, _('output version information and exit')),
119 ('h', 'help', None, _('display help and exit')),
118 ('h', 'help', None, _('display help and exit')),
120 ('', 'hidden', False, _('consider hidden changesets')),
119 ('', 'hidden', False, _('consider hidden changesets')),
121 ]
120 ]
122
121
123 dryrunopts = [('n', 'dry-run', None,
122 dryrunopts = [('n', 'dry-run', None,
124 _('do not perform actions, just print output'))]
123 _('do not perform actions, just print output'))]
125
124
126 remoteopts = [
125 remoteopts = [
127 ('e', 'ssh', '',
126 ('e', 'ssh', '',
128 _('specify ssh command to use'), _('CMD')),
127 _('specify ssh command to use'), _('CMD')),
129 ('', 'remotecmd', '',
128 ('', 'remotecmd', '',
130 _('specify hg command to run on the remote side'), _('CMD')),
129 _('specify hg command to run on the remote side'), _('CMD')),
131 ('', 'insecure', None,
130 ('', 'insecure', None,
132 _('do not verify server certificate (ignoring web.cacerts config)')),
131 _('do not verify server certificate (ignoring web.cacerts config)')),
133 ]
132 ]
134
133
135 walkopts = [
134 walkopts = [
136 ('I', 'include', [],
135 ('I', 'include', [],
137 _('include names matching the given patterns'), _('PATTERN')),
136 _('include names matching the given patterns'), _('PATTERN')),
138 ('X', 'exclude', [],
137 ('X', 'exclude', [],
139 _('exclude names matching the given patterns'), _('PATTERN')),
138 _('exclude names matching the given patterns'), _('PATTERN')),
140 ]
139 ]
141
140
142 commitopts = [
141 commitopts = [
143 ('m', 'message', '',
142 ('m', 'message', '',
144 _('use text as commit message'), _('TEXT')),
143 _('use text as commit message'), _('TEXT')),
145 ('l', 'logfile', '',
144 ('l', 'logfile', '',
146 _('read commit message from file'), _('FILE')),
145 _('read commit message from file'), _('FILE')),
147 ]
146 ]
148
147
149 commitopts2 = [
148 commitopts2 = [
150 ('d', 'date', '',
149 ('d', 'date', '',
151 _('record the specified date as commit date'), _('DATE')),
150 _('record the specified date as commit date'), _('DATE')),
152 ('u', 'user', '',
151 ('u', 'user', '',
153 _('record the specified user as committer'), _('USER')),
152 _('record the specified user as committer'), _('USER')),
154 ]
153 ]
155
154
156 # hidden for now
155 # hidden for now
157 formatteropts = [
156 formatteropts = [
158 ('T', 'template', '',
157 ('T', 'template', '',
159 _('display with template (EXPERIMENTAL)'), _('TEMPLATE')),
158 _('display with template (EXPERIMENTAL)'), _('TEMPLATE')),
160 ]
159 ]
161
160
162 templateopts = [
161 templateopts = [
163 ('', 'style', '',
162 ('', 'style', '',
164 _('display using template map file (DEPRECATED)'), _('STYLE')),
163 _('display using template map file (DEPRECATED)'), _('STYLE')),
165 ('T', 'template', '',
164 ('T', 'template', '',
166 _('display with template'), _('TEMPLATE')),
165 _('display with template'), _('TEMPLATE')),
167 ]
166 ]
168
167
169 logopts = [
168 logopts = [
170 ('p', 'patch', None, _('show patch')),
169 ('p', 'patch', None, _('show patch')),
171 ('g', 'git', None, _('use git extended diff format')),
170 ('g', 'git', None, _('use git extended diff format')),
172 ('l', 'limit', '',
171 ('l', 'limit', '',
173 _('limit number of changes displayed'), _('NUM')),
172 _('limit number of changes displayed'), _('NUM')),
174 ('M', 'no-merges', None, _('do not show merges')),
173 ('M', 'no-merges', None, _('do not show merges')),
175 ('', 'stat', None, _('output diffstat-style summary of changes')),
174 ('', 'stat', None, _('output diffstat-style summary of changes')),
176 ('G', 'graph', None, _("show the revision DAG")),
175 ('G', 'graph', None, _("show the revision DAG")),
177 ] + templateopts
176 ] + templateopts
178
177
179 diffopts = [
178 diffopts = [
180 ('a', 'text', None, _('treat all files as text')),
179 ('a', 'text', None, _('treat all files as text')),
181 ('g', 'git', None, _('use git extended diff format')),
180 ('g', 'git', None, _('use git extended diff format')),
182 ('', 'nodates', None, _('omit dates from diff headers'))
181 ('', 'nodates', None, _('omit dates from diff headers'))
183 ]
182 ]
184
183
185 diffwsopts = [
184 diffwsopts = [
186 ('w', 'ignore-all-space', None,
185 ('w', 'ignore-all-space', None,
187 _('ignore white space when comparing lines')),
186 _('ignore white space when comparing lines')),
188 ('b', 'ignore-space-change', None,
187 ('b', 'ignore-space-change', None,
189 _('ignore changes in the amount of white space')),
188 _('ignore changes in the amount of white space')),
190 ('B', 'ignore-blank-lines', None,
189 ('B', 'ignore-blank-lines', None,
191 _('ignore changes whose lines are all blank')),
190 _('ignore changes whose lines are all blank')),
192 ]
191 ]
193
192
194 diffopts2 = [
193 diffopts2 = [
195 ('', 'noprefix', None, _('omit a/ and b/ prefixes from filenames')),
194 ('', 'noprefix', None, _('omit a/ and b/ prefixes from filenames')),
196 ('p', 'show-function', None, _('show which function each change is in')),
195 ('p', 'show-function', None, _('show which function each change is in')),
197 ('', 'reverse', None, _('produce a diff that undoes the changes')),
196 ('', 'reverse', None, _('produce a diff that undoes the changes')),
198 ] + diffwsopts + [
197 ] + diffwsopts + [
199 ('U', 'unified', '',
198 ('U', 'unified', '',
200 _('number of lines of context to show'), _('NUM')),
199 _('number of lines of context to show'), _('NUM')),
201 ('', 'stat', None, _('output diffstat-style summary of changes')),
200 ('', 'stat', None, _('output diffstat-style summary of changes')),
202 ('', 'root', '', _('produce diffs relative to subdirectory'), _('DIR')),
201 ('', 'root', '', _('produce diffs relative to subdirectory'), _('DIR')),
203 ]
202 ]
204
203
205 mergetoolopts = [
204 mergetoolopts = [
206 ('t', 'tool', '', _('specify merge tool')),
205 ('t', 'tool', '', _('specify merge tool')),
207 ]
206 ]
208
207
209 similarityopts = [
208 similarityopts = [
210 ('s', 'similarity', '',
209 ('s', 'similarity', '',
211 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
210 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
212 ]
211 ]
213
212
214 subrepoopts = [
213 subrepoopts = [
215 ('S', 'subrepos', None,
214 ('S', 'subrepos', None,
216 _('recurse into subrepositories'))
215 _('recurse into subrepositories'))
217 ]
216 ]
218
217
219 debugrevlogopts = [
218 debugrevlogopts = [
220 ('c', 'changelog', False, _('open changelog')),
219 ('c', 'changelog', False, _('open changelog')),
221 ('m', 'manifest', False, _('open manifest')),
220 ('m', 'manifest', False, _('open manifest')),
222 ('', 'dir', '', _('open directory manifest')),
221 ('', 'dir', '', _('open directory manifest')),
223 ]
222 ]
224
223
225 # Commands start here, listed alphabetically
224 # Commands start here, listed alphabetically
226
225
227 @command('^add',
226 @command('^add',
228 walkopts + subrepoopts + dryrunopts,
227 walkopts + subrepoopts + dryrunopts,
229 _('[OPTION]... [FILE]...'),
228 _('[OPTION]... [FILE]...'),
230 inferrepo=True)
229 inferrepo=True)
231 def add(ui, repo, *pats, **opts):
230 def add(ui, repo, *pats, **opts):
232 """add the specified files on the next commit
231 """add the specified files on the next commit
233
232
234 Schedule files to be version controlled and added to the
233 Schedule files to be version controlled and added to the
235 repository.
234 repository.
236
235
237 The files will be added to the repository at the next commit. To
236 The files will be added to the repository at the next commit. To
238 undo an add before that, see :hg:`forget`.
237 undo an add before that, see :hg:`forget`.
239
238
240 If no names are given, add all files to the repository (except
239 If no names are given, add all files to the repository (except
241 files matching ``.hgignore``).
240 files matching ``.hgignore``).
242
241
243 .. container:: verbose
242 .. container:: verbose
244
243
245 Examples:
244 Examples:
246
245
247 - New (unknown) files are added
246 - New (unknown) files are added
248 automatically by :hg:`add`::
247 automatically by :hg:`add`::
249
248
250 $ ls
249 $ ls
251 foo.c
250 foo.c
252 $ hg status
251 $ hg status
253 ? foo.c
252 ? foo.c
254 $ hg add
253 $ hg add
255 adding foo.c
254 adding foo.c
256 $ hg status
255 $ hg status
257 A foo.c
256 A foo.c
258
257
259 - Specific files to be added can be specified::
258 - Specific files to be added can be specified::
260
259
261 $ ls
260 $ ls
262 bar.c foo.c
261 bar.c foo.c
263 $ hg status
262 $ hg status
264 ? bar.c
263 ? bar.c
265 ? foo.c
264 ? foo.c
266 $ hg add bar.c
265 $ hg add bar.c
267 $ hg status
266 $ hg status
268 A bar.c
267 A bar.c
269 ? foo.c
268 ? foo.c
270
269
271 Returns 0 if all files are successfully added.
270 Returns 0 if all files are successfully added.
272 """
271 """
273
272
274 m = scmutil.match(repo[None], pats, opts)
273 m = scmutil.match(repo[None], pats, opts)
275 rejected = cmdutil.add(ui, repo, m, "", False, **opts)
274 rejected = cmdutil.add(ui, repo, m, "", False, **opts)
276 return rejected and 1 or 0
275 return rejected and 1 or 0
277
276
278 @command('addremove',
277 @command('addremove',
279 similarityopts + subrepoopts + walkopts + dryrunopts,
278 similarityopts + subrepoopts + walkopts + dryrunopts,
280 _('[OPTION]... [FILE]...'),
279 _('[OPTION]... [FILE]...'),
281 inferrepo=True)
280 inferrepo=True)
282 def addremove(ui, repo, *pats, **opts):
281 def addremove(ui, repo, *pats, **opts):
283 """add all new files, delete all missing files
282 """add all new files, delete all missing files
284
283
285 Add all new files and remove all missing files from the
284 Add all new files and remove all missing files from the
286 repository.
285 repository.
287
286
288 Unless names are given, new files are ignored if they match any of
287 Unless names are given, new files are ignored if they match any of
289 the patterns in ``.hgignore``. As with add, these changes take
288 the patterns in ``.hgignore``. As with add, these changes take
290 effect at the next commit.
289 effect at the next commit.
291
290
292 Use the -s/--similarity option to detect renamed files. This
291 Use the -s/--similarity option to detect renamed files. This
293 option takes a percentage between 0 (disabled) and 100 (files must
292 option takes a percentage between 0 (disabled) and 100 (files must
294 be identical) as its parameter. With a parameter greater than 0,
293 be identical) as its parameter. With a parameter greater than 0,
295 this compares every removed file with every added file and records
294 this compares every removed file with every added file and records
296 those similar enough as renames. Detecting renamed files this way
295 those similar enough as renames. Detecting renamed files this way
297 can be expensive. After using this option, :hg:`status -C` can be
296 can be expensive. After using this option, :hg:`status -C` can be
298 used to check which files were identified as moved or renamed. If
297 used to check which files were identified as moved or renamed. If
299 not specified, -s/--similarity defaults to 100 and only renames of
298 not specified, -s/--similarity defaults to 100 and only renames of
300 identical files are detected.
299 identical files are detected.
301
300
302 .. container:: verbose
301 .. container:: verbose
303
302
304 Examples:
303 Examples:
305
304
306 - A number of files (bar.c and foo.c) are new,
305 - A number of files (bar.c and foo.c) are new,
307 while foobar.c has been removed (without using :hg:`remove`)
306 while foobar.c has been removed (without using :hg:`remove`)
308 from the repository::
307 from the repository::
309
308
310 $ ls
309 $ ls
311 bar.c foo.c
310 bar.c foo.c
312 $ hg status
311 $ hg status
313 ! foobar.c
312 ! foobar.c
314 ? bar.c
313 ? bar.c
315 ? foo.c
314 ? foo.c
316 $ hg addremove
315 $ hg addremove
317 adding bar.c
316 adding bar.c
318 adding foo.c
317 adding foo.c
319 removing foobar.c
318 removing foobar.c
320 $ hg status
319 $ hg status
321 A bar.c
320 A bar.c
322 A foo.c
321 A foo.c
323 R foobar.c
322 R foobar.c
324
323
325 - A file foobar.c was moved to foo.c without using :hg:`rename`.
324 - A file foobar.c was moved to foo.c without using :hg:`rename`.
326 Afterwards, it was edited slightly::
325 Afterwards, it was edited slightly::
327
326
328 $ ls
327 $ ls
329 foo.c
328 foo.c
330 $ hg status
329 $ hg status
331 ! foobar.c
330 ! foobar.c
332 ? foo.c
331 ? foo.c
333 $ hg addremove --similarity 90
332 $ hg addremove --similarity 90
334 removing foobar.c
333 removing foobar.c
335 adding foo.c
334 adding foo.c
336 recording removal of foobar.c as rename to foo.c (94% similar)
335 recording removal of foobar.c as rename to foo.c (94% similar)
337 $ hg status -C
336 $ hg status -C
338 A foo.c
337 A foo.c
339 foobar.c
338 foobar.c
340 R foobar.c
339 R foobar.c
341
340
342 Returns 0 if all files are successfully added.
341 Returns 0 if all files are successfully added.
343 """
342 """
344 try:
343 try:
345 sim = float(opts.get('similarity') or 100)
344 sim = float(opts.get('similarity') or 100)
346 except ValueError:
345 except ValueError:
347 raise error.Abort(_('similarity must be a number'))
346 raise error.Abort(_('similarity must be a number'))
348 if sim < 0 or sim > 100:
347 if sim < 0 or sim > 100:
349 raise error.Abort(_('similarity must be between 0 and 100'))
348 raise error.Abort(_('similarity must be between 0 and 100'))
350 matcher = scmutil.match(repo[None], pats, opts)
349 matcher = scmutil.match(repo[None], pats, opts)
351 return scmutil.addremove(repo, matcher, "", opts, similarity=sim / 100.0)
350 return scmutil.addremove(repo, matcher, "", opts, similarity=sim / 100.0)
352
351
353 @command('^annotate|blame',
352 @command('^annotate|blame',
354 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
353 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
355 ('', 'follow', None,
354 ('', 'follow', None,
356 _('follow copies/renames and list the filename (DEPRECATED)')),
355 _('follow copies/renames and list the filename (DEPRECATED)')),
357 ('', 'no-follow', None, _("don't follow copies and renames")),
356 ('', 'no-follow', None, _("don't follow copies and renames")),
358 ('a', 'text', None, _('treat all files as text')),
357 ('a', 'text', None, _('treat all files as text')),
359 ('u', 'user', None, _('list the author (long with -v)')),
358 ('u', 'user', None, _('list the author (long with -v)')),
360 ('f', 'file', None, _('list the filename')),
359 ('f', 'file', None, _('list the filename')),
361 ('d', 'date', None, _('list the date (short with -q)')),
360 ('d', 'date', None, _('list the date (short with -q)')),
362 ('n', 'number', None, _('list the revision number (default)')),
361 ('n', 'number', None, _('list the revision number (default)')),
363 ('c', 'changeset', None, _('list the changeset')),
362 ('c', 'changeset', None, _('list the changeset')),
364 ('l', 'line-number', None, _('show line number at the first appearance'))
363 ('l', 'line-number', None, _('show line number at the first appearance'))
365 ] + diffwsopts + walkopts + formatteropts,
364 ] + diffwsopts + walkopts + formatteropts,
366 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
365 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
367 inferrepo=True)
366 inferrepo=True)
368 def annotate(ui, repo, *pats, **opts):
367 def annotate(ui, repo, *pats, **opts):
369 """show changeset information by line for each file
368 """show changeset information by line for each file
370
369
371 List changes in files, showing the revision id responsible for
370 List changes in files, showing the revision id responsible for
372 each line.
371 each line.
373
372
374 This command is useful for discovering when a change was made and
373 This command is useful for discovering when a change was made and
375 by whom.
374 by whom.
376
375
377 If you include --file, --user, or --date, the revision number is
376 If you include --file, --user, or --date, the revision number is
378 suppressed unless you also include --number.
377 suppressed unless you also include --number.
379
378
380 Without the -a/--text option, annotate will avoid processing files
379 Without the -a/--text option, annotate will avoid processing files
381 it detects as binary. With -a, annotate will annotate the file
380 it detects as binary. With -a, annotate will annotate the file
382 anyway, although the results will probably be neither useful
381 anyway, although the results will probably be neither useful
383 nor desirable.
382 nor desirable.
384
383
385 Returns 0 on success.
384 Returns 0 on success.
386 """
385 """
387 if not pats:
386 if not pats:
388 raise error.Abort(_('at least one filename or pattern is required'))
387 raise error.Abort(_('at least one filename or pattern is required'))
389
388
390 if opts.get('follow'):
389 if opts.get('follow'):
391 # --follow is deprecated and now just an alias for -f/--file
390 # --follow is deprecated and now just an alias for -f/--file
392 # to mimic the behavior of Mercurial before version 1.5
391 # to mimic the behavior of Mercurial before version 1.5
393 opts['file'] = True
392 opts['file'] = True
394
393
395 ctx = scmutil.revsingle(repo, opts.get('rev'))
394 ctx = scmutil.revsingle(repo, opts.get('rev'))
396
395
397 fm = ui.formatter('annotate', opts)
396 fm = ui.formatter('annotate', opts)
398 if ui.quiet:
397 if ui.quiet:
399 datefunc = util.shortdate
398 datefunc = util.shortdate
400 else:
399 else:
401 datefunc = util.datestr
400 datefunc = util.datestr
402 if ctx.rev() is None:
401 if ctx.rev() is None:
403 def hexfn(node):
402 def hexfn(node):
404 if node is None:
403 if node is None:
405 return None
404 return None
406 else:
405 else:
407 return fm.hexfunc(node)
406 return fm.hexfunc(node)
408 if opts.get('changeset'):
407 if opts.get('changeset'):
409 # omit "+" suffix which is appended to node hex
408 # omit "+" suffix which is appended to node hex
410 def formatrev(rev):
409 def formatrev(rev):
411 if rev is None:
410 if rev is None:
412 return '%d' % ctx.p1().rev()
411 return '%d' % ctx.p1().rev()
413 else:
412 else:
414 return '%d' % rev
413 return '%d' % rev
415 else:
414 else:
416 def formatrev(rev):
415 def formatrev(rev):
417 if rev is None:
416 if rev is None:
418 return '%d+' % ctx.p1().rev()
417 return '%d+' % ctx.p1().rev()
419 else:
418 else:
420 return '%d ' % rev
419 return '%d ' % rev
421 def formathex(hex):
420 def formathex(hex):
422 if hex is None:
421 if hex is None:
423 return '%s+' % fm.hexfunc(ctx.p1().node())
422 return '%s+' % fm.hexfunc(ctx.p1().node())
424 else:
423 else:
425 return '%s ' % hex
424 return '%s ' % hex
426 else:
425 else:
427 hexfn = fm.hexfunc
426 hexfn = fm.hexfunc
428 formatrev = formathex = str
427 formatrev = formathex = str
429
428
430 opmap = [('user', ' ', lambda x: x[0].user(), ui.shortuser),
429 opmap = [('user', ' ', lambda x: x[0].user(), ui.shortuser),
431 ('number', ' ', lambda x: x[0].rev(), formatrev),
430 ('number', ' ', lambda x: x[0].rev(), formatrev),
432 ('changeset', ' ', lambda x: hexfn(x[0].node()), formathex),
431 ('changeset', ' ', lambda x: hexfn(x[0].node()), formathex),
433 ('date', ' ', lambda x: x[0].date(), util.cachefunc(datefunc)),
432 ('date', ' ', lambda x: x[0].date(), util.cachefunc(datefunc)),
434 ('file', ' ', lambda x: x[0].path(), str),
433 ('file', ' ', lambda x: x[0].path(), str),
435 ('line_number', ':', lambda x: x[1], str),
434 ('line_number', ':', lambda x: x[1], str),
436 ]
435 ]
437 fieldnamemap = {'number': 'rev', 'changeset': 'node'}
436 fieldnamemap = {'number': 'rev', 'changeset': 'node'}
438
437
439 if (not opts.get('user') and not opts.get('changeset')
438 if (not opts.get('user') and not opts.get('changeset')
440 and not opts.get('date') and not opts.get('file')):
439 and not opts.get('date') and not opts.get('file')):
441 opts['number'] = True
440 opts['number'] = True
442
441
443 linenumber = opts.get('line_number') is not None
442 linenumber = opts.get('line_number') is not None
444 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
443 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
445 raise error.Abort(_('at least one of -n/-c is required for -l'))
444 raise error.Abort(_('at least one of -n/-c is required for -l'))
446
445
447 if fm.isplain():
446 if fm.isplain():
448 def makefunc(get, fmt):
447 def makefunc(get, fmt):
449 return lambda x: fmt(get(x))
448 return lambda x: fmt(get(x))
450 else:
449 else:
451 def makefunc(get, fmt):
450 def makefunc(get, fmt):
452 return get
451 return get
453 funcmap = [(makefunc(get, fmt), sep) for op, sep, get, fmt in opmap
452 funcmap = [(makefunc(get, fmt), sep) for op, sep, get, fmt in opmap
454 if opts.get(op)]
453 if opts.get(op)]
455 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
454 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
456 fields = ' '.join(fieldnamemap.get(op, op) for op, sep, get, fmt in opmap
455 fields = ' '.join(fieldnamemap.get(op, op) for op, sep, get, fmt in opmap
457 if opts.get(op))
456 if opts.get(op))
458
457
459 def bad(x, y):
458 def bad(x, y):
460 raise error.Abort("%s: %s" % (x, y))
459 raise error.Abort("%s: %s" % (x, y))
461
460
462 m = scmutil.match(ctx, pats, opts, badfn=bad)
461 m = scmutil.match(ctx, pats, opts, badfn=bad)
463
462
464 follow = not opts.get('no_follow')
463 follow = not opts.get('no_follow')
465 diffopts = patch.difffeatureopts(ui, opts, section='annotate',
464 diffopts = patch.difffeatureopts(ui, opts, section='annotate',
466 whitespace=True)
465 whitespace=True)
467 for abs in ctx.walk(m):
466 for abs in ctx.walk(m):
468 fctx = ctx[abs]
467 fctx = ctx[abs]
469 if not opts.get('text') and util.binary(fctx.data()):
468 if not opts.get('text') and util.binary(fctx.data()):
470 fm.plain(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
469 fm.plain(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
471 continue
470 continue
472
471
473 lines = fctx.annotate(follow=follow, linenumber=linenumber,
472 lines = fctx.annotate(follow=follow, linenumber=linenumber,
474 diffopts=diffopts)
473 diffopts=diffopts)
475 if not lines:
474 if not lines:
476 continue
475 continue
477 formats = []
476 formats = []
478 pieces = []
477 pieces = []
479
478
480 for f, sep in funcmap:
479 for f, sep in funcmap:
481 l = [f(n) for n, dummy in lines]
480 l = [f(n) for n, dummy in lines]
482 if fm.isplain():
481 if fm.isplain():
483 sizes = [encoding.colwidth(x) for x in l]
482 sizes = [encoding.colwidth(x) for x in l]
484 ml = max(sizes)
483 ml = max(sizes)
485 formats.append([sep + ' ' * (ml - w) + '%s' for w in sizes])
484 formats.append([sep + ' ' * (ml - w) + '%s' for w in sizes])
486 else:
485 else:
487 formats.append(['%s' for x in l])
486 formats.append(['%s' for x in l])
488 pieces.append(l)
487 pieces.append(l)
489
488
490 for f, p, l in zip(zip(*formats), zip(*pieces), lines):
489 for f, p, l in zip(zip(*formats), zip(*pieces), lines):
491 fm.startitem()
490 fm.startitem()
492 fm.write(fields, "".join(f), *p)
491 fm.write(fields, "".join(f), *p)
493 fm.write('line', ": %s", l[1])
492 fm.write('line', ": %s", l[1])
494
493
495 if not lines[-1][1].endswith('\n'):
494 if not lines[-1][1].endswith('\n'):
496 fm.plain('\n')
495 fm.plain('\n')
497
496
498 fm.end()
497 fm.end()
499
498
500 @command('archive',
499 @command('archive',
501 [('', 'no-decode', None, _('do not pass files through decoders')),
500 [('', 'no-decode', None, _('do not pass files through decoders')),
502 ('p', 'prefix', '', _('directory prefix for files in archive'),
501 ('p', 'prefix', '', _('directory prefix for files in archive'),
503 _('PREFIX')),
502 _('PREFIX')),
504 ('r', 'rev', '', _('revision to distribute'), _('REV')),
503 ('r', 'rev', '', _('revision to distribute'), _('REV')),
505 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
504 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
506 ] + subrepoopts + walkopts,
505 ] + subrepoopts + walkopts,
507 _('[OPTION]... DEST'))
506 _('[OPTION]... DEST'))
508 def archive(ui, repo, dest, **opts):
507 def archive(ui, repo, dest, **opts):
509 '''create an unversioned archive of a repository revision
508 '''create an unversioned archive of a repository revision
510
509
511 By default, the revision used is the parent of the working
510 By default, the revision used is the parent of the working
512 directory; use -r/--rev to specify a different revision.
511 directory; use -r/--rev to specify a different revision.
513
512
514 The archive type is automatically detected based on file
513 The archive type is automatically detected based on file
515 extension (to override, use -t/--type).
514 extension (to override, use -t/--type).
516
515
517 .. container:: verbose
516 .. container:: verbose
518
517
519 Examples:
518 Examples:
520
519
521 - create a zip file containing the 1.0 release::
520 - create a zip file containing the 1.0 release::
522
521
523 hg archive -r 1.0 project-1.0.zip
522 hg archive -r 1.0 project-1.0.zip
524
523
525 - create a tarball excluding .hg files::
524 - create a tarball excluding .hg files::
526
525
527 hg archive project.tar.gz -X ".hg*"
526 hg archive project.tar.gz -X ".hg*"
528
527
529 Valid types are:
528 Valid types are:
530
529
531 :``files``: a directory full of files (default)
530 :``files``: a directory full of files (default)
532 :``tar``: tar archive, uncompressed
531 :``tar``: tar archive, uncompressed
533 :``tbz2``: tar archive, compressed using bzip2
532 :``tbz2``: tar archive, compressed using bzip2
534 :``tgz``: tar archive, compressed using gzip
533 :``tgz``: tar archive, compressed using gzip
535 :``uzip``: zip archive, uncompressed
534 :``uzip``: zip archive, uncompressed
536 :``zip``: zip archive, compressed using deflate
535 :``zip``: zip archive, compressed using deflate
537
536
538 The exact name of the destination archive or directory is given
537 The exact name of the destination archive or directory is given
539 using a format string; see :hg:`help export` for details.
538 using a format string; see :hg:`help export` for details.
540
539
541 Each member added to an archive file has a directory prefix
540 Each member added to an archive file has a directory prefix
542 prepended. Use -p/--prefix to specify a format string for the
541 prepended. Use -p/--prefix to specify a format string for the
543 prefix. The default is the basename of the archive, with suffixes
542 prefix. The default is the basename of the archive, with suffixes
544 removed.
543 removed.
545
544
546 Returns 0 on success.
545 Returns 0 on success.
547 '''
546 '''
548
547
549 ctx = scmutil.revsingle(repo, opts.get('rev'))
548 ctx = scmutil.revsingle(repo, opts.get('rev'))
550 if not ctx:
549 if not ctx:
551 raise error.Abort(_('no working directory: please specify a revision'))
550 raise error.Abort(_('no working directory: please specify a revision'))
552 node = ctx.node()
551 node = ctx.node()
553 dest = cmdutil.makefilename(repo, dest, node)
552 dest = cmdutil.makefilename(repo, dest, node)
554 if os.path.realpath(dest) == repo.root:
553 if os.path.realpath(dest) == repo.root:
555 raise error.Abort(_('repository root cannot be destination'))
554 raise error.Abort(_('repository root cannot be destination'))
556
555
557 kind = opts.get('type') or archival.guesskind(dest) or 'files'
556 kind = opts.get('type') or archival.guesskind(dest) or 'files'
558 prefix = opts.get('prefix')
557 prefix = opts.get('prefix')
559
558
560 if dest == '-':
559 if dest == '-':
561 if kind == 'files':
560 if kind == 'files':
562 raise error.Abort(_('cannot archive plain files to stdout'))
561 raise error.Abort(_('cannot archive plain files to stdout'))
563 dest = cmdutil.makefileobj(repo, dest)
562 dest = cmdutil.makefileobj(repo, dest)
564 if not prefix:
563 if not prefix:
565 prefix = os.path.basename(repo.root) + '-%h'
564 prefix = os.path.basename(repo.root) + '-%h'
566
565
567 prefix = cmdutil.makefilename(repo, prefix, node)
566 prefix = cmdutil.makefilename(repo, prefix, node)
568 matchfn = scmutil.match(ctx, [], opts)
567 matchfn = scmutil.match(ctx, [], opts)
569 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
568 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
570 matchfn, prefix, subrepos=opts.get('subrepos'))
569 matchfn, prefix, subrepos=opts.get('subrepos'))
571
570
572 @command('backout',
571 @command('backout',
573 [('', 'merge', None, _('merge with old dirstate parent after backout')),
572 [('', 'merge', None, _('merge with old dirstate parent after backout')),
574 ('', 'commit', None,
573 ('', 'commit', None,
575 _('commit if no conflicts were encountered (DEPRECATED)')),
574 _('commit if no conflicts were encountered (DEPRECATED)')),
576 ('', 'no-commit', None, _('do not commit')),
575 ('', 'no-commit', None, _('do not commit')),
577 ('', 'parent', '',
576 ('', 'parent', '',
578 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
577 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
579 ('r', 'rev', '', _('revision to backout'), _('REV')),
578 ('r', 'rev', '', _('revision to backout'), _('REV')),
580 ('e', 'edit', False, _('invoke editor on commit messages')),
579 ('e', 'edit', False, _('invoke editor on commit messages')),
581 ] + mergetoolopts + walkopts + commitopts + commitopts2,
580 ] + mergetoolopts + walkopts + commitopts + commitopts2,
582 _('[OPTION]... [-r] REV'))
581 _('[OPTION]... [-r] REV'))
583 def backout(ui, repo, node=None, rev=None, **opts):
582 def backout(ui, repo, node=None, rev=None, **opts):
584 '''reverse effect of earlier changeset
583 '''reverse effect of earlier changeset
585
584
586 Prepare a new changeset with the effect of REV undone in the
585 Prepare a new changeset with the effect of REV undone in the
587 current working directory. If no conflicts were encountered,
586 current working directory. If no conflicts were encountered,
588 it will be committed immediately.
587 it will be committed immediately.
589
588
590 If REV is the parent of the working directory, then this new changeset
589 If REV is the parent of the working directory, then this new changeset
591 is committed automatically (unless --no-commit is specified).
590 is committed automatically (unless --no-commit is specified).
592
591
593 .. note::
592 .. note::
594
593
595 :hg:`backout` cannot be used to fix either an unwanted or
594 :hg:`backout` cannot be used to fix either an unwanted or
596 incorrect merge.
595 incorrect merge.
597
596
598 .. container:: verbose
597 .. container:: verbose
599
598
600 Examples:
599 Examples:
601
600
602 - Reverse the effect of the parent of the working directory.
601 - Reverse the effect of the parent of the working directory.
603 This backout will be committed immediately::
602 This backout will be committed immediately::
604
603
605 hg backout -r .
604 hg backout -r .
606
605
607 - Reverse the effect of previous bad revision 23::
606 - Reverse the effect of previous bad revision 23::
608
607
609 hg backout -r 23
608 hg backout -r 23
610
609
611 - Reverse the effect of previous bad revision 23 and
610 - Reverse the effect of previous bad revision 23 and
612 leave changes uncommitted::
611 leave changes uncommitted::
613
612
614 hg backout -r 23 --no-commit
613 hg backout -r 23 --no-commit
615 hg commit -m "Backout revision 23"
614 hg commit -m "Backout revision 23"
616
615
617 By default, the pending changeset will have one parent,
616 By default, the pending changeset will have one parent,
618 maintaining a linear history. With --merge, the pending
617 maintaining a linear history. With --merge, the pending
619 changeset will instead have two parents: the old parent of the
618 changeset will instead have two parents: the old parent of the
620 working directory and a new child of REV that simply undoes REV.
619 working directory and a new child of REV that simply undoes REV.
621
620
622 Before version 1.7, the behavior without --merge was equivalent
621 Before version 1.7, the behavior without --merge was equivalent
623 to specifying --merge followed by :hg:`update --clean .` to
622 to specifying --merge followed by :hg:`update --clean .` to
624 cancel the merge and leave the child of REV as a head to be
623 cancel the merge and leave the child of REV as a head to be
625 merged separately.
624 merged separately.
626
625
627 See :hg:`help dates` for a list of formats valid for -d/--date.
626 See :hg:`help dates` for a list of formats valid for -d/--date.
628
627
629 See :hg:`help revert` for a way to restore files to the state
628 See :hg:`help revert` for a way to restore files to the state
630 of another revision.
629 of another revision.
631
630
632 Returns 0 on success, 1 if nothing to backout or there are unresolved
631 Returns 0 on success, 1 if nothing to backout or there are unresolved
633 files.
632 files.
634 '''
633 '''
635 wlock = lock = None
634 wlock = lock = None
636 try:
635 try:
637 wlock = repo.wlock()
636 wlock = repo.wlock()
638 lock = repo.lock()
637 lock = repo.lock()
639 return _dobackout(ui, repo, node, rev, **opts)
638 return _dobackout(ui, repo, node, rev, **opts)
640 finally:
639 finally:
641 release(lock, wlock)
640 release(lock, wlock)
642
641
643 def _dobackout(ui, repo, node=None, rev=None, **opts):
642 def _dobackout(ui, repo, node=None, rev=None, **opts):
644 if opts.get('commit') and opts.get('no_commit'):
643 if opts.get('commit') and opts.get('no_commit'):
645 raise error.Abort(_("cannot use --commit with --no-commit"))
644 raise error.Abort(_("cannot use --commit with --no-commit"))
646 if opts.get('merge') and opts.get('no_commit'):
645 if opts.get('merge') and opts.get('no_commit'):
647 raise error.Abort(_("cannot use --merge with --no-commit"))
646 raise error.Abort(_("cannot use --merge with --no-commit"))
648
647
649 if rev and node:
648 if rev and node:
650 raise error.Abort(_("please specify just one revision"))
649 raise error.Abort(_("please specify just one revision"))
651
650
652 if not rev:
651 if not rev:
653 rev = node
652 rev = node
654
653
655 if not rev:
654 if not rev:
656 raise error.Abort(_("please specify a revision to backout"))
655 raise error.Abort(_("please specify a revision to backout"))
657
656
658 date = opts.get('date')
657 date = opts.get('date')
659 if date:
658 if date:
660 opts['date'] = util.parsedate(date)
659 opts['date'] = util.parsedate(date)
661
660
662 cmdutil.checkunfinished(repo)
661 cmdutil.checkunfinished(repo)
663 cmdutil.bailifchanged(repo)
662 cmdutil.bailifchanged(repo)
664 node = scmutil.revsingle(repo, rev).node()
663 node = scmutil.revsingle(repo, rev).node()
665
664
666 op1, op2 = repo.dirstate.parents()
665 op1, op2 = repo.dirstate.parents()
667 if not repo.changelog.isancestor(node, op1):
666 if not repo.changelog.isancestor(node, op1):
668 raise error.Abort(_('cannot backout change that is not an ancestor'))
667 raise error.Abort(_('cannot backout change that is not an ancestor'))
669
668
670 p1, p2 = repo.changelog.parents(node)
669 p1, p2 = repo.changelog.parents(node)
671 if p1 == nullid:
670 if p1 == nullid:
672 raise error.Abort(_('cannot backout a change with no parents'))
671 raise error.Abort(_('cannot backout a change with no parents'))
673 if p2 != nullid:
672 if p2 != nullid:
674 if not opts.get('parent'):
673 if not opts.get('parent'):
675 raise error.Abort(_('cannot backout a merge changeset'))
674 raise error.Abort(_('cannot backout a merge changeset'))
676 p = repo.lookup(opts['parent'])
675 p = repo.lookup(opts['parent'])
677 if p not in (p1, p2):
676 if p not in (p1, p2):
678 raise error.Abort(_('%s is not a parent of %s') %
677 raise error.Abort(_('%s is not a parent of %s') %
679 (short(p), short(node)))
678 (short(p), short(node)))
680 parent = p
679 parent = p
681 else:
680 else:
682 if opts.get('parent'):
681 if opts.get('parent'):
683 raise error.Abort(_('cannot use --parent on non-merge changeset'))
682 raise error.Abort(_('cannot use --parent on non-merge changeset'))
684 parent = p1
683 parent = p1
685
684
686 # the backout should appear on the same branch
685 # the backout should appear on the same branch
687 branch = repo.dirstate.branch()
686 branch = repo.dirstate.branch()
688 bheads = repo.branchheads(branch)
687 bheads = repo.branchheads(branch)
689 rctx = scmutil.revsingle(repo, hex(parent))
688 rctx = scmutil.revsingle(repo, hex(parent))
690 if not opts.get('merge') and op1 != node:
689 if not opts.get('merge') and op1 != node:
691 dsguard = dirstateguard.dirstateguard(repo, 'backout')
690 dsguard = dirstateguard.dirstateguard(repo, 'backout')
692 try:
691 try:
693 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
692 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
694 'backout')
693 'backout')
695 stats = mergemod.update(repo, parent, True, True, node, False)
694 stats = mergemod.update(repo, parent, True, True, node, False)
696 repo.setparents(op1, op2)
695 repo.setparents(op1, op2)
697 dsguard.close()
696 dsguard.close()
698 hg._showstats(repo, stats)
697 hg._showstats(repo, stats)
699 if stats[3]:
698 if stats[3]:
700 repo.ui.status(_("use 'hg resolve' to retry unresolved "
699 repo.ui.status(_("use 'hg resolve' to retry unresolved "
701 "file merges\n"))
700 "file merges\n"))
702 return 1
701 return 1
703 finally:
702 finally:
704 ui.setconfig('ui', 'forcemerge', '', '')
703 ui.setconfig('ui', 'forcemerge', '', '')
705 lockmod.release(dsguard)
704 lockmod.release(dsguard)
706 else:
705 else:
707 hg.clean(repo, node, show_stats=False)
706 hg.clean(repo, node, show_stats=False)
708 repo.dirstate.setbranch(branch)
707 repo.dirstate.setbranch(branch)
709 cmdutil.revert(ui, repo, rctx, repo.dirstate.parents())
708 cmdutil.revert(ui, repo, rctx, repo.dirstate.parents())
710
709
711 if opts.get('no_commit'):
710 if opts.get('no_commit'):
712 msg = _("changeset %s backed out, "
711 msg = _("changeset %s backed out, "
713 "don't forget to commit.\n")
712 "don't forget to commit.\n")
714 ui.status(msg % short(node))
713 ui.status(msg % short(node))
715 return 0
714 return 0
716
715
717 def commitfunc(ui, repo, message, match, opts):
716 def commitfunc(ui, repo, message, match, opts):
718 editform = 'backout'
717 editform = 'backout'
719 e = cmdutil.getcommiteditor(editform=editform, **opts)
718 e = cmdutil.getcommiteditor(editform=editform, **opts)
720 if not message:
719 if not message:
721 # we don't translate commit messages
720 # we don't translate commit messages
722 message = "Backed out changeset %s" % short(node)
721 message = "Backed out changeset %s" % short(node)
723 e = cmdutil.getcommiteditor(edit=True, editform=editform)
722 e = cmdutil.getcommiteditor(edit=True, editform=editform)
724 return repo.commit(message, opts.get('user'), opts.get('date'),
723 return repo.commit(message, opts.get('user'), opts.get('date'),
725 match, editor=e)
724 match, editor=e)
726 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
725 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
727 if not newnode:
726 if not newnode:
728 ui.status(_("nothing changed\n"))
727 ui.status(_("nothing changed\n"))
729 return 1
728 return 1
730 cmdutil.commitstatus(repo, newnode, branch, bheads)
729 cmdutil.commitstatus(repo, newnode, branch, bheads)
731
730
732 def nice(node):
731 def nice(node):
733 return '%d:%s' % (repo.changelog.rev(node), short(node))
732 return '%d:%s' % (repo.changelog.rev(node), short(node))
734 ui.status(_('changeset %s backs out changeset %s\n') %
733 ui.status(_('changeset %s backs out changeset %s\n') %
735 (nice(repo.changelog.tip()), nice(node)))
734 (nice(repo.changelog.tip()), nice(node)))
736 if opts.get('merge') and op1 != node:
735 if opts.get('merge') and op1 != node:
737 hg.clean(repo, op1, show_stats=False)
736 hg.clean(repo, op1, show_stats=False)
738 ui.status(_('merging with changeset %s\n')
737 ui.status(_('merging with changeset %s\n')
739 % nice(repo.changelog.tip()))
738 % nice(repo.changelog.tip()))
740 try:
739 try:
741 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
740 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
742 'backout')
741 'backout')
743 return hg.merge(repo, hex(repo.changelog.tip()))
742 return hg.merge(repo, hex(repo.changelog.tip()))
744 finally:
743 finally:
745 ui.setconfig('ui', 'forcemerge', '', '')
744 ui.setconfig('ui', 'forcemerge', '', '')
746 return 0
745 return 0
747
746
748 @command('bisect',
747 @command('bisect',
749 [('r', 'reset', False, _('reset bisect state')),
748 [('r', 'reset', False, _('reset bisect state')),
750 ('g', 'good', False, _('mark changeset good')),
749 ('g', 'good', False, _('mark changeset good')),
751 ('b', 'bad', False, _('mark changeset bad')),
750 ('b', 'bad', False, _('mark changeset bad')),
752 ('s', 'skip', False, _('skip testing changeset')),
751 ('s', 'skip', False, _('skip testing changeset')),
753 ('e', 'extend', False, _('extend the bisect range')),
752 ('e', 'extend', False, _('extend the bisect range')),
754 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
753 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
755 ('U', 'noupdate', False, _('do not update to target'))],
754 ('U', 'noupdate', False, _('do not update to target'))],
756 _("[-gbsr] [-U] [-c CMD] [REV]"))
755 _("[-gbsr] [-U] [-c CMD] [REV]"))
757 def bisect(ui, repo, rev=None, extra=None, command=None,
756 def bisect(ui, repo, rev=None, extra=None, command=None,
758 reset=None, good=None, bad=None, skip=None, extend=None,
757 reset=None, good=None, bad=None, skip=None, extend=None,
759 noupdate=None):
758 noupdate=None):
760 """subdivision search of changesets
759 """subdivision search of changesets
761
760
762 This command helps to find changesets which introduce problems. To
761 This command helps to find changesets which introduce problems. To
763 use, mark the earliest changeset you know exhibits the problem as
762 use, mark the earliest changeset you know exhibits the problem as
764 bad, then mark the latest changeset which is free from the problem
763 bad, then mark the latest changeset which is free from the problem
765 as good. Bisect will update your working directory to a revision
764 as good. Bisect will update your working directory to a revision
766 for testing (unless the -U/--noupdate option is specified). Once
765 for testing (unless the -U/--noupdate option is specified). Once
767 you have performed tests, mark the working directory as good or
766 you have performed tests, mark the working directory as good or
768 bad, and bisect will either update to another candidate changeset
767 bad, and bisect will either update to another candidate changeset
769 or announce that it has found the bad revision.
768 or announce that it has found the bad revision.
770
769
771 As a shortcut, you can also use the revision argument to mark a
770 As a shortcut, you can also use the revision argument to mark a
772 revision as good or bad without checking it out first.
771 revision as good or bad without checking it out first.
773
772
774 If you supply a command, it will be used for automatic bisection.
773 If you supply a command, it will be used for automatic bisection.
775 The environment variable HG_NODE will contain the ID of the
774 The environment variable HG_NODE will contain the ID of the
776 changeset being tested. The exit status of the command will be
775 changeset being tested. The exit status of the command will be
777 used to mark revisions as good or bad: status 0 means good, 125
776 used to mark revisions as good or bad: status 0 means good, 125
778 means to skip the revision, 127 (command not found) will abort the
777 means to skip the revision, 127 (command not found) will abort the
779 bisection, and any other non-zero exit status means the revision
778 bisection, and any other non-zero exit status means the revision
780 is bad.
779 is bad.
781
780
782 .. container:: verbose
781 .. container:: verbose
783
782
784 Some examples:
783 Some examples:
785
784
786 - start a bisection with known bad revision 34, and good revision 12::
785 - start a bisection with known bad revision 34, and good revision 12::
787
786
788 hg bisect --bad 34
787 hg bisect --bad 34
789 hg bisect --good 12
788 hg bisect --good 12
790
789
791 - advance the current bisection by marking current revision as good or
790 - advance the current bisection by marking current revision as good or
792 bad::
791 bad::
793
792
794 hg bisect --good
793 hg bisect --good
795 hg bisect --bad
794 hg bisect --bad
796
795
797 - mark the current revision, or a known revision, to be skipped (e.g. if
796 - mark the current revision, or a known revision, to be skipped (e.g. if
798 that revision is not usable because of another issue)::
797 that revision is not usable because of another issue)::
799
798
800 hg bisect --skip
799 hg bisect --skip
801 hg bisect --skip 23
800 hg bisect --skip 23
802
801
803 - skip all revisions that do not touch directories ``foo`` or ``bar``::
802 - skip all revisions that do not touch directories ``foo`` or ``bar``::
804
803
805 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
804 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
806
805
807 - forget the current bisection::
806 - forget the current bisection::
808
807
809 hg bisect --reset
808 hg bisect --reset
810
809
811 - use 'make && make tests' to automatically find the first broken
810 - use 'make && make tests' to automatically find the first broken
812 revision::
811 revision::
813
812
814 hg bisect --reset
813 hg bisect --reset
815 hg bisect --bad 34
814 hg bisect --bad 34
816 hg bisect --good 12
815 hg bisect --good 12
817 hg bisect --command "make && make tests"
816 hg bisect --command "make && make tests"
818
817
819 - see all changesets whose states are already known in the current
818 - see all changesets whose states are already known in the current
820 bisection::
819 bisection::
821
820
822 hg log -r "bisect(pruned)"
821 hg log -r "bisect(pruned)"
823
822
824 - see the changeset currently being bisected (especially useful
823 - see the changeset currently being bisected (especially useful
825 if running with -U/--noupdate)::
824 if running with -U/--noupdate)::
826
825
827 hg log -r "bisect(current)"
826 hg log -r "bisect(current)"
828
827
829 - see all changesets that took part in the current bisection::
828 - see all changesets that took part in the current bisection::
830
829
831 hg log -r "bisect(range)"
830 hg log -r "bisect(range)"
832
831
833 - you can even get a nice graph::
832 - you can even get a nice graph::
834
833
835 hg log --graph -r "bisect(range)"
834 hg log --graph -r "bisect(range)"
836
835
837 See :hg:`help revsets` for more about the `bisect()` keyword.
836 See :hg:`help revsets` for more about the `bisect()` keyword.
838
837
839 Returns 0 on success.
838 Returns 0 on success.
840 """
839 """
841 # backward compatibility
840 # backward compatibility
842 if rev in "good bad reset init".split():
841 if rev in "good bad reset init".split():
843 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
842 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
844 cmd, rev, extra = rev, extra, None
843 cmd, rev, extra = rev, extra, None
845 if cmd == "good":
844 if cmd == "good":
846 good = True
845 good = True
847 elif cmd == "bad":
846 elif cmd == "bad":
848 bad = True
847 bad = True
849 else:
848 else:
850 reset = True
849 reset = True
851 elif extra or good + bad + skip + reset + extend + bool(command) > 1:
850 elif extra or good + bad + skip + reset + extend + bool(command) > 1:
852 raise error.Abort(_('incompatible arguments'))
851 raise error.Abort(_('incompatible arguments'))
853
852
854 cmdutil.checkunfinished(repo)
853 cmdutil.checkunfinished(repo)
855
854
856 if reset:
855 if reset:
857 hbisect.resetstate(repo)
856 hbisect.resetstate(repo)
858 return
857 return
859
858
860 state = hbisect.load_state(repo)
859 state = hbisect.load_state(repo)
861
860
862 # update state
861 # update state
863 if good or bad or skip:
862 if good or bad or skip:
864 if rev:
863 if rev:
865 nodes = [repo.lookup(i) for i in scmutil.revrange(repo, [rev])]
864 nodes = [repo.lookup(i) for i in scmutil.revrange(repo, [rev])]
866 else:
865 else:
867 nodes = [repo.lookup('.')]
866 nodes = [repo.lookup('.')]
868 if good:
867 if good:
869 state['good'] += nodes
868 state['good'] += nodes
870 elif bad:
869 elif bad:
871 state['bad'] += nodes
870 state['bad'] += nodes
872 elif skip:
871 elif skip:
873 state['skip'] += nodes
872 state['skip'] += nodes
874 hbisect.save_state(repo, state)
873 hbisect.save_state(repo, state)
875 if not (state['good'] and state['bad']):
874 if not (state['good'] and state['bad']):
876 return
875 return
877
876
878 def mayupdate(repo, node, show_stats=True):
877 def mayupdate(repo, node, show_stats=True):
879 """common used update sequence"""
878 """common used update sequence"""
880 if noupdate:
879 if noupdate:
881 return
880 return
882 cmdutil.bailifchanged(repo)
881 cmdutil.bailifchanged(repo)
883 return hg.clean(repo, node, show_stats=show_stats)
882 return hg.clean(repo, node, show_stats=show_stats)
884
883
885 displayer = cmdutil.show_changeset(ui, repo, {})
884 displayer = cmdutil.show_changeset(ui, repo, {})
886
885
887 if command:
886 if command:
888 changesets = 1
887 changesets = 1
889 if noupdate:
888 if noupdate:
890 try:
889 try:
891 node = state['current'][0]
890 node = state['current'][0]
892 except LookupError:
891 except LookupError:
893 raise error.Abort(_('current bisect revision is unknown - '
892 raise error.Abort(_('current bisect revision is unknown - '
894 'start a new bisect to fix'))
893 'start a new bisect to fix'))
895 else:
894 else:
896 node, p2 = repo.dirstate.parents()
895 node, p2 = repo.dirstate.parents()
897 if p2 != nullid:
896 if p2 != nullid:
898 raise error.Abort(_('current bisect revision is a merge'))
897 raise error.Abort(_('current bisect revision is a merge'))
899 if rev:
898 if rev:
900 node = repo[scmutil.revsingle(repo, rev, node)].node()
899 node = repo[scmutil.revsingle(repo, rev, node)].node()
901 try:
900 try:
902 while changesets:
901 while changesets:
903 # update state
902 # update state
904 state['current'] = [node]
903 state['current'] = [node]
905 hbisect.save_state(repo, state)
904 hbisect.save_state(repo, state)
906 status = ui.system(command, environ={'HG_NODE': hex(node)})
905 status = ui.system(command, environ={'HG_NODE': hex(node)})
907 if status == 125:
906 if status == 125:
908 transition = "skip"
907 transition = "skip"
909 elif status == 0:
908 elif status == 0:
910 transition = "good"
909 transition = "good"
911 # status < 0 means process was killed
910 # status < 0 means process was killed
912 elif status == 127:
911 elif status == 127:
913 raise error.Abort(_("failed to execute %s") % command)
912 raise error.Abort(_("failed to execute %s") % command)
914 elif status < 0:
913 elif status < 0:
915 raise error.Abort(_("%s killed") % command)
914 raise error.Abort(_("%s killed") % command)
916 else:
915 else:
917 transition = "bad"
916 transition = "bad"
918 state[transition].append(node)
917 state[transition].append(node)
919 ctx = repo[node]
918 ctx = repo[node]
920 ui.status(_('changeset %d:%s: %s\n') % (ctx, ctx, transition))
919 ui.status(_('changeset %d:%s: %s\n') % (ctx, ctx, transition))
921 hbisect.checkstate(state)
920 hbisect.checkstate(state)
922 # bisect
921 # bisect
923 nodes, changesets, bgood = hbisect.bisect(repo.changelog, state)
922 nodes, changesets, bgood = hbisect.bisect(repo.changelog, state)
924 # update to next check
923 # update to next check
925 node = nodes[0]
924 node = nodes[0]
926 mayupdate(repo, node, show_stats=False)
925 mayupdate(repo, node, show_stats=False)
927 finally:
926 finally:
928 state['current'] = [node]
927 state['current'] = [node]
929 hbisect.save_state(repo, state)
928 hbisect.save_state(repo, state)
930 hbisect.printresult(ui, repo, state, displayer, nodes, bgood)
929 hbisect.printresult(ui, repo, state, displayer, nodes, bgood)
931 return
930 return
932
931
933 hbisect.checkstate(state)
932 hbisect.checkstate(state)
934
933
935 # actually bisect
934 # actually bisect
936 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
935 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
937 if extend:
936 if extend:
938 if not changesets:
937 if not changesets:
939 extendnode = hbisect.extendrange(repo, state, nodes, good)
938 extendnode = hbisect.extendrange(repo, state, nodes, good)
940 if extendnode is not None:
939 if extendnode is not None:
941 ui.write(_("Extending search to changeset %d:%s\n")
940 ui.write(_("Extending search to changeset %d:%s\n")
942 % (extendnode.rev(), extendnode))
941 % (extendnode.rev(), extendnode))
943 state['current'] = [extendnode.node()]
942 state['current'] = [extendnode.node()]
944 hbisect.save_state(repo, state)
943 hbisect.save_state(repo, state)
945 return mayupdate(repo, extendnode.node())
944 return mayupdate(repo, extendnode.node())
946 raise error.Abort(_("nothing to extend"))
945 raise error.Abort(_("nothing to extend"))
947
946
948 if changesets == 0:
947 if changesets == 0:
949 hbisect.printresult(ui, repo, state, displayer, nodes, good)
948 hbisect.printresult(ui, repo, state, displayer, nodes, good)
950 else:
949 else:
951 assert len(nodes) == 1 # only a single node can be tested next
950 assert len(nodes) == 1 # only a single node can be tested next
952 node = nodes[0]
951 node = nodes[0]
953 # compute the approximate number of remaining tests
952 # compute the approximate number of remaining tests
954 tests, size = 0, 2
953 tests, size = 0, 2
955 while size <= changesets:
954 while size <= changesets:
956 tests, size = tests + 1, size * 2
955 tests, size = tests + 1, size * 2
957 rev = repo.changelog.rev(node)
956 rev = repo.changelog.rev(node)
958 ui.write(_("Testing changeset %d:%s "
957 ui.write(_("Testing changeset %d:%s "
959 "(%d changesets remaining, ~%d tests)\n")
958 "(%d changesets remaining, ~%d tests)\n")
960 % (rev, short(node), changesets, tests))
959 % (rev, short(node), changesets, tests))
961 state['current'] = [node]
960 state['current'] = [node]
962 hbisect.save_state(repo, state)
961 hbisect.save_state(repo, state)
963 return mayupdate(repo, node)
962 return mayupdate(repo, node)
964
963
965 @command('bookmarks|bookmark',
964 @command('bookmarks|bookmark',
966 [('f', 'force', False, _('force')),
965 [('f', 'force', False, _('force')),
967 ('r', 'rev', '', _('revision for bookmark action'), _('REV')),
966 ('r', 'rev', '', _('revision for bookmark action'), _('REV')),
968 ('d', 'delete', False, _('delete a given bookmark')),
967 ('d', 'delete', False, _('delete a given bookmark')),
969 ('m', 'rename', '', _('rename a given bookmark'), _('OLD')),
968 ('m', 'rename', '', _('rename a given bookmark'), _('OLD')),
970 ('i', 'inactive', False, _('mark a bookmark inactive')),
969 ('i', 'inactive', False, _('mark a bookmark inactive')),
971 ] + formatteropts,
970 ] + formatteropts,
972 _('hg bookmarks [OPTIONS]... [NAME]...'))
971 _('hg bookmarks [OPTIONS]... [NAME]...'))
973 def bookmark(ui, repo, *names, **opts):
972 def bookmark(ui, repo, *names, **opts):
974 '''create a new bookmark or list existing bookmarks
973 '''create a new bookmark or list existing bookmarks
975
974
976 Bookmarks are labels on changesets to help track lines of development.
975 Bookmarks are labels on changesets to help track lines of development.
977 Bookmarks are unversioned and can be moved, renamed and deleted.
976 Bookmarks are unversioned and can be moved, renamed and deleted.
978 Deleting or moving a bookmark has no effect on the associated changesets.
977 Deleting or moving a bookmark has no effect on the associated changesets.
979
978
980 Creating or updating to a bookmark causes it to be marked as 'active'.
979 Creating or updating to a bookmark causes it to be marked as 'active'.
981 The active bookmark is indicated with a '*'.
980 The active bookmark is indicated with a '*'.
982 When a commit is made, the active bookmark will advance to the new commit.
981 When a commit is made, the active bookmark will advance to the new commit.
983 A plain :hg:`update` will also advance an active bookmark, if possible.
982 A plain :hg:`update` will also advance an active bookmark, if possible.
984 Updating away from a bookmark will cause it to be deactivated.
983 Updating away from a bookmark will cause it to be deactivated.
985
984
986 Bookmarks can be pushed and pulled between repositories (see
985 Bookmarks can be pushed and pulled between repositories (see
987 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
986 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
988 diverged, a new 'divergent bookmark' of the form 'name@path' will
987 diverged, a new 'divergent bookmark' of the form 'name@path' will
989 be created. Using :hg:`merge` will resolve the divergence.
988 be created. Using :hg:`merge` will resolve the divergence.
990
989
991 A bookmark named '@' has the special property that :hg:`clone` will
990 A bookmark named '@' has the special property that :hg:`clone` will
992 check it out by default if it exists.
991 check it out by default if it exists.
993
992
994 .. container:: verbose
993 .. container:: verbose
995
994
996 Examples:
995 Examples:
997
996
998 - create an active bookmark for a new line of development::
997 - create an active bookmark for a new line of development::
999
998
1000 hg book new-feature
999 hg book new-feature
1001
1000
1002 - create an inactive bookmark as a place marker::
1001 - create an inactive bookmark as a place marker::
1003
1002
1004 hg book -i reviewed
1003 hg book -i reviewed
1005
1004
1006 - create an inactive bookmark on another changeset::
1005 - create an inactive bookmark on another changeset::
1007
1006
1008 hg book -r .^ tested
1007 hg book -r .^ tested
1009
1008
1010 - rename bookmark turkey to dinner::
1009 - rename bookmark turkey to dinner::
1011
1010
1012 hg book -m turkey dinner
1011 hg book -m turkey dinner
1013
1012
1014 - move the '@' bookmark from another branch::
1013 - move the '@' bookmark from another branch::
1015
1014
1016 hg book -f @
1015 hg book -f @
1017 '''
1016 '''
1018 force = opts.get('force')
1017 force = opts.get('force')
1019 rev = opts.get('rev')
1018 rev = opts.get('rev')
1020 delete = opts.get('delete')
1019 delete = opts.get('delete')
1021 rename = opts.get('rename')
1020 rename = opts.get('rename')
1022 inactive = opts.get('inactive')
1021 inactive = opts.get('inactive')
1023
1022
1024 def checkformat(mark):
1023 def checkformat(mark):
1025 mark = mark.strip()
1024 mark = mark.strip()
1026 if not mark:
1025 if not mark:
1027 raise error.Abort(_("bookmark names cannot consist entirely of "
1026 raise error.Abort(_("bookmark names cannot consist entirely of "
1028 "whitespace"))
1027 "whitespace"))
1029 scmutil.checknewlabel(repo, mark, 'bookmark')
1028 scmutil.checknewlabel(repo, mark, 'bookmark')
1030 return mark
1029 return mark
1031
1030
1032 def checkconflict(repo, mark, cur, force=False, target=None):
1031 def checkconflict(repo, mark, cur, force=False, target=None):
1033 if mark in marks and not force:
1032 if mark in marks and not force:
1034 if target:
1033 if target:
1035 if marks[mark] == target and target == cur:
1034 if marks[mark] == target and target == cur:
1036 # re-activating a bookmark
1035 # re-activating a bookmark
1037 return
1036 return
1038 anc = repo.changelog.ancestors([repo[target].rev()])
1037 anc = repo.changelog.ancestors([repo[target].rev()])
1039 bmctx = repo[marks[mark]]
1038 bmctx = repo[marks[mark]]
1040 divs = [repo[b].node() for b in marks
1039 divs = [repo[b].node() for b in marks
1041 if b.split('@', 1)[0] == mark.split('@', 1)[0]]
1040 if b.split('@', 1)[0] == mark.split('@', 1)[0]]
1042
1041
1043 # allow resolving a single divergent bookmark even if moving
1042 # allow resolving a single divergent bookmark even if moving
1044 # the bookmark across branches when a revision is specified
1043 # the bookmark across branches when a revision is specified
1045 # that contains a divergent bookmark
1044 # that contains a divergent bookmark
1046 if bmctx.rev() not in anc and target in divs:
1045 if bmctx.rev() not in anc and target in divs:
1047 bookmarks.deletedivergent(repo, [target], mark)
1046 bookmarks.deletedivergent(repo, [target], mark)
1048 return
1047 return
1049
1048
1050 deletefrom = [b for b in divs
1049 deletefrom = [b for b in divs
1051 if repo[b].rev() in anc or b == target]
1050 if repo[b].rev() in anc or b == target]
1052 bookmarks.deletedivergent(repo, deletefrom, mark)
1051 bookmarks.deletedivergent(repo, deletefrom, mark)
1053 if bookmarks.validdest(repo, bmctx, repo[target]):
1052 if bookmarks.validdest(repo, bmctx, repo[target]):
1054 ui.status(_("moving bookmark '%s' forward from %s\n") %
1053 ui.status(_("moving bookmark '%s' forward from %s\n") %
1055 (mark, short(bmctx.node())))
1054 (mark, short(bmctx.node())))
1056 return
1055 return
1057 raise error.Abort(_("bookmark '%s' already exists "
1056 raise error.Abort(_("bookmark '%s' already exists "
1058 "(use -f to force)") % mark)
1057 "(use -f to force)") % mark)
1059 if ((mark in repo.branchmap() or mark == repo.dirstate.branch())
1058 if ((mark in repo.branchmap() or mark == repo.dirstate.branch())
1060 and not force):
1059 and not force):
1061 raise error.Abort(
1060 raise error.Abort(
1062 _("a bookmark cannot have the name of an existing branch"))
1061 _("a bookmark cannot have the name of an existing branch"))
1063
1062
1064 if delete and rename:
1063 if delete and rename:
1065 raise error.Abort(_("--delete and --rename are incompatible"))
1064 raise error.Abort(_("--delete and --rename are incompatible"))
1066 if delete and rev:
1065 if delete and rev:
1067 raise error.Abort(_("--rev is incompatible with --delete"))
1066 raise error.Abort(_("--rev is incompatible with --delete"))
1068 if rename and rev:
1067 if rename and rev:
1069 raise error.Abort(_("--rev is incompatible with --rename"))
1068 raise error.Abort(_("--rev is incompatible with --rename"))
1070 if not names and (delete or rev):
1069 if not names and (delete or rev):
1071 raise error.Abort(_("bookmark name required"))
1070 raise error.Abort(_("bookmark name required"))
1072
1071
1073 if delete or rename or names or inactive:
1072 if delete or rename or names or inactive:
1074 wlock = lock = tr = None
1073 wlock = lock = tr = None
1075 try:
1074 try:
1076 wlock = repo.wlock()
1075 wlock = repo.wlock()
1077 lock = repo.lock()
1076 lock = repo.lock()
1078 cur = repo.changectx('.').node()
1077 cur = repo.changectx('.').node()
1079 marks = repo._bookmarks
1078 marks = repo._bookmarks
1080 if delete:
1079 if delete:
1081 tr = repo.transaction('bookmark')
1080 tr = repo.transaction('bookmark')
1082 for mark in names:
1081 for mark in names:
1083 if mark not in marks:
1082 if mark not in marks:
1084 raise error.Abort(_("bookmark '%s' does not exist") %
1083 raise error.Abort(_("bookmark '%s' does not exist") %
1085 mark)
1084 mark)
1086 if mark == repo._activebookmark:
1085 if mark == repo._activebookmark:
1087 bookmarks.deactivate(repo)
1086 bookmarks.deactivate(repo)
1088 del marks[mark]
1087 del marks[mark]
1089
1088
1090 elif rename:
1089 elif rename:
1091 tr = repo.transaction('bookmark')
1090 tr = repo.transaction('bookmark')
1092 if not names:
1091 if not names:
1093 raise error.Abort(_("new bookmark name required"))
1092 raise error.Abort(_("new bookmark name required"))
1094 elif len(names) > 1:
1093 elif len(names) > 1:
1095 raise error.Abort(_("only one new bookmark name allowed"))
1094 raise error.Abort(_("only one new bookmark name allowed"))
1096 mark = checkformat(names[0])
1095 mark = checkformat(names[0])
1097 if rename not in marks:
1096 if rename not in marks:
1098 raise error.Abort(_("bookmark '%s' does not exist")
1097 raise error.Abort(_("bookmark '%s' does not exist")
1099 % rename)
1098 % rename)
1100 checkconflict(repo, mark, cur, force)
1099 checkconflict(repo, mark, cur, force)
1101 marks[mark] = marks[rename]
1100 marks[mark] = marks[rename]
1102 if repo._activebookmark == rename and not inactive:
1101 if repo._activebookmark == rename and not inactive:
1103 bookmarks.activate(repo, mark)
1102 bookmarks.activate(repo, mark)
1104 del marks[rename]
1103 del marks[rename]
1105 elif names:
1104 elif names:
1106 tr = repo.transaction('bookmark')
1105 tr = repo.transaction('bookmark')
1107 newact = None
1106 newact = None
1108 for mark in names:
1107 for mark in names:
1109 mark = checkformat(mark)
1108 mark = checkformat(mark)
1110 if newact is None:
1109 if newact is None:
1111 newact = mark
1110 newact = mark
1112 if inactive and mark == repo._activebookmark:
1111 if inactive and mark == repo._activebookmark:
1113 bookmarks.deactivate(repo)
1112 bookmarks.deactivate(repo)
1114 return
1113 return
1115 tgt = cur
1114 tgt = cur
1116 if rev:
1115 if rev:
1117 tgt = scmutil.revsingle(repo, rev).node()
1116 tgt = scmutil.revsingle(repo, rev).node()
1118 checkconflict(repo, mark, cur, force, tgt)
1117 checkconflict(repo, mark, cur, force, tgt)
1119 marks[mark] = tgt
1118 marks[mark] = tgt
1120 if not inactive and cur == marks[newact] and not rev:
1119 if not inactive and cur == marks[newact] and not rev:
1121 bookmarks.activate(repo, newact)
1120 bookmarks.activate(repo, newact)
1122 elif cur != tgt and newact == repo._activebookmark:
1121 elif cur != tgt and newact == repo._activebookmark:
1123 bookmarks.deactivate(repo)
1122 bookmarks.deactivate(repo)
1124 elif inactive:
1123 elif inactive:
1125 if len(marks) == 0:
1124 if len(marks) == 0:
1126 ui.status(_("no bookmarks set\n"))
1125 ui.status(_("no bookmarks set\n"))
1127 elif not repo._activebookmark:
1126 elif not repo._activebookmark:
1128 ui.status(_("no active bookmark\n"))
1127 ui.status(_("no active bookmark\n"))
1129 else:
1128 else:
1130 bookmarks.deactivate(repo)
1129 bookmarks.deactivate(repo)
1131 if tr is not None:
1130 if tr is not None:
1132 marks.recordchange(tr)
1131 marks.recordchange(tr)
1133 tr.close()
1132 tr.close()
1134 finally:
1133 finally:
1135 lockmod.release(tr, lock, wlock)
1134 lockmod.release(tr, lock, wlock)
1136 else: # show bookmarks
1135 else: # show bookmarks
1137 fm = ui.formatter('bookmarks', opts)
1136 fm = ui.formatter('bookmarks', opts)
1138 hexfn = fm.hexfunc
1137 hexfn = fm.hexfunc
1139 marks = repo._bookmarks
1138 marks = repo._bookmarks
1140 if len(marks) == 0 and fm.isplain():
1139 if len(marks) == 0 and fm.isplain():
1141 ui.status(_("no bookmarks set\n"))
1140 ui.status(_("no bookmarks set\n"))
1142 for bmark, n in sorted(marks.iteritems()):
1141 for bmark, n in sorted(marks.iteritems()):
1143 active = repo._activebookmark
1142 active = repo._activebookmark
1144 if bmark == active:
1143 if bmark == active:
1145 prefix, label = '*', activebookmarklabel
1144 prefix, label = '*', activebookmarklabel
1146 else:
1145 else:
1147 prefix, label = ' ', ''
1146 prefix, label = ' ', ''
1148
1147
1149 fm.startitem()
1148 fm.startitem()
1150 if not ui.quiet:
1149 if not ui.quiet:
1151 fm.plain(' %s ' % prefix, label=label)
1150 fm.plain(' %s ' % prefix, label=label)
1152 fm.write('bookmark', '%s', bmark, label=label)
1151 fm.write('bookmark', '%s', bmark, label=label)
1153 pad = " " * (25 - encoding.colwidth(bmark))
1152 pad = " " * (25 - encoding.colwidth(bmark))
1154 fm.condwrite(not ui.quiet, 'rev node', pad + ' %d:%s',
1153 fm.condwrite(not ui.quiet, 'rev node', pad + ' %d:%s',
1155 repo.changelog.rev(n), hexfn(n), label=label)
1154 repo.changelog.rev(n), hexfn(n), label=label)
1156 fm.data(active=(bmark == active))
1155 fm.data(active=(bmark == active))
1157 fm.plain('\n')
1156 fm.plain('\n')
1158 fm.end()
1157 fm.end()
1159
1158
1160 @command('branch',
1159 @command('branch',
1161 [('f', 'force', None,
1160 [('f', 'force', None,
1162 _('set branch name even if it shadows an existing branch')),
1161 _('set branch name even if it shadows an existing branch')),
1163 ('C', 'clean', None, _('reset branch name to parent branch name'))],
1162 ('C', 'clean', None, _('reset branch name to parent branch name'))],
1164 _('[-fC] [NAME]'))
1163 _('[-fC] [NAME]'))
1165 def branch(ui, repo, label=None, **opts):
1164 def branch(ui, repo, label=None, **opts):
1166 """set or show the current branch name
1165 """set or show the current branch name
1167
1166
1168 .. note::
1167 .. note::
1169
1168
1170 Branch names are permanent and global. Use :hg:`bookmark` to create a
1169 Branch names are permanent and global. Use :hg:`bookmark` to create a
1171 light-weight bookmark instead. See :hg:`help glossary` for more
1170 light-weight bookmark instead. See :hg:`help glossary` for more
1172 information about named branches and bookmarks.
1171 information about named branches and bookmarks.
1173
1172
1174 With no argument, show the current branch name. With one argument,
1173 With no argument, show the current branch name. With one argument,
1175 set the working directory branch name (the branch will not exist
1174 set the working directory branch name (the branch will not exist
1176 in the repository until the next commit). Standard practice
1175 in the repository until the next commit). Standard practice
1177 recommends that primary development take place on the 'default'
1176 recommends that primary development take place on the 'default'
1178 branch.
1177 branch.
1179
1178
1180 Unless -f/--force is specified, branch will not let you set a
1179 Unless -f/--force is specified, branch will not let you set a
1181 branch name that already exists.
1180 branch name that already exists.
1182
1181
1183 Use -C/--clean to reset the working directory branch to that of
1182 Use -C/--clean to reset the working directory branch to that of
1184 the parent of the working directory, negating a previous branch
1183 the parent of the working directory, negating a previous branch
1185 change.
1184 change.
1186
1185
1187 Use the command :hg:`update` to switch to an existing branch. Use
1186 Use the command :hg:`update` to switch to an existing branch. Use
1188 :hg:`commit --close-branch` to mark this branch head as closed.
1187 :hg:`commit --close-branch` to mark this branch head as closed.
1189 When all heads of a branch are closed, the branch will be
1188 When all heads of a branch are closed, the branch will be
1190 considered closed.
1189 considered closed.
1191
1190
1192 Returns 0 on success.
1191 Returns 0 on success.
1193 """
1192 """
1194 if label:
1193 if label:
1195 label = label.strip()
1194 label = label.strip()
1196
1195
1197 if not opts.get('clean') and not label:
1196 if not opts.get('clean') and not label:
1198 ui.write("%s\n" % repo.dirstate.branch())
1197 ui.write("%s\n" % repo.dirstate.branch())
1199 return
1198 return
1200
1199
1201 with repo.wlock():
1200 with repo.wlock():
1202 if opts.get('clean'):
1201 if opts.get('clean'):
1203 label = repo[None].p1().branch()
1202 label = repo[None].p1().branch()
1204 repo.dirstate.setbranch(label)
1203 repo.dirstate.setbranch(label)
1205 ui.status(_('reset working directory to branch %s\n') % label)
1204 ui.status(_('reset working directory to branch %s\n') % label)
1206 elif label:
1205 elif label:
1207 if not opts.get('force') and label in repo.branchmap():
1206 if not opts.get('force') and label in repo.branchmap():
1208 if label not in [p.branch() for p in repo[None].parents()]:
1207 if label not in [p.branch() for p in repo[None].parents()]:
1209 raise error.Abort(_('a branch of the same name already'
1208 raise error.Abort(_('a branch of the same name already'
1210 ' exists'),
1209 ' exists'),
1211 # i18n: "it" refers to an existing branch
1210 # i18n: "it" refers to an existing branch
1212 hint=_("use 'hg update' to switch to it"))
1211 hint=_("use 'hg update' to switch to it"))
1213 scmutil.checknewlabel(repo, label, 'branch')
1212 scmutil.checknewlabel(repo, label, 'branch')
1214 repo.dirstate.setbranch(label)
1213 repo.dirstate.setbranch(label)
1215 ui.status(_('marked working directory as branch %s\n') % label)
1214 ui.status(_('marked working directory as branch %s\n') % label)
1216
1215
1217 # find any open named branches aside from default
1216 # find any open named branches aside from default
1218 others = [n for n, h, t, c in repo.branchmap().iterbranches()
1217 others = [n for n, h, t, c in repo.branchmap().iterbranches()
1219 if n != "default" and not c]
1218 if n != "default" and not c]
1220 if not others:
1219 if not others:
1221 ui.status(_('(branches are permanent and global, '
1220 ui.status(_('(branches are permanent and global, '
1222 'did you want a bookmark?)\n'))
1221 'did you want a bookmark?)\n'))
1223
1222
1224 @command('branches',
1223 @command('branches',
1225 [('a', 'active', False,
1224 [('a', 'active', False,
1226 _('show only branches that have unmerged heads (DEPRECATED)')),
1225 _('show only branches that have unmerged heads (DEPRECATED)')),
1227 ('c', 'closed', False, _('show normal and closed branches')),
1226 ('c', 'closed', False, _('show normal and closed branches')),
1228 ] + formatteropts,
1227 ] + formatteropts,
1229 _('[-c]'))
1228 _('[-c]'))
1230 def branches(ui, repo, active=False, closed=False, **opts):
1229 def branches(ui, repo, active=False, closed=False, **opts):
1231 """list repository named branches
1230 """list repository named branches
1232
1231
1233 List the repository's named branches, indicating which ones are
1232 List the repository's named branches, indicating which ones are
1234 inactive. If -c/--closed is specified, also list branches which have
1233 inactive. If -c/--closed is specified, also list branches which have
1235 been marked closed (see :hg:`commit --close-branch`).
1234 been marked closed (see :hg:`commit --close-branch`).
1236
1235
1237 Use the command :hg:`update` to switch to an existing branch.
1236 Use the command :hg:`update` to switch to an existing branch.
1238
1237
1239 Returns 0.
1238 Returns 0.
1240 """
1239 """
1241
1240
1242 fm = ui.formatter('branches', opts)
1241 fm = ui.formatter('branches', opts)
1243 hexfunc = fm.hexfunc
1242 hexfunc = fm.hexfunc
1244
1243
1245 allheads = set(repo.heads())
1244 allheads = set(repo.heads())
1246 branches = []
1245 branches = []
1247 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1246 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1248 isactive = not isclosed and bool(set(heads) & allheads)
1247 isactive = not isclosed and bool(set(heads) & allheads)
1249 branches.append((tag, repo[tip], isactive, not isclosed))
1248 branches.append((tag, repo[tip], isactive, not isclosed))
1250 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]),
1249 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]),
1251 reverse=True)
1250 reverse=True)
1252
1251
1253 for tag, ctx, isactive, isopen in branches:
1252 for tag, ctx, isactive, isopen in branches:
1254 if active and not isactive:
1253 if active and not isactive:
1255 continue
1254 continue
1256 if isactive:
1255 if isactive:
1257 label = 'branches.active'
1256 label = 'branches.active'
1258 notice = ''
1257 notice = ''
1259 elif not isopen:
1258 elif not isopen:
1260 if not closed:
1259 if not closed:
1261 continue
1260 continue
1262 label = 'branches.closed'
1261 label = 'branches.closed'
1263 notice = _(' (closed)')
1262 notice = _(' (closed)')
1264 else:
1263 else:
1265 label = 'branches.inactive'
1264 label = 'branches.inactive'
1266 notice = _(' (inactive)')
1265 notice = _(' (inactive)')
1267 current = (tag == repo.dirstate.branch())
1266 current = (tag == repo.dirstate.branch())
1268 if current:
1267 if current:
1269 label = 'branches.current'
1268 label = 'branches.current'
1270
1269
1271 fm.startitem()
1270 fm.startitem()
1272 fm.write('branch', '%s', tag, label=label)
1271 fm.write('branch', '%s', tag, label=label)
1273 rev = ctx.rev()
1272 rev = ctx.rev()
1274 padsize = max(31 - len(str(rev)) - encoding.colwidth(tag), 0)
1273 padsize = max(31 - len(str(rev)) - encoding.colwidth(tag), 0)
1275 fmt = ' ' * padsize + ' %d:%s'
1274 fmt = ' ' * padsize + ' %d:%s'
1276 fm.condwrite(not ui.quiet, 'rev node', fmt, rev, hexfunc(ctx.node()),
1275 fm.condwrite(not ui.quiet, 'rev node', fmt, rev, hexfunc(ctx.node()),
1277 label='log.changeset changeset.%s' % ctx.phasestr())
1276 label='log.changeset changeset.%s' % ctx.phasestr())
1278 fm.data(active=isactive, closed=not isopen, current=current)
1277 fm.data(active=isactive, closed=not isopen, current=current)
1279 if not ui.quiet:
1278 if not ui.quiet:
1280 fm.plain(notice)
1279 fm.plain(notice)
1281 fm.plain('\n')
1280 fm.plain('\n')
1282 fm.end()
1281 fm.end()
1283
1282
1284 @command('bundle',
1283 @command('bundle',
1285 [('f', 'force', None, _('run even when the destination is unrelated')),
1284 [('f', 'force', None, _('run even when the destination is unrelated')),
1286 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
1285 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
1287 _('REV')),
1286 _('REV')),
1288 ('b', 'branch', [], _('a specific branch you would like to bundle'),
1287 ('b', 'branch', [], _('a specific branch you would like to bundle'),
1289 _('BRANCH')),
1288 _('BRANCH')),
1290 ('', 'base', [],
1289 ('', 'base', [],
1291 _('a base changeset assumed to be available at the destination'),
1290 _('a base changeset assumed to be available at the destination'),
1292 _('REV')),
1291 _('REV')),
1293 ('a', 'all', None, _('bundle all changesets in the repository')),
1292 ('a', 'all', None, _('bundle all changesets in the repository')),
1294 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
1293 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
1295 ] + remoteopts,
1294 ] + remoteopts,
1296 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]'))
1295 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]'))
1297 def bundle(ui, repo, fname, dest=None, **opts):
1296 def bundle(ui, repo, fname, dest=None, **opts):
1298 """create a changegroup file
1297 """create a changegroup file
1299
1298
1300 Generate a changegroup file collecting changesets to be added
1299 Generate a changegroup file collecting changesets to be added
1301 to a repository.
1300 to a repository.
1302
1301
1303 To create a bundle containing all changesets, use -a/--all
1302 To create a bundle containing all changesets, use -a/--all
1304 (or --base null). Otherwise, hg assumes the destination will have
1303 (or --base null). Otherwise, hg assumes the destination will have
1305 all the nodes you specify with --base parameters. Otherwise, hg
1304 all the nodes you specify with --base parameters. Otherwise, hg
1306 will assume the repository has all the nodes in destination, or
1305 will assume the repository has all the nodes in destination, or
1307 default-push/default if no destination is specified.
1306 default-push/default if no destination is specified.
1308
1307
1309 You can change bundle format with the -t/--type option. You can
1308 You can change bundle format with the -t/--type option. You can
1310 specify a compression, a bundle version or both using a dash
1309 specify a compression, a bundle version or both using a dash
1311 (comp-version). The available compression methods are: none, bzip2,
1310 (comp-version). The available compression methods are: none, bzip2,
1312 and gzip (by default, bundles are compressed using bzip2). The
1311 and gzip (by default, bundles are compressed using bzip2). The
1313 available formats are: v1, v2 (default to most suitable).
1312 available formats are: v1, v2 (default to most suitable).
1314
1313
1315 The bundle file can then be transferred using conventional means
1314 The bundle file can then be transferred using conventional means
1316 and applied to another repository with the unbundle or pull
1315 and applied to another repository with the unbundle or pull
1317 command. This is useful when direct push and pull are not
1316 command. This is useful when direct push and pull are not
1318 available or when exporting an entire repository is undesirable.
1317 available or when exporting an entire repository is undesirable.
1319
1318
1320 Applying bundles preserves all changeset contents including
1319 Applying bundles preserves all changeset contents including
1321 permissions, copy/rename information, and revision history.
1320 permissions, copy/rename information, and revision history.
1322
1321
1323 Returns 0 on success, 1 if no changes found.
1322 Returns 0 on success, 1 if no changes found.
1324 """
1323 """
1325 revs = None
1324 revs = None
1326 if 'rev' in opts:
1325 if 'rev' in opts:
1327 revstrings = opts['rev']
1326 revstrings = opts['rev']
1328 revs = scmutil.revrange(repo, revstrings)
1327 revs = scmutil.revrange(repo, revstrings)
1329 if revstrings and not revs:
1328 if revstrings and not revs:
1330 raise error.Abort(_('no commits to bundle'))
1329 raise error.Abort(_('no commits to bundle'))
1331
1330
1332 bundletype = opts.get('type', 'bzip2').lower()
1331 bundletype = opts.get('type', 'bzip2').lower()
1333 try:
1332 try:
1334 bcompression, cgversion, params = exchange.parsebundlespec(
1333 bcompression, cgversion, params = exchange.parsebundlespec(
1335 repo, bundletype, strict=False)
1334 repo, bundletype, strict=False)
1336 except error.UnsupportedBundleSpecification as e:
1335 except error.UnsupportedBundleSpecification as e:
1337 raise error.Abort(str(e),
1336 raise error.Abort(str(e),
1338 hint=_("see 'hg help bundle' for supported "
1337 hint=_("see 'hg help bundle' for supported "
1339 "values for --type"))
1338 "values for --type"))
1340
1339
1341 # Packed bundles are a pseudo bundle format for now.
1340 # Packed bundles are a pseudo bundle format for now.
1342 if cgversion == 's1':
1341 if cgversion == 's1':
1343 raise error.Abort(_('packed bundles cannot be produced by "hg bundle"'),
1342 raise error.Abort(_('packed bundles cannot be produced by "hg bundle"'),
1344 hint=_("use 'hg debugcreatestreamclonebundle'"))
1343 hint=_("use 'hg debugcreatestreamclonebundle'"))
1345
1344
1346 if opts.get('all'):
1345 if opts.get('all'):
1347 if dest:
1346 if dest:
1348 raise error.Abort(_("--all is incompatible with specifying "
1347 raise error.Abort(_("--all is incompatible with specifying "
1349 "a destination"))
1348 "a destination"))
1350 if opts.get('base'):
1349 if opts.get('base'):
1351 ui.warn(_("ignoring --base because --all was specified\n"))
1350 ui.warn(_("ignoring --base because --all was specified\n"))
1352 base = ['null']
1351 base = ['null']
1353 else:
1352 else:
1354 base = scmutil.revrange(repo, opts.get('base'))
1353 base = scmutil.revrange(repo, opts.get('base'))
1355 # TODO: get desired bundlecaps from command line.
1354 # TODO: get desired bundlecaps from command line.
1356 bundlecaps = None
1355 bundlecaps = None
1357 if cgversion not in changegroup.supportedoutgoingversions(repo):
1356 if cgversion not in changegroup.supportedoutgoingversions(repo):
1358 raise error.Abort(_("repository does not support bundle version %s") %
1357 raise error.Abort(_("repository does not support bundle version %s") %
1359 cgversion)
1358 cgversion)
1360
1359
1361 if base:
1360 if base:
1362 if dest:
1361 if dest:
1363 raise error.Abort(_("--base is incompatible with specifying "
1362 raise error.Abort(_("--base is incompatible with specifying "
1364 "a destination"))
1363 "a destination"))
1365 common = [repo.lookup(rev) for rev in base]
1364 common = [repo.lookup(rev) for rev in base]
1366 heads = revs and map(repo.lookup, revs) or None
1365 heads = revs and map(repo.lookup, revs) or None
1367 outgoing = discovery.outgoing(repo, common, heads)
1366 outgoing = discovery.outgoing(repo, common, heads)
1368 cg = changegroup.getchangegroup(repo, 'bundle', outgoing,
1367 cg = changegroup.getchangegroup(repo, 'bundle', outgoing,
1369 bundlecaps=bundlecaps,
1368 bundlecaps=bundlecaps,
1370 version=cgversion)
1369 version=cgversion)
1371 outgoing = None
1370 outgoing = None
1372 else:
1371 else:
1373 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1372 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1374 dest, branches = hg.parseurl(dest, opts.get('branch'))
1373 dest, branches = hg.parseurl(dest, opts.get('branch'))
1375 other = hg.peer(repo, opts, dest)
1374 other = hg.peer(repo, opts, dest)
1376 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1375 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1377 heads = revs and map(repo.lookup, revs) or revs
1376 heads = revs and map(repo.lookup, revs) or revs
1378 outgoing = discovery.findcommonoutgoing(repo, other,
1377 outgoing = discovery.findcommonoutgoing(repo, other,
1379 onlyheads=heads,
1378 onlyheads=heads,
1380 force=opts.get('force'),
1379 force=opts.get('force'),
1381 portable=True)
1380 portable=True)
1382 cg = changegroup.getlocalchangegroup(repo, 'bundle', outgoing,
1381 cg = changegroup.getlocalchangegroup(repo, 'bundle', outgoing,
1383 bundlecaps, version=cgversion)
1382 bundlecaps, version=cgversion)
1384 if not cg:
1383 if not cg:
1385 scmutil.nochangesfound(ui, repo, outgoing and outgoing.excluded)
1384 scmutil.nochangesfound(ui, repo, outgoing and outgoing.excluded)
1386 return 1
1385 return 1
1387
1386
1388 if cgversion == '01': #bundle1
1387 if cgversion == '01': #bundle1
1389 if bcompression is None:
1388 if bcompression is None:
1390 bcompression = 'UN'
1389 bcompression = 'UN'
1391 bversion = 'HG10' + bcompression
1390 bversion = 'HG10' + bcompression
1392 bcompression = None
1391 bcompression = None
1393 else:
1392 else:
1394 assert cgversion == '02'
1393 assert cgversion == '02'
1395 bversion = 'HG20'
1394 bversion = 'HG20'
1396
1395
1397 bundle2.writebundle(ui, cg, fname, bversion, compression=bcompression)
1396 bundle2.writebundle(ui, cg, fname, bversion, compression=bcompression)
1398
1397
1399 @command('cat',
1398 @command('cat',
1400 [('o', 'output', '',
1399 [('o', 'output', '',
1401 _('print output to file with formatted name'), _('FORMAT')),
1400 _('print output to file with formatted name'), _('FORMAT')),
1402 ('r', 'rev', '', _('print the given revision'), _('REV')),
1401 ('r', 'rev', '', _('print the given revision'), _('REV')),
1403 ('', 'decode', None, _('apply any matching decode filter')),
1402 ('', 'decode', None, _('apply any matching decode filter')),
1404 ] + walkopts,
1403 ] + walkopts,
1405 _('[OPTION]... FILE...'),
1404 _('[OPTION]... FILE...'),
1406 inferrepo=True)
1405 inferrepo=True)
1407 def cat(ui, repo, file1, *pats, **opts):
1406 def cat(ui, repo, file1, *pats, **opts):
1408 """output the current or given revision of files
1407 """output the current or given revision of files
1409
1408
1410 Print the specified files as they were at the given revision. If
1409 Print the specified files as they were at the given revision. If
1411 no revision is given, the parent of the working directory is used.
1410 no revision is given, the parent of the working directory is used.
1412
1411
1413 Output may be to a file, in which case the name of the file is
1412 Output may be to a file, in which case the name of the file is
1414 given using a format string. The formatting rules as follows:
1413 given using a format string. The formatting rules as follows:
1415
1414
1416 :``%%``: literal "%" character
1415 :``%%``: literal "%" character
1417 :``%s``: basename of file being printed
1416 :``%s``: basename of file being printed
1418 :``%d``: dirname of file being printed, or '.' if in repository root
1417 :``%d``: dirname of file being printed, or '.' if in repository root
1419 :``%p``: root-relative path name of file being printed
1418 :``%p``: root-relative path name of file being printed
1420 :``%H``: changeset hash (40 hexadecimal digits)
1419 :``%H``: changeset hash (40 hexadecimal digits)
1421 :``%R``: changeset revision number
1420 :``%R``: changeset revision number
1422 :``%h``: short-form changeset hash (12 hexadecimal digits)
1421 :``%h``: short-form changeset hash (12 hexadecimal digits)
1423 :``%r``: zero-padded changeset revision number
1422 :``%r``: zero-padded changeset revision number
1424 :``%b``: basename of the exporting repository
1423 :``%b``: basename of the exporting repository
1425
1424
1426 Returns 0 on success.
1425 Returns 0 on success.
1427 """
1426 """
1428 ctx = scmutil.revsingle(repo, opts.get('rev'))
1427 ctx = scmutil.revsingle(repo, opts.get('rev'))
1429 m = scmutil.match(ctx, (file1,) + pats, opts)
1428 m = scmutil.match(ctx, (file1,) + pats, opts)
1430
1429
1431 return cmdutil.cat(ui, repo, ctx, m, '', **opts)
1430 return cmdutil.cat(ui, repo, ctx, m, '', **opts)
1432
1431
1433 @command('^clone',
1432 @command('^clone',
1434 [('U', 'noupdate', None, _('the clone will include an empty working '
1433 [('U', 'noupdate', None, _('the clone will include an empty working '
1435 'directory (only a repository)')),
1434 'directory (only a repository)')),
1436 ('u', 'updaterev', '', _('revision, tag, or branch to check out'),
1435 ('u', 'updaterev', '', _('revision, tag, or branch to check out'),
1437 _('REV')),
1436 _('REV')),
1438 ('r', 'rev', [], _('include the specified changeset'), _('REV')),
1437 ('r', 'rev', [], _('include the specified changeset'), _('REV')),
1439 ('b', 'branch', [], _('clone only the specified branch'), _('BRANCH')),
1438 ('b', 'branch', [], _('clone only the specified branch'), _('BRANCH')),
1440 ('', 'pull', None, _('use pull protocol to copy metadata')),
1439 ('', 'pull', None, _('use pull protocol to copy metadata')),
1441 ('', 'uncompressed', None, _('use uncompressed transfer (fast over LAN)')),
1440 ('', 'uncompressed', None, _('use uncompressed transfer (fast over LAN)')),
1442 ] + remoteopts,
1441 ] + remoteopts,
1443 _('[OPTION]... SOURCE [DEST]'),
1442 _('[OPTION]... SOURCE [DEST]'),
1444 norepo=True)
1443 norepo=True)
1445 def clone(ui, source, dest=None, **opts):
1444 def clone(ui, source, dest=None, **opts):
1446 """make a copy of an existing repository
1445 """make a copy of an existing repository
1447
1446
1448 Create a copy of an existing repository in a new directory.
1447 Create a copy of an existing repository in a new directory.
1449
1448
1450 If no destination directory name is specified, it defaults to the
1449 If no destination directory name is specified, it defaults to the
1451 basename of the source.
1450 basename of the source.
1452
1451
1453 The location of the source is added to the new repository's
1452 The location of the source is added to the new repository's
1454 ``.hg/hgrc`` file, as the default to be used for future pulls.
1453 ``.hg/hgrc`` file, as the default to be used for future pulls.
1455
1454
1456 Only local paths and ``ssh://`` URLs are supported as
1455 Only local paths and ``ssh://`` URLs are supported as
1457 destinations. For ``ssh://`` destinations, no working directory or
1456 destinations. For ``ssh://`` destinations, no working directory or
1458 ``.hg/hgrc`` will be created on the remote side.
1457 ``.hg/hgrc`` will be created on the remote side.
1459
1458
1460 If the source repository has a bookmark called '@' set, that
1459 If the source repository has a bookmark called '@' set, that
1461 revision will be checked out in the new repository by default.
1460 revision will be checked out in the new repository by default.
1462
1461
1463 To check out a particular version, use -u/--update, or
1462 To check out a particular version, use -u/--update, or
1464 -U/--noupdate to create a clone with no working directory.
1463 -U/--noupdate to create a clone with no working directory.
1465
1464
1466 To pull only a subset of changesets, specify one or more revisions
1465 To pull only a subset of changesets, specify one or more revisions
1467 identifiers with -r/--rev or branches with -b/--branch. The
1466 identifiers with -r/--rev or branches with -b/--branch. The
1468 resulting clone will contain only the specified changesets and
1467 resulting clone will contain only the specified changesets and
1469 their ancestors. These options (or 'clone src#rev dest') imply
1468 their ancestors. These options (or 'clone src#rev dest') imply
1470 --pull, even for local source repositories.
1469 --pull, even for local source repositories.
1471
1470
1472 .. note::
1471 .. note::
1473
1472
1474 Specifying a tag will include the tagged changeset but not the
1473 Specifying a tag will include the tagged changeset but not the
1475 changeset containing the tag.
1474 changeset containing the tag.
1476
1475
1477 .. container:: verbose
1476 .. container:: verbose
1478
1477
1479 For efficiency, hardlinks are used for cloning whenever the
1478 For efficiency, hardlinks are used for cloning whenever the
1480 source and destination are on the same filesystem (note this
1479 source and destination are on the same filesystem (note this
1481 applies only to the repository data, not to the working
1480 applies only to the repository data, not to the working
1482 directory). Some filesystems, such as AFS, implement hardlinking
1481 directory). Some filesystems, such as AFS, implement hardlinking
1483 incorrectly, but do not report errors. In these cases, use the
1482 incorrectly, but do not report errors. In these cases, use the
1484 --pull option to avoid hardlinking.
1483 --pull option to avoid hardlinking.
1485
1484
1486 In some cases, you can clone repositories and the working
1485 In some cases, you can clone repositories and the working
1487 directory using full hardlinks with ::
1486 directory using full hardlinks with ::
1488
1487
1489 $ cp -al REPO REPOCLONE
1488 $ cp -al REPO REPOCLONE
1490
1489
1491 This is the fastest way to clone, but it is not always safe. The
1490 This is the fastest way to clone, but it is not always safe. The
1492 operation is not atomic (making sure REPO is not modified during
1491 operation is not atomic (making sure REPO is not modified during
1493 the operation is up to you) and you have to make sure your
1492 the operation is up to you) and you have to make sure your
1494 editor breaks hardlinks (Emacs and most Linux Kernel tools do
1493 editor breaks hardlinks (Emacs and most Linux Kernel tools do
1495 so). Also, this is not compatible with certain extensions that
1494 so). Also, this is not compatible with certain extensions that
1496 place their metadata under the .hg directory, such as mq.
1495 place their metadata under the .hg directory, such as mq.
1497
1496
1498 Mercurial will update the working directory to the first applicable
1497 Mercurial will update the working directory to the first applicable
1499 revision from this list:
1498 revision from this list:
1500
1499
1501 a) null if -U or the source repository has no changesets
1500 a) null if -U or the source repository has no changesets
1502 b) if -u . and the source repository is local, the first parent of
1501 b) if -u . and the source repository is local, the first parent of
1503 the source repository's working directory
1502 the source repository's working directory
1504 c) the changeset specified with -u (if a branch name, this means the
1503 c) the changeset specified with -u (if a branch name, this means the
1505 latest head of that branch)
1504 latest head of that branch)
1506 d) the changeset specified with -r
1505 d) the changeset specified with -r
1507 e) the tipmost head specified with -b
1506 e) the tipmost head specified with -b
1508 f) the tipmost head specified with the url#branch source syntax
1507 f) the tipmost head specified with the url#branch source syntax
1509 g) the revision marked with the '@' bookmark, if present
1508 g) the revision marked with the '@' bookmark, if present
1510 h) the tipmost head of the default branch
1509 h) the tipmost head of the default branch
1511 i) tip
1510 i) tip
1512
1511
1513 When cloning from servers that support it, Mercurial may fetch
1512 When cloning from servers that support it, Mercurial may fetch
1514 pre-generated data from a server-advertised URL. When this is done,
1513 pre-generated data from a server-advertised URL. When this is done,
1515 hooks operating on incoming changesets and changegroups may fire twice,
1514 hooks operating on incoming changesets and changegroups may fire twice,
1516 once for the bundle fetched from the URL and another for any additional
1515 once for the bundle fetched from the URL and another for any additional
1517 data not fetched from this URL. In addition, if an error occurs, the
1516 data not fetched from this URL. In addition, if an error occurs, the
1518 repository may be rolled back to a partial clone. This behavior may
1517 repository may be rolled back to a partial clone. This behavior may
1519 change in future releases. See :hg:`help -e clonebundles` for more.
1518 change in future releases. See :hg:`help -e clonebundles` for more.
1520
1519
1521 Examples:
1520 Examples:
1522
1521
1523 - clone a remote repository to a new directory named hg/::
1522 - clone a remote repository to a new directory named hg/::
1524
1523
1525 hg clone https://www.mercurial-scm.org/repo/hg/
1524 hg clone https://www.mercurial-scm.org/repo/hg/
1526
1525
1527 - create a lightweight local clone::
1526 - create a lightweight local clone::
1528
1527
1529 hg clone project/ project-feature/
1528 hg clone project/ project-feature/
1530
1529
1531 - clone from an absolute path on an ssh server (note double-slash)::
1530 - clone from an absolute path on an ssh server (note double-slash)::
1532
1531
1533 hg clone ssh://user@server//home/projects/alpha/
1532 hg clone ssh://user@server//home/projects/alpha/
1534
1533
1535 - do a high-speed clone over a LAN while checking out a
1534 - do a high-speed clone over a LAN while checking out a
1536 specified version::
1535 specified version::
1537
1536
1538 hg clone --uncompressed http://server/repo -u 1.5
1537 hg clone --uncompressed http://server/repo -u 1.5
1539
1538
1540 - create a repository without changesets after a particular revision::
1539 - create a repository without changesets after a particular revision::
1541
1540
1542 hg clone -r 04e544 experimental/ good/
1541 hg clone -r 04e544 experimental/ good/
1543
1542
1544 - clone (and track) a particular named branch::
1543 - clone (and track) a particular named branch::
1545
1544
1546 hg clone https://www.mercurial-scm.org/repo/hg/#stable
1545 hg clone https://www.mercurial-scm.org/repo/hg/#stable
1547
1546
1548 See :hg:`help urls` for details on specifying URLs.
1547 See :hg:`help urls` for details on specifying URLs.
1549
1548
1550 Returns 0 on success.
1549 Returns 0 on success.
1551 """
1550 """
1552 if opts.get('noupdate') and opts.get('updaterev'):
1551 if opts.get('noupdate') and opts.get('updaterev'):
1553 raise error.Abort(_("cannot specify both --noupdate and --updaterev"))
1552 raise error.Abort(_("cannot specify both --noupdate and --updaterev"))
1554
1553
1555 r = hg.clone(ui, opts, source, dest,
1554 r = hg.clone(ui, opts, source, dest,
1556 pull=opts.get('pull'),
1555 pull=opts.get('pull'),
1557 stream=opts.get('uncompressed'),
1556 stream=opts.get('uncompressed'),
1558 rev=opts.get('rev'),
1557 rev=opts.get('rev'),
1559 update=opts.get('updaterev') or not opts.get('noupdate'),
1558 update=opts.get('updaterev') or not opts.get('noupdate'),
1560 branch=opts.get('branch'),
1559 branch=opts.get('branch'),
1561 shareopts=opts.get('shareopts'))
1560 shareopts=opts.get('shareopts'))
1562
1561
1563 return r is None
1562 return r is None
1564
1563
1565 @command('^commit|ci',
1564 @command('^commit|ci',
1566 [('A', 'addremove', None,
1565 [('A', 'addremove', None,
1567 _('mark new/missing files as added/removed before committing')),
1566 _('mark new/missing files as added/removed before committing')),
1568 ('', 'close-branch', None,
1567 ('', 'close-branch', None,
1569 _('mark a branch head as closed')),
1568 _('mark a branch head as closed')),
1570 ('', 'amend', None, _('amend the parent of the working directory')),
1569 ('', 'amend', None, _('amend the parent of the working directory')),
1571 ('s', 'secret', None, _('use the secret phase for committing')),
1570 ('s', 'secret', None, _('use the secret phase for committing')),
1572 ('e', 'edit', None, _('invoke editor on commit messages')),
1571 ('e', 'edit', None, _('invoke editor on commit messages')),
1573 ('i', 'interactive', None, _('use interactive mode')),
1572 ('i', 'interactive', None, _('use interactive mode')),
1574 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1573 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1575 _('[OPTION]... [FILE]...'),
1574 _('[OPTION]... [FILE]...'),
1576 inferrepo=True)
1575 inferrepo=True)
1577 def commit(ui, repo, *pats, **opts):
1576 def commit(ui, repo, *pats, **opts):
1578 """commit the specified files or all outstanding changes
1577 """commit the specified files or all outstanding changes
1579
1578
1580 Commit changes to the given files into the repository. Unlike a
1579 Commit changes to the given files into the repository. Unlike a
1581 centralized SCM, this operation is a local operation. See
1580 centralized SCM, this operation is a local operation. See
1582 :hg:`push` for a way to actively distribute your changes.
1581 :hg:`push` for a way to actively distribute your changes.
1583
1582
1584 If a list of files is omitted, all changes reported by :hg:`status`
1583 If a list of files is omitted, all changes reported by :hg:`status`
1585 will be committed.
1584 will be committed.
1586
1585
1587 If you are committing the result of a merge, do not provide any
1586 If you are committing the result of a merge, do not provide any
1588 filenames or -I/-X filters.
1587 filenames or -I/-X filters.
1589
1588
1590 If no commit message is specified, Mercurial starts your
1589 If no commit message is specified, Mercurial starts your
1591 configured editor where you can enter a message. In case your
1590 configured editor where you can enter a message. In case your
1592 commit fails, you will find a backup of your message in
1591 commit fails, you will find a backup of your message in
1593 ``.hg/last-message.txt``.
1592 ``.hg/last-message.txt``.
1594
1593
1595 The --close-branch flag can be used to mark the current branch
1594 The --close-branch flag can be used to mark the current branch
1596 head closed. When all heads of a branch are closed, the branch
1595 head closed. When all heads of a branch are closed, the branch
1597 will be considered closed and no longer listed.
1596 will be considered closed and no longer listed.
1598
1597
1599 The --amend flag can be used to amend the parent of the
1598 The --amend flag can be used to amend the parent of the
1600 working directory with a new commit that contains the changes
1599 working directory with a new commit that contains the changes
1601 in the parent in addition to those currently reported by :hg:`status`,
1600 in the parent in addition to those currently reported by :hg:`status`,
1602 if there are any. The old commit is stored in a backup bundle in
1601 if there are any. The old commit is stored in a backup bundle in
1603 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1602 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1604 on how to restore it).
1603 on how to restore it).
1605
1604
1606 Message, user and date are taken from the amended commit unless
1605 Message, user and date are taken from the amended commit unless
1607 specified. When a message isn't specified on the command line,
1606 specified. When a message isn't specified on the command line,
1608 the editor will open with the message of the amended commit.
1607 the editor will open with the message of the amended commit.
1609
1608
1610 It is not possible to amend public changesets (see :hg:`help phases`)
1609 It is not possible to amend public changesets (see :hg:`help phases`)
1611 or changesets that have children.
1610 or changesets that have children.
1612
1611
1613 See :hg:`help dates` for a list of formats valid for -d/--date.
1612 See :hg:`help dates` for a list of formats valid for -d/--date.
1614
1613
1615 Returns 0 on success, 1 if nothing changed.
1614 Returns 0 on success, 1 if nothing changed.
1616
1615
1617 .. container:: verbose
1616 .. container:: verbose
1618
1617
1619 Examples:
1618 Examples:
1620
1619
1621 - commit all files ending in .py::
1620 - commit all files ending in .py::
1622
1621
1623 hg commit --include "set:**.py"
1622 hg commit --include "set:**.py"
1624
1623
1625 - commit all non-binary files::
1624 - commit all non-binary files::
1626
1625
1627 hg commit --exclude "set:binary()"
1626 hg commit --exclude "set:binary()"
1628
1627
1629 - amend the current commit and set the date to now::
1628 - amend the current commit and set the date to now::
1630
1629
1631 hg commit --amend --date now
1630 hg commit --amend --date now
1632 """
1631 """
1633 wlock = lock = None
1632 wlock = lock = None
1634 try:
1633 try:
1635 wlock = repo.wlock()
1634 wlock = repo.wlock()
1636 lock = repo.lock()
1635 lock = repo.lock()
1637 return _docommit(ui, repo, *pats, **opts)
1636 return _docommit(ui, repo, *pats, **opts)
1638 finally:
1637 finally:
1639 release(lock, wlock)
1638 release(lock, wlock)
1640
1639
1641 def _docommit(ui, repo, *pats, **opts):
1640 def _docommit(ui, repo, *pats, **opts):
1642 if opts.get('interactive'):
1641 if opts.get('interactive'):
1643 opts.pop('interactive')
1642 opts.pop('interactive')
1644 ret = cmdutil.dorecord(ui, repo, commit, None, False,
1643 ret = cmdutil.dorecord(ui, repo, commit, None, False,
1645 cmdutil.recordfilter, *pats, **opts)
1644 cmdutil.recordfilter, *pats, **opts)
1646 # ret can be 0 (no changes to record) or the value returned by
1645 # ret can be 0 (no changes to record) or the value returned by
1647 # commit(), 1 if nothing changed or None on success.
1646 # commit(), 1 if nothing changed or None on success.
1648 return 1 if ret == 0 else ret
1647 return 1 if ret == 0 else ret
1649
1648
1650 if opts.get('subrepos'):
1649 if opts.get('subrepos'):
1651 if opts.get('amend'):
1650 if opts.get('amend'):
1652 raise error.Abort(_('cannot amend with --subrepos'))
1651 raise error.Abort(_('cannot amend with --subrepos'))
1653 # Let --subrepos on the command line override config setting.
1652 # Let --subrepos on the command line override config setting.
1654 ui.setconfig('ui', 'commitsubrepos', True, 'commit')
1653 ui.setconfig('ui', 'commitsubrepos', True, 'commit')
1655
1654
1656 cmdutil.checkunfinished(repo, commit=True)
1655 cmdutil.checkunfinished(repo, commit=True)
1657
1656
1658 branch = repo[None].branch()
1657 branch = repo[None].branch()
1659 bheads = repo.branchheads(branch)
1658 bheads = repo.branchheads(branch)
1660
1659
1661 extra = {}
1660 extra = {}
1662 if opts.get('close_branch'):
1661 if opts.get('close_branch'):
1663 extra['close'] = 1
1662 extra['close'] = 1
1664
1663
1665 if not bheads:
1664 if not bheads:
1666 raise error.Abort(_('can only close branch heads'))
1665 raise error.Abort(_('can only close branch heads'))
1667 elif opts.get('amend'):
1666 elif opts.get('amend'):
1668 if repo[None].parents()[0].p1().branch() != branch and \
1667 if repo[None].parents()[0].p1().branch() != branch and \
1669 repo[None].parents()[0].p2().branch() != branch:
1668 repo[None].parents()[0].p2().branch() != branch:
1670 raise error.Abort(_('can only close branch heads'))
1669 raise error.Abort(_('can only close branch heads'))
1671
1670
1672 if opts.get('amend'):
1671 if opts.get('amend'):
1673 if ui.configbool('ui', 'commitsubrepos'):
1672 if ui.configbool('ui', 'commitsubrepos'):
1674 raise error.Abort(_('cannot amend with ui.commitsubrepos enabled'))
1673 raise error.Abort(_('cannot amend with ui.commitsubrepos enabled'))
1675
1674
1676 old = repo['.']
1675 old = repo['.']
1677 if not old.mutable():
1676 if not old.mutable():
1678 raise error.Abort(_('cannot amend public changesets'))
1677 raise error.Abort(_('cannot amend public changesets'))
1679 if len(repo[None].parents()) > 1:
1678 if len(repo[None].parents()) > 1:
1680 raise error.Abort(_('cannot amend while merging'))
1679 raise error.Abort(_('cannot amend while merging'))
1681 allowunstable = obsolete.isenabled(repo, obsolete.allowunstableopt)
1680 allowunstable = obsolete.isenabled(repo, obsolete.allowunstableopt)
1682 if not allowunstable and old.children():
1681 if not allowunstable and old.children():
1683 raise error.Abort(_('cannot amend changeset with children'))
1682 raise error.Abort(_('cannot amend changeset with children'))
1684
1683
1685 # Currently histedit gets confused if an amend happens while histedit
1684 # Currently histedit gets confused if an amend happens while histedit
1686 # is in progress. Since we have a checkunfinished command, we are
1685 # is in progress. Since we have a checkunfinished command, we are
1687 # temporarily honoring it.
1686 # temporarily honoring it.
1688 #
1687 #
1689 # Note: eventually this guard will be removed. Please do not expect
1688 # Note: eventually this guard will be removed. Please do not expect
1690 # this behavior to remain.
1689 # this behavior to remain.
1691 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
1690 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
1692 cmdutil.checkunfinished(repo)
1691 cmdutil.checkunfinished(repo)
1693
1692
1694 # commitfunc is used only for temporary amend commit by cmdutil.amend
1693 # commitfunc is used only for temporary amend commit by cmdutil.amend
1695 def commitfunc(ui, repo, message, match, opts):
1694 def commitfunc(ui, repo, message, match, opts):
1696 return repo.commit(message,
1695 return repo.commit(message,
1697 opts.get('user') or old.user(),
1696 opts.get('user') or old.user(),
1698 opts.get('date') or old.date(),
1697 opts.get('date') or old.date(),
1699 match,
1698 match,
1700 extra=extra)
1699 extra=extra)
1701
1700
1702 node = cmdutil.amend(ui, repo, commitfunc, old, extra, pats, opts)
1701 node = cmdutil.amend(ui, repo, commitfunc, old, extra, pats, opts)
1703 if node == old.node():
1702 if node == old.node():
1704 ui.status(_("nothing changed\n"))
1703 ui.status(_("nothing changed\n"))
1705 return 1
1704 return 1
1706 else:
1705 else:
1707 def commitfunc(ui, repo, message, match, opts):
1706 def commitfunc(ui, repo, message, match, opts):
1708 backup = ui.backupconfig('phases', 'new-commit')
1707 backup = ui.backupconfig('phases', 'new-commit')
1709 baseui = repo.baseui
1708 baseui = repo.baseui
1710 basebackup = baseui.backupconfig('phases', 'new-commit')
1709 basebackup = baseui.backupconfig('phases', 'new-commit')
1711 try:
1710 try:
1712 if opts.get('secret'):
1711 if opts.get('secret'):
1713 ui.setconfig('phases', 'new-commit', 'secret', 'commit')
1712 ui.setconfig('phases', 'new-commit', 'secret', 'commit')
1714 # Propagate to subrepos
1713 # Propagate to subrepos
1715 baseui.setconfig('phases', 'new-commit', 'secret', 'commit')
1714 baseui.setconfig('phases', 'new-commit', 'secret', 'commit')
1716
1715
1717 editform = cmdutil.mergeeditform(repo[None], 'commit.normal')
1716 editform = cmdutil.mergeeditform(repo[None], 'commit.normal')
1718 editor = cmdutil.getcommiteditor(editform=editform, **opts)
1717 editor = cmdutil.getcommiteditor(editform=editform, **opts)
1719 return repo.commit(message, opts.get('user'), opts.get('date'),
1718 return repo.commit(message, opts.get('user'), opts.get('date'),
1720 match,
1719 match,
1721 editor=editor,
1720 editor=editor,
1722 extra=extra)
1721 extra=extra)
1723 finally:
1722 finally:
1724 ui.restoreconfig(backup)
1723 ui.restoreconfig(backup)
1725 repo.baseui.restoreconfig(basebackup)
1724 repo.baseui.restoreconfig(basebackup)
1726
1725
1727
1726
1728 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1727 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1729
1728
1730 if not node:
1729 if not node:
1731 stat = cmdutil.postcommitstatus(repo, pats, opts)
1730 stat = cmdutil.postcommitstatus(repo, pats, opts)
1732 if stat[3]:
1731 if stat[3]:
1733 ui.status(_("nothing changed (%d missing files, see "
1732 ui.status(_("nothing changed (%d missing files, see "
1734 "'hg status')\n") % len(stat[3]))
1733 "'hg status')\n") % len(stat[3]))
1735 else:
1734 else:
1736 ui.status(_("nothing changed\n"))
1735 ui.status(_("nothing changed\n"))
1737 return 1
1736 return 1
1738
1737
1739 cmdutil.commitstatus(repo, node, branch, bheads, opts)
1738 cmdutil.commitstatus(repo, node, branch, bheads, opts)
1740
1739
1741 @command('config|showconfig|debugconfig',
1740 @command('config|showconfig|debugconfig',
1742 [('u', 'untrusted', None, _('show untrusted configuration options')),
1741 [('u', 'untrusted', None, _('show untrusted configuration options')),
1743 ('e', 'edit', None, _('edit user config')),
1742 ('e', 'edit', None, _('edit user config')),
1744 ('l', 'local', None, _('edit repository config')),
1743 ('l', 'local', None, _('edit repository config')),
1745 ('g', 'global', None, _('edit global config'))] + formatteropts,
1744 ('g', 'global', None, _('edit global config'))] + formatteropts,
1746 _('[-u] [NAME]...'),
1745 _('[-u] [NAME]...'),
1747 optionalrepo=True)
1746 optionalrepo=True)
1748 def config(ui, repo, *values, **opts):
1747 def config(ui, repo, *values, **opts):
1749 """show combined config settings from all hgrc files
1748 """show combined config settings from all hgrc files
1750
1749
1751 With no arguments, print names and values of all config items.
1750 With no arguments, print names and values of all config items.
1752
1751
1753 With one argument of the form section.name, print just the value
1752 With one argument of the form section.name, print just the value
1754 of that config item.
1753 of that config item.
1755
1754
1756 With multiple arguments, print names and values of all config
1755 With multiple arguments, print names and values of all config
1757 items with matching section names.
1756 items with matching section names.
1758
1757
1759 With --edit, start an editor on the user-level config file. With
1758 With --edit, start an editor on the user-level config file. With
1760 --global, edit the system-wide config file. With --local, edit the
1759 --global, edit the system-wide config file. With --local, edit the
1761 repository-level config file.
1760 repository-level config file.
1762
1761
1763 With --debug, the source (filename and line number) is printed
1762 With --debug, the source (filename and line number) is printed
1764 for each config item.
1763 for each config item.
1765
1764
1766 See :hg:`help config` for more information about config files.
1765 See :hg:`help config` for more information about config files.
1767
1766
1768 Returns 0 on success, 1 if NAME does not exist.
1767 Returns 0 on success, 1 if NAME does not exist.
1769
1768
1770 """
1769 """
1771
1770
1772 if opts.get('edit') or opts.get('local') or opts.get('global'):
1771 if opts.get('edit') or opts.get('local') or opts.get('global'):
1773 if opts.get('local') and opts.get('global'):
1772 if opts.get('local') and opts.get('global'):
1774 raise error.Abort(_("can't use --local and --global together"))
1773 raise error.Abort(_("can't use --local and --global together"))
1775
1774
1776 if opts.get('local'):
1775 if opts.get('local'):
1777 if not repo:
1776 if not repo:
1778 raise error.Abort(_("can't use --local outside a repository"))
1777 raise error.Abort(_("can't use --local outside a repository"))
1779 paths = [repo.join('hgrc')]
1778 paths = [repo.join('hgrc')]
1780 elif opts.get('global'):
1779 elif opts.get('global'):
1781 paths = scmutil.systemrcpath()
1780 paths = scmutil.systemrcpath()
1782 else:
1781 else:
1783 paths = scmutil.userrcpath()
1782 paths = scmutil.userrcpath()
1784
1783
1785 for f in paths:
1784 for f in paths:
1786 if os.path.exists(f):
1785 if os.path.exists(f):
1787 break
1786 break
1788 else:
1787 else:
1789 if opts.get('global'):
1788 if opts.get('global'):
1790 samplehgrc = uimod.samplehgrcs['global']
1789 samplehgrc = uimod.samplehgrcs['global']
1791 elif opts.get('local'):
1790 elif opts.get('local'):
1792 samplehgrc = uimod.samplehgrcs['local']
1791 samplehgrc = uimod.samplehgrcs['local']
1793 else:
1792 else:
1794 samplehgrc = uimod.samplehgrcs['user']
1793 samplehgrc = uimod.samplehgrcs['user']
1795
1794
1796 f = paths[0]
1795 f = paths[0]
1797 fp = open(f, "w")
1796 fp = open(f, "w")
1798 fp.write(samplehgrc)
1797 fp.write(samplehgrc)
1799 fp.close()
1798 fp.close()
1800
1799
1801 editor = ui.geteditor()
1800 editor = ui.geteditor()
1802 ui.system("%s \"%s\"" % (editor, f),
1801 ui.system("%s \"%s\"" % (editor, f),
1803 onerr=error.Abort, errprefix=_("edit failed"))
1802 onerr=error.Abort, errprefix=_("edit failed"))
1804 return
1803 return
1805
1804
1806 fm = ui.formatter('config', opts)
1805 fm = ui.formatter('config', opts)
1807 for f in scmutil.rcpath():
1806 for f in scmutil.rcpath():
1808 ui.debug('read config from: %s\n' % f)
1807 ui.debug('read config from: %s\n' % f)
1809 untrusted = bool(opts.get('untrusted'))
1808 untrusted = bool(opts.get('untrusted'))
1810 if values:
1809 if values:
1811 sections = [v for v in values if '.' not in v]
1810 sections = [v for v in values if '.' not in v]
1812 items = [v for v in values if '.' in v]
1811 items = [v for v in values if '.' in v]
1813 if len(items) > 1 or items and sections:
1812 if len(items) > 1 or items and sections:
1814 raise error.Abort(_('only one config item permitted'))
1813 raise error.Abort(_('only one config item permitted'))
1815 matched = False
1814 matched = False
1816 for section, name, value in ui.walkconfig(untrusted=untrusted):
1815 for section, name, value in ui.walkconfig(untrusted=untrusted):
1817 value = str(value)
1816 value = str(value)
1818 if fm.isplain():
1817 if fm.isplain():
1819 value = value.replace('\n', '\\n')
1818 value = value.replace('\n', '\\n')
1820 entryname = section + '.' + name
1819 entryname = section + '.' + name
1821 if values:
1820 if values:
1822 for v in values:
1821 for v in values:
1823 if v == section:
1822 if v == section:
1824 fm.startitem()
1823 fm.startitem()
1825 fm.condwrite(ui.debugflag, 'source', '%s: ',
1824 fm.condwrite(ui.debugflag, 'source', '%s: ',
1826 ui.configsource(section, name, untrusted))
1825 ui.configsource(section, name, untrusted))
1827 fm.write('name value', '%s=%s\n', entryname, value)
1826 fm.write('name value', '%s=%s\n', entryname, value)
1828 matched = True
1827 matched = True
1829 elif v == entryname:
1828 elif v == entryname:
1830 fm.startitem()
1829 fm.startitem()
1831 fm.condwrite(ui.debugflag, 'source', '%s: ',
1830 fm.condwrite(ui.debugflag, 'source', '%s: ',
1832 ui.configsource(section, name, untrusted))
1831 ui.configsource(section, name, untrusted))
1833 fm.write('value', '%s\n', value)
1832 fm.write('value', '%s\n', value)
1834 fm.data(name=entryname)
1833 fm.data(name=entryname)
1835 matched = True
1834 matched = True
1836 else:
1835 else:
1837 fm.startitem()
1836 fm.startitem()
1838 fm.condwrite(ui.debugflag, 'source', '%s: ',
1837 fm.condwrite(ui.debugflag, 'source', '%s: ',
1839 ui.configsource(section, name, untrusted))
1838 ui.configsource(section, name, untrusted))
1840 fm.write('name value', '%s=%s\n', entryname, value)
1839 fm.write('name value', '%s=%s\n', entryname, value)
1841 matched = True
1840 matched = True
1842 fm.end()
1841 fm.end()
1843 if matched:
1842 if matched:
1844 return 0
1843 return 0
1845 return 1
1844 return 1
1846
1845
1847 @command('copy|cp',
1846 @command('copy|cp',
1848 [('A', 'after', None, _('record a copy that has already occurred')),
1847 [('A', 'after', None, _('record a copy that has already occurred')),
1849 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1848 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1850 ] + walkopts + dryrunopts,
1849 ] + walkopts + dryrunopts,
1851 _('[OPTION]... [SOURCE]... DEST'))
1850 _('[OPTION]... [SOURCE]... DEST'))
1852 def copy(ui, repo, *pats, **opts):
1851 def copy(ui, repo, *pats, **opts):
1853 """mark files as copied for the next commit
1852 """mark files as copied for the next commit
1854
1853
1855 Mark dest as having copies of source files. If dest is a
1854 Mark dest as having copies of source files. If dest is a
1856 directory, copies are put in that directory. If dest is a file,
1855 directory, copies are put in that directory. If dest is a file,
1857 the source must be a single file.
1856 the source must be a single file.
1858
1857
1859 By default, this command copies the contents of files as they
1858 By default, this command copies the contents of files as they
1860 exist in the working directory. If invoked with -A/--after, the
1859 exist in the working directory. If invoked with -A/--after, the
1861 operation is recorded, but no copying is performed.
1860 operation is recorded, but no copying is performed.
1862
1861
1863 This command takes effect with the next commit. To undo a copy
1862 This command takes effect with the next commit. To undo a copy
1864 before that, see :hg:`revert`.
1863 before that, see :hg:`revert`.
1865
1864
1866 Returns 0 on success, 1 if errors are encountered.
1865 Returns 0 on success, 1 if errors are encountered.
1867 """
1866 """
1868 with repo.wlock(False):
1867 with repo.wlock(False):
1869 return cmdutil.copy(ui, repo, pats, opts)
1868 return cmdutil.copy(ui, repo, pats, opts)
1870
1869
1871 @command('debugdag',
1870 @command('debugdag',
1872 [('t', 'tags', None, _('use tags as labels')),
1871 [('t', 'tags', None, _('use tags as labels')),
1873 ('b', 'branches', None, _('annotate with branch names')),
1872 ('b', 'branches', None, _('annotate with branch names')),
1874 ('', 'dots', None, _('use dots for runs')),
1873 ('', 'dots', None, _('use dots for runs')),
1875 ('s', 'spaces', None, _('separate elements by spaces'))],
1874 ('s', 'spaces', None, _('separate elements by spaces'))],
1876 _('[OPTION]... [FILE [REV]...]'),
1875 _('[OPTION]... [FILE [REV]...]'),
1877 optionalrepo=True)
1876 optionalrepo=True)
1878 def debugdag(ui, repo, file_=None, *revs, **opts):
1877 def debugdag(ui, repo, file_=None, *revs, **opts):
1879 """format the changelog or an index DAG as a concise textual description
1878 """format the changelog or an index DAG as a concise textual description
1880
1879
1881 If you pass a revlog index, the revlog's DAG is emitted. If you list
1880 If you pass a revlog index, the revlog's DAG is emitted. If you list
1882 revision numbers, they get labeled in the output as rN.
1881 revision numbers, they get labeled in the output as rN.
1883
1882
1884 Otherwise, the changelog DAG of the current repo is emitted.
1883 Otherwise, the changelog DAG of the current repo is emitted.
1885 """
1884 """
1886 spaces = opts.get('spaces')
1885 spaces = opts.get('spaces')
1887 dots = opts.get('dots')
1886 dots = opts.get('dots')
1888 if file_:
1887 if file_:
1889 rlog = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1888 rlog = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1890 revs = set((int(r) for r in revs))
1889 revs = set((int(r) for r in revs))
1891 def events():
1890 def events():
1892 for r in rlog:
1891 for r in rlog:
1893 yield 'n', (r, list(p for p in rlog.parentrevs(r)
1892 yield 'n', (r, list(p for p in rlog.parentrevs(r)
1894 if p != -1))
1893 if p != -1))
1895 if r in revs:
1894 if r in revs:
1896 yield 'l', (r, "r%i" % r)
1895 yield 'l', (r, "r%i" % r)
1897 elif repo:
1896 elif repo:
1898 cl = repo.changelog
1897 cl = repo.changelog
1899 tags = opts.get('tags')
1898 tags = opts.get('tags')
1900 branches = opts.get('branches')
1899 branches = opts.get('branches')
1901 if tags:
1900 if tags:
1902 labels = {}
1901 labels = {}
1903 for l, n in repo.tags().items():
1902 for l, n in repo.tags().items():
1904 labels.setdefault(cl.rev(n), []).append(l)
1903 labels.setdefault(cl.rev(n), []).append(l)
1905 def events():
1904 def events():
1906 b = "default"
1905 b = "default"
1907 for r in cl:
1906 for r in cl:
1908 if branches:
1907 if branches:
1909 newb = cl.read(cl.node(r))[5]['branch']
1908 newb = cl.read(cl.node(r))[5]['branch']
1910 if newb != b:
1909 if newb != b:
1911 yield 'a', newb
1910 yield 'a', newb
1912 b = newb
1911 b = newb
1913 yield 'n', (r, list(p for p in cl.parentrevs(r)
1912 yield 'n', (r, list(p for p in cl.parentrevs(r)
1914 if p != -1))
1913 if p != -1))
1915 if tags:
1914 if tags:
1916 ls = labels.get(r)
1915 ls = labels.get(r)
1917 if ls:
1916 if ls:
1918 for l in ls:
1917 for l in ls:
1919 yield 'l', (r, l)
1918 yield 'l', (r, l)
1920 else:
1919 else:
1921 raise error.Abort(_('need repo for changelog dag'))
1920 raise error.Abort(_('need repo for changelog dag'))
1922
1921
1923 for line in dagparser.dagtextlines(events(),
1922 for line in dagparser.dagtextlines(events(),
1924 addspaces=spaces,
1923 addspaces=spaces,
1925 wraplabels=True,
1924 wraplabels=True,
1926 wrapannotations=True,
1925 wrapannotations=True,
1927 wrapnonlinear=dots,
1926 wrapnonlinear=dots,
1928 usedots=dots,
1927 usedots=dots,
1929 maxlinewidth=70):
1928 maxlinewidth=70):
1930 ui.write(line)
1929 ui.write(line)
1931 ui.write("\n")
1930 ui.write("\n")
1932
1931
1933 @command('debugdata', debugrevlogopts, _('-c|-m|FILE REV'))
1932 @command('debugdata', debugrevlogopts, _('-c|-m|FILE REV'))
1934 def debugdata(ui, repo, file_, rev=None, **opts):
1933 def debugdata(ui, repo, file_, rev=None, **opts):
1935 """dump the contents of a data file revision"""
1934 """dump the contents of a data file revision"""
1936 if opts.get('changelog') or opts.get('manifest') or opts.get('dir'):
1935 if opts.get('changelog') or opts.get('manifest') or opts.get('dir'):
1937 if rev is not None:
1936 if rev is not None:
1938 raise error.CommandError('debugdata', _('invalid arguments'))
1937 raise error.CommandError('debugdata', _('invalid arguments'))
1939 file_, rev = None, file_
1938 file_, rev = None, file_
1940 elif rev is None:
1939 elif rev is None:
1941 raise error.CommandError('debugdata', _('invalid arguments'))
1940 raise error.CommandError('debugdata', _('invalid arguments'))
1942 r = cmdutil.openrevlog(repo, 'debugdata', file_, opts)
1941 r = cmdutil.openrevlog(repo, 'debugdata', file_, opts)
1943 try:
1942 try:
1944 ui.write(r.revision(r.lookup(rev)))
1943 ui.write(r.revision(r.lookup(rev)))
1945 except KeyError:
1944 except KeyError:
1946 raise error.Abort(_('invalid revision identifier %s') % rev)
1945 raise error.Abort(_('invalid revision identifier %s') % rev)
1947
1946
1948 @command('debugdate',
1947 @command('debugdate',
1949 [('e', 'extended', None, _('try extended date formats'))],
1948 [('e', 'extended', None, _('try extended date formats'))],
1950 _('[-e] DATE [RANGE]'),
1949 _('[-e] DATE [RANGE]'),
1951 norepo=True, optionalrepo=True)
1950 norepo=True, optionalrepo=True)
1952 def debugdate(ui, date, range=None, **opts):
1951 def debugdate(ui, date, range=None, **opts):
1953 """parse and display a date"""
1952 """parse and display a date"""
1954 if opts["extended"]:
1953 if opts["extended"]:
1955 d = util.parsedate(date, util.extendeddateformats)
1954 d = util.parsedate(date, util.extendeddateformats)
1956 else:
1955 else:
1957 d = util.parsedate(date)
1956 d = util.parsedate(date)
1958 ui.write(("internal: %s %s\n") % d)
1957 ui.write(("internal: %s %s\n") % d)
1959 ui.write(("standard: %s\n") % util.datestr(d))
1958 ui.write(("standard: %s\n") % util.datestr(d))
1960 if range:
1959 if range:
1961 m = util.matchdate(range)
1960 m = util.matchdate(range)
1962 ui.write(("match: %s\n") % m(d[0]))
1961 ui.write(("match: %s\n") % m(d[0]))
1963
1962
1964 @command('debugdiscovery',
1963 @command('debugdiscovery',
1965 [('', 'old', None, _('use old-style discovery')),
1964 [('', 'old', None, _('use old-style discovery')),
1966 ('', 'nonheads', None,
1965 ('', 'nonheads', None,
1967 _('use old-style discovery with non-heads included')),
1966 _('use old-style discovery with non-heads included')),
1968 ] + remoteopts,
1967 ] + remoteopts,
1969 _('[-l REV] [-r REV] [-b BRANCH]... [OTHER]'))
1968 _('[-l REV] [-r REV] [-b BRANCH]... [OTHER]'))
1970 def debugdiscovery(ui, repo, remoteurl="default", **opts):
1969 def debugdiscovery(ui, repo, remoteurl="default", **opts):
1971 """runs the changeset discovery protocol in isolation"""
1970 """runs the changeset discovery protocol in isolation"""
1972 remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl),
1971 remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl),
1973 opts.get('branch'))
1972 opts.get('branch'))
1974 remote = hg.peer(repo, opts, remoteurl)
1973 remote = hg.peer(repo, opts, remoteurl)
1975 ui.status(_('comparing with %s\n') % util.hidepassword(remoteurl))
1974 ui.status(_('comparing with %s\n') % util.hidepassword(remoteurl))
1976
1975
1977 # make sure tests are repeatable
1976 # make sure tests are repeatable
1978 random.seed(12323)
1977 random.seed(12323)
1979
1978
1980 def doit(localheads, remoteheads, remote=remote):
1979 def doit(localheads, remoteheads, remote=remote):
1981 if opts.get('old'):
1980 if opts.get('old'):
1982 if localheads:
1981 if localheads:
1983 raise error.Abort('cannot use localheads with old style '
1982 raise error.Abort('cannot use localheads with old style '
1984 'discovery')
1983 'discovery')
1985 if not util.safehasattr(remote, 'branches'):
1984 if not util.safehasattr(remote, 'branches'):
1986 # enable in-client legacy support
1985 # enable in-client legacy support
1987 remote = localrepo.locallegacypeer(remote.local())
1986 remote = localrepo.locallegacypeer(remote.local())
1988 common, _in, hds = treediscovery.findcommonincoming(repo, remote,
1987 common, _in, hds = treediscovery.findcommonincoming(repo, remote,
1989 force=True)
1988 force=True)
1990 common = set(common)
1989 common = set(common)
1991 if not opts.get('nonheads'):
1990 if not opts.get('nonheads'):
1992 ui.write(("unpruned common: %s\n") %
1991 ui.write(("unpruned common: %s\n") %
1993 " ".join(sorted(short(n) for n in common)))
1992 " ".join(sorted(short(n) for n in common)))
1994 dag = dagutil.revlogdag(repo.changelog)
1993 dag = dagutil.revlogdag(repo.changelog)
1995 all = dag.ancestorset(dag.internalizeall(common))
1994 all = dag.ancestorset(dag.internalizeall(common))
1996 common = dag.externalizeall(dag.headsetofconnecteds(all))
1995 common = dag.externalizeall(dag.headsetofconnecteds(all))
1997 else:
1996 else:
1998 common, any, hds = setdiscovery.findcommonheads(ui, repo, remote)
1997 common, any, hds = setdiscovery.findcommonheads(ui, repo, remote)
1999 common = set(common)
1998 common = set(common)
2000 rheads = set(hds)
1999 rheads = set(hds)
2001 lheads = set(repo.heads())
2000 lheads = set(repo.heads())
2002 ui.write(("common heads: %s\n") %
2001 ui.write(("common heads: %s\n") %
2003 " ".join(sorted(short(n) for n in common)))
2002 " ".join(sorted(short(n) for n in common)))
2004 if lheads <= common:
2003 if lheads <= common:
2005 ui.write(("local is subset\n"))
2004 ui.write(("local is subset\n"))
2006 elif rheads <= common:
2005 elif rheads <= common:
2007 ui.write(("remote is subset\n"))
2006 ui.write(("remote is subset\n"))
2008
2007
2009 serverlogs = opts.get('serverlog')
2008 serverlogs = opts.get('serverlog')
2010 if serverlogs:
2009 if serverlogs:
2011 for filename in serverlogs:
2010 for filename in serverlogs:
2012 with open(filename, 'r') as logfile:
2011 with open(filename, 'r') as logfile:
2013 line = logfile.readline()
2012 line = logfile.readline()
2014 while line:
2013 while line:
2015 parts = line.strip().split(';')
2014 parts = line.strip().split(';')
2016 op = parts[1]
2015 op = parts[1]
2017 if op == 'cg':
2016 if op == 'cg':
2018 pass
2017 pass
2019 elif op == 'cgss':
2018 elif op == 'cgss':
2020 doit(parts[2].split(' '), parts[3].split(' '))
2019 doit(parts[2].split(' '), parts[3].split(' '))
2021 elif op == 'unb':
2020 elif op == 'unb':
2022 doit(parts[3].split(' '), parts[2].split(' '))
2021 doit(parts[3].split(' '), parts[2].split(' '))
2023 line = logfile.readline()
2022 line = logfile.readline()
2024 else:
2023 else:
2025 remoterevs, _checkout = hg.addbranchrevs(repo, remote, branches,
2024 remoterevs, _checkout = hg.addbranchrevs(repo, remote, branches,
2026 opts.get('remote_head'))
2025 opts.get('remote_head'))
2027 localrevs = opts.get('local_head')
2026 localrevs = opts.get('local_head')
2028 doit(localrevs, remoterevs)
2027 doit(localrevs, remoterevs)
2029
2028
2030 @command('debugextensions', formatteropts, [], norepo=True)
2029 @command('debugextensions', formatteropts, [], norepo=True)
2031 def debugextensions(ui, **opts):
2030 def debugextensions(ui, **opts):
2032 '''show information about active extensions'''
2031 '''show information about active extensions'''
2033 exts = extensions.extensions(ui)
2032 exts = extensions.extensions(ui)
2034 hgver = util.version()
2033 hgver = util.version()
2035 fm = ui.formatter('debugextensions', opts)
2034 fm = ui.formatter('debugextensions', opts)
2036 for extname, extmod in sorted(exts, key=operator.itemgetter(0)):
2035 for extname, extmod in sorted(exts, key=operator.itemgetter(0)):
2037 isinternal = extensions.ismoduleinternal(extmod)
2036 isinternal = extensions.ismoduleinternal(extmod)
2038 extsource = extmod.__file__
2037 extsource = extmod.__file__
2039 if isinternal:
2038 if isinternal:
2040 exttestedwith = [] # never expose magic string to users
2039 exttestedwith = [] # never expose magic string to users
2041 else:
2040 else:
2042 exttestedwith = getattr(extmod, 'testedwith', '').split()
2041 exttestedwith = getattr(extmod, 'testedwith', '').split()
2043 extbuglink = getattr(extmod, 'buglink', None)
2042 extbuglink = getattr(extmod, 'buglink', None)
2044
2043
2045 fm.startitem()
2044 fm.startitem()
2046
2045
2047 if ui.quiet or ui.verbose:
2046 if ui.quiet or ui.verbose:
2048 fm.write('name', '%s\n', extname)
2047 fm.write('name', '%s\n', extname)
2049 else:
2048 else:
2050 fm.write('name', '%s', extname)
2049 fm.write('name', '%s', extname)
2051 if isinternal or hgver in exttestedwith:
2050 if isinternal or hgver in exttestedwith:
2052 fm.plain('\n')
2051 fm.plain('\n')
2053 elif not exttestedwith:
2052 elif not exttestedwith:
2054 fm.plain(_(' (untested!)\n'))
2053 fm.plain(_(' (untested!)\n'))
2055 else:
2054 else:
2056 lasttestedversion = exttestedwith[-1]
2055 lasttestedversion = exttestedwith[-1]
2057 fm.plain(' (%s!)\n' % lasttestedversion)
2056 fm.plain(' (%s!)\n' % lasttestedversion)
2058
2057
2059 fm.condwrite(ui.verbose and extsource, 'source',
2058 fm.condwrite(ui.verbose and extsource, 'source',
2060 _(' location: %s\n'), extsource or "")
2059 _(' location: %s\n'), extsource or "")
2061
2060
2062 if ui.verbose:
2061 if ui.verbose:
2063 fm.plain(_(' bundled: %s\n') % ['no', 'yes'][isinternal])
2062 fm.plain(_(' bundled: %s\n') % ['no', 'yes'][isinternal])
2064 fm.data(bundled=isinternal)
2063 fm.data(bundled=isinternal)
2065
2064
2066 fm.condwrite(ui.verbose and exttestedwith, 'testedwith',
2065 fm.condwrite(ui.verbose and exttestedwith, 'testedwith',
2067 _(' tested with: %s\n'),
2066 _(' tested with: %s\n'),
2068 fm.formatlist(exttestedwith, name='ver'))
2067 fm.formatlist(exttestedwith, name='ver'))
2069
2068
2070 fm.condwrite(ui.verbose and extbuglink, 'buglink',
2069 fm.condwrite(ui.verbose and extbuglink, 'buglink',
2071 _(' bug reporting: %s\n'), extbuglink or "")
2070 _(' bug reporting: %s\n'), extbuglink or "")
2072
2071
2073 fm.end()
2072 fm.end()
2074
2073
2075 @command('debugfileset',
2074 @command('debugfileset',
2076 [('r', 'rev', '', _('apply the filespec on this revision'), _('REV'))],
2075 [('r', 'rev', '', _('apply the filespec on this revision'), _('REV'))],
2077 _('[-r REV] FILESPEC'))
2076 _('[-r REV] FILESPEC'))
2078 def debugfileset(ui, repo, expr, **opts):
2077 def debugfileset(ui, repo, expr, **opts):
2079 '''parse and apply a fileset specification'''
2078 '''parse and apply a fileset specification'''
2080 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
2079 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
2081 if ui.verbose:
2080 if ui.verbose:
2082 tree = fileset.parse(expr)
2081 tree = fileset.parse(expr)
2083 ui.note(fileset.prettyformat(tree), "\n")
2082 ui.note(fileset.prettyformat(tree), "\n")
2084
2083
2085 for f in ctx.getfileset(expr):
2084 for f in ctx.getfileset(expr):
2086 ui.write("%s\n" % f)
2085 ui.write("%s\n" % f)
2087
2086
2088 @command('debugfsinfo', [], _('[PATH]'), norepo=True)
2087 @command('debugfsinfo', [], _('[PATH]'), norepo=True)
2089 def debugfsinfo(ui, path="."):
2088 def debugfsinfo(ui, path="."):
2090 """show information detected about current filesystem"""
2089 """show information detected about current filesystem"""
2091 util.writefile('.debugfsinfo', '')
2090 util.writefile('.debugfsinfo', '')
2092 ui.write(('exec: %s\n') % (util.checkexec(path) and 'yes' or 'no'))
2091 ui.write(('exec: %s\n') % (util.checkexec(path) and 'yes' or 'no'))
2093 ui.write(('symlink: %s\n') % (util.checklink(path) and 'yes' or 'no'))
2092 ui.write(('symlink: %s\n') % (util.checklink(path) and 'yes' or 'no'))
2094 ui.write(('hardlink: %s\n') % (util.checknlink(path) and 'yes' or 'no'))
2093 ui.write(('hardlink: %s\n') % (util.checknlink(path) and 'yes' or 'no'))
2095 ui.write(('case-sensitive: %s\n') % (util.fscasesensitive('.debugfsinfo')
2094 ui.write(('case-sensitive: %s\n') % (util.fscasesensitive('.debugfsinfo')
2096 and 'yes' or 'no'))
2095 and 'yes' or 'no'))
2097 os.unlink('.debugfsinfo')
2096 os.unlink('.debugfsinfo')
2098
2097
2099 @command('debuggetbundle',
2098 @command('debuggetbundle',
2100 [('H', 'head', [], _('id of head node'), _('ID')),
2099 [('H', 'head', [], _('id of head node'), _('ID')),
2101 ('C', 'common', [], _('id of common node'), _('ID')),
2100 ('C', 'common', [], _('id of common node'), _('ID')),
2102 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE'))],
2101 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE'))],
2103 _('REPO FILE [-H|-C ID]...'),
2102 _('REPO FILE [-H|-C ID]...'),
2104 norepo=True)
2103 norepo=True)
2105 def debuggetbundle(ui, repopath, bundlepath, head=None, common=None, **opts):
2104 def debuggetbundle(ui, repopath, bundlepath, head=None, common=None, **opts):
2106 """retrieves a bundle from a repo
2105 """retrieves a bundle from a repo
2107
2106
2108 Every ID must be a full-length hex node id string. Saves the bundle to the
2107 Every ID must be a full-length hex node id string. Saves the bundle to the
2109 given file.
2108 given file.
2110 """
2109 """
2111 repo = hg.peer(ui, opts, repopath)
2110 repo = hg.peer(ui, opts, repopath)
2112 if not repo.capable('getbundle'):
2111 if not repo.capable('getbundle'):
2113 raise error.Abort("getbundle() not supported by target repository")
2112 raise error.Abort("getbundle() not supported by target repository")
2114 args = {}
2113 args = {}
2115 if common:
2114 if common:
2116 args['common'] = [bin(s) for s in common]
2115 args['common'] = [bin(s) for s in common]
2117 if head:
2116 if head:
2118 args['heads'] = [bin(s) for s in head]
2117 args['heads'] = [bin(s) for s in head]
2119 # TODO: get desired bundlecaps from command line.
2118 # TODO: get desired bundlecaps from command line.
2120 args['bundlecaps'] = None
2119 args['bundlecaps'] = None
2121 bundle = repo.getbundle('debug', **args)
2120 bundle = repo.getbundle('debug', **args)
2122
2121
2123 bundletype = opts.get('type', 'bzip2').lower()
2122 bundletype = opts.get('type', 'bzip2').lower()
2124 btypes = {'none': 'HG10UN',
2123 btypes = {'none': 'HG10UN',
2125 'bzip2': 'HG10BZ',
2124 'bzip2': 'HG10BZ',
2126 'gzip': 'HG10GZ',
2125 'gzip': 'HG10GZ',
2127 'bundle2': 'HG20'}
2126 'bundle2': 'HG20'}
2128 bundletype = btypes.get(bundletype)
2127 bundletype = btypes.get(bundletype)
2129 if bundletype not in bundle2.bundletypes:
2128 if bundletype not in bundle2.bundletypes:
2130 raise error.Abort(_('unknown bundle type specified with --type'))
2129 raise error.Abort(_('unknown bundle type specified with --type'))
2131 bundle2.writebundle(ui, bundle, bundlepath, bundletype)
2130 bundle2.writebundle(ui, bundle, bundlepath, bundletype)
2132
2131
2133 @command('debugignore', [], '[FILE]')
2132 @command('debugignore', [], '[FILE]')
2134 def debugignore(ui, repo, *files, **opts):
2133 def debugignore(ui, repo, *files, **opts):
2135 """display the combined ignore pattern and information about ignored files
2134 """display the combined ignore pattern and information about ignored files
2136
2135
2137 With no argument display the combined ignore pattern.
2136 With no argument display the combined ignore pattern.
2138
2137
2139 Given space separated file names, shows if the given file is ignored and
2138 Given space separated file names, shows if the given file is ignored and
2140 if so, show the ignore rule (file and line number) that matched it.
2139 if so, show the ignore rule (file and line number) that matched it.
2141 """
2140 """
2142 ignore = repo.dirstate._ignore
2141 ignore = repo.dirstate._ignore
2143 if not files:
2142 if not files:
2144 # Show all the patterns
2143 # Show all the patterns
2145 includepat = getattr(ignore, 'includepat', None)
2144 includepat = getattr(ignore, 'includepat', None)
2146 if includepat is not None:
2145 if includepat is not None:
2147 ui.write("%s\n" % includepat)
2146 ui.write("%s\n" % includepat)
2148 else:
2147 else:
2149 raise error.Abort(_("no ignore patterns found"))
2148 raise error.Abort(_("no ignore patterns found"))
2150 else:
2149 else:
2151 for f in files:
2150 for f in files:
2152 nf = util.normpath(f)
2151 nf = util.normpath(f)
2153 ignored = None
2152 ignored = None
2154 ignoredata = None
2153 ignoredata = None
2155 if nf != '.':
2154 if nf != '.':
2156 if ignore(nf):
2155 if ignore(nf):
2157 ignored = nf
2156 ignored = nf
2158 ignoredata = repo.dirstate._ignorefileandline(nf)
2157 ignoredata = repo.dirstate._ignorefileandline(nf)
2159 else:
2158 else:
2160 for p in util.finddirs(nf):
2159 for p in util.finddirs(nf):
2161 if ignore(p):
2160 if ignore(p):
2162 ignored = p
2161 ignored = p
2163 ignoredata = repo.dirstate._ignorefileandline(p)
2162 ignoredata = repo.dirstate._ignorefileandline(p)
2164 break
2163 break
2165 if ignored:
2164 if ignored:
2166 if ignored == nf:
2165 if ignored == nf:
2167 ui.write(_("%s is ignored\n") % f)
2166 ui.write(_("%s is ignored\n") % f)
2168 else:
2167 else:
2169 ui.write(_("%s is ignored because of "
2168 ui.write(_("%s is ignored because of "
2170 "containing folder %s\n")
2169 "containing folder %s\n")
2171 % (f, ignored))
2170 % (f, ignored))
2172 ignorefile, lineno, line = ignoredata
2171 ignorefile, lineno, line = ignoredata
2173 ui.write(_("(ignore rule in %s, line %d: '%s')\n")
2172 ui.write(_("(ignore rule in %s, line %d: '%s')\n")
2174 % (ignorefile, lineno, line))
2173 % (ignorefile, lineno, line))
2175 else:
2174 else:
2176 ui.write(_("%s is not ignored\n") % f)
2175 ui.write(_("%s is not ignored\n") % f)
2177
2176
2178 @command('debugindex', debugrevlogopts +
2177 @command('debugindex', debugrevlogopts +
2179 [('f', 'format', 0, _('revlog format'), _('FORMAT'))],
2178 [('f', 'format', 0, _('revlog format'), _('FORMAT'))],
2180 _('[-f FORMAT] -c|-m|FILE'),
2179 _('[-f FORMAT] -c|-m|FILE'),
2181 optionalrepo=True)
2180 optionalrepo=True)
2182 def debugindex(ui, repo, file_=None, **opts):
2181 def debugindex(ui, repo, file_=None, **opts):
2183 """dump the contents of an index file"""
2182 """dump the contents of an index file"""
2184 r = cmdutil.openrevlog(repo, 'debugindex', file_, opts)
2183 r = cmdutil.openrevlog(repo, 'debugindex', file_, opts)
2185 format = opts.get('format', 0)
2184 format = opts.get('format', 0)
2186 if format not in (0, 1):
2185 if format not in (0, 1):
2187 raise error.Abort(_("unknown format %d") % format)
2186 raise error.Abort(_("unknown format %d") % format)
2188
2187
2189 generaldelta = r.version & revlog.REVLOGGENERALDELTA
2188 generaldelta = r.version & revlog.REVLOGGENERALDELTA
2190 if generaldelta:
2189 if generaldelta:
2191 basehdr = ' delta'
2190 basehdr = ' delta'
2192 else:
2191 else:
2193 basehdr = ' base'
2192 basehdr = ' base'
2194
2193
2195 if ui.debugflag:
2194 if ui.debugflag:
2196 shortfn = hex
2195 shortfn = hex
2197 else:
2196 else:
2198 shortfn = short
2197 shortfn = short
2199
2198
2200 # There might not be anything in r, so have a sane default
2199 # There might not be anything in r, so have a sane default
2201 idlen = 12
2200 idlen = 12
2202 for i in r:
2201 for i in r:
2203 idlen = len(shortfn(r.node(i)))
2202 idlen = len(shortfn(r.node(i)))
2204 break
2203 break
2205
2204
2206 if format == 0:
2205 if format == 0:
2207 ui.write((" rev offset length " + basehdr + " linkrev"
2206 ui.write((" rev offset length " + basehdr + " linkrev"
2208 " %s %s p2\n") % ("nodeid".ljust(idlen), "p1".ljust(idlen)))
2207 " %s %s p2\n") % ("nodeid".ljust(idlen), "p1".ljust(idlen)))
2209 elif format == 1:
2208 elif format == 1:
2210 ui.write((" rev flag offset length"
2209 ui.write((" rev flag offset length"
2211 " size " + basehdr + " link p1 p2"
2210 " size " + basehdr + " link p1 p2"
2212 " %s\n") % "nodeid".rjust(idlen))
2211 " %s\n") % "nodeid".rjust(idlen))
2213
2212
2214 for i in r:
2213 for i in r:
2215 node = r.node(i)
2214 node = r.node(i)
2216 if generaldelta:
2215 if generaldelta:
2217 base = r.deltaparent(i)
2216 base = r.deltaparent(i)
2218 else:
2217 else:
2219 base = r.chainbase(i)
2218 base = r.chainbase(i)
2220 if format == 0:
2219 if format == 0:
2221 try:
2220 try:
2222 pp = r.parents(node)
2221 pp = r.parents(node)
2223 except Exception:
2222 except Exception:
2224 pp = [nullid, nullid]
2223 pp = [nullid, nullid]
2225 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
2224 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
2226 i, r.start(i), r.length(i), base, r.linkrev(i),
2225 i, r.start(i), r.length(i), base, r.linkrev(i),
2227 shortfn(node), shortfn(pp[0]), shortfn(pp[1])))
2226 shortfn(node), shortfn(pp[0]), shortfn(pp[1])))
2228 elif format == 1:
2227 elif format == 1:
2229 pr = r.parentrevs(i)
2228 pr = r.parentrevs(i)
2230 ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d % 6d %s\n" % (
2229 ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d % 6d %s\n" % (
2231 i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
2230 i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
2232 base, r.linkrev(i), pr[0], pr[1], shortfn(node)))
2231 base, r.linkrev(i), pr[0], pr[1], shortfn(node)))
2233
2232
2234 @command('debugindexdot', debugrevlogopts,
2233 @command('debugindexdot', debugrevlogopts,
2235 _('-c|-m|FILE'), optionalrepo=True)
2234 _('-c|-m|FILE'), optionalrepo=True)
2236 def debugindexdot(ui, repo, file_=None, **opts):
2235 def debugindexdot(ui, repo, file_=None, **opts):
2237 """dump an index DAG as a graphviz dot file"""
2236 """dump an index DAG as a graphviz dot file"""
2238 r = cmdutil.openrevlog(repo, 'debugindexdot', file_, opts)
2237 r = cmdutil.openrevlog(repo, 'debugindexdot', file_, opts)
2239 ui.write(("digraph G {\n"))
2238 ui.write(("digraph G {\n"))
2240 for i in r:
2239 for i in r:
2241 node = r.node(i)
2240 node = r.node(i)
2242 pp = r.parents(node)
2241 pp = r.parents(node)
2243 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
2242 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
2244 if pp[1] != nullid:
2243 if pp[1] != nullid:
2245 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
2244 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
2246 ui.write("}\n")
2245 ui.write("}\n")
2247
2246
2248 @command('debugdeltachain',
2247 @command('debugdeltachain',
2249 debugrevlogopts + formatteropts,
2248 debugrevlogopts + formatteropts,
2250 _('-c|-m|FILE'),
2249 _('-c|-m|FILE'),
2251 optionalrepo=True)
2250 optionalrepo=True)
2252 def debugdeltachain(ui, repo, file_=None, **opts):
2251 def debugdeltachain(ui, repo, file_=None, **opts):
2253 """dump information about delta chains in a revlog
2252 """dump information about delta chains in a revlog
2254
2253
2255 Output can be templatized. Available template keywords are:
2254 Output can be templatized. Available template keywords are:
2256
2255
2257 :``rev``: revision number
2256 :``rev``: revision number
2258 :``chainid``: delta chain identifier (numbered by unique base)
2257 :``chainid``: delta chain identifier (numbered by unique base)
2259 :``chainlen``: delta chain length to this revision
2258 :``chainlen``: delta chain length to this revision
2260 :``prevrev``: previous revision in delta chain
2259 :``prevrev``: previous revision in delta chain
2261 :``deltatype``: role of delta / how it was computed
2260 :``deltatype``: role of delta / how it was computed
2262 :``compsize``: compressed size of revision
2261 :``compsize``: compressed size of revision
2263 :``uncompsize``: uncompressed size of revision
2262 :``uncompsize``: uncompressed size of revision
2264 :``chainsize``: total size of compressed revisions in chain
2263 :``chainsize``: total size of compressed revisions in chain
2265 :``chainratio``: total chain size divided by uncompressed revision size
2264 :``chainratio``: total chain size divided by uncompressed revision size
2266 (new delta chains typically start at ratio 2.00)
2265 (new delta chains typically start at ratio 2.00)
2267 :``lindist``: linear distance from base revision in delta chain to end
2266 :``lindist``: linear distance from base revision in delta chain to end
2268 of this revision
2267 of this revision
2269 :``extradist``: total size of revisions not part of this delta chain from
2268 :``extradist``: total size of revisions not part of this delta chain from
2270 base of delta chain to end of this revision; a measurement
2269 base of delta chain to end of this revision; a measurement
2271 of how much extra data we need to read/seek across to read
2270 of how much extra data we need to read/seek across to read
2272 the delta chain for this revision
2271 the delta chain for this revision
2273 :``extraratio``: extradist divided by chainsize; another representation of
2272 :``extraratio``: extradist divided by chainsize; another representation of
2274 how much unrelated data is needed to load this delta chain
2273 how much unrelated data is needed to load this delta chain
2275 """
2274 """
2276 r = cmdutil.openrevlog(repo, 'debugdeltachain', file_, opts)
2275 r = cmdutil.openrevlog(repo, 'debugdeltachain', file_, opts)
2277 index = r.index
2276 index = r.index
2278 generaldelta = r.version & revlog.REVLOGGENERALDELTA
2277 generaldelta = r.version & revlog.REVLOGGENERALDELTA
2279
2278
2280 def revinfo(rev):
2279 def revinfo(rev):
2281 e = index[rev]
2280 e = index[rev]
2282 compsize = e[1]
2281 compsize = e[1]
2283 uncompsize = e[2]
2282 uncompsize = e[2]
2284 chainsize = 0
2283 chainsize = 0
2285
2284
2286 if generaldelta:
2285 if generaldelta:
2287 if e[3] == e[5]:
2286 if e[3] == e[5]:
2288 deltatype = 'p1'
2287 deltatype = 'p1'
2289 elif e[3] == e[6]:
2288 elif e[3] == e[6]:
2290 deltatype = 'p2'
2289 deltatype = 'p2'
2291 elif e[3] == rev - 1:
2290 elif e[3] == rev - 1:
2292 deltatype = 'prev'
2291 deltatype = 'prev'
2293 elif e[3] == rev:
2292 elif e[3] == rev:
2294 deltatype = 'base'
2293 deltatype = 'base'
2295 else:
2294 else:
2296 deltatype = 'other'
2295 deltatype = 'other'
2297 else:
2296 else:
2298 if e[3] == rev:
2297 if e[3] == rev:
2299 deltatype = 'base'
2298 deltatype = 'base'
2300 else:
2299 else:
2301 deltatype = 'prev'
2300 deltatype = 'prev'
2302
2301
2303 chain = r._deltachain(rev)[0]
2302 chain = r._deltachain(rev)[0]
2304 for iterrev in chain:
2303 for iterrev in chain:
2305 e = index[iterrev]
2304 e = index[iterrev]
2306 chainsize += e[1]
2305 chainsize += e[1]
2307
2306
2308 return compsize, uncompsize, deltatype, chain, chainsize
2307 return compsize, uncompsize, deltatype, chain, chainsize
2309
2308
2310 fm = ui.formatter('debugdeltachain', opts)
2309 fm = ui.formatter('debugdeltachain', opts)
2311
2310
2312 fm.plain(' rev chain# chainlen prev delta '
2311 fm.plain(' rev chain# chainlen prev delta '
2313 'size rawsize chainsize ratio lindist extradist '
2312 'size rawsize chainsize ratio lindist extradist '
2314 'extraratio\n')
2313 'extraratio\n')
2315
2314
2316 chainbases = {}
2315 chainbases = {}
2317 for rev in r:
2316 for rev in r:
2318 comp, uncomp, deltatype, chain, chainsize = revinfo(rev)
2317 comp, uncomp, deltatype, chain, chainsize = revinfo(rev)
2319 chainbase = chain[0]
2318 chainbase = chain[0]
2320 chainid = chainbases.setdefault(chainbase, len(chainbases) + 1)
2319 chainid = chainbases.setdefault(chainbase, len(chainbases) + 1)
2321 basestart = r.start(chainbase)
2320 basestart = r.start(chainbase)
2322 revstart = r.start(rev)
2321 revstart = r.start(rev)
2323 lineardist = revstart + comp - basestart
2322 lineardist = revstart + comp - basestart
2324 extradist = lineardist - chainsize
2323 extradist = lineardist - chainsize
2325 try:
2324 try:
2326 prevrev = chain[-2]
2325 prevrev = chain[-2]
2327 except IndexError:
2326 except IndexError:
2328 prevrev = -1
2327 prevrev = -1
2329
2328
2330 chainratio = float(chainsize) / float(uncomp)
2329 chainratio = float(chainsize) / float(uncomp)
2331 extraratio = float(extradist) / float(chainsize)
2330 extraratio = float(extradist) / float(chainsize)
2332
2331
2333 fm.startitem()
2332 fm.startitem()
2334 fm.write('rev chainid chainlen prevrev deltatype compsize '
2333 fm.write('rev chainid chainlen prevrev deltatype compsize '
2335 'uncompsize chainsize chainratio lindist extradist '
2334 'uncompsize chainsize chainratio lindist extradist '
2336 'extraratio',
2335 'extraratio',
2337 '%7d %7d %8d %8d %7s %10d %10d %10d %9.5f %9d %9d %10.5f\n',
2336 '%7d %7d %8d %8d %7s %10d %10d %10d %9.5f %9d %9d %10.5f\n',
2338 rev, chainid, len(chain), prevrev, deltatype, comp,
2337 rev, chainid, len(chain), prevrev, deltatype, comp,
2339 uncomp, chainsize, chainratio, lineardist, extradist,
2338 uncomp, chainsize, chainratio, lineardist, extradist,
2340 extraratio,
2339 extraratio,
2341 rev=rev, chainid=chainid, chainlen=len(chain),
2340 rev=rev, chainid=chainid, chainlen=len(chain),
2342 prevrev=prevrev, deltatype=deltatype, compsize=comp,
2341 prevrev=prevrev, deltatype=deltatype, compsize=comp,
2343 uncompsize=uncomp, chainsize=chainsize,
2342 uncompsize=uncomp, chainsize=chainsize,
2344 chainratio=chainratio, lindist=lineardist,
2343 chainratio=chainratio, lindist=lineardist,
2345 extradist=extradist, extraratio=extraratio)
2344 extradist=extradist, extraratio=extraratio)
2346
2345
2347 fm.end()
2346 fm.end()
2348
2347
2349 @command('debuginstall', [] + formatteropts, '', norepo=True)
2348 @command('debuginstall', [] + formatteropts, '', norepo=True)
2350 def debuginstall(ui, **opts):
2349 def debuginstall(ui, **opts):
2351 '''test Mercurial installation
2350 '''test Mercurial installation
2352
2351
2353 Returns 0 on success.
2352 Returns 0 on success.
2354 '''
2353 '''
2355
2354
2356 def writetemp(contents):
2355 def writetemp(contents):
2357 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
2356 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
2358 f = os.fdopen(fd, "wb")
2357 f = os.fdopen(fd, "wb")
2359 f.write(contents)
2358 f.write(contents)
2360 f.close()
2359 f.close()
2361 return name
2360 return name
2362
2361
2363 problems = 0
2362 problems = 0
2364
2363
2365 fm = ui.formatter('debuginstall', opts)
2364 fm = ui.formatter('debuginstall', opts)
2366 fm.startitem()
2365 fm.startitem()
2367
2366
2368 # encoding
2367 # encoding
2369 fm.write('encoding', _("checking encoding (%s)...\n"), encoding.encoding)
2368 fm.write('encoding', _("checking encoding (%s)...\n"), encoding.encoding)
2370 err = None
2369 err = None
2371 try:
2370 try:
2372 encoding.fromlocal("test")
2371 encoding.fromlocal("test")
2373 except error.Abort as inst:
2372 except error.Abort as inst:
2374 err = inst
2373 err = inst
2375 problems += 1
2374 problems += 1
2376 fm.condwrite(err, 'encodingerror', _(" %s\n"
2375 fm.condwrite(err, 'encodingerror', _(" %s\n"
2377 " (check that your locale is properly set)\n"), err)
2376 " (check that your locale is properly set)\n"), err)
2378
2377
2379 # Python
2378 # Python
2380 fm.write('pythonexe', _("checking Python executable (%s)\n"),
2379 fm.write('pythonexe', _("checking Python executable (%s)\n"),
2381 sys.executable)
2380 sys.executable)
2382 fm.write('pythonver', _("checking Python version (%s)\n"),
2381 fm.write('pythonver', _("checking Python version (%s)\n"),
2383 ("%s.%s.%s" % sys.version_info[:3]))
2382 ("%s.%s.%s" % sys.version_info[:3]))
2384 fm.write('pythonlib', _("checking Python lib (%s)...\n"),
2383 fm.write('pythonlib', _("checking Python lib (%s)...\n"),
2385 os.path.dirname(os.__file__))
2384 os.path.dirname(os.__file__))
2386
2385
2387 security = set(sslutil.supportedprotocols)
2386 security = set(sslutil.supportedprotocols)
2388 if sslutil.hassni:
2387 if sslutil.hassni:
2389 security.add('sni')
2388 security.add('sni')
2390
2389
2391 fm.write('pythonsecurity', _("checking Python security support (%s)\n"),
2390 fm.write('pythonsecurity', _("checking Python security support (%s)\n"),
2392 fm.formatlist(sorted(security), name='protocol',
2391 fm.formatlist(sorted(security), name='protocol',
2393 fmt='%s', sep=','))
2392 fmt='%s', sep=','))
2394
2393
2395 # These are warnings, not errors. So don't increment problem count. This
2394 # These are warnings, not errors. So don't increment problem count. This
2396 # may change in the future.
2395 # may change in the future.
2397 if 'tls1.2' not in security:
2396 if 'tls1.2' not in security:
2398 fm.plain(_(' TLS 1.2 not supported by Python install; '
2397 fm.plain(_(' TLS 1.2 not supported by Python install; '
2399 'network connections lack modern security\n'))
2398 'network connections lack modern security\n'))
2400 if 'sni' not in security:
2399 if 'sni' not in security:
2401 fm.plain(_(' SNI not supported by Python install; may have '
2400 fm.plain(_(' SNI not supported by Python install; may have '
2402 'connectivity issues with some servers\n'))
2401 'connectivity issues with some servers\n'))
2403
2402
2404 # TODO print CA cert info
2403 # TODO print CA cert info
2405
2404
2406 # hg version
2405 # hg version
2407 hgver = util.version()
2406 hgver = util.version()
2408 fm.write('hgver', _("checking Mercurial version (%s)\n"),
2407 fm.write('hgver', _("checking Mercurial version (%s)\n"),
2409 hgver.split('+')[0])
2408 hgver.split('+')[0])
2410 fm.write('hgverextra', _("checking Mercurial custom build (%s)\n"),
2409 fm.write('hgverextra', _("checking Mercurial custom build (%s)\n"),
2411 '+'.join(hgver.split('+')[1:]))
2410 '+'.join(hgver.split('+')[1:]))
2412
2411
2413 # compiled modules
2412 # compiled modules
2414 fm.write('hgmodulepolicy', _("checking module policy (%s)\n"),
2413 fm.write('hgmodulepolicy', _("checking module policy (%s)\n"),
2415 policy.policy)
2414 policy.policy)
2416 fm.write('hgmodules', _("checking installed modules (%s)...\n"),
2415 fm.write('hgmodules', _("checking installed modules (%s)...\n"),
2417 os.path.dirname(__file__))
2416 os.path.dirname(__file__))
2418
2417
2419 err = None
2418 err = None
2420 try:
2419 try:
2421 from . import (
2420 from . import (
2422 base85,
2421 base85,
2423 bdiff,
2422 bdiff,
2424 mpatch,
2423 mpatch,
2425 osutil,
2424 osutil,
2426 )
2425 )
2427 dir(bdiff), dir(mpatch), dir(base85), dir(osutil) # quiet pyflakes
2426 dir(bdiff), dir(mpatch), dir(base85), dir(osutil) # quiet pyflakes
2428 except Exception as inst:
2427 except Exception as inst:
2429 err = inst
2428 err = inst
2430 problems += 1
2429 problems += 1
2431 fm.condwrite(err, 'extensionserror', " %s\n", err)
2430 fm.condwrite(err, 'extensionserror', " %s\n", err)
2432
2431
2433 compengines = util.compengines._engines.values()
2432 compengines = util.compengines._engines.values()
2434 fm.write('compengines', _('checking registered compression engines (%s)\n'),
2433 fm.write('compengines', _('checking registered compression engines (%s)\n'),
2435 fm.formatlist(sorted(e.name() for e in compengines),
2434 fm.formatlist(sorted(e.name() for e in compengines),
2436 name='compengine', fmt='%s', sep=', '))
2435 name='compengine', fmt='%s', sep=', '))
2437 fm.write('compenginesavail', _('checking available compression engines '
2436 fm.write('compenginesavail', _('checking available compression engines '
2438 '(%s)\n'),
2437 '(%s)\n'),
2439 fm.formatlist(sorted(e.name() for e in compengines
2438 fm.formatlist(sorted(e.name() for e in compengines
2440 if e.available()),
2439 if e.available()),
2441 name='compengine', fmt='%s', sep=', '))
2440 name='compengine', fmt='%s', sep=', '))
2442
2441
2443 # templates
2442 # templates
2444 p = templater.templatepaths()
2443 p = templater.templatepaths()
2445 fm.write('templatedirs', 'checking templates (%s)...\n', ' '.join(p))
2444 fm.write('templatedirs', 'checking templates (%s)...\n', ' '.join(p))
2446 fm.condwrite(not p, '', _(" no template directories found\n"))
2445 fm.condwrite(not p, '', _(" no template directories found\n"))
2447 if p:
2446 if p:
2448 m = templater.templatepath("map-cmdline.default")
2447 m = templater.templatepath("map-cmdline.default")
2449 if m:
2448 if m:
2450 # template found, check if it is working
2449 # template found, check if it is working
2451 err = None
2450 err = None
2452 try:
2451 try:
2453 templater.templater.frommapfile(m)
2452 templater.templater.frommapfile(m)
2454 except Exception as inst:
2453 except Exception as inst:
2455 err = inst
2454 err = inst
2456 p = None
2455 p = None
2457 fm.condwrite(err, 'defaulttemplateerror', " %s\n", err)
2456 fm.condwrite(err, 'defaulttemplateerror', " %s\n", err)
2458 else:
2457 else:
2459 p = None
2458 p = None
2460 fm.condwrite(p, 'defaulttemplate',
2459 fm.condwrite(p, 'defaulttemplate',
2461 _("checking default template (%s)\n"), m)
2460 _("checking default template (%s)\n"), m)
2462 fm.condwrite(not m, 'defaulttemplatenotfound',
2461 fm.condwrite(not m, 'defaulttemplatenotfound',
2463 _(" template '%s' not found\n"), "default")
2462 _(" template '%s' not found\n"), "default")
2464 if not p:
2463 if not p:
2465 problems += 1
2464 problems += 1
2466 fm.condwrite(not p, '',
2465 fm.condwrite(not p, '',
2467 _(" (templates seem to have been installed incorrectly)\n"))
2466 _(" (templates seem to have been installed incorrectly)\n"))
2468
2467
2469 # editor
2468 # editor
2470 editor = ui.geteditor()
2469 editor = ui.geteditor()
2471 editor = util.expandpath(editor)
2470 editor = util.expandpath(editor)
2472 fm.write('editor', _("checking commit editor... (%s)\n"), editor)
2471 fm.write('editor', _("checking commit editor... (%s)\n"), editor)
2473 cmdpath = util.findexe(shlex.split(editor)[0])
2472 cmdpath = util.findexe(shlex.split(editor)[0])
2474 fm.condwrite(not cmdpath and editor == 'vi', 'vinotfound',
2473 fm.condwrite(not cmdpath and editor == 'vi', 'vinotfound',
2475 _(" No commit editor set and can't find %s in PATH\n"
2474 _(" No commit editor set and can't find %s in PATH\n"
2476 " (specify a commit editor in your configuration"
2475 " (specify a commit editor in your configuration"
2477 " file)\n"), not cmdpath and editor == 'vi' and editor)
2476 " file)\n"), not cmdpath and editor == 'vi' and editor)
2478 fm.condwrite(not cmdpath and editor != 'vi', 'editornotfound',
2477 fm.condwrite(not cmdpath and editor != 'vi', 'editornotfound',
2479 _(" Can't find editor '%s' in PATH\n"
2478 _(" Can't find editor '%s' in PATH\n"
2480 " (specify a commit editor in your configuration"
2479 " (specify a commit editor in your configuration"
2481 " file)\n"), not cmdpath and editor)
2480 " file)\n"), not cmdpath and editor)
2482 if not cmdpath and editor != 'vi':
2481 if not cmdpath and editor != 'vi':
2483 problems += 1
2482 problems += 1
2484
2483
2485 # check username
2484 # check username
2486 username = None
2485 username = None
2487 err = None
2486 err = None
2488 try:
2487 try:
2489 username = ui.username()
2488 username = ui.username()
2490 except error.Abort as e:
2489 except error.Abort as e:
2491 err = e
2490 err = e
2492 problems += 1
2491 problems += 1
2493
2492
2494 fm.condwrite(username, 'username', _("checking username (%s)\n"), username)
2493 fm.condwrite(username, 'username', _("checking username (%s)\n"), username)
2495 fm.condwrite(err, 'usernameerror', _("checking username...\n %s\n"
2494 fm.condwrite(err, 'usernameerror', _("checking username...\n %s\n"
2496 " (specify a username in your configuration file)\n"), err)
2495 " (specify a username in your configuration file)\n"), err)
2497
2496
2498 fm.condwrite(not problems, '',
2497 fm.condwrite(not problems, '',
2499 _("no problems detected\n"))
2498 _("no problems detected\n"))
2500 if not problems:
2499 if not problems:
2501 fm.data(problems=problems)
2500 fm.data(problems=problems)
2502 fm.condwrite(problems, 'problems',
2501 fm.condwrite(problems, 'problems',
2503 _("%d problems detected,"
2502 _("%d problems detected,"
2504 " please check your install!\n"), problems)
2503 " please check your install!\n"), problems)
2505 fm.end()
2504 fm.end()
2506
2505
2507 return problems
2506 return problems
2508
2507
2509 @command('debugknown', [], _('REPO ID...'), norepo=True)
2508 @command('debugknown', [], _('REPO ID...'), norepo=True)
2510 def debugknown(ui, repopath, *ids, **opts):
2509 def debugknown(ui, repopath, *ids, **opts):
2511 """test whether node ids are known to a repo
2510 """test whether node ids are known to a repo
2512
2511
2513 Every ID must be a full-length hex node id string. Returns a list of 0s
2512 Every ID must be a full-length hex node id string. Returns a list of 0s
2514 and 1s indicating unknown/known.
2513 and 1s indicating unknown/known.
2515 """
2514 """
2516 repo = hg.peer(ui, opts, repopath)
2515 repo = hg.peer(ui, opts, repopath)
2517 if not repo.capable('known'):
2516 if not repo.capable('known'):
2518 raise error.Abort("known() not supported by target repository")
2517 raise error.Abort("known() not supported by target repository")
2519 flags = repo.known([bin(s) for s in ids])
2518 flags = repo.known([bin(s) for s in ids])
2520 ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
2519 ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
2521
2520
2522 @command('debuglabelcomplete', [], _('LABEL...'))
2521 @command('debuglabelcomplete', [], _('LABEL...'))
2523 def debuglabelcomplete(ui, repo, *args):
2522 def debuglabelcomplete(ui, repo, *args):
2524 '''backwards compatibility with old bash completion scripts (DEPRECATED)'''
2523 '''backwards compatibility with old bash completion scripts (DEPRECATED)'''
2525 debugnamecomplete(ui, repo, *args)
2524 debugnamecomplete(ui, repo, *args)
2526
2525
2527 @command('debugmergestate', [], '')
2526 @command('debugmergestate', [], '')
2528 def debugmergestate(ui, repo, *args):
2527 def debugmergestate(ui, repo, *args):
2529 """print merge state
2528 """print merge state
2530
2529
2531 Use --verbose to print out information about whether v1 or v2 merge state
2530 Use --verbose to print out information about whether v1 or v2 merge state
2532 was chosen."""
2531 was chosen."""
2533 def _hashornull(h):
2532 def _hashornull(h):
2534 if h == nullhex:
2533 if h == nullhex:
2535 return 'null'
2534 return 'null'
2536 else:
2535 else:
2537 return h
2536 return h
2538
2537
2539 def printrecords(version):
2538 def printrecords(version):
2540 ui.write(('* version %s records\n') % version)
2539 ui.write(('* version %s records\n') % version)
2541 if version == 1:
2540 if version == 1:
2542 records = v1records
2541 records = v1records
2543 else:
2542 else:
2544 records = v2records
2543 records = v2records
2545
2544
2546 for rtype, record in records:
2545 for rtype, record in records:
2547 # pretty print some record types
2546 # pretty print some record types
2548 if rtype == 'L':
2547 if rtype == 'L':
2549 ui.write(('local: %s\n') % record)
2548 ui.write(('local: %s\n') % record)
2550 elif rtype == 'O':
2549 elif rtype == 'O':
2551 ui.write(('other: %s\n') % record)
2550 ui.write(('other: %s\n') % record)
2552 elif rtype == 'm':
2551 elif rtype == 'm':
2553 driver, mdstate = record.split('\0', 1)
2552 driver, mdstate = record.split('\0', 1)
2554 ui.write(('merge driver: %s (state "%s")\n')
2553 ui.write(('merge driver: %s (state "%s")\n')
2555 % (driver, mdstate))
2554 % (driver, mdstate))
2556 elif rtype in 'FDC':
2555 elif rtype in 'FDC':
2557 r = record.split('\0')
2556 r = record.split('\0')
2558 f, state, hash, lfile, afile, anode, ofile = r[0:7]
2557 f, state, hash, lfile, afile, anode, ofile = r[0:7]
2559 if version == 1:
2558 if version == 1:
2560 onode = 'not stored in v1 format'
2559 onode = 'not stored in v1 format'
2561 flags = r[7]
2560 flags = r[7]
2562 else:
2561 else:
2563 onode, flags = r[7:9]
2562 onode, flags = r[7:9]
2564 ui.write(('file: %s (record type "%s", state "%s", hash %s)\n')
2563 ui.write(('file: %s (record type "%s", state "%s", hash %s)\n')
2565 % (f, rtype, state, _hashornull(hash)))
2564 % (f, rtype, state, _hashornull(hash)))
2566 ui.write((' local path: %s (flags "%s")\n') % (lfile, flags))
2565 ui.write((' local path: %s (flags "%s")\n') % (lfile, flags))
2567 ui.write((' ancestor path: %s (node %s)\n')
2566 ui.write((' ancestor path: %s (node %s)\n')
2568 % (afile, _hashornull(anode)))
2567 % (afile, _hashornull(anode)))
2569 ui.write((' other path: %s (node %s)\n')
2568 ui.write((' other path: %s (node %s)\n')
2570 % (ofile, _hashornull(onode)))
2569 % (ofile, _hashornull(onode)))
2571 elif rtype == 'f':
2570 elif rtype == 'f':
2572 filename, rawextras = record.split('\0', 1)
2571 filename, rawextras = record.split('\0', 1)
2573 extras = rawextras.split('\0')
2572 extras = rawextras.split('\0')
2574 i = 0
2573 i = 0
2575 extrastrings = []
2574 extrastrings = []
2576 while i < len(extras):
2575 while i < len(extras):
2577 extrastrings.append('%s = %s' % (extras[i], extras[i + 1]))
2576 extrastrings.append('%s = %s' % (extras[i], extras[i + 1]))
2578 i += 2
2577 i += 2
2579
2578
2580 ui.write(('file extras: %s (%s)\n')
2579 ui.write(('file extras: %s (%s)\n')
2581 % (filename, ', '.join(extrastrings)))
2580 % (filename, ', '.join(extrastrings)))
2582 elif rtype == 'l':
2581 elif rtype == 'l':
2583 labels = record.split('\0', 2)
2582 labels = record.split('\0', 2)
2584 labels = [l for l in labels if len(l) > 0]
2583 labels = [l for l in labels if len(l) > 0]
2585 ui.write(('labels:\n'))
2584 ui.write(('labels:\n'))
2586 ui.write((' local: %s\n' % labels[0]))
2585 ui.write((' local: %s\n' % labels[0]))
2587 ui.write((' other: %s\n' % labels[1]))
2586 ui.write((' other: %s\n' % labels[1]))
2588 if len(labels) > 2:
2587 if len(labels) > 2:
2589 ui.write((' base: %s\n' % labels[2]))
2588 ui.write((' base: %s\n' % labels[2]))
2590 else:
2589 else:
2591 ui.write(('unrecognized entry: %s\t%s\n')
2590 ui.write(('unrecognized entry: %s\t%s\n')
2592 % (rtype, record.replace('\0', '\t')))
2591 % (rtype, record.replace('\0', '\t')))
2593
2592
2594 # Avoid mergestate.read() since it may raise an exception for unsupported
2593 # Avoid mergestate.read() since it may raise an exception for unsupported
2595 # merge state records. We shouldn't be doing this, but this is OK since this
2594 # merge state records. We shouldn't be doing this, but this is OK since this
2596 # command is pretty low-level.
2595 # command is pretty low-level.
2597 ms = mergemod.mergestate(repo)
2596 ms = mergemod.mergestate(repo)
2598
2597
2599 # sort so that reasonable information is on top
2598 # sort so that reasonable information is on top
2600 v1records = ms._readrecordsv1()
2599 v1records = ms._readrecordsv1()
2601 v2records = ms._readrecordsv2()
2600 v2records = ms._readrecordsv2()
2602 order = 'LOml'
2601 order = 'LOml'
2603 def key(r):
2602 def key(r):
2604 idx = order.find(r[0])
2603 idx = order.find(r[0])
2605 if idx == -1:
2604 if idx == -1:
2606 return (1, r[1])
2605 return (1, r[1])
2607 else:
2606 else:
2608 return (0, idx)
2607 return (0, idx)
2609 v1records.sort(key=key)
2608 v1records.sort(key=key)
2610 v2records.sort(key=key)
2609 v2records.sort(key=key)
2611
2610
2612 if not v1records and not v2records:
2611 if not v1records and not v2records:
2613 ui.write(('no merge state found\n'))
2612 ui.write(('no merge state found\n'))
2614 elif not v2records:
2613 elif not v2records:
2615 ui.note(('no version 2 merge state\n'))
2614 ui.note(('no version 2 merge state\n'))
2616 printrecords(1)
2615 printrecords(1)
2617 elif ms._v1v2match(v1records, v2records):
2616 elif ms._v1v2match(v1records, v2records):
2618 ui.note(('v1 and v2 states match: using v2\n'))
2617 ui.note(('v1 and v2 states match: using v2\n'))
2619 printrecords(2)
2618 printrecords(2)
2620 else:
2619 else:
2621 ui.note(('v1 and v2 states mismatch: using v1\n'))
2620 ui.note(('v1 and v2 states mismatch: using v1\n'))
2622 printrecords(1)
2621 printrecords(1)
2623 if ui.verbose:
2622 if ui.verbose:
2624 printrecords(2)
2623 printrecords(2)
2625
2624
2626 @command('debugnamecomplete', [], _('NAME...'))
2625 @command('debugnamecomplete', [], _('NAME...'))
2627 def debugnamecomplete(ui, repo, *args):
2626 def debugnamecomplete(ui, repo, *args):
2628 '''complete "names" - tags, open branch names, bookmark names'''
2627 '''complete "names" - tags, open branch names, bookmark names'''
2629
2628
2630 names = set()
2629 names = set()
2631 # since we previously only listed open branches, we will handle that
2630 # since we previously only listed open branches, we will handle that
2632 # specially (after this for loop)
2631 # specially (after this for loop)
2633 for name, ns in repo.names.iteritems():
2632 for name, ns in repo.names.iteritems():
2634 if name != 'branches':
2633 if name != 'branches':
2635 names.update(ns.listnames(repo))
2634 names.update(ns.listnames(repo))
2636 names.update(tag for (tag, heads, tip, closed)
2635 names.update(tag for (tag, heads, tip, closed)
2637 in repo.branchmap().iterbranches() if not closed)
2636 in repo.branchmap().iterbranches() if not closed)
2638 completions = set()
2637 completions = set()
2639 if not args:
2638 if not args:
2640 args = ['']
2639 args = ['']
2641 for a in args:
2640 for a in args:
2642 completions.update(n for n in names if n.startswith(a))
2641 completions.update(n for n in names if n.startswith(a))
2643 ui.write('\n'.join(sorted(completions)))
2642 ui.write('\n'.join(sorted(completions)))
2644 ui.write('\n')
2643 ui.write('\n')
2645
2644
2646 @command('debuglocks',
2645 @command('debuglocks',
2647 [('L', 'force-lock', None, _('free the store lock (DANGEROUS)')),
2646 [('L', 'force-lock', None, _('free the store lock (DANGEROUS)')),
2648 ('W', 'force-wlock', None,
2647 ('W', 'force-wlock', None,
2649 _('free the working state lock (DANGEROUS)'))],
2648 _('free the working state lock (DANGEROUS)'))],
2650 _('[OPTION]...'))
2649 _('[OPTION]...'))
2651 def debuglocks(ui, repo, **opts):
2650 def debuglocks(ui, repo, **opts):
2652 """show or modify state of locks
2651 """show or modify state of locks
2653
2652
2654 By default, this command will show which locks are held. This
2653 By default, this command will show which locks are held. This
2655 includes the user and process holding the lock, the amount of time
2654 includes the user and process holding the lock, the amount of time
2656 the lock has been held, and the machine name where the process is
2655 the lock has been held, and the machine name where the process is
2657 running if it's not local.
2656 running if it's not local.
2658
2657
2659 Locks protect the integrity of Mercurial's data, so should be
2658 Locks protect the integrity of Mercurial's data, so should be
2660 treated with care. System crashes or other interruptions may cause
2659 treated with care. System crashes or other interruptions may cause
2661 locks to not be properly released, though Mercurial will usually
2660 locks to not be properly released, though Mercurial will usually
2662 detect and remove such stale locks automatically.
2661 detect and remove such stale locks automatically.
2663
2662
2664 However, detecting stale locks may not always be possible (for
2663 However, detecting stale locks may not always be possible (for
2665 instance, on a shared filesystem). Removing locks may also be
2664 instance, on a shared filesystem). Removing locks may also be
2666 blocked by filesystem permissions.
2665 blocked by filesystem permissions.
2667
2666
2668 Returns 0 if no locks are held.
2667 Returns 0 if no locks are held.
2669
2668
2670 """
2669 """
2671
2670
2672 if opts.get('force_lock'):
2671 if opts.get('force_lock'):
2673 repo.svfs.unlink('lock')
2672 repo.svfs.unlink('lock')
2674 if opts.get('force_wlock'):
2673 if opts.get('force_wlock'):
2675 repo.vfs.unlink('wlock')
2674 repo.vfs.unlink('wlock')
2676 if opts.get('force_lock') or opts.get('force_lock'):
2675 if opts.get('force_lock') or opts.get('force_lock'):
2677 return 0
2676 return 0
2678
2677
2679 now = time.time()
2678 now = time.time()
2680 held = 0
2679 held = 0
2681
2680
2682 def report(vfs, name, method):
2681 def report(vfs, name, method):
2683 # this causes stale locks to get reaped for more accurate reporting
2682 # this causes stale locks to get reaped for more accurate reporting
2684 try:
2683 try:
2685 l = method(False)
2684 l = method(False)
2686 except error.LockHeld:
2685 except error.LockHeld:
2687 l = None
2686 l = None
2688
2687
2689 if l:
2688 if l:
2690 l.release()
2689 l.release()
2691 else:
2690 else:
2692 try:
2691 try:
2693 stat = vfs.lstat(name)
2692 stat = vfs.lstat(name)
2694 age = now - stat.st_mtime
2693 age = now - stat.st_mtime
2695 user = util.username(stat.st_uid)
2694 user = util.username(stat.st_uid)
2696 locker = vfs.readlock(name)
2695 locker = vfs.readlock(name)
2697 if ":" in locker:
2696 if ":" in locker:
2698 host, pid = locker.split(':')
2697 host, pid = locker.split(':')
2699 if host == socket.gethostname():
2698 if host == socket.gethostname():
2700 locker = 'user %s, process %s' % (user, pid)
2699 locker = 'user %s, process %s' % (user, pid)
2701 else:
2700 else:
2702 locker = 'user %s, process %s, host %s' \
2701 locker = 'user %s, process %s, host %s' \
2703 % (user, pid, host)
2702 % (user, pid, host)
2704 ui.write(("%-6s %s (%ds)\n") % (name + ":", locker, age))
2703 ui.write(("%-6s %s (%ds)\n") % (name + ":", locker, age))
2705 return 1
2704 return 1
2706 except OSError as e:
2705 except OSError as e:
2707 if e.errno != errno.ENOENT:
2706 if e.errno != errno.ENOENT:
2708 raise
2707 raise
2709
2708
2710 ui.write(("%-6s free\n") % (name + ":"))
2709 ui.write(("%-6s free\n") % (name + ":"))
2711 return 0
2710 return 0
2712
2711
2713 held += report(repo.svfs, "lock", repo.lock)
2712 held += report(repo.svfs, "lock", repo.lock)
2714 held += report(repo.vfs, "wlock", repo.wlock)
2713 held += report(repo.vfs, "wlock", repo.wlock)
2715
2714
2716 return held
2715 return held
2717
2716
2718 @command('debugobsolete',
2717 @command('debugobsolete',
2719 [('', 'flags', 0, _('markers flag')),
2718 [('', 'flags', 0, _('markers flag')),
2720 ('', 'record-parents', False,
2719 ('', 'record-parents', False,
2721 _('record parent information for the precursor')),
2720 _('record parent information for the precursor')),
2722 ('r', 'rev', [], _('display markers relevant to REV')),
2721 ('r', 'rev', [], _('display markers relevant to REV')),
2723 ('', 'index', False, _('display index of the marker')),
2722 ('', 'index', False, _('display index of the marker')),
2724 ('', 'delete', [], _('delete markers specified by indices')),
2723 ('', 'delete', [], _('delete markers specified by indices')),
2725 ] + commitopts2 + formatteropts,
2724 ] + commitopts2 + formatteropts,
2726 _('[OBSOLETED [REPLACEMENT ...]]'))
2725 _('[OBSOLETED [REPLACEMENT ...]]'))
2727 def debugobsolete(ui, repo, precursor=None, *successors, **opts):
2726 def debugobsolete(ui, repo, precursor=None, *successors, **opts):
2728 """create arbitrary obsolete marker
2727 """create arbitrary obsolete marker
2729
2728
2730 With no arguments, displays the list of obsolescence markers."""
2729 With no arguments, displays the list of obsolescence markers."""
2731
2730
2732 def parsenodeid(s):
2731 def parsenodeid(s):
2733 try:
2732 try:
2734 # We do not use revsingle/revrange functions here to accept
2733 # We do not use revsingle/revrange functions here to accept
2735 # arbitrary node identifiers, possibly not present in the
2734 # arbitrary node identifiers, possibly not present in the
2736 # local repository.
2735 # local repository.
2737 n = bin(s)
2736 n = bin(s)
2738 if len(n) != len(nullid):
2737 if len(n) != len(nullid):
2739 raise TypeError()
2738 raise TypeError()
2740 return n
2739 return n
2741 except TypeError:
2740 except TypeError:
2742 raise error.Abort('changeset references must be full hexadecimal '
2741 raise error.Abort('changeset references must be full hexadecimal '
2743 'node identifiers')
2742 'node identifiers')
2744
2743
2745 if opts.get('delete'):
2744 if opts.get('delete'):
2746 indices = []
2745 indices = []
2747 for v in opts.get('delete'):
2746 for v in opts.get('delete'):
2748 try:
2747 try:
2749 indices.append(int(v))
2748 indices.append(int(v))
2750 except ValueError:
2749 except ValueError:
2751 raise error.Abort(_('invalid index value: %r') % v,
2750 raise error.Abort(_('invalid index value: %r') % v,
2752 hint=_('use integers for indices'))
2751 hint=_('use integers for indices'))
2753
2752
2754 if repo.currenttransaction():
2753 if repo.currenttransaction():
2755 raise error.Abort(_('cannot delete obsmarkers in the middle '
2754 raise error.Abort(_('cannot delete obsmarkers in the middle '
2756 'of transaction.'))
2755 'of transaction.'))
2757
2756
2758 with repo.lock():
2757 with repo.lock():
2759 n = repair.deleteobsmarkers(repo.obsstore, indices)
2758 n = repair.deleteobsmarkers(repo.obsstore, indices)
2760 ui.write(_('deleted %i obsolescence markers\n') % n)
2759 ui.write(_('deleted %i obsolescence markers\n') % n)
2761
2760
2762 return
2761 return
2763
2762
2764 if precursor is not None:
2763 if precursor is not None:
2765 if opts['rev']:
2764 if opts['rev']:
2766 raise error.Abort('cannot select revision when creating marker')
2765 raise error.Abort('cannot select revision when creating marker')
2767 metadata = {}
2766 metadata = {}
2768 metadata['user'] = opts['user'] or ui.username()
2767 metadata['user'] = opts['user'] or ui.username()
2769 succs = tuple(parsenodeid(succ) for succ in successors)
2768 succs = tuple(parsenodeid(succ) for succ in successors)
2770 l = repo.lock()
2769 l = repo.lock()
2771 try:
2770 try:
2772 tr = repo.transaction('debugobsolete')
2771 tr = repo.transaction('debugobsolete')
2773 try:
2772 try:
2774 date = opts.get('date')
2773 date = opts.get('date')
2775 if date:
2774 if date:
2776 date = util.parsedate(date)
2775 date = util.parsedate(date)
2777 else:
2776 else:
2778 date = None
2777 date = None
2779 prec = parsenodeid(precursor)
2778 prec = parsenodeid(precursor)
2780 parents = None
2779 parents = None
2781 if opts['record_parents']:
2780 if opts['record_parents']:
2782 if prec not in repo.unfiltered():
2781 if prec not in repo.unfiltered():
2783 raise error.Abort('cannot used --record-parents on '
2782 raise error.Abort('cannot used --record-parents on '
2784 'unknown changesets')
2783 'unknown changesets')
2785 parents = repo.unfiltered()[prec].parents()
2784 parents = repo.unfiltered()[prec].parents()
2786 parents = tuple(p.node() for p in parents)
2785 parents = tuple(p.node() for p in parents)
2787 repo.obsstore.create(tr, prec, succs, opts['flags'],
2786 repo.obsstore.create(tr, prec, succs, opts['flags'],
2788 parents=parents, date=date,
2787 parents=parents, date=date,
2789 metadata=metadata)
2788 metadata=metadata)
2790 tr.close()
2789 tr.close()
2791 except ValueError as exc:
2790 except ValueError as exc:
2792 raise error.Abort(_('bad obsmarker input: %s') % exc)
2791 raise error.Abort(_('bad obsmarker input: %s') % exc)
2793 finally:
2792 finally:
2794 tr.release()
2793 tr.release()
2795 finally:
2794 finally:
2796 l.release()
2795 l.release()
2797 else:
2796 else:
2798 if opts['rev']:
2797 if opts['rev']:
2799 revs = scmutil.revrange(repo, opts['rev'])
2798 revs = scmutil.revrange(repo, opts['rev'])
2800 nodes = [repo[r].node() for r in revs]
2799 nodes = [repo[r].node() for r in revs]
2801 markers = list(obsolete.getmarkers(repo, nodes=nodes))
2800 markers = list(obsolete.getmarkers(repo, nodes=nodes))
2802 markers.sort(key=lambda x: x._data)
2801 markers.sort(key=lambda x: x._data)
2803 else:
2802 else:
2804 markers = obsolete.getmarkers(repo)
2803 markers = obsolete.getmarkers(repo)
2805
2804
2806 markerstoiter = markers
2805 markerstoiter = markers
2807 isrelevant = lambda m: True
2806 isrelevant = lambda m: True
2808 if opts.get('rev') and opts.get('index'):
2807 if opts.get('rev') and opts.get('index'):
2809 markerstoiter = obsolete.getmarkers(repo)
2808 markerstoiter = obsolete.getmarkers(repo)
2810 markerset = set(markers)
2809 markerset = set(markers)
2811 isrelevant = lambda m: m in markerset
2810 isrelevant = lambda m: m in markerset
2812
2811
2813 fm = ui.formatter('debugobsolete', opts)
2812 fm = ui.formatter('debugobsolete', opts)
2814 for i, m in enumerate(markerstoiter):
2813 for i, m in enumerate(markerstoiter):
2815 if not isrelevant(m):
2814 if not isrelevant(m):
2816 # marker can be irrelevant when we're iterating over a set
2815 # marker can be irrelevant when we're iterating over a set
2817 # of markers (markerstoiter) which is bigger than the set
2816 # of markers (markerstoiter) which is bigger than the set
2818 # of markers we want to display (markers)
2817 # of markers we want to display (markers)
2819 # this can happen if both --index and --rev options are
2818 # this can happen if both --index and --rev options are
2820 # provided and thus we need to iterate over all of the markers
2819 # provided and thus we need to iterate over all of the markers
2821 # to get the correct indices, but only display the ones that
2820 # to get the correct indices, but only display the ones that
2822 # are relevant to --rev value
2821 # are relevant to --rev value
2823 continue
2822 continue
2824 fm.startitem()
2823 fm.startitem()
2825 ind = i if opts.get('index') else None
2824 ind = i if opts.get('index') else None
2826 cmdutil.showmarker(fm, m, index=ind)
2825 cmdutil.showmarker(fm, m, index=ind)
2827 fm.end()
2826 fm.end()
2828
2827
2829 @command('debugpathcomplete',
2828 @command('debugpathcomplete',
2830 [('f', 'full', None, _('complete an entire path')),
2829 [('f', 'full', None, _('complete an entire path')),
2831 ('n', 'normal', None, _('show only normal files')),
2830 ('n', 'normal', None, _('show only normal files')),
2832 ('a', 'added', None, _('show only added files')),
2831 ('a', 'added', None, _('show only added files')),
2833 ('r', 'removed', None, _('show only removed files'))],
2832 ('r', 'removed', None, _('show only removed files'))],
2834 _('FILESPEC...'))
2833 _('FILESPEC...'))
2835 def debugpathcomplete(ui, repo, *specs, **opts):
2834 def debugpathcomplete(ui, repo, *specs, **opts):
2836 '''complete part or all of a tracked path
2835 '''complete part or all of a tracked path
2837
2836
2838 This command supports shells that offer path name completion. It
2837 This command supports shells that offer path name completion. It
2839 currently completes only files already known to the dirstate.
2838 currently completes only files already known to the dirstate.
2840
2839
2841 Completion extends only to the next path segment unless
2840 Completion extends only to the next path segment unless
2842 --full is specified, in which case entire paths are used.'''
2841 --full is specified, in which case entire paths are used.'''
2843
2842
2844 def complete(path, acceptable):
2843 def complete(path, acceptable):
2845 dirstate = repo.dirstate
2844 dirstate = repo.dirstate
2846 spec = os.path.normpath(os.path.join(os.getcwd(), path))
2845 spec = os.path.normpath(os.path.join(os.getcwd(), path))
2847 rootdir = repo.root + os.sep
2846 rootdir = repo.root + os.sep
2848 if spec != repo.root and not spec.startswith(rootdir):
2847 if spec != repo.root and not spec.startswith(rootdir):
2849 return [], []
2848 return [], []
2850 if os.path.isdir(spec):
2849 if os.path.isdir(spec):
2851 spec += '/'
2850 spec += '/'
2852 spec = spec[len(rootdir):]
2851 spec = spec[len(rootdir):]
2853 fixpaths = pycompat.ossep != '/'
2852 fixpaths = pycompat.ossep != '/'
2854 if fixpaths:
2853 if fixpaths:
2855 spec = spec.replace(os.sep, '/')
2854 spec = spec.replace(os.sep, '/')
2856 speclen = len(spec)
2855 speclen = len(spec)
2857 fullpaths = opts['full']
2856 fullpaths = opts['full']
2858 files, dirs = set(), set()
2857 files, dirs = set(), set()
2859 adddir, addfile = dirs.add, files.add
2858 adddir, addfile = dirs.add, files.add
2860 for f, st in dirstate.iteritems():
2859 for f, st in dirstate.iteritems():
2861 if f.startswith(spec) and st[0] in acceptable:
2860 if f.startswith(spec) and st[0] in acceptable:
2862 if fixpaths:
2861 if fixpaths:
2863 f = f.replace('/', os.sep)
2862 f = f.replace('/', os.sep)
2864 if fullpaths:
2863 if fullpaths:
2865 addfile(f)
2864 addfile(f)
2866 continue
2865 continue
2867 s = f.find(os.sep, speclen)
2866 s = f.find(os.sep, speclen)
2868 if s >= 0:
2867 if s >= 0:
2869 adddir(f[:s])
2868 adddir(f[:s])
2870 else:
2869 else:
2871 addfile(f)
2870 addfile(f)
2872 return files, dirs
2871 return files, dirs
2873
2872
2874 acceptable = ''
2873 acceptable = ''
2875 if opts['normal']:
2874 if opts['normal']:
2876 acceptable += 'nm'
2875 acceptable += 'nm'
2877 if opts['added']:
2876 if opts['added']:
2878 acceptable += 'a'
2877 acceptable += 'a'
2879 if opts['removed']:
2878 if opts['removed']:
2880 acceptable += 'r'
2879 acceptable += 'r'
2881 cwd = repo.getcwd()
2880 cwd = repo.getcwd()
2882 if not specs:
2881 if not specs:
2883 specs = ['.']
2882 specs = ['.']
2884
2883
2885 files, dirs = set(), set()
2884 files, dirs = set(), set()
2886 for spec in specs:
2885 for spec in specs:
2887 f, d = complete(spec, acceptable or 'nmar')
2886 f, d = complete(spec, acceptable or 'nmar')
2888 files.update(f)
2887 files.update(f)
2889 dirs.update(d)
2888 dirs.update(d)
2890 files.update(dirs)
2889 files.update(dirs)
2891 ui.write('\n'.join(repo.pathto(p, cwd) for p in sorted(files)))
2890 ui.write('\n'.join(repo.pathto(p, cwd) for p in sorted(files)))
2892 ui.write('\n')
2891 ui.write('\n')
2893
2892
2894 @command('debugpushkey', [], _('REPO NAMESPACE [KEY OLD NEW]'), norepo=True)
2893 @command('debugpushkey', [], _('REPO NAMESPACE [KEY OLD NEW]'), norepo=True)
2895 def debugpushkey(ui, repopath, namespace, *keyinfo, **opts):
2894 def debugpushkey(ui, repopath, namespace, *keyinfo, **opts):
2896 '''access the pushkey key/value protocol
2895 '''access the pushkey key/value protocol
2897
2896
2898 With two args, list the keys in the given namespace.
2897 With two args, list the keys in the given namespace.
2899
2898
2900 With five args, set a key to new if it currently is set to old.
2899 With five args, set a key to new if it currently is set to old.
2901 Reports success or failure.
2900 Reports success or failure.
2902 '''
2901 '''
2903
2902
2904 target = hg.peer(ui, {}, repopath)
2903 target = hg.peer(ui, {}, repopath)
2905 if keyinfo:
2904 if keyinfo:
2906 key, old, new = keyinfo
2905 key, old, new = keyinfo
2907 r = target.pushkey(namespace, key, old, new)
2906 r = target.pushkey(namespace, key, old, new)
2908 ui.status(str(r) + '\n')
2907 ui.status(str(r) + '\n')
2909 return not r
2908 return not r
2910 else:
2909 else:
2911 for k, v in sorted(target.listkeys(namespace).iteritems()):
2910 for k, v in sorted(target.listkeys(namespace).iteritems()):
2912 ui.write("%s\t%s\n" % (k.encode('string-escape'),
2911 ui.write("%s\t%s\n" % (k.encode('string-escape'),
2913 v.encode('string-escape')))
2912 v.encode('string-escape')))
2914
2913
2915 @command('debugpvec', [], _('A B'))
2914 @command('debugpvec', [], _('A B'))
2916 def debugpvec(ui, repo, a, b=None):
2915 def debugpvec(ui, repo, a, b=None):
2917 ca = scmutil.revsingle(repo, a)
2916 ca = scmutil.revsingle(repo, a)
2918 cb = scmutil.revsingle(repo, b)
2917 cb = scmutil.revsingle(repo, b)
2919 pa = pvec.ctxpvec(ca)
2918 pa = pvec.ctxpvec(ca)
2920 pb = pvec.ctxpvec(cb)
2919 pb = pvec.ctxpvec(cb)
2921 if pa == pb:
2920 if pa == pb:
2922 rel = "="
2921 rel = "="
2923 elif pa > pb:
2922 elif pa > pb:
2924 rel = ">"
2923 rel = ">"
2925 elif pa < pb:
2924 elif pa < pb:
2926 rel = "<"
2925 rel = "<"
2927 elif pa | pb:
2926 elif pa | pb:
2928 rel = "|"
2927 rel = "|"
2929 ui.write(_("a: %s\n") % pa)
2928 ui.write(_("a: %s\n") % pa)
2930 ui.write(_("b: %s\n") % pb)
2929 ui.write(_("b: %s\n") % pb)
2931 ui.write(_("depth(a): %d depth(b): %d\n") % (pa._depth, pb._depth))
2930 ui.write(_("depth(a): %d depth(b): %d\n") % (pa._depth, pb._depth))
2932 ui.write(_("delta: %d hdist: %d distance: %d relation: %s\n") %
2931 ui.write(_("delta: %d hdist: %d distance: %d relation: %s\n") %
2933 (abs(pa._depth - pb._depth), pvec._hamming(pa._vec, pb._vec),
2932 (abs(pa._depth - pb._depth), pvec._hamming(pa._vec, pb._vec),
2934 pa.distance(pb), rel))
2933 pa.distance(pb), rel))
2935
2934
2936 @command('debugrebuilddirstate|debugrebuildstate',
2935 @command('debugrebuilddirstate|debugrebuildstate',
2937 [('r', 'rev', '', _('revision to rebuild to'), _('REV')),
2936 [('r', 'rev', '', _('revision to rebuild to'), _('REV')),
2938 ('', 'minimal', None, _('only rebuild files that are inconsistent with '
2937 ('', 'minimal', None, _('only rebuild files that are inconsistent with '
2939 'the working copy parent')),
2938 'the working copy parent')),
2940 ],
2939 ],
2941 _('[-r REV]'))
2940 _('[-r REV]'))
2942 def debugrebuilddirstate(ui, repo, rev, **opts):
2941 def debugrebuilddirstate(ui, repo, rev, **opts):
2943 """rebuild the dirstate as it would look like for the given revision
2942 """rebuild the dirstate as it would look like for the given revision
2944
2943
2945 If no revision is specified the first current parent will be used.
2944 If no revision is specified the first current parent will be used.
2946
2945
2947 The dirstate will be set to the files of the given revision.
2946 The dirstate will be set to the files of the given revision.
2948 The actual working directory content or existing dirstate
2947 The actual working directory content or existing dirstate
2949 information such as adds or removes is not considered.
2948 information such as adds or removes is not considered.
2950
2949
2951 ``minimal`` will only rebuild the dirstate status for files that claim to be
2950 ``minimal`` will only rebuild the dirstate status for files that claim to be
2952 tracked but are not in the parent manifest, or that exist in the parent
2951 tracked but are not in the parent manifest, or that exist in the parent
2953 manifest but are not in the dirstate. It will not change adds, removes, or
2952 manifest but are not in the dirstate. It will not change adds, removes, or
2954 modified files that are in the working copy parent.
2953 modified files that are in the working copy parent.
2955
2954
2956 One use of this command is to make the next :hg:`status` invocation
2955 One use of this command is to make the next :hg:`status` invocation
2957 check the actual file content.
2956 check the actual file content.
2958 """
2957 """
2959 ctx = scmutil.revsingle(repo, rev)
2958 ctx = scmutil.revsingle(repo, rev)
2960 with repo.wlock():
2959 with repo.wlock():
2961 dirstate = repo.dirstate
2960 dirstate = repo.dirstate
2962 changedfiles = None
2961 changedfiles = None
2963 # See command doc for what minimal does.
2962 # See command doc for what minimal does.
2964 if opts.get('minimal'):
2963 if opts.get('minimal'):
2965 manifestfiles = set(ctx.manifest().keys())
2964 manifestfiles = set(ctx.manifest().keys())
2966 dirstatefiles = set(dirstate)
2965 dirstatefiles = set(dirstate)
2967 manifestonly = manifestfiles - dirstatefiles
2966 manifestonly = manifestfiles - dirstatefiles
2968 dsonly = dirstatefiles - manifestfiles
2967 dsonly = dirstatefiles - manifestfiles
2969 dsnotadded = set(f for f in dsonly if dirstate[f] != 'a')
2968 dsnotadded = set(f for f in dsonly if dirstate[f] != 'a')
2970 changedfiles = manifestonly | dsnotadded
2969 changedfiles = manifestonly | dsnotadded
2971
2970
2972 dirstate.rebuild(ctx.node(), ctx.manifest(), changedfiles)
2971 dirstate.rebuild(ctx.node(), ctx.manifest(), changedfiles)
2973
2972
2974 @command('debugrebuildfncache', [], '')
2973 @command('debugrebuildfncache', [], '')
2975 def debugrebuildfncache(ui, repo):
2974 def debugrebuildfncache(ui, repo):
2976 """rebuild the fncache file"""
2975 """rebuild the fncache file"""
2977 repair.rebuildfncache(ui, repo)
2976 repair.rebuildfncache(ui, repo)
2978
2977
2979 @command('debugrename',
2978 @command('debugrename',
2980 [('r', 'rev', '', _('revision to debug'), _('REV'))],
2979 [('r', 'rev', '', _('revision to debug'), _('REV'))],
2981 _('[-r REV] FILE'))
2980 _('[-r REV] FILE'))
2982 def debugrename(ui, repo, file1, *pats, **opts):
2981 def debugrename(ui, repo, file1, *pats, **opts):
2983 """dump rename information"""
2982 """dump rename information"""
2984
2983
2985 ctx = scmutil.revsingle(repo, opts.get('rev'))
2984 ctx = scmutil.revsingle(repo, opts.get('rev'))
2986 m = scmutil.match(ctx, (file1,) + pats, opts)
2985 m = scmutil.match(ctx, (file1,) + pats, opts)
2987 for abs in ctx.walk(m):
2986 for abs in ctx.walk(m):
2988 fctx = ctx[abs]
2987 fctx = ctx[abs]
2989 o = fctx.filelog().renamed(fctx.filenode())
2988 o = fctx.filelog().renamed(fctx.filenode())
2990 rel = m.rel(abs)
2989 rel = m.rel(abs)
2991 if o:
2990 if o:
2992 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
2991 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
2993 else:
2992 else:
2994 ui.write(_("%s not renamed\n") % rel)
2993 ui.write(_("%s not renamed\n") % rel)
2995
2994
2996 @command('debugrevlog', debugrevlogopts +
2995 @command('debugrevlog', debugrevlogopts +
2997 [('d', 'dump', False, _('dump index data'))],
2996 [('d', 'dump', False, _('dump index data'))],
2998 _('-c|-m|FILE'),
2997 _('-c|-m|FILE'),
2999 optionalrepo=True)
2998 optionalrepo=True)
3000 def debugrevlog(ui, repo, file_=None, **opts):
2999 def debugrevlog(ui, repo, file_=None, **opts):
3001 """show data and statistics about a revlog"""
3000 """show data and statistics about a revlog"""
3002 r = cmdutil.openrevlog(repo, 'debugrevlog', file_, opts)
3001 r = cmdutil.openrevlog(repo, 'debugrevlog', file_, opts)
3003
3002
3004 if opts.get("dump"):
3003 if opts.get("dump"):
3005 numrevs = len(r)
3004 numrevs = len(r)
3006 ui.write(("# rev p1rev p2rev start end deltastart base p1 p2"
3005 ui.write(("# rev p1rev p2rev start end deltastart base p1 p2"
3007 " rawsize totalsize compression heads chainlen\n"))
3006 " rawsize totalsize compression heads chainlen\n"))
3008 ts = 0
3007 ts = 0
3009 heads = set()
3008 heads = set()
3010
3009
3011 for rev in xrange(numrevs):
3010 for rev in xrange(numrevs):
3012 dbase = r.deltaparent(rev)
3011 dbase = r.deltaparent(rev)
3013 if dbase == -1:
3012 if dbase == -1:
3014 dbase = rev
3013 dbase = rev
3015 cbase = r.chainbase(rev)
3014 cbase = r.chainbase(rev)
3016 clen = r.chainlen(rev)
3015 clen = r.chainlen(rev)
3017 p1, p2 = r.parentrevs(rev)
3016 p1, p2 = r.parentrevs(rev)
3018 rs = r.rawsize(rev)
3017 rs = r.rawsize(rev)
3019 ts = ts + rs
3018 ts = ts + rs
3020 heads -= set(r.parentrevs(rev))
3019 heads -= set(r.parentrevs(rev))
3021 heads.add(rev)
3020 heads.add(rev)
3022 try:
3021 try:
3023 compression = ts / r.end(rev)
3022 compression = ts / r.end(rev)
3024 except ZeroDivisionError:
3023 except ZeroDivisionError:
3025 compression = 0
3024 compression = 0
3026 ui.write("%5d %5d %5d %5d %5d %10d %4d %4d %4d %7d %9d "
3025 ui.write("%5d %5d %5d %5d %5d %10d %4d %4d %4d %7d %9d "
3027 "%11d %5d %8d\n" %
3026 "%11d %5d %8d\n" %
3028 (rev, p1, p2, r.start(rev), r.end(rev),
3027 (rev, p1, p2, r.start(rev), r.end(rev),
3029 r.start(dbase), r.start(cbase),
3028 r.start(dbase), r.start(cbase),
3030 r.start(p1), r.start(p2),
3029 r.start(p1), r.start(p2),
3031 rs, ts, compression, len(heads), clen))
3030 rs, ts, compression, len(heads), clen))
3032 return 0
3031 return 0
3033
3032
3034 v = r.version
3033 v = r.version
3035 format = v & 0xFFFF
3034 format = v & 0xFFFF
3036 flags = []
3035 flags = []
3037 gdelta = False
3036 gdelta = False
3038 if v & revlog.REVLOGNGINLINEDATA:
3037 if v & revlog.REVLOGNGINLINEDATA:
3039 flags.append('inline')
3038 flags.append('inline')
3040 if v & revlog.REVLOGGENERALDELTA:
3039 if v & revlog.REVLOGGENERALDELTA:
3041 gdelta = True
3040 gdelta = True
3042 flags.append('generaldelta')
3041 flags.append('generaldelta')
3043 if not flags:
3042 if not flags:
3044 flags = ['(none)']
3043 flags = ['(none)']
3045
3044
3046 nummerges = 0
3045 nummerges = 0
3047 numfull = 0
3046 numfull = 0
3048 numprev = 0
3047 numprev = 0
3049 nump1 = 0
3048 nump1 = 0
3050 nump2 = 0
3049 nump2 = 0
3051 numother = 0
3050 numother = 0
3052 nump1prev = 0
3051 nump1prev = 0
3053 nump2prev = 0
3052 nump2prev = 0
3054 chainlengths = []
3053 chainlengths = []
3055
3054
3056 datasize = [None, 0, 0]
3055 datasize = [None, 0, 0]
3057 fullsize = [None, 0, 0]
3056 fullsize = [None, 0, 0]
3058 deltasize = [None, 0, 0]
3057 deltasize = [None, 0, 0]
3059 chunktypecounts = {}
3058 chunktypecounts = {}
3060 chunktypesizes = {}
3059 chunktypesizes = {}
3061
3060
3062 def addsize(size, l):
3061 def addsize(size, l):
3063 if l[0] is None or size < l[0]:
3062 if l[0] is None or size < l[0]:
3064 l[0] = size
3063 l[0] = size
3065 if size > l[1]:
3064 if size > l[1]:
3066 l[1] = size
3065 l[1] = size
3067 l[2] += size
3066 l[2] += size
3068
3067
3069 numrevs = len(r)
3068 numrevs = len(r)
3070 for rev in xrange(numrevs):
3069 for rev in xrange(numrevs):
3071 p1, p2 = r.parentrevs(rev)
3070 p1, p2 = r.parentrevs(rev)
3072 delta = r.deltaparent(rev)
3071 delta = r.deltaparent(rev)
3073 if format > 0:
3072 if format > 0:
3074 addsize(r.rawsize(rev), datasize)
3073 addsize(r.rawsize(rev), datasize)
3075 if p2 != nullrev:
3074 if p2 != nullrev:
3076 nummerges += 1
3075 nummerges += 1
3077 size = r.length(rev)
3076 size = r.length(rev)
3078 if delta == nullrev:
3077 if delta == nullrev:
3079 chainlengths.append(0)
3078 chainlengths.append(0)
3080 numfull += 1
3079 numfull += 1
3081 addsize(size, fullsize)
3080 addsize(size, fullsize)
3082 else:
3081 else:
3083 chainlengths.append(chainlengths[delta] + 1)
3082 chainlengths.append(chainlengths[delta] + 1)
3084 addsize(size, deltasize)
3083 addsize(size, deltasize)
3085 if delta == rev - 1:
3084 if delta == rev - 1:
3086 numprev += 1
3085 numprev += 1
3087 if delta == p1:
3086 if delta == p1:
3088 nump1prev += 1
3087 nump1prev += 1
3089 elif delta == p2:
3088 elif delta == p2:
3090 nump2prev += 1
3089 nump2prev += 1
3091 elif delta == p1:
3090 elif delta == p1:
3092 nump1 += 1
3091 nump1 += 1
3093 elif delta == p2:
3092 elif delta == p2:
3094 nump2 += 1
3093 nump2 += 1
3095 elif delta != nullrev:
3094 elif delta != nullrev:
3096 numother += 1
3095 numother += 1
3097
3096
3098 # Obtain data on the raw chunks in the revlog.
3097 # Obtain data on the raw chunks in the revlog.
3099 chunk = r._chunkraw(rev, rev)[1]
3098 chunk = r._chunkraw(rev, rev)[1]
3100 if chunk:
3099 if chunk:
3101 chunktype = chunk[0]
3100 chunktype = chunk[0]
3102 else:
3101 else:
3103 chunktype = 'empty'
3102 chunktype = 'empty'
3104
3103
3105 if chunktype not in chunktypecounts:
3104 if chunktype not in chunktypecounts:
3106 chunktypecounts[chunktype] = 0
3105 chunktypecounts[chunktype] = 0
3107 chunktypesizes[chunktype] = 0
3106 chunktypesizes[chunktype] = 0
3108
3107
3109 chunktypecounts[chunktype] += 1
3108 chunktypecounts[chunktype] += 1
3110 chunktypesizes[chunktype] += size
3109 chunktypesizes[chunktype] += size
3111
3110
3112 # Adjust size min value for empty cases
3111 # Adjust size min value for empty cases
3113 for size in (datasize, fullsize, deltasize):
3112 for size in (datasize, fullsize, deltasize):
3114 if size[0] is None:
3113 if size[0] is None:
3115 size[0] = 0
3114 size[0] = 0
3116
3115
3117 numdeltas = numrevs - numfull
3116 numdeltas = numrevs - numfull
3118 numoprev = numprev - nump1prev - nump2prev
3117 numoprev = numprev - nump1prev - nump2prev
3119 totalrawsize = datasize[2]
3118 totalrawsize = datasize[2]
3120 datasize[2] /= numrevs
3119 datasize[2] /= numrevs
3121 fulltotal = fullsize[2]
3120 fulltotal = fullsize[2]
3122 fullsize[2] /= numfull
3121 fullsize[2] /= numfull
3123 deltatotal = deltasize[2]
3122 deltatotal = deltasize[2]
3124 if numrevs - numfull > 0:
3123 if numrevs - numfull > 0:
3125 deltasize[2] /= numrevs - numfull
3124 deltasize[2] /= numrevs - numfull
3126 totalsize = fulltotal + deltatotal
3125 totalsize = fulltotal + deltatotal
3127 avgchainlen = sum(chainlengths) / numrevs
3126 avgchainlen = sum(chainlengths) / numrevs
3128 maxchainlen = max(chainlengths)
3127 maxchainlen = max(chainlengths)
3129 compratio = 1
3128 compratio = 1
3130 if totalsize:
3129 if totalsize:
3131 compratio = totalrawsize / totalsize
3130 compratio = totalrawsize / totalsize
3132
3131
3133 basedfmtstr = '%%%dd\n'
3132 basedfmtstr = '%%%dd\n'
3134 basepcfmtstr = '%%%dd %s(%%5.2f%%%%)\n'
3133 basepcfmtstr = '%%%dd %s(%%5.2f%%%%)\n'
3135
3134
3136 def dfmtstr(max):
3135 def dfmtstr(max):
3137 return basedfmtstr % len(str(max))
3136 return basedfmtstr % len(str(max))
3138 def pcfmtstr(max, padding=0):
3137 def pcfmtstr(max, padding=0):
3139 return basepcfmtstr % (len(str(max)), ' ' * padding)
3138 return basepcfmtstr % (len(str(max)), ' ' * padding)
3140
3139
3141 def pcfmt(value, total):
3140 def pcfmt(value, total):
3142 if total:
3141 if total:
3143 return (value, 100 * float(value) / total)
3142 return (value, 100 * float(value) / total)
3144 else:
3143 else:
3145 return value, 100.0
3144 return value, 100.0
3146
3145
3147 ui.write(('format : %d\n') % format)
3146 ui.write(('format : %d\n') % format)
3148 ui.write(('flags : %s\n') % ', '.join(flags))
3147 ui.write(('flags : %s\n') % ', '.join(flags))
3149
3148
3150 ui.write('\n')
3149 ui.write('\n')
3151 fmt = pcfmtstr(totalsize)
3150 fmt = pcfmtstr(totalsize)
3152 fmt2 = dfmtstr(totalsize)
3151 fmt2 = dfmtstr(totalsize)
3153 ui.write(('revisions : ') + fmt2 % numrevs)
3152 ui.write(('revisions : ') + fmt2 % numrevs)
3154 ui.write((' merges : ') + fmt % pcfmt(nummerges, numrevs))
3153 ui.write((' merges : ') + fmt % pcfmt(nummerges, numrevs))
3155 ui.write((' normal : ') + fmt % pcfmt(numrevs - nummerges, numrevs))
3154 ui.write((' normal : ') + fmt % pcfmt(numrevs - nummerges, numrevs))
3156 ui.write(('revisions : ') + fmt2 % numrevs)
3155 ui.write(('revisions : ') + fmt2 % numrevs)
3157 ui.write((' full : ') + fmt % pcfmt(numfull, numrevs))
3156 ui.write((' full : ') + fmt % pcfmt(numfull, numrevs))
3158 ui.write((' deltas : ') + fmt % pcfmt(numdeltas, numrevs))
3157 ui.write((' deltas : ') + fmt % pcfmt(numdeltas, numrevs))
3159 ui.write(('revision size : ') + fmt2 % totalsize)
3158 ui.write(('revision size : ') + fmt2 % totalsize)
3160 ui.write((' full : ') + fmt % pcfmt(fulltotal, totalsize))
3159 ui.write((' full : ') + fmt % pcfmt(fulltotal, totalsize))
3161 ui.write((' deltas : ') + fmt % pcfmt(deltatotal, totalsize))
3160 ui.write((' deltas : ') + fmt % pcfmt(deltatotal, totalsize))
3162
3161
3163 def fmtchunktype(chunktype):
3162 def fmtchunktype(chunktype):
3164 if chunktype == 'empty':
3163 if chunktype == 'empty':
3165 return ' %s : ' % chunktype
3164 return ' %s : ' % chunktype
3166 elif chunktype in string.ascii_letters:
3165 elif chunktype in string.ascii_letters:
3167 return ' 0x%s (%s) : ' % (hex(chunktype), chunktype)
3166 return ' 0x%s (%s) : ' % (hex(chunktype), chunktype)
3168 else:
3167 else:
3169 return ' 0x%s : ' % hex(chunktype)
3168 return ' 0x%s : ' % hex(chunktype)
3170
3169
3171 ui.write('\n')
3170 ui.write('\n')
3172 ui.write(('chunks : ') + fmt2 % numrevs)
3171 ui.write(('chunks : ') + fmt2 % numrevs)
3173 for chunktype in sorted(chunktypecounts):
3172 for chunktype in sorted(chunktypecounts):
3174 ui.write(fmtchunktype(chunktype))
3173 ui.write(fmtchunktype(chunktype))
3175 ui.write(fmt % pcfmt(chunktypecounts[chunktype], numrevs))
3174 ui.write(fmt % pcfmt(chunktypecounts[chunktype], numrevs))
3176 ui.write(('chunks size : ') + fmt2 % totalsize)
3175 ui.write(('chunks size : ') + fmt2 % totalsize)
3177 for chunktype in sorted(chunktypecounts):
3176 for chunktype in sorted(chunktypecounts):
3178 ui.write(fmtchunktype(chunktype))
3177 ui.write(fmtchunktype(chunktype))
3179 ui.write(fmt % pcfmt(chunktypesizes[chunktype], totalsize))
3178 ui.write(fmt % pcfmt(chunktypesizes[chunktype], totalsize))
3180
3179
3181 ui.write('\n')
3180 ui.write('\n')
3182 fmt = dfmtstr(max(avgchainlen, compratio))
3181 fmt = dfmtstr(max(avgchainlen, compratio))
3183 ui.write(('avg chain length : ') + fmt % avgchainlen)
3182 ui.write(('avg chain length : ') + fmt % avgchainlen)
3184 ui.write(('max chain length : ') + fmt % maxchainlen)
3183 ui.write(('max chain length : ') + fmt % maxchainlen)
3185 ui.write(('compression ratio : ') + fmt % compratio)
3184 ui.write(('compression ratio : ') + fmt % compratio)
3186
3185
3187 if format > 0:
3186 if format > 0:
3188 ui.write('\n')
3187 ui.write('\n')
3189 ui.write(('uncompressed data size (min/max/avg) : %d / %d / %d\n')
3188 ui.write(('uncompressed data size (min/max/avg) : %d / %d / %d\n')
3190 % tuple(datasize))
3189 % tuple(datasize))
3191 ui.write(('full revision size (min/max/avg) : %d / %d / %d\n')
3190 ui.write(('full revision size (min/max/avg) : %d / %d / %d\n')
3192 % tuple(fullsize))
3191 % tuple(fullsize))
3193 ui.write(('delta size (min/max/avg) : %d / %d / %d\n')
3192 ui.write(('delta size (min/max/avg) : %d / %d / %d\n')
3194 % tuple(deltasize))
3193 % tuple(deltasize))
3195
3194
3196 if numdeltas > 0:
3195 if numdeltas > 0:
3197 ui.write('\n')
3196 ui.write('\n')
3198 fmt = pcfmtstr(numdeltas)
3197 fmt = pcfmtstr(numdeltas)
3199 fmt2 = pcfmtstr(numdeltas, 4)
3198 fmt2 = pcfmtstr(numdeltas, 4)
3200 ui.write(('deltas against prev : ') + fmt % pcfmt(numprev, numdeltas))
3199 ui.write(('deltas against prev : ') + fmt % pcfmt(numprev, numdeltas))
3201 if numprev > 0:
3200 if numprev > 0:
3202 ui.write((' where prev = p1 : ') + fmt2 % pcfmt(nump1prev,
3201 ui.write((' where prev = p1 : ') + fmt2 % pcfmt(nump1prev,
3203 numprev))
3202 numprev))
3204 ui.write((' where prev = p2 : ') + fmt2 % pcfmt(nump2prev,
3203 ui.write((' where prev = p2 : ') + fmt2 % pcfmt(nump2prev,
3205 numprev))
3204 numprev))
3206 ui.write((' other : ') + fmt2 % pcfmt(numoprev,
3205 ui.write((' other : ') + fmt2 % pcfmt(numoprev,
3207 numprev))
3206 numprev))
3208 if gdelta:
3207 if gdelta:
3209 ui.write(('deltas against p1 : ')
3208 ui.write(('deltas against p1 : ')
3210 + fmt % pcfmt(nump1, numdeltas))
3209 + fmt % pcfmt(nump1, numdeltas))
3211 ui.write(('deltas against p2 : ')
3210 ui.write(('deltas against p2 : ')
3212 + fmt % pcfmt(nump2, numdeltas))
3211 + fmt % pcfmt(nump2, numdeltas))
3213 ui.write(('deltas against other : ') + fmt % pcfmt(numother,
3212 ui.write(('deltas against other : ') + fmt % pcfmt(numother,
3214 numdeltas))
3213 numdeltas))
3215
3214
3216 @command('debugrevspec',
3215 @command('debugrevspec',
3217 [('', 'optimize', None,
3216 [('', 'optimize', None,
3218 _('print parsed tree after optimizing (DEPRECATED)')),
3217 _('print parsed tree after optimizing (DEPRECATED)')),
3219 ('p', 'show-stage', [],
3218 ('p', 'show-stage', [],
3220 _('print parsed tree at the given stage'), _('NAME')),
3219 _('print parsed tree at the given stage'), _('NAME')),
3221 ('', 'no-optimized', False, _('evaluate tree without optimization')),
3220 ('', 'no-optimized', False, _('evaluate tree without optimization')),
3222 ('', 'verify-optimized', False, _('verify optimized result')),
3221 ('', 'verify-optimized', False, _('verify optimized result')),
3223 ],
3222 ],
3224 ('REVSPEC'))
3223 ('REVSPEC'))
3225 def debugrevspec(ui, repo, expr, **opts):
3224 def debugrevspec(ui, repo, expr, **opts):
3226 """parse and apply a revision specification
3225 """parse and apply a revision specification
3227
3226
3228 Use -p/--show-stage option to print the parsed tree at the given stages.
3227 Use -p/--show-stage option to print the parsed tree at the given stages.
3229 Use -p all to print tree at every stage.
3228 Use -p all to print tree at every stage.
3230
3229
3231 Use --verify-optimized to compare the optimized result with the unoptimized
3230 Use --verify-optimized to compare the optimized result with the unoptimized
3232 one. Returns 1 if the optimized result differs.
3231 one. Returns 1 if the optimized result differs.
3233 """
3232 """
3234 stages = [
3233 stages = [
3235 ('parsed', lambda tree: tree),
3234 ('parsed', lambda tree: tree),
3236 ('expanded', lambda tree: revset.expandaliases(ui, tree)),
3235 ('expanded', lambda tree: revset.expandaliases(ui, tree)),
3237 ('concatenated', revset.foldconcat),
3236 ('concatenated', revset.foldconcat),
3238 ('analyzed', revset.analyze),
3237 ('analyzed', revset.analyze),
3239 ('optimized', revset.optimize),
3238 ('optimized', revset.optimize),
3240 ]
3239 ]
3241 if opts['no_optimized']:
3240 if opts['no_optimized']:
3242 stages = stages[:-1]
3241 stages = stages[:-1]
3243 if opts['verify_optimized'] and opts['no_optimized']:
3242 if opts['verify_optimized'] and opts['no_optimized']:
3244 raise error.Abort(_('cannot use --verify-optimized with '
3243 raise error.Abort(_('cannot use --verify-optimized with '
3245 '--no-optimized'))
3244 '--no-optimized'))
3246 stagenames = set(n for n, f in stages)
3245 stagenames = set(n for n, f in stages)
3247
3246
3248 showalways = set()
3247 showalways = set()
3249 showchanged = set()
3248 showchanged = set()
3250 if ui.verbose and not opts['show_stage']:
3249 if ui.verbose and not opts['show_stage']:
3251 # show parsed tree by --verbose (deprecated)
3250 # show parsed tree by --verbose (deprecated)
3252 showalways.add('parsed')
3251 showalways.add('parsed')
3253 showchanged.update(['expanded', 'concatenated'])
3252 showchanged.update(['expanded', 'concatenated'])
3254 if opts['optimize']:
3253 if opts['optimize']:
3255 showalways.add('optimized')
3254 showalways.add('optimized')
3256 if opts['show_stage'] and opts['optimize']:
3255 if opts['show_stage'] and opts['optimize']:
3257 raise error.Abort(_('cannot use --optimize with --show-stage'))
3256 raise error.Abort(_('cannot use --optimize with --show-stage'))
3258 if opts['show_stage'] == ['all']:
3257 if opts['show_stage'] == ['all']:
3259 showalways.update(stagenames)
3258 showalways.update(stagenames)
3260 else:
3259 else:
3261 for n in opts['show_stage']:
3260 for n in opts['show_stage']:
3262 if n not in stagenames:
3261 if n not in stagenames:
3263 raise error.Abort(_('invalid stage name: %s') % n)
3262 raise error.Abort(_('invalid stage name: %s') % n)
3264 showalways.update(opts['show_stage'])
3263 showalways.update(opts['show_stage'])
3265
3264
3266 treebystage = {}
3265 treebystage = {}
3267 printedtree = None
3266 printedtree = None
3268 tree = revset.parse(expr, lookup=repo.__contains__)
3267 tree = revset.parse(expr, lookup=repo.__contains__)
3269 for n, f in stages:
3268 for n, f in stages:
3270 treebystage[n] = tree = f(tree)
3269 treebystage[n] = tree = f(tree)
3271 if n in showalways or (n in showchanged and tree != printedtree):
3270 if n in showalways or (n in showchanged and tree != printedtree):
3272 if opts['show_stage'] or n != 'parsed':
3271 if opts['show_stage'] or n != 'parsed':
3273 ui.write(("* %s:\n") % n)
3272 ui.write(("* %s:\n") % n)
3274 ui.write(revset.prettyformat(tree), "\n")
3273 ui.write(revset.prettyformat(tree), "\n")
3275 printedtree = tree
3274 printedtree = tree
3276
3275
3277 if opts['verify_optimized']:
3276 if opts['verify_optimized']:
3278 arevs = revset.makematcher(treebystage['analyzed'])(repo)
3277 arevs = revset.makematcher(treebystage['analyzed'])(repo)
3279 brevs = revset.makematcher(treebystage['optimized'])(repo)
3278 brevs = revset.makematcher(treebystage['optimized'])(repo)
3280 if ui.verbose:
3279 if ui.verbose:
3281 ui.note(("* analyzed set:\n"), revset.prettyformatset(arevs), "\n")
3280 ui.note(("* analyzed set:\n"), revset.prettyformatset(arevs), "\n")
3282 ui.note(("* optimized set:\n"), revset.prettyformatset(brevs), "\n")
3281 ui.note(("* optimized set:\n"), revset.prettyformatset(brevs), "\n")
3283 arevs = list(arevs)
3282 arevs = list(arevs)
3284 brevs = list(brevs)
3283 brevs = list(brevs)
3285 if arevs == brevs:
3284 if arevs == brevs:
3286 return 0
3285 return 0
3287 ui.write(('--- analyzed\n'), label='diff.file_a')
3286 ui.write(('--- analyzed\n'), label='diff.file_a')
3288 ui.write(('+++ optimized\n'), label='diff.file_b')
3287 ui.write(('+++ optimized\n'), label='diff.file_b')
3289 sm = difflib.SequenceMatcher(None, arevs, brevs)
3288 sm = difflib.SequenceMatcher(None, arevs, brevs)
3290 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
3289 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
3291 if tag in ('delete', 'replace'):
3290 if tag in ('delete', 'replace'):
3292 for c in arevs[alo:ahi]:
3291 for c in arevs[alo:ahi]:
3293 ui.write('-%s\n' % c, label='diff.deleted')
3292 ui.write('-%s\n' % c, label='diff.deleted')
3294 if tag in ('insert', 'replace'):
3293 if tag in ('insert', 'replace'):
3295 for c in brevs[blo:bhi]:
3294 for c in brevs[blo:bhi]:
3296 ui.write('+%s\n' % c, label='diff.inserted')
3295 ui.write('+%s\n' % c, label='diff.inserted')
3297 if tag == 'equal':
3296 if tag == 'equal':
3298 for c in arevs[alo:ahi]:
3297 for c in arevs[alo:ahi]:
3299 ui.write(' %s\n' % c)
3298 ui.write(' %s\n' % c)
3300 return 1
3299 return 1
3301
3300
3302 func = revset.makematcher(tree)
3301 func = revset.makematcher(tree)
3303 revs = func(repo)
3302 revs = func(repo)
3304 if ui.verbose:
3303 if ui.verbose:
3305 ui.note(("* set:\n"), revset.prettyformatset(revs), "\n")
3304 ui.note(("* set:\n"), revset.prettyformatset(revs), "\n")
3306 for c in revs:
3305 for c in revs:
3307 ui.write("%s\n" % c)
3306 ui.write("%s\n" % c)
3308
3307
3309 @command('debugsetparents', [], _('REV1 [REV2]'))
3308 @command('debugsetparents', [], _('REV1 [REV2]'))
3310 def debugsetparents(ui, repo, rev1, rev2=None):
3309 def debugsetparents(ui, repo, rev1, rev2=None):
3311 """manually set the parents of the current working directory
3310 """manually set the parents of the current working directory
3312
3311
3313 This is useful for writing repository conversion tools, but should
3312 This is useful for writing repository conversion tools, but should
3314 be used with care. For example, neither the working directory nor the
3313 be used with care. For example, neither the working directory nor the
3315 dirstate is updated, so file status may be incorrect after running this
3314 dirstate is updated, so file status may be incorrect after running this
3316 command.
3315 command.
3317
3316
3318 Returns 0 on success.
3317 Returns 0 on success.
3319 """
3318 """
3320
3319
3321 r1 = scmutil.revsingle(repo, rev1).node()
3320 r1 = scmutil.revsingle(repo, rev1).node()
3322 r2 = scmutil.revsingle(repo, rev2, 'null').node()
3321 r2 = scmutil.revsingle(repo, rev2, 'null').node()
3323
3322
3324 with repo.wlock():
3323 with repo.wlock():
3325 repo.setparents(r1, r2)
3324 repo.setparents(r1, r2)
3326
3325
3327 @command('debugdirstate|debugstate',
3326 @command('debugdirstate|debugstate',
3328 [('', 'nodates', None, _('do not display the saved mtime')),
3327 [('', 'nodates', None, _('do not display the saved mtime')),
3329 ('', 'datesort', None, _('sort by saved mtime'))],
3328 ('', 'datesort', None, _('sort by saved mtime'))],
3330 _('[OPTION]...'))
3329 _('[OPTION]...'))
3331 def debugstate(ui, repo, **opts):
3330 def debugstate(ui, repo, **opts):
3332 """show the contents of the current dirstate"""
3331 """show the contents of the current dirstate"""
3333
3332
3334 nodates = opts.get('nodates')
3333 nodates = opts.get('nodates')
3335 datesort = opts.get('datesort')
3334 datesort = opts.get('datesort')
3336
3335
3337 timestr = ""
3336 timestr = ""
3338 if datesort:
3337 if datesort:
3339 keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
3338 keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
3340 else:
3339 else:
3341 keyfunc = None # sort by filename
3340 keyfunc = None # sort by filename
3342 for file_, ent in sorted(repo.dirstate._map.iteritems(), key=keyfunc):
3341 for file_, ent in sorted(repo.dirstate._map.iteritems(), key=keyfunc):
3343 if ent[3] == -1:
3342 if ent[3] == -1:
3344 timestr = 'unset '
3343 timestr = 'unset '
3345 elif nodates:
3344 elif nodates:
3346 timestr = 'set '
3345 timestr = 'set '
3347 else:
3346 else:
3348 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
3347 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
3349 time.localtime(ent[3]))
3348 time.localtime(ent[3]))
3350 if ent[1] & 0o20000:
3349 if ent[1] & 0o20000:
3351 mode = 'lnk'
3350 mode = 'lnk'
3352 else:
3351 else:
3353 mode = '%3o' % (ent[1] & 0o777 & ~util.umask)
3352 mode = '%3o' % (ent[1] & 0o777 & ~util.umask)
3354 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
3353 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
3355 for f in repo.dirstate.copies():
3354 for f in repo.dirstate.copies():
3356 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
3355 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
3357
3356
3358 @command('debugsub',
3357 @command('debugsub',
3359 [('r', 'rev', '',
3358 [('r', 'rev', '',
3360 _('revision to check'), _('REV'))],
3359 _('revision to check'), _('REV'))],
3361 _('[-r REV] [REV]'))
3360 _('[-r REV] [REV]'))
3362 def debugsub(ui, repo, rev=None):
3361 def debugsub(ui, repo, rev=None):
3363 ctx = scmutil.revsingle(repo, rev, None)
3362 ctx = scmutil.revsingle(repo, rev, None)
3364 for k, v in sorted(ctx.substate.items()):
3363 for k, v in sorted(ctx.substate.items()):
3365 ui.write(('path %s\n') % k)
3364 ui.write(('path %s\n') % k)
3366 ui.write((' source %s\n') % v[0])
3365 ui.write((' source %s\n') % v[0])
3367 ui.write((' revision %s\n') % v[1])
3366 ui.write((' revision %s\n') % v[1])
3368
3367
3369 @command('debugsuccessorssets',
3368 @command('debugsuccessorssets',
3370 [],
3369 [],
3371 _('[REV]'))
3370 _('[REV]'))
3372 def debugsuccessorssets(ui, repo, *revs):
3371 def debugsuccessorssets(ui, repo, *revs):
3373 """show set of successors for revision
3372 """show set of successors for revision
3374
3373
3375 A successors set of changeset A is a consistent group of revisions that
3374 A successors set of changeset A is a consistent group of revisions that
3376 succeed A. It contains non-obsolete changesets only.
3375 succeed A. It contains non-obsolete changesets only.
3377
3376
3378 In most cases a changeset A has a single successors set containing a single
3377 In most cases a changeset A has a single successors set containing a single
3379 successor (changeset A replaced by A').
3378 successor (changeset A replaced by A').
3380
3379
3381 A changeset that is made obsolete with no successors are called "pruned".
3380 A changeset that is made obsolete with no successors are called "pruned".
3382 Such changesets have no successors sets at all.
3381 Such changesets have no successors sets at all.
3383
3382
3384 A changeset that has been "split" will have a successors set containing
3383 A changeset that has been "split" will have a successors set containing
3385 more than one successor.
3384 more than one successor.
3386
3385
3387 A changeset that has been rewritten in multiple different ways is called
3386 A changeset that has been rewritten in multiple different ways is called
3388 "divergent". Such changesets have multiple successor sets (each of which
3387 "divergent". Such changesets have multiple successor sets (each of which
3389 may also be split, i.e. have multiple successors).
3388 may also be split, i.e. have multiple successors).
3390
3389
3391 Results are displayed as follows::
3390 Results are displayed as follows::
3392
3391
3393 <rev1>
3392 <rev1>
3394 <successors-1A>
3393 <successors-1A>
3395 <rev2>
3394 <rev2>
3396 <successors-2A>
3395 <successors-2A>
3397 <successors-2B1> <successors-2B2> <successors-2B3>
3396 <successors-2B1> <successors-2B2> <successors-2B3>
3398
3397
3399 Here rev2 has two possible (i.e. divergent) successors sets. The first
3398 Here rev2 has two possible (i.e. divergent) successors sets. The first
3400 holds one element, whereas the second holds three (i.e. the changeset has
3399 holds one element, whereas the second holds three (i.e. the changeset has
3401 been split).
3400 been split).
3402 """
3401 """
3403 # passed to successorssets caching computation from one call to another
3402 # passed to successorssets caching computation from one call to another
3404 cache = {}
3403 cache = {}
3405 ctx2str = str
3404 ctx2str = str
3406 node2str = short
3405 node2str = short
3407 if ui.debug():
3406 if ui.debug():
3408 def ctx2str(ctx):
3407 def ctx2str(ctx):
3409 return ctx.hex()
3408 return ctx.hex()
3410 node2str = hex
3409 node2str = hex
3411 for rev in scmutil.revrange(repo, revs):
3410 for rev in scmutil.revrange(repo, revs):
3412 ctx = repo[rev]
3411 ctx = repo[rev]
3413 ui.write('%s\n'% ctx2str(ctx))
3412 ui.write('%s\n'% ctx2str(ctx))
3414 for succsset in obsolete.successorssets(repo, ctx.node(), cache):
3413 for succsset in obsolete.successorssets(repo, ctx.node(), cache):
3415 if succsset:
3414 if succsset:
3416 ui.write(' ')
3415 ui.write(' ')
3417 ui.write(node2str(succsset[0]))
3416 ui.write(node2str(succsset[0]))
3418 for node in succsset[1:]:
3417 for node in succsset[1:]:
3419 ui.write(' ')
3418 ui.write(' ')
3420 ui.write(node2str(node))
3419 ui.write(node2str(node))
3421 ui.write('\n')
3420 ui.write('\n')
3422
3421
3423 @command('debugtemplate',
3422 @command('debugtemplate',
3424 [('r', 'rev', [], _('apply template on changesets'), _('REV')),
3423 [('r', 'rev', [], _('apply template on changesets'), _('REV')),
3425 ('D', 'define', [], _('define template keyword'), _('KEY=VALUE'))],
3424 ('D', 'define', [], _('define template keyword'), _('KEY=VALUE'))],
3426 _('[-r REV]... [-D KEY=VALUE]... TEMPLATE'),
3425 _('[-r REV]... [-D KEY=VALUE]... TEMPLATE'),
3427 optionalrepo=True)
3426 optionalrepo=True)
3428 def debugtemplate(ui, repo, tmpl, **opts):
3427 def debugtemplate(ui, repo, tmpl, **opts):
3429 """parse and apply a template
3428 """parse and apply a template
3430
3429
3431 If -r/--rev is given, the template is processed as a log template and
3430 If -r/--rev is given, the template is processed as a log template and
3432 applied to the given changesets. Otherwise, it is processed as a generic
3431 applied to the given changesets. Otherwise, it is processed as a generic
3433 template.
3432 template.
3434
3433
3435 Use --verbose to print the parsed tree.
3434 Use --verbose to print the parsed tree.
3436 """
3435 """
3437 revs = None
3436 revs = None
3438 if opts['rev']:
3437 if opts['rev']:
3439 if repo is None:
3438 if repo is None:
3440 raise error.RepoError(_('there is no Mercurial repository here '
3439 raise error.RepoError(_('there is no Mercurial repository here '
3441 '(.hg not found)'))
3440 '(.hg not found)'))
3442 revs = scmutil.revrange(repo, opts['rev'])
3441 revs = scmutil.revrange(repo, opts['rev'])
3443
3442
3444 props = {}
3443 props = {}
3445 for d in opts['define']:
3444 for d in opts['define']:
3446 try:
3445 try:
3447 k, v = (e.strip() for e in d.split('=', 1))
3446 k, v = (e.strip() for e in d.split('=', 1))
3448 if not k:
3447 if not k:
3449 raise ValueError
3448 raise ValueError
3450 props[k] = v
3449 props[k] = v
3451 except ValueError:
3450 except ValueError:
3452 raise error.Abort(_('malformed keyword definition: %s') % d)
3451 raise error.Abort(_('malformed keyword definition: %s') % d)
3453
3452
3454 if ui.verbose:
3453 if ui.verbose:
3455 aliases = ui.configitems('templatealias')
3454 aliases = ui.configitems('templatealias')
3456 tree = templater.parse(tmpl)
3455 tree = templater.parse(tmpl)
3457 ui.note(templater.prettyformat(tree), '\n')
3456 ui.note(templater.prettyformat(tree), '\n')
3458 newtree = templater.expandaliases(tree, aliases)
3457 newtree = templater.expandaliases(tree, aliases)
3459 if newtree != tree:
3458 if newtree != tree:
3460 ui.note(("* expanded:\n"), templater.prettyformat(newtree), '\n')
3459 ui.note(("* expanded:\n"), templater.prettyformat(newtree), '\n')
3461
3460
3462 mapfile = None
3461 mapfile = None
3463 if revs is None:
3462 if revs is None:
3464 k = 'debugtemplate'
3463 k = 'debugtemplate'
3465 t = formatter.maketemplater(ui, k, tmpl)
3464 t = formatter.maketemplater(ui, k, tmpl)
3466 ui.write(templater.stringify(t(k, **props)))
3465 ui.write(templater.stringify(t(k, **props)))
3467 else:
3466 else:
3468 displayer = cmdutil.changeset_templater(ui, repo, None, opts, tmpl,
3467 displayer = cmdutil.changeset_templater(ui, repo, None, opts, tmpl,
3469 mapfile, buffered=False)
3468 mapfile, buffered=False)
3470 for r in revs:
3469 for r in revs:
3471 displayer.show(repo[r], **props)
3470 displayer.show(repo[r], **props)
3472 displayer.close()
3471 displayer.close()
3473
3472
3474 @command('debugwalk', walkopts, _('[OPTION]... [FILE]...'), inferrepo=True)
3473 @command('debugwalk', walkopts, _('[OPTION]... [FILE]...'), inferrepo=True)
3475 def debugwalk(ui, repo, *pats, **opts):
3474 def debugwalk(ui, repo, *pats, **opts):
3476 """show how files match on given patterns"""
3475 """show how files match on given patterns"""
3477 m = scmutil.match(repo[None], pats, opts)
3476 m = scmutil.match(repo[None], pats, opts)
3478 items = list(repo.walk(m))
3477 items = list(repo.walk(m))
3479 if not items:
3478 if not items:
3480 return
3479 return
3481 f = lambda fn: fn
3480 f = lambda fn: fn
3482 if ui.configbool('ui', 'slash') and pycompat.ossep != '/':
3481 if ui.configbool('ui', 'slash') and pycompat.ossep != '/':
3483 f = lambda fn: util.normpath(fn)
3482 f = lambda fn: util.normpath(fn)
3484 fmt = 'f %%-%ds %%-%ds %%s' % (
3483 fmt = 'f %%-%ds %%-%ds %%s' % (
3485 max([len(abs) for abs in items]),
3484 max([len(abs) for abs in items]),
3486 max([len(m.rel(abs)) for abs in items]))
3485 max([len(m.rel(abs)) for abs in items]))
3487 for abs in items:
3486 for abs in items:
3488 line = fmt % (abs, f(m.rel(abs)), m.exact(abs) and 'exact' or '')
3487 line = fmt % (abs, f(m.rel(abs)), m.exact(abs) and 'exact' or '')
3489 ui.write("%s\n" % line.rstrip())
3488 ui.write("%s\n" % line.rstrip())
3490
3489
3491 @command('debugwireargs',
3490 @command('debugwireargs',
3492 [('', 'three', '', 'three'),
3491 [('', 'three', '', 'three'),
3493 ('', 'four', '', 'four'),
3492 ('', 'four', '', 'four'),
3494 ('', 'five', '', 'five'),
3493 ('', 'five', '', 'five'),
3495 ] + remoteopts,
3494 ] + remoteopts,
3496 _('REPO [OPTIONS]... [ONE [TWO]]'),
3495 _('REPO [OPTIONS]... [ONE [TWO]]'),
3497 norepo=True)
3496 norepo=True)
3498 def debugwireargs(ui, repopath, *vals, **opts):
3497 def debugwireargs(ui, repopath, *vals, **opts):
3499 repo = hg.peer(ui, opts, repopath)
3498 repo = hg.peer(ui, opts, repopath)
3500 for opt in remoteopts:
3499 for opt in remoteopts:
3501 del opts[opt[1]]
3500 del opts[opt[1]]
3502 args = {}
3501 args = {}
3503 for k, v in opts.iteritems():
3502 for k, v in opts.iteritems():
3504 if v:
3503 if v:
3505 args[k] = v
3504 args[k] = v
3506 # run twice to check that we don't mess up the stream for the next command
3505 # run twice to check that we don't mess up the stream for the next command
3507 res1 = repo.debugwireargs(*vals, **args)
3506 res1 = repo.debugwireargs(*vals, **args)
3508 res2 = repo.debugwireargs(*vals, **args)
3507 res2 = repo.debugwireargs(*vals, **args)
3509 ui.write("%s\n" % res1)
3508 ui.write("%s\n" % res1)
3510 if res1 != res2:
3509 if res1 != res2:
3511 ui.warn("%s\n" % res2)
3510 ui.warn("%s\n" % res2)
3512
3511
3513 @command('^diff',
3512 @command('^diff',
3514 [('r', 'rev', [], _('revision'), _('REV')),
3513 [('r', 'rev', [], _('revision'), _('REV')),
3515 ('c', 'change', '', _('change made by revision'), _('REV'))
3514 ('c', 'change', '', _('change made by revision'), _('REV'))
3516 ] + diffopts + diffopts2 + walkopts + subrepoopts,
3515 ] + diffopts + diffopts2 + walkopts + subrepoopts,
3517 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
3516 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
3518 inferrepo=True)
3517 inferrepo=True)
3519 def diff(ui, repo, *pats, **opts):
3518 def diff(ui, repo, *pats, **opts):
3520 """diff repository (or selected files)
3519 """diff repository (or selected files)
3521
3520
3522 Show differences between revisions for the specified files.
3521 Show differences between revisions for the specified files.
3523
3522
3524 Differences between files are shown using the unified diff format.
3523 Differences between files are shown using the unified diff format.
3525
3524
3526 .. note::
3525 .. note::
3527
3526
3528 :hg:`diff` may generate unexpected results for merges, as it will
3527 :hg:`diff` may generate unexpected results for merges, as it will
3529 default to comparing against the working directory's first
3528 default to comparing against the working directory's first
3530 parent changeset if no revisions are specified.
3529 parent changeset if no revisions are specified.
3531
3530
3532 When two revision arguments are given, then changes are shown
3531 When two revision arguments are given, then changes are shown
3533 between those revisions. If only one revision is specified then
3532 between those revisions. If only one revision is specified then
3534 that revision is compared to the working directory, and, when no
3533 that revision is compared to the working directory, and, when no
3535 revisions are specified, the working directory files are compared
3534 revisions are specified, the working directory files are compared
3536 to its first parent.
3535 to its first parent.
3537
3536
3538 Alternatively you can specify -c/--change with a revision to see
3537 Alternatively you can specify -c/--change with a revision to see
3539 the changes in that changeset relative to its first parent.
3538 the changes in that changeset relative to its first parent.
3540
3539
3541 Without the -a/--text option, diff will avoid generating diffs of
3540 Without the -a/--text option, diff will avoid generating diffs of
3542 files it detects as binary. With -a, diff will generate a diff
3541 files it detects as binary. With -a, diff will generate a diff
3543 anyway, probably with undesirable results.
3542 anyway, probably with undesirable results.
3544
3543
3545 Use the -g/--git option to generate diffs in the git extended diff
3544 Use the -g/--git option to generate diffs in the git extended diff
3546 format. For more information, read :hg:`help diffs`.
3545 format. For more information, read :hg:`help diffs`.
3547
3546
3548 .. container:: verbose
3547 .. container:: verbose
3549
3548
3550 Examples:
3549 Examples:
3551
3550
3552 - compare a file in the current working directory to its parent::
3551 - compare a file in the current working directory to its parent::
3553
3552
3554 hg diff foo.c
3553 hg diff foo.c
3555
3554
3556 - compare two historical versions of a directory, with rename info::
3555 - compare two historical versions of a directory, with rename info::
3557
3556
3558 hg diff --git -r 1.0:1.2 lib/
3557 hg diff --git -r 1.0:1.2 lib/
3559
3558
3560 - get change stats relative to the last change on some date::
3559 - get change stats relative to the last change on some date::
3561
3560
3562 hg diff --stat -r "date('may 2')"
3561 hg diff --stat -r "date('may 2')"
3563
3562
3564 - diff all newly-added files that contain a keyword::
3563 - diff all newly-added files that contain a keyword::
3565
3564
3566 hg diff "set:added() and grep(GNU)"
3565 hg diff "set:added() and grep(GNU)"
3567
3566
3568 - compare a revision and its parents::
3567 - compare a revision and its parents::
3569
3568
3570 hg diff -c 9353 # compare against first parent
3569 hg diff -c 9353 # compare against first parent
3571 hg diff -r 9353^:9353 # same using revset syntax
3570 hg diff -r 9353^:9353 # same using revset syntax
3572 hg diff -r 9353^2:9353 # compare against the second parent
3571 hg diff -r 9353^2:9353 # compare against the second parent
3573
3572
3574 Returns 0 on success.
3573 Returns 0 on success.
3575 """
3574 """
3576
3575
3577 revs = opts.get('rev')
3576 revs = opts.get('rev')
3578 change = opts.get('change')
3577 change = opts.get('change')
3579 stat = opts.get('stat')
3578 stat = opts.get('stat')
3580 reverse = opts.get('reverse')
3579 reverse = opts.get('reverse')
3581
3580
3582 if revs and change:
3581 if revs and change:
3583 msg = _('cannot specify --rev and --change at the same time')
3582 msg = _('cannot specify --rev and --change at the same time')
3584 raise error.Abort(msg)
3583 raise error.Abort(msg)
3585 elif change:
3584 elif change:
3586 node2 = scmutil.revsingle(repo, change, None).node()
3585 node2 = scmutil.revsingle(repo, change, None).node()
3587 node1 = repo[node2].p1().node()
3586 node1 = repo[node2].p1().node()
3588 else:
3587 else:
3589 node1, node2 = scmutil.revpair(repo, revs)
3588 node1, node2 = scmutil.revpair(repo, revs)
3590
3589
3591 if reverse:
3590 if reverse:
3592 node1, node2 = node2, node1
3591 node1, node2 = node2, node1
3593
3592
3594 diffopts = patch.diffallopts(ui, opts)
3593 diffopts = patch.diffallopts(ui, opts)
3595 m = scmutil.match(repo[node2], pats, opts)
3594 m = scmutil.match(repo[node2], pats, opts)
3596 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
3595 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
3597 listsubrepos=opts.get('subrepos'),
3596 listsubrepos=opts.get('subrepos'),
3598 root=opts.get('root'))
3597 root=opts.get('root'))
3599
3598
3600 @command('^export',
3599 @command('^export',
3601 [('o', 'output', '',
3600 [('o', 'output', '',
3602 _('print output to file with formatted name'), _('FORMAT')),
3601 _('print output to file with formatted name'), _('FORMAT')),
3603 ('', 'switch-parent', None, _('diff against the second parent')),
3602 ('', 'switch-parent', None, _('diff against the second parent')),
3604 ('r', 'rev', [], _('revisions to export'), _('REV')),
3603 ('r', 'rev', [], _('revisions to export'), _('REV')),
3605 ] + diffopts,
3604 ] + diffopts,
3606 _('[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'))
3605 _('[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'))
3607 def export(ui, repo, *changesets, **opts):
3606 def export(ui, repo, *changesets, **opts):
3608 """dump the header and diffs for one or more changesets
3607 """dump the header and diffs for one or more changesets
3609
3608
3610 Print the changeset header and diffs for one or more revisions.
3609 Print the changeset header and diffs for one or more revisions.
3611 If no revision is given, the parent of the working directory is used.
3610 If no revision is given, the parent of the working directory is used.
3612
3611
3613 The information shown in the changeset header is: author, date,
3612 The information shown in the changeset header is: author, date,
3614 branch name (if non-default), changeset hash, parent(s) and commit
3613 branch name (if non-default), changeset hash, parent(s) and commit
3615 comment.
3614 comment.
3616
3615
3617 .. note::
3616 .. note::
3618
3617
3619 :hg:`export` may generate unexpected diff output for merge
3618 :hg:`export` may generate unexpected diff output for merge
3620 changesets, as it will compare the merge changeset against its
3619 changesets, as it will compare the merge changeset against its
3621 first parent only.
3620 first parent only.
3622
3621
3623 Output may be to a file, in which case the name of the file is
3622 Output may be to a file, in which case the name of the file is
3624 given using a format string. The formatting rules are as follows:
3623 given using a format string. The formatting rules are as follows:
3625
3624
3626 :``%%``: literal "%" character
3625 :``%%``: literal "%" character
3627 :``%H``: changeset hash (40 hexadecimal digits)
3626 :``%H``: changeset hash (40 hexadecimal digits)
3628 :``%N``: number of patches being generated
3627 :``%N``: number of patches being generated
3629 :``%R``: changeset revision number
3628 :``%R``: changeset revision number
3630 :``%b``: basename of the exporting repository
3629 :``%b``: basename of the exporting repository
3631 :``%h``: short-form changeset hash (12 hexadecimal digits)
3630 :``%h``: short-form changeset hash (12 hexadecimal digits)
3632 :``%m``: first line of the commit message (only alphanumeric characters)
3631 :``%m``: first line of the commit message (only alphanumeric characters)
3633 :``%n``: zero-padded sequence number, starting at 1
3632 :``%n``: zero-padded sequence number, starting at 1
3634 :``%r``: zero-padded changeset revision number
3633 :``%r``: zero-padded changeset revision number
3635
3634
3636 Without the -a/--text option, export will avoid generating diffs
3635 Without the -a/--text option, export will avoid generating diffs
3637 of files it detects as binary. With -a, export will generate a
3636 of files it detects as binary. With -a, export will generate a
3638 diff anyway, probably with undesirable results.
3637 diff anyway, probably with undesirable results.
3639
3638
3640 Use the -g/--git option to generate diffs in the git extended diff
3639 Use the -g/--git option to generate diffs in the git extended diff
3641 format. See :hg:`help diffs` for more information.
3640 format. See :hg:`help diffs` for more information.
3642
3641
3643 With the --switch-parent option, the diff will be against the
3642 With the --switch-parent option, the diff will be against the
3644 second parent. It can be useful to review a merge.
3643 second parent. It can be useful to review a merge.
3645
3644
3646 .. container:: verbose
3645 .. container:: verbose
3647
3646
3648 Examples:
3647 Examples:
3649
3648
3650 - use export and import to transplant a bugfix to the current
3649 - use export and import to transplant a bugfix to the current
3651 branch::
3650 branch::
3652
3651
3653 hg export -r 9353 | hg import -
3652 hg export -r 9353 | hg import -
3654
3653
3655 - export all the changesets between two revisions to a file with
3654 - export all the changesets between two revisions to a file with
3656 rename information::
3655 rename information::
3657
3656
3658 hg export --git -r 123:150 > changes.txt
3657 hg export --git -r 123:150 > changes.txt
3659
3658
3660 - split outgoing changes into a series of patches with
3659 - split outgoing changes into a series of patches with
3661 descriptive names::
3660 descriptive names::
3662
3661
3663 hg export -r "outgoing()" -o "%n-%m.patch"
3662 hg export -r "outgoing()" -o "%n-%m.patch"
3664
3663
3665 Returns 0 on success.
3664 Returns 0 on success.
3666 """
3665 """
3667 changesets += tuple(opts.get('rev', []))
3666 changesets += tuple(opts.get('rev', []))
3668 if not changesets:
3667 if not changesets:
3669 changesets = ['.']
3668 changesets = ['.']
3670 revs = scmutil.revrange(repo, changesets)
3669 revs = scmutil.revrange(repo, changesets)
3671 if not revs:
3670 if not revs:
3672 raise error.Abort(_("export requires at least one changeset"))
3671 raise error.Abort(_("export requires at least one changeset"))
3673 if len(revs) > 1:
3672 if len(revs) > 1:
3674 ui.note(_('exporting patches:\n'))
3673 ui.note(_('exporting patches:\n'))
3675 else:
3674 else:
3676 ui.note(_('exporting patch:\n'))
3675 ui.note(_('exporting patch:\n'))
3677 cmdutil.export(repo, revs, template=opts.get('output'),
3676 cmdutil.export(repo, revs, template=opts.get('output'),
3678 switch_parent=opts.get('switch_parent'),
3677 switch_parent=opts.get('switch_parent'),
3679 opts=patch.diffallopts(ui, opts))
3678 opts=patch.diffallopts(ui, opts))
3680
3679
3681 @command('files',
3680 @command('files',
3682 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3681 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3683 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3682 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3684 ] + walkopts + formatteropts + subrepoopts,
3683 ] + walkopts + formatteropts + subrepoopts,
3685 _('[OPTION]... [FILE]...'))
3684 _('[OPTION]... [FILE]...'))
3686 def files(ui, repo, *pats, **opts):
3685 def files(ui, repo, *pats, **opts):
3687 """list tracked files
3686 """list tracked files
3688
3687
3689 Print files under Mercurial control in the working directory or
3688 Print files under Mercurial control in the working directory or
3690 specified revision for given files (excluding removed files).
3689 specified revision for given files (excluding removed files).
3691 Files can be specified as filenames or filesets.
3690 Files can be specified as filenames or filesets.
3692
3691
3693 If no files are given to match, this command prints the names
3692 If no files are given to match, this command prints the names
3694 of all files under Mercurial control.
3693 of all files under Mercurial control.
3695
3694
3696 .. container:: verbose
3695 .. container:: verbose
3697
3696
3698 Examples:
3697 Examples:
3699
3698
3700 - list all files under the current directory::
3699 - list all files under the current directory::
3701
3700
3702 hg files .
3701 hg files .
3703
3702
3704 - shows sizes and flags for current revision::
3703 - shows sizes and flags for current revision::
3705
3704
3706 hg files -vr .
3705 hg files -vr .
3707
3706
3708 - list all files named README::
3707 - list all files named README::
3709
3708
3710 hg files -I "**/README"
3709 hg files -I "**/README"
3711
3710
3712 - list all binary files::
3711 - list all binary files::
3713
3712
3714 hg files "set:binary()"
3713 hg files "set:binary()"
3715
3714
3716 - find files containing a regular expression::
3715 - find files containing a regular expression::
3717
3716
3718 hg files "set:grep('bob')"
3717 hg files "set:grep('bob')"
3719
3718
3720 - search tracked file contents with xargs and grep::
3719 - search tracked file contents with xargs and grep::
3721
3720
3722 hg files -0 | xargs -0 grep foo
3721 hg files -0 | xargs -0 grep foo
3723
3722
3724 See :hg:`help patterns` and :hg:`help filesets` for more information
3723 See :hg:`help patterns` and :hg:`help filesets` for more information
3725 on specifying file patterns.
3724 on specifying file patterns.
3726
3725
3727 Returns 0 if a match is found, 1 otherwise.
3726 Returns 0 if a match is found, 1 otherwise.
3728
3727
3729 """
3728 """
3730 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
3729 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
3731
3730
3732 end = '\n'
3731 end = '\n'
3733 if opts.get('print0'):
3732 if opts.get('print0'):
3734 end = '\0'
3733 end = '\0'
3735 fmt = '%s' + end
3734 fmt = '%s' + end
3736
3735
3737 m = scmutil.match(ctx, pats, opts)
3736 m = scmutil.match(ctx, pats, opts)
3738 with ui.formatter('files', opts) as fm:
3737 with ui.formatter('files', opts) as fm:
3739 return cmdutil.files(ui, ctx, m, fm, fmt, opts.get('subrepos'))
3738 return cmdutil.files(ui, ctx, m, fm, fmt, opts.get('subrepos'))
3740
3739
3741 @command('^forget', walkopts, _('[OPTION]... FILE...'), inferrepo=True)
3740 @command('^forget', walkopts, _('[OPTION]... FILE...'), inferrepo=True)
3742 def forget(ui, repo, *pats, **opts):
3741 def forget(ui, repo, *pats, **opts):
3743 """forget the specified files on the next commit
3742 """forget the specified files on the next commit
3744
3743
3745 Mark the specified files so they will no longer be tracked
3744 Mark the specified files so they will no longer be tracked
3746 after the next commit.
3745 after the next commit.
3747
3746
3748 This only removes files from the current branch, not from the
3747 This only removes files from the current branch, not from the
3749 entire project history, and it does not delete them from the
3748 entire project history, and it does not delete them from the
3750 working directory.
3749 working directory.
3751
3750
3752 To delete the file from the working directory, see :hg:`remove`.
3751 To delete the file from the working directory, see :hg:`remove`.
3753
3752
3754 To undo a forget before the next commit, see :hg:`add`.
3753 To undo a forget before the next commit, see :hg:`add`.
3755
3754
3756 .. container:: verbose
3755 .. container:: verbose
3757
3756
3758 Examples:
3757 Examples:
3759
3758
3760 - forget newly-added binary files::
3759 - forget newly-added binary files::
3761
3760
3762 hg forget "set:added() and binary()"
3761 hg forget "set:added() and binary()"
3763
3762
3764 - forget files that would be excluded by .hgignore::
3763 - forget files that would be excluded by .hgignore::
3765
3764
3766 hg forget "set:hgignore()"
3765 hg forget "set:hgignore()"
3767
3766
3768 Returns 0 on success.
3767 Returns 0 on success.
3769 """
3768 """
3770
3769
3771 if not pats:
3770 if not pats:
3772 raise error.Abort(_('no files specified'))
3771 raise error.Abort(_('no files specified'))
3773
3772
3774 m = scmutil.match(repo[None], pats, opts)
3773 m = scmutil.match(repo[None], pats, opts)
3775 rejected = cmdutil.forget(ui, repo, m, prefix="", explicitonly=False)[0]
3774 rejected = cmdutil.forget(ui, repo, m, prefix="", explicitonly=False)[0]
3776 return rejected and 1 or 0
3775 return rejected and 1 or 0
3777
3776
3778 @command(
3777 @command(
3779 'graft',
3778 'graft',
3780 [('r', 'rev', [], _('revisions to graft'), _('REV')),
3779 [('r', 'rev', [], _('revisions to graft'), _('REV')),
3781 ('c', 'continue', False, _('resume interrupted graft')),
3780 ('c', 'continue', False, _('resume interrupted graft')),
3782 ('e', 'edit', False, _('invoke editor on commit messages')),
3781 ('e', 'edit', False, _('invoke editor on commit messages')),
3783 ('', 'log', None, _('append graft info to log message')),
3782 ('', 'log', None, _('append graft info to log message')),
3784 ('f', 'force', False, _('force graft')),
3783 ('f', 'force', False, _('force graft')),
3785 ('D', 'currentdate', False,
3784 ('D', 'currentdate', False,
3786 _('record the current date as commit date')),
3785 _('record the current date as commit date')),
3787 ('U', 'currentuser', False,
3786 ('U', 'currentuser', False,
3788 _('record the current user as committer'), _('DATE'))]
3787 _('record the current user as committer'), _('DATE'))]
3789 + commitopts2 + mergetoolopts + dryrunopts,
3788 + commitopts2 + mergetoolopts + dryrunopts,
3790 _('[OPTION]... [-r REV]... REV...'))
3789 _('[OPTION]... [-r REV]... REV...'))
3791 def graft(ui, repo, *revs, **opts):
3790 def graft(ui, repo, *revs, **opts):
3792 '''copy changes from other branches onto the current branch
3791 '''copy changes from other branches onto the current branch
3793
3792
3794 This command uses Mercurial's merge logic to copy individual
3793 This command uses Mercurial's merge logic to copy individual
3795 changes from other branches without merging branches in the
3794 changes from other branches without merging branches in the
3796 history graph. This is sometimes known as 'backporting' or
3795 history graph. This is sometimes known as 'backporting' or
3797 'cherry-picking'. By default, graft will copy user, date, and
3796 'cherry-picking'. By default, graft will copy user, date, and
3798 description from the source changesets.
3797 description from the source changesets.
3799
3798
3800 Changesets that are ancestors of the current revision, that have
3799 Changesets that are ancestors of the current revision, that have
3801 already been grafted, or that are merges will be skipped.
3800 already been grafted, or that are merges will be skipped.
3802
3801
3803 If --log is specified, log messages will have a comment appended
3802 If --log is specified, log messages will have a comment appended
3804 of the form::
3803 of the form::
3805
3804
3806 (grafted from CHANGESETHASH)
3805 (grafted from CHANGESETHASH)
3807
3806
3808 If --force is specified, revisions will be grafted even if they
3807 If --force is specified, revisions will be grafted even if they
3809 are already ancestors of or have been grafted to the destination.
3808 are already ancestors of or have been grafted to the destination.
3810 This is useful when the revisions have since been backed out.
3809 This is useful when the revisions have since been backed out.
3811
3810
3812 If a graft merge results in conflicts, the graft process is
3811 If a graft merge results in conflicts, the graft process is
3813 interrupted so that the current merge can be manually resolved.
3812 interrupted so that the current merge can be manually resolved.
3814 Once all conflicts are addressed, the graft process can be
3813 Once all conflicts are addressed, the graft process can be
3815 continued with the -c/--continue option.
3814 continued with the -c/--continue option.
3816
3815
3817 .. note::
3816 .. note::
3818
3817
3819 The -c/--continue option does not reapply earlier options, except
3818 The -c/--continue option does not reapply earlier options, except
3820 for --force.
3819 for --force.
3821
3820
3822 .. container:: verbose
3821 .. container:: verbose
3823
3822
3824 Examples:
3823 Examples:
3825
3824
3826 - copy a single change to the stable branch and edit its description::
3825 - copy a single change to the stable branch and edit its description::
3827
3826
3828 hg update stable
3827 hg update stable
3829 hg graft --edit 9393
3828 hg graft --edit 9393
3830
3829
3831 - graft a range of changesets with one exception, updating dates::
3830 - graft a range of changesets with one exception, updating dates::
3832
3831
3833 hg graft -D "2085::2093 and not 2091"
3832 hg graft -D "2085::2093 and not 2091"
3834
3833
3835 - continue a graft after resolving conflicts::
3834 - continue a graft after resolving conflicts::
3836
3835
3837 hg graft -c
3836 hg graft -c
3838
3837
3839 - show the source of a grafted changeset::
3838 - show the source of a grafted changeset::
3840
3839
3841 hg log --debug -r .
3840 hg log --debug -r .
3842
3841
3843 - show revisions sorted by date::
3842 - show revisions sorted by date::
3844
3843
3845 hg log -r "sort(all(), date)"
3844 hg log -r "sort(all(), date)"
3846
3845
3847 See :hg:`help revisions` and :hg:`help revsets` for more about
3846 See :hg:`help revisions` and :hg:`help revsets` for more about
3848 specifying revisions.
3847 specifying revisions.
3849
3848
3850 Returns 0 on successful completion.
3849 Returns 0 on successful completion.
3851 '''
3850 '''
3852 with repo.wlock():
3851 with repo.wlock():
3853 return _dograft(ui, repo, *revs, **opts)
3852 return _dograft(ui, repo, *revs, **opts)
3854
3853
3855 def _dograft(ui, repo, *revs, **opts):
3854 def _dograft(ui, repo, *revs, **opts):
3856 if revs and opts.get('rev'):
3855 if revs and opts.get('rev'):
3857 ui.warn(_('warning: inconsistent use of --rev might give unexpected '
3856 ui.warn(_('warning: inconsistent use of --rev might give unexpected '
3858 'revision ordering!\n'))
3857 'revision ordering!\n'))
3859
3858
3860 revs = list(revs)
3859 revs = list(revs)
3861 revs.extend(opts.get('rev'))
3860 revs.extend(opts.get('rev'))
3862
3861
3863 if not opts.get('user') and opts.get('currentuser'):
3862 if not opts.get('user') and opts.get('currentuser'):
3864 opts['user'] = ui.username()
3863 opts['user'] = ui.username()
3865 if not opts.get('date') and opts.get('currentdate'):
3864 if not opts.get('date') and opts.get('currentdate'):
3866 opts['date'] = "%d %d" % util.makedate()
3865 opts['date'] = "%d %d" % util.makedate()
3867
3866
3868 editor = cmdutil.getcommiteditor(editform='graft', **opts)
3867 editor = cmdutil.getcommiteditor(editform='graft', **opts)
3869
3868
3870 cont = False
3869 cont = False
3871 if opts.get('continue'):
3870 if opts.get('continue'):
3872 cont = True
3871 cont = True
3873 if revs:
3872 if revs:
3874 raise error.Abort(_("can't specify --continue and revisions"))
3873 raise error.Abort(_("can't specify --continue and revisions"))
3875 # read in unfinished revisions
3874 # read in unfinished revisions
3876 try:
3875 try:
3877 nodes = repo.vfs.read('graftstate').splitlines()
3876 nodes = repo.vfs.read('graftstate').splitlines()
3878 revs = [repo[node].rev() for node in nodes]
3877 revs = [repo[node].rev() for node in nodes]
3879 except IOError as inst:
3878 except IOError as inst:
3880 if inst.errno != errno.ENOENT:
3879 if inst.errno != errno.ENOENT:
3881 raise
3880 raise
3882 cmdutil.wrongtooltocontinue(repo, _('graft'))
3881 cmdutil.wrongtooltocontinue(repo, _('graft'))
3883 else:
3882 else:
3884 cmdutil.checkunfinished(repo)
3883 cmdutil.checkunfinished(repo)
3885 cmdutil.bailifchanged(repo)
3884 cmdutil.bailifchanged(repo)
3886 if not revs:
3885 if not revs:
3887 raise error.Abort(_('no revisions specified'))
3886 raise error.Abort(_('no revisions specified'))
3888 revs = scmutil.revrange(repo, revs)
3887 revs = scmutil.revrange(repo, revs)
3889
3888
3890 skipped = set()
3889 skipped = set()
3891 # check for merges
3890 # check for merges
3892 for rev in repo.revs('%ld and merge()', revs):
3891 for rev in repo.revs('%ld and merge()', revs):
3893 ui.warn(_('skipping ungraftable merge revision %s\n') % rev)
3892 ui.warn(_('skipping ungraftable merge revision %s\n') % rev)
3894 skipped.add(rev)
3893 skipped.add(rev)
3895 revs = [r for r in revs if r not in skipped]
3894 revs = [r for r in revs if r not in skipped]
3896 if not revs:
3895 if not revs:
3897 return -1
3896 return -1
3898
3897
3899 # Don't check in the --continue case, in effect retaining --force across
3898 # Don't check in the --continue case, in effect retaining --force across
3900 # --continues. That's because without --force, any revisions we decided to
3899 # --continues. That's because without --force, any revisions we decided to
3901 # skip would have been filtered out here, so they wouldn't have made their
3900 # skip would have been filtered out here, so they wouldn't have made their
3902 # way to the graftstate. With --force, any revisions we would have otherwise
3901 # way to the graftstate. With --force, any revisions we would have otherwise
3903 # skipped would not have been filtered out, and if they hadn't been applied
3902 # skipped would not have been filtered out, and if they hadn't been applied
3904 # already, they'd have been in the graftstate.
3903 # already, they'd have been in the graftstate.
3905 if not (cont or opts.get('force')):
3904 if not (cont or opts.get('force')):
3906 # check for ancestors of dest branch
3905 # check for ancestors of dest branch
3907 crev = repo['.'].rev()
3906 crev = repo['.'].rev()
3908 ancestors = repo.changelog.ancestors([crev], inclusive=True)
3907 ancestors = repo.changelog.ancestors([crev], inclusive=True)
3909 # XXX make this lazy in the future
3908 # XXX make this lazy in the future
3910 # don't mutate while iterating, create a copy
3909 # don't mutate while iterating, create a copy
3911 for rev in list(revs):
3910 for rev in list(revs):
3912 if rev in ancestors:
3911 if rev in ancestors:
3913 ui.warn(_('skipping ancestor revision %d:%s\n') %
3912 ui.warn(_('skipping ancestor revision %d:%s\n') %
3914 (rev, repo[rev]))
3913 (rev, repo[rev]))
3915 # XXX remove on list is slow
3914 # XXX remove on list is slow
3916 revs.remove(rev)
3915 revs.remove(rev)
3917 if not revs:
3916 if not revs:
3918 return -1
3917 return -1
3919
3918
3920 # analyze revs for earlier grafts
3919 # analyze revs for earlier grafts
3921 ids = {}
3920 ids = {}
3922 for ctx in repo.set("%ld", revs):
3921 for ctx in repo.set("%ld", revs):
3923 ids[ctx.hex()] = ctx.rev()
3922 ids[ctx.hex()] = ctx.rev()
3924 n = ctx.extra().get('source')
3923 n = ctx.extra().get('source')
3925 if n:
3924 if n:
3926 ids[n] = ctx.rev()
3925 ids[n] = ctx.rev()
3927
3926
3928 # check ancestors for earlier grafts
3927 # check ancestors for earlier grafts
3929 ui.debug('scanning for duplicate grafts\n')
3928 ui.debug('scanning for duplicate grafts\n')
3930
3929
3931 for rev in repo.changelog.findmissingrevs(revs, [crev]):
3930 for rev in repo.changelog.findmissingrevs(revs, [crev]):
3932 ctx = repo[rev]
3931 ctx = repo[rev]
3933 n = ctx.extra().get('source')
3932 n = ctx.extra().get('source')
3934 if n in ids:
3933 if n in ids:
3935 try:
3934 try:
3936 r = repo[n].rev()
3935 r = repo[n].rev()
3937 except error.RepoLookupError:
3936 except error.RepoLookupError:
3938 r = None
3937 r = None
3939 if r in revs:
3938 if r in revs:
3940 ui.warn(_('skipping revision %d:%s '
3939 ui.warn(_('skipping revision %d:%s '
3941 '(already grafted to %d:%s)\n')
3940 '(already grafted to %d:%s)\n')
3942 % (r, repo[r], rev, ctx))
3941 % (r, repo[r], rev, ctx))
3943 revs.remove(r)
3942 revs.remove(r)
3944 elif ids[n] in revs:
3943 elif ids[n] in revs:
3945 if r is None:
3944 if r is None:
3946 ui.warn(_('skipping already grafted revision %d:%s '
3945 ui.warn(_('skipping already grafted revision %d:%s '
3947 '(%d:%s also has unknown origin %s)\n')
3946 '(%d:%s also has unknown origin %s)\n')
3948 % (ids[n], repo[ids[n]], rev, ctx, n[:12]))
3947 % (ids[n], repo[ids[n]], rev, ctx, n[:12]))
3949 else:
3948 else:
3950 ui.warn(_('skipping already grafted revision %d:%s '
3949 ui.warn(_('skipping already grafted revision %d:%s '
3951 '(%d:%s also has origin %d:%s)\n')
3950 '(%d:%s also has origin %d:%s)\n')
3952 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12]))
3951 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12]))
3953 revs.remove(ids[n])
3952 revs.remove(ids[n])
3954 elif ctx.hex() in ids:
3953 elif ctx.hex() in ids:
3955 r = ids[ctx.hex()]
3954 r = ids[ctx.hex()]
3956 ui.warn(_('skipping already grafted revision %d:%s '
3955 ui.warn(_('skipping already grafted revision %d:%s '
3957 '(was grafted from %d:%s)\n') %
3956 '(was grafted from %d:%s)\n') %
3958 (r, repo[r], rev, ctx))
3957 (r, repo[r], rev, ctx))
3959 revs.remove(r)
3958 revs.remove(r)
3960 if not revs:
3959 if not revs:
3961 return -1
3960 return -1
3962
3961
3963 for pos, ctx in enumerate(repo.set("%ld", revs)):
3962 for pos, ctx in enumerate(repo.set("%ld", revs)):
3964 desc = '%d:%s "%s"' % (ctx.rev(), ctx,
3963 desc = '%d:%s "%s"' % (ctx.rev(), ctx,
3965 ctx.description().split('\n', 1)[0])
3964 ctx.description().split('\n', 1)[0])
3966 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
3965 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
3967 if names:
3966 if names:
3968 desc += ' (%s)' % ' '.join(names)
3967 desc += ' (%s)' % ' '.join(names)
3969 ui.status(_('grafting %s\n') % desc)
3968 ui.status(_('grafting %s\n') % desc)
3970 if opts.get('dry_run'):
3969 if opts.get('dry_run'):
3971 continue
3970 continue
3972
3971
3973 source = ctx.extra().get('source')
3972 source = ctx.extra().get('source')
3974 extra = {}
3973 extra = {}
3975 if source:
3974 if source:
3976 extra['source'] = source
3975 extra['source'] = source
3977 extra['intermediate-source'] = ctx.hex()
3976 extra['intermediate-source'] = ctx.hex()
3978 else:
3977 else:
3979 extra['source'] = ctx.hex()
3978 extra['source'] = ctx.hex()
3980 user = ctx.user()
3979 user = ctx.user()
3981 if opts.get('user'):
3980 if opts.get('user'):
3982 user = opts['user']
3981 user = opts['user']
3983 date = ctx.date()
3982 date = ctx.date()
3984 if opts.get('date'):
3983 if opts.get('date'):
3985 date = opts['date']
3984 date = opts['date']
3986 message = ctx.description()
3985 message = ctx.description()
3987 if opts.get('log'):
3986 if opts.get('log'):
3988 message += '\n(grafted from %s)' % ctx.hex()
3987 message += '\n(grafted from %s)' % ctx.hex()
3989
3988
3990 # we don't merge the first commit when continuing
3989 # we don't merge the first commit when continuing
3991 if not cont:
3990 if not cont:
3992 # perform the graft merge with p1(rev) as 'ancestor'
3991 # perform the graft merge with p1(rev) as 'ancestor'
3993 try:
3992 try:
3994 # ui.forcemerge is an internal variable, do not document
3993 # ui.forcemerge is an internal variable, do not document
3995 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
3994 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
3996 'graft')
3995 'graft')
3997 stats = mergemod.graft(repo, ctx, ctx.p1(),
3996 stats = mergemod.graft(repo, ctx, ctx.p1(),
3998 ['local', 'graft'])
3997 ['local', 'graft'])
3999 finally:
3998 finally:
4000 repo.ui.setconfig('ui', 'forcemerge', '', 'graft')
3999 repo.ui.setconfig('ui', 'forcemerge', '', 'graft')
4001 # report any conflicts
4000 # report any conflicts
4002 if stats and stats[3] > 0:
4001 if stats and stats[3] > 0:
4003 # write out state for --continue
4002 # write out state for --continue
4004 nodelines = [repo[rev].hex() + "\n" for rev in revs[pos:]]
4003 nodelines = [repo[rev].hex() + "\n" for rev in revs[pos:]]
4005 repo.vfs.write('graftstate', ''.join(nodelines))
4004 repo.vfs.write('graftstate', ''.join(nodelines))
4006 extra = ''
4005 extra = ''
4007 if opts.get('user'):
4006 if opts.get('user'):
4008 extra += ' --user %s' % util.shellquote(opts['user'])
4007 extra += ' --user %s' % util.shellquote(opts['user'])
4009 if opts.get('date'):
4008 if opts.get('date'):
4010 extra += ' --date %s' % util.shellquote(opts['date'])
4009 extra += ' --date %s' % util.shellquote(opts['date'])
4011 if opts.get('log'):
4010 if opts.get('log'):
4012 extra += ' --log'
4011 extra += ' --log'
4013 hint=_("use 'hg resolve' and 'hg graft --continue%s'") % extra
4012 hint=_("use 'hg resolve' and 'hg graft --continue%s'") % extra
4014 raise error.Abort(
4013 raise error.Abort(
4015 _("unresolved conflicts, can't continue"),
4014 _("unresolved conflicts, can't continue"),
4016 hint=hint)
4015 hint=hint)
4017 else:
4016 else:
4018 cont = False
4017 cont = False
4019
4018
4020 # commit
4019 # commit
4021 node = repo.commit(text=message, user=user,
4020 node = repo.commit(text=message, user=user,
4022 date=date, extra=extra, editor=editor)
4021 date=date, extra=extra, editor=editor)
4023 if node is None:
4022 if node is None:
4024 ui.warn(
4023 ui.warn(
4025 _('note: graft of %d:%s created no changes to commit\n') %
4024 _('note: graft of %d:%s created no changes to commit\n') %
4026 (ctx.rev(), ctx))
4025 (ctx.rev(), ctx))
4027
4026
4028 # remove state when we complete successfully
4027 # remove state when we complete successfully
4029 if not opts.get('dry_run'):
4028 if not opts.get('dry_run'):
4030 util.unlinkpath(repo.join('graftstate'), ignoremissing=True)
4029 util.unlinkpath(repo.join('graftstate'), ignoremissing=True)
4031
4030
4032 return 0
4031 return 0
4033
4032
4034 @command('grep',
4033 @command('grep',
4035 [('0', 'print0', None, _('end fields with NUL')),
4034 [('0', 'print0', None, _('end fields with NUL')),
4036 ('', 'all', None, _('print all revisions that match')),
4035 ('', 'all', None, _('print all revisions that match')),
4037 ('a', 'text', None, _('treat all files as text')),
4036 ('a', 'text', None, _('treat all files as text')),
4038 ('f', 'follow', None,
4037 ('f', 'follow', None,
4039 _('follow changeset history,'
4038 _('follow changeset history,'
4040 ' or file history across copies and renames')),
4039 ' or file history across copies and renames')),
4041 ('i', 'ignore-case', None, _('ignore case when matching')),
4040 ('i', 'ignore-case', None, _('ignore case when matching')),
4042 ('l', 'files-with-matches', None,
4041 ('l', 'files-with-matches', None,
4043 _('print only filenames and revisions that match')),
4042 _('print only filenames and revisions that match')),
4044 ('n', 'line-number', None, _('print matching line numbers')),
4043 ('n', 'line-number', None, _('print matching line numbers')),
4045 ('r', 'rev', [],
4044 ('r', 'rev', [],
4046 _('only search files changed within revision range'), _('REV')),
4045 _('only search files changed within revision range'), _('REV')),
4047 ('u', 'user', None, _('list the author (long with -v)')),
4046 ('u', 'user', None, _('list the author (long with -v)')),
4048 ('d', 'date', None, _('list the date (short with -q)')),
4047 ('d', 'date', None, _('list the date (short with -q)')),
4049 ] + formatteropts + walkopts,
4048 ] + formatteropts + walkopts,
4050 _('[OPTION]... PATTERN [FILE]...'),
4049 _('[OPTION]... PATTERN [FILE]...'),
4051 inferrepo=True)
4050 inferrepo=True)
4052 def grep(ui, repo, pattern, *pats, **opts):
4051 def grep(ui, repo, pattern, *pats, **opts):
4053 """search revision history for a pattern in specified files
4052 """search revision history for a pattern in specified files
4054
4053
4055 Search revision history for a regular expression in the specified
4054 Search revision history for a regular expression in the specified
4056 files or the entire project.
4055 files or the entire project.
4057
4056
4058 By default, grep prints the most recent revision number for each
4057 By default, grep prints the most recent revision number for each
4059 file in which it finds a match. To get it to print every revision
4058 file in which it finds a match. To get it to print every revision
4060 that contains a change in match status ("-" for a match that becomes
4059 that contains a change in match status ("-" for a match that becomes
4061 a non-match, or "+" for a non-match that becomes a match), use the
4060 a non-match, or "+" for a non-match that becomes a match), use the
4062 --all flag.
4061 --all flag.
4063
4062
4064 PATTERN can be any Python (roughly Perl-compatible) regular
4063 PATTERN can be any Python (roughly Perl-compatible) regular
4065 expression.
4064 expression.
4066
4065
4067 If no FILEs are specified (and -f/--follow isn't set), all files in
4066 If no FILEs are specified (and -f/--follow isn't set), all files in
4068 the repository are searched, including those that don't exist in the
4067 the repository are searched, including those that don't exist in the
4069 current branch or have been deleted in a prior changeset.
4068 current branch or have been deleted in a prior changeset.
4070
4069
4071 Returns 0 if a match is found, 1 otherwise.
4070 Returns 0 if a match is found, 1 otherwise.
4072 """
4071 """
4073 reflags = re.M
4072 reflags = re.M
4074 if opts.get('ignore_case'):
4073 if opts.get('ignore_case'):
4075 reflags |= re.I
4074 reflags |= re.I
4076 try:
4075 try:
4077 regexp = util.re.compile(pattern, reflags)
4076 regexp = util.re.compile(pattern, reflags)
4078 except re.error as inst:
4077 except re.error as inst:
4079 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
4078 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
4080 return 1
4079 return 1
4081 sep, eol = ':', '\n'
4080 sep, eol = ':', '\n'
4082 if opts.get('print0'):
4081 if opts.get('print0'):
4083 sep = eol = '\0'
4082 sep = eol = '\0'
4084
4083
4085 getfile = util.lrucachefunc(repo.file)
4084 getfile = util.lrucachefunc(repo.file)
4086
4085
4087 def matchlines(body):
4086 def matchlines(body):
4088 begin = 0
4087 begin = 0
4089 linenum = 0
4088 linenum = 0
4090 while begin < len(body):
4089 while begin < len(body):
4091 match = regexp.search(body, begin)
4090 match = regexp.search(body, begin)
4092 if not match:
4091 if not match:
4093 break
4092 break
4094 mstart, mend = match.span()
4093 mstart, mend = match.span()
4095 linenum += body.count('\n', begin, mstart) + 1
4094 linenum += body.count('\n', begin, mstart) + 1
4096 lstart = body.rfind('\n', begin, mstart) + 1 or begin
4095 lstart = body.rfind('\n', begin, mstart) + 1 or begin
4097 begin = body.find('\n', mend) + 1 or len(body) + 1
4096 begin = body.find('\n', mend) + 1 or len(body) + 1
4098 lend = begin - 1
4097 lend = begin - 1
4099 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
4098 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
4100
4099
4101 class linestate(object):
4100 class linestate(object):
4102 def __init__(self, line, linenum, colstart, colend):
4101 def __init__(self, line, linenum, colstart, colend):
4103 self.line = line
4102 self.line = line
4104 self.linenum = linenum
4103 self.linenum = linenum
4105 self.colstart = colstart
4104 self.colstart = colstart
4106 self.colend = colend
4105 self.colend = colend
4107
4106
4108 def __hash__(self):
4107 def __hash__(self):
4109 return hash((self.linenum, self.line))
4108 return hash((self.linenum, self.line))
4110
4109
4111 def __eq__(self, other):
4110 def __eq__(self, other):
4112 return self.line == other.line
4111 return self.line == other.line
4113
4112
4114 def findpos(self):
4113 def findpos(self):
4115 """Iterate all (start, end) indices of matches"""
4114 """Iterate all (start, end) indices of matches"""
4116 yield self.colstart, self.colend
4115 yield self.colstart, self.colend
4117 p = self.colend
4116 p = self.colend
4118 while p < len(self.line):
4117 while p < len(self.line):
4119 m = regexp.search(self.line, p)
4118 m = regexp.search(self.line, p)
4120 if not m:
4119 if not m:
4121 break
4120 break
4122 yield m.span()
4121 yield m.span()
4123 p = m.end()
4122 p = m.end()
4124
4123
4125 matches = {}
4124 matches = {}
4126 copies = {}
4125 copies = {}
4127 def grepbody(fn, rev, body):
4126 def grepbody(fn, rev, body):
4128 matches[rev].setdefault(fn, [])
4127 matches[rev].setdefault(fn, [])
4129 m = matches[rev][fn]
4128 m = matches[rev][fn]
4130 for lnum, cstart, cend, line in matchlines(body):
4129 for lnum, cstart, cend, line in matchlines(body):
4131 s = linestate(line, lnum, cstart, cend)
4130 s = linestate(line, lnum, cstart, cend)
4132 m.append(s)
4131 m.append(s)
4133
4132
4134 def difflinestates(a, b):
4133 def difflinestates(a, b):
4135 sm = difflib.SequenceMatcher(None, a, b)
4134 sm = difflib.SequenceMatcher(None, a, b)
4136 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
4135 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
4137 if tag == 'insert':
4136 if tag == 'insert':
4138 for i in xrange(blo, bhi):
4137 for i in xrange(blo, bhi):
4139 yield ('+', b[i])
4138 yield ('+', b[i])
4140 elif tag == 'delete':
4139 elif tag == 'delete':
4141 for i in xrange(alo, ahi):
4140 for i in xrange(alo, ahi):
4142 yield ('-', a[i])
4141 yield ('-', a[i])
4143 elif tag == 'replace':
4142 elif tag == 'replace':
4144 for i in xrange(alo, ahi):
4143 for i in xrange(alo, ahi):
4145 yield ('-', a[i])
4144 yield ('-', a[i])
4146 for i in xrange(blo, bhi):
4145 for i in xrange(blo, bhi):
4147 yield ('+', b[i])
4146 yield ('+', b[i])
4148
4147
4149 def display(fm, fn, ctx, pstates, states):
4148 def display(fm, fn, ctx, pstates, states):
4150 rev = ctx.rev()
4149 rev = ctx.rev()
4151 if fm.isplain():
4150 if fm.isplain():
4152 formatuser = ui.shortuser
4151 formatuser = ui.shortuser
4153 else:
4152 else:
4154 formatuser = str
4153 formatuser = str
4155 if ui.quiet:
4154 if ui.quiet:
4156 datefmt = '%Y-%m-%d'
4155 datefmt = '%Y-%m-%d'
4157 else:
4156 else:
4158 datefmt = '%a %b %d %H:%M:%S %Y %1%2'
4157 datefmt = '%a %b %d %H:%M:%S %Y %1%2'
4159 found = False
4158 found = False
4160 @util.cachefunc
4159 @util.cachefunc
4161 def binary():
4160 def binary():
4162 flog = getfile(fn)
4161 flog = getfile(fn)
4163 return util.binary(flog.read(ctx.filenode(fn)))
4162 return util.binary(flog.read(ctx.filenode(fn)))
4164
4163
4165 fieldnamemap = {'filename': 'file', 'linenumber': 'line_number'}
4164 fieldnamemap = {'filename': 'file', 'linenumber': 'line_number'}
4166 if opts.get('all'):
4165 if opts.get('all'):
4167 iter = difflinestates(pstates, states)
4166 iter = difflinestates(pstates, states)
4168 else:
4167 else:
4169 iter = [('', l) for l in states]
4168 iter = [('', l) for l in states]
4170 for change, l in iter:
4169 for change, l in iter:
4171 fm.startitem()
4170 fm.startitem()
4172 fm.data(node=fm.hexfunc(ctx.node()))
4171 fm.data(node=fm.hexfunc(ctx.node()))
4173 cols = [
4172 cols = [
4174 ('filename', fn, True),
4173 ('filename', fn, True),
4175 ('rev', rev, True),
4174 ('rev', rev, True),
4176 ('linenumber', l.linenum, opts.get('line_number')),
4175 ('linenumber', l.linenum, opts.get('line_number')),
4177 ]
4176 ]
4178 if opts.get('all'):
4177 if opts.get('all'):
4179 cols.append(('change', change, True))
4178 cols.append(('change', change, True))
4180 cols.extend([
4179 cols.extend([
4181 ('user', formatuser(ctx.user()), opts.get('user')),
4180 ('user', formatuser(ctx.user()), opts.get('user')),
4182 ('date', fm.formatdate(ctx.date(), datefmt), opts.get('date')),
4181 ('date', fm.formatdate(ctx.date(), datefmt), opts.get('date')),
4183 ])
4182 ])
4184 lastcol = next(name for name, data, cond in reversed(cols) if cond)
4183 lastcol = next(name for name, data, cond in reversed(cols) if cond)
4185 for name, data, cond in cols:
4184 for name, data, cond in cols:
4186 field = fieldnamemap.get(name, name)
4185 field = fieldnamemap.get(name, name)
4187 fm.condwrite(cond, field, '%s', data, label='grep.%s' % name)
4186 fm.condwrite(cond, field, '%s', data, label='grep.%s' % name)
4188 if cond and name != lastcol:
4187 if cond and name != lastcol:
4189 fm.plain(sep, label='grep.sep')
4188 fm.plain(sep, label='grep.sep')
4190 if not opts.get('files_with_matches'):
4189 if not opts.get('files_with_matches'):
4191 fm.plain(sep, label='grep.sep')
4190 fm.plain(sep, label='grep.sep')
4192 if not opts.get('text') and binary():
4191 if not opts.get('text') and binary():
4193 fm.plain(_(" Binary file matches"))
4192 fm.plain(_(" Binary file matches"))
4194 else:
4193 else:
4195 displaymatches(fm.nested('texts'), l)
4194 displaymatches(fm.nested('texts'), l)
4196 fm.plain(eol)
4195 fm.plain(eol)
4197 found = True
4196 found = True
4198 if opts.get('files_with_matches'):
4197 if opts.get('files_with_matches'):
4199 break
4198 break
4200 return found
4199 return found
4201
4200
4202 def displaymatches(fm, l):
4201 def displaymatches(fm, l):
4203 p = 0
4202 p = 0
4204 for s, e in l.findpos():
4203 for s, e in l.findpos():
4205 if p < s:
4204 if p < s:
4206 fm.startitem()
4205 fm.startitem()
4207 fm.write('text', '%s', l.line[p:s])
4206 fm.write('text', '%s', l.line[p:s])
4208 fm.data(matched=False)
4207 fm.data(matched=False)
4209 fm.startitem()
4208 fm.startitem()
4210 fm.write('text', '%s', l.line[s:e], label='grep.match')
4209 fm.write('text', '%s', l.line[s:e], label='grep.match')
4211 fm.data(matched=True)
4210 fm.data(matched=True)
4212 p = e
4211 p = e
4213 if p < len(l.line):
4212 if p < len(l.line):
4214 fm.startitem()
4213 fm.startitem()
4215 fm.write('text', '%s', l.line[p:])
4214 fm.write('text', '%s', l.line[p:])
4216 fm.data(matched=False)
4215 fm.data(matched=False)
4217 fm.end()
4216 fm.end()
4218
4217
4219 skip = {}
4218 skip = {}
4220 revfiles = {}
4219 revfiles = {}
4221 matchfn = scmutil.match(repo[None], pats, opts)
4220 matchfn = scmutil.match(repo[None], pats, opts)
4222 found = False
4221 found = False
4223 follow = opts.get('follow')
4222 follow = opts.get('follow')
4224
4223
4225 def prep(ctx, fns):
4224 def prep(ctx, fns):
4226 rev = ctx.rev()
4225 rev = ctx.rev()
4227 pctx = ctx.p1()
4226 pctx = ctx.p1()
4228 parent = pctx.rev()
4227 parent = pctx.rev()
4229 matches.setdefault(rev, {})
4228 matches.setdefault(rev, {})
4230 matches.setdefault(parent, {})
4229 matches.setdefault(parent, {})
4231 files = revfiles.setdefault(rev, [])
4230 files = revfiles.setdefault(rev, [])
4232 for fn in fns:
4231 for fn in fns:
4233 flog = getfile(fn)
4232 flog = getfile(fn)
4234 try:
4233 try:
4235 fnode = ctx.filenode(fn)
4234 fnode = ctx.filenode(fn)
4236 except error.LookupError:
4235 except error.LookupError:
4237 continue
4236 continue
4238
4237
4239 copied = flog.renamed(fnode)
4238 copied = flog.renamed(fnode)
4240 copy = follow and copied and copied[0]
4239 copy = follow and copied and copied[0]
4241 if copy:
4240 if copy:
4242 copies.setdefault(rev, {})[fn] = copy
4241 copies.setdefault(rev, {})[fn] = copy
4243 if fn in skip:
4242 if fn in skip:
4244 if copy:
4243 if copy:
4245 skip[copy] = True
4244 skip[copy] = True
4246 continue
4245 continue
4247 files.append(fn)
4246 files.append(fn)
4248
4247
4249 if fn not in matches[rev]:
4248 if fn not in matches[rev]:
4250 grepbody(fn, rev, flog.read(fnode))
4249 grepbody(fn, rev, flog.read(fnode))
4251
4250
4252 pfn = copy or fn
4251 pfn = copy or fn
4253 if pfn not in matches[parent]:
4252 if pfn not in matches[parent]:
4254 try:
4253 try:
4255 fnode = pctx.filenode(pfn)
4254 fnode = pctx.filenode(pfn)
4256 grepbody(pfn, parent, flog.read(fnode))
4255 grepbody(pfn, parent, flog.read(fnode))
4257 except error.LookupError:
4256 except error.LookupError:
4258 pass
4257 pass
4259
4258
4260 fm = ui.formatter('grep', opts)
4259 fm = ui.formatter('grep', opts)
4261 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
4260 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
4262 rev = ctx.rev()
4261 rev = ctx.rev()
4263 parent = ctx.p1().rev()
4262 parent = ctx.p1().rev()
4264 for fn in sorted(revfiles.get(rev, [])):
4263 for fn in sorted(revfiles.get(rev, [])):
4265 states = matches[rev][fn]
4264 states = matches[rev][fn]
4266 copy = copies.get(rev, {}).get(fn)
4265 copy = copies.get(rev, {}).get(fn)
4267 if fn in skip:
4266 if fn in skip:
4268 if copy:
4267 if copy:
4269 skip[copy] = True
4268 skip[copy] = True
4270 continue
4269 continue
4271 pstates = matches.get(parent, {}).get(copy or fn, [])
4270 pstates = matches.get(parent, {}).get(copy or fn, [])
4272 if pstates or states:
4271 if pstates or states:
4273 r = display(fm, fn, ctx, pstates, states)
4272 r = display(fm, fn, ctx, pstates, states)
4274 found = found or r
4273 found = found or r
4275 if r and not opts.get('all'):
4274 if r and not opts.get('all'):
4276 skip[fn] = True
4275 skip[fn] = True
4277 if copy:
4276 if copy:
4278 skip[copy] = True
4277 skip[copy] = True
4279 del matches[rev]
4278 del matches[rev]
4280 del revfiles[rev]
4279 del revfiles[rev]
4281 fm.end()
4280 fm.end()
4282
4281
4283 return not found
4282 return not found
4284
4283
4285 @command('heads',
4284 @command('heads',
4286 [('r', 'rev', '',
4285 [('r', 'rev', '',
4287 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
4286 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
4288 ('t', 'topo', False, _('show topological heads only')),
4287 ('t', 'topo', False, _('show topological heads only')),
4289 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
4288 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
4290 ('c', 'closed', False, _('show normal and closed branch heads')),
4289 ('c', 'closed', False, _('show normal and closed branch heads')),
4291 ] + templateopts,
4290 ] + templateopts,
4292 _('[-ct] [-r STARTREV] [REV]...'))
4291 _('[-ct] [-r STARTREV] [REV]...'))
4293 def heads(ui, repo, *branchrevs, **opts):
4292 def heads(ui, repo, *branchrevs, **opts):
4294 """show branch heads
4293 """show branch heads
4295
4294
4296 With no arguments, show all open branch heads in the repository.
4295 With no arguments, show all open branch heads in the repository.
4297 Branch heads are changesets that have no descendants on the
4296 Branch heads are changesets that have no descendants on the
4298 same branch. They are where development generally takes place and
4297 same branch. They are where development generally takes place and
4299 are the usual targets for update and merge operations.
4298 are the usual targets for update and merge operations.
4300
4299
4301 If one or more REVs are given, only open branch heads on the
4300 If one or more REVs are given, only open branch heads on the
4302 branches associated with the specified changesets are shown. This
4301 branches associated with the specified changesets are shown. This
4303 means that you can use :hg:`heads .` to see the heads on the
4302 means that you can use :hg:`heads .` to see the heads on the
4304 currently checked-out branch.
4303 currently checked-out branch.
4305
4304
4306 If -c/--closed is specified, also show branch heads marked closed
4305 If -c/--closed is specified, also show branch heads marked closed
4307 (see :hg:`commit --close-branch`).
4306 (see :hg:`commit --close-branch`).
4308
4307
4309 If STARTREV is specified, only those heads that are descendants of
4308 If STARTREV is specified, only those heads that are descendants of
4310 STARTREV will be displayed.
4309 STARTREV will be displayed.
4311
4310
4312 If -t/--topo is specified, named branch mechanics will be ignored and only
4311 If -t/--topo is specified, named branch mechanics will be ignored and only
4313 topological heads (changesets with no children) will be shown.
4312 topological heads (changesets with no children) will be shown.
4314
4313
4315 Returns 0 if matching heads are found, 1 if not.
4314 Returns 0 if matching heads are found, 1 if not.
4316 """
4315 """
4317
4316
4318 start = None
4317 start = None
4319 if 'rev' in opts:
4318 if 'rev' in opts:
4320 start = scmutil.revsingle(repo, opts['rev'], None).node()
4319 start = scmutil.revsingle(repo, opts['rev'], None).node()
4321
4320
4322 if opts.get('topo'):
4321 if opts.get('topo'):
4323 heads = [repo[h] for h in repo.heads(start)]
4322 heads = [repo[h] for h in repo.heads(start)]
4324 else:
4323 else:
4325 heads = []
4324 heads = []
4326 for branch in repo.branchmap():
4325 for branch in repo.branchmap():
4327 heads += repo.branchheads(branch, start, opts.get('closed'))
4326 heads += repo.branchheads(branch, start, opts.get('closed'))
4328 heads = [repo[h] for h in heads]
4327 heads = [repo[h] for h in heads]
4329
4328
4330 if branchrevs:
4329 if branchrevs:
4331 branches = set(repo[br].branch() for br in branchrevs)
4330 branches = set(repo[br].branch() for br in branchrevs)
4332 heads = [h for h in heads if h.branch() in branches]
4331 heads = [h for h in heads if h.branch() in branches]
4333
4332
4334 if opts.get('active') and branchrevs:
4333 if opts.get('active') and branchrevs:
4335 dagheads = repo.heads(start)
4334 dagheads = repo.heads(start)
4336 heads = [h for h in heads if h.node() in dagheads]
4335 heads = [h for h in heads if h.node() in dagheads]
4337
4336
4338 if branchrevs:
4337 if branchrevs:
4339 haveheads = set(h.branch() for h in heads)
4338 haveheads = set(h.branch() for h in heads)
4340 if branches - haveheads:
4339 if branches - haveheads:
4341 headless = ', '.join(b for b in branches - haveheads)
4340 headless = ', '.join(b for b in branches - haveheads)
4342 msg = _('no open branch heads found on branches %s')
4341 msg = _('no open branch heads found on branches %s')
4343 if opts.get('rev'):
4342 if opts.get('rev'):
4344 msg += _(' (started at %s)') % opts['rev']
4343 msg += _(' (started at %s)') % opts['rev']
4345 ui.warn((msg + '\n') % headless)
4344 ui.warn((msg + '\n') % headless)
4346
4345
4347 if not heads:
4346 if not heads:
4348 return 1
4347 return 1
4349
4348
4350 heads = sorted(heads, key=lambda x: -x.rev())
4349 heads = sorted(heads, key=lambda x: -x.rev())
4351 displayer = cmdutil.show_changeset(ui, repo, opts)
4350 displayer = cmdutil.show_changeset(ui, repo, opts)
4352 for ctx in heads:
4351 for ctx in heads:
4353 displayer.show(ctx)
4352 displayer.show(ctx)
4354 displayer.close()
4353 displayer.close()
4355
4354
4356 @command('help',
4355 @command('help',
4357 [('e', 'extension', None, _('show only help for extensions')),
4356 [('e', 'extension', None, _('show only help for extensions')),
4358 ('c', 'command', None, _('show only help for commands')),
4357 ('c', 'command', None, _('show only help for commands')),
4359 ('k', 'keyword', None, _('show topics matching keyword')),
4358 ('k', 'keyword', None, _('show topics matching keyword')),
4360 ('s', 'system', [], _('show help for specific platform(s)')),
4359 ('s', 'system', [], _('show help for specific platform(s)')),
4361 ],
4360 ],
4362 _('[-ecks] [TOPIC]'),
4361 _('[-ecks] [TOPIC]'),
4363 norepo=True)
4362 norepo=True)
4364 def help_(ui, name=None, **opts):
4363 def help_(ui, name=None, **opts):
4365 """show help for a given topic or a help overview
4364 """show help for a given topic or a help overview
4366
4365
4367 With no arguments, print a list of commands with short help messages.
4366 With no arguments, print a list of commands with short help messages.
4368
4367
4369 Given a topic, extension, or command name, print help for that
4368 Given a topic, extension, or command name, print help for that
4370 topic.
4369 topic.
4371
4370
4372 Returns 0 if successful.
4371 Returns 0 if successful.
4373 """
4372 """
4374
4373
4375 textwidth = ui.configint('ui', 'textwidth', 78)
4374 textwidth = ui.configint('ui', 'textwidth', 78)
4376 termwidth = ui.termwidth() - 2
4375 termwidth = ui.termwidth() - 2
4377 if textwidth <= 0 or termwidth < textwidth:
4376 if textwidth <= 0 or termwidth < textwidth:
4378 textwidth = termwidth
4377 textwidth = termwidth
4379
4378
4380 keep = opts.get('system') or []
4379 keep = opts.get('system') or []
4381 if len(keep) == 0:
4380 if len(keep) == 0:
4382 if sys.platform.startswith('win'):
4381 if sys.platform.startswith('win'):
4383 keep.append('windows')
4382 keep.append('windows')
4384 elif sys.platform == 'OpenVMS':
4383 elif sys.platform == 'OpenVMS':
4385 keep.append('vms')
4384 keep.append('vms')
4386 elif sys.platform == 'plan9':
4385 elif sys.platform == 'plan9':
4387 keep.append('plan9')
4386 keep.append('plan9')
4388 else:
4387 else:
4389 keep.append('unix')
4388 keep.append('unix')
4390 keep.append(sys.platform.lower())
4389 keep.append(sys.platform.lower())
4391 if ui.verbose:
4390 if ui.verbose:
4392 keep.append('verbose')
4391 keep.append('verbose')
4393
4392
4394 section = None
4393 section = None
4395 subtopic = None
4394 subtopic = None
4396 if name and '.' in name:
4395 if name and '.' in name:
4397 name, remaining = name.split('.', 1)
4396 name, remaining = name.split('.', 1)
4398 remaining = encoding.lower(remaining)
4397 remaining = encoding.lower(remaining)
4399 if '.' in remaining:
4398 if '.' in remaining:
4400 subtopic, section = remaining.split('.', 1)
4399 subtopic, section = remaining.split('.', 1)
4401 else:
4400 else:
4402 if name in help.subtopics:
4401 if name in help.subtopics:
4403 subtopic = remaining
4402 subtopic = remaining
4404 else:
4403 else:
4405 section = remaining
4404 section = remaining
4406
4405
4407 text = help.help_(ui, name, subtopic=subtopic, **opts)
4406 text = help.help_(ui, name, subtopic=subtopic, **opts)
4408
4407
4409 formatted, pruned = minirst.format(text, textwidth, keep=keep,
4408 formatted, pruned = minirst.format(text, textwidth, keep=keep,
4410 section=section)
4409 section=section)
4411
4410
4412 # We could have been given a weird ".foo" section without a name
4411 # We could have been given a weird ".foo" section without a name
4413 # to look for, or we could have simply failed to found "foo.bar"
4412 # to look for, or we could have simply failed to found "foo.bar"
4414 # because bar isn't a section of foo
4413 # because bar isn't a section of foo
4415 if section and not (formatted and name):
4414 if section and not (formatted and name):
4416 raise error.Abort(_("help section not found"))
4415 raise error.Abort(_("help section not found"))
4417
4416
4418 if 'verbose' in pruned:
4417 if 'verbose' in pruned:
4419 keep.append('omitted')
4418 keep.append('omitted')
4420 else:
4419 else:
4421 keep.append('notomitted')
4420 keep.append('notomitted')
4422 formatted, pruned = minirst.format(text, textwidth, keep=keep,
4421 formatted, pruned = minirst.format(text, textwidth, keep=keep,
4423 section=section)
4422 section=section)
4424 ui.write(formatted)
4423 ui.write(formatted)
4425
4424
4426
4425
4427 @command('identify|id',
4426 @command('identify|id',
4428 [('r', 'rev', '',
4427 [('r', 'rev', '',
4429 _('identify the specified revision'), _('REV')),
4428 _('identify the specified revision'), _('REV')),
4430 ('n', 'num', None, _('show local revision number')),
4429 ('n', 'num', None, _('show local revision number')),
4431 ('i', 'id', None, _('show global revision id')),
4430 ('i', 'id', None, _('show global revision id')),
4432 ('b', 'branch', None, _('show branch')),
4431 ('b', 'branch', None, _('show branch')),
4433 ('t', 'tags', None, _('show tags')),
4432 ('t', 'tags', None, _('show tags')),
4434 ('B', 'bookmarks', None, _('show bookmarks')),
4433 ('B', 'bookmarks', None, _('show bookmarks')),
4435 ] + remoteopts,
4434 ] + remoteopts,
4436 _('[-nibtB] [-r REV] [SOURCE]'),
4435 _('[-nibtB] [-r REV] [SOURCE]'),
4437 optionalrepo=True)
4436 optionalrepo=True)
4438 def identify(ui, repo, source=None, rev=None,
4437 def identify(ui, repo, source=None, rev=None,
4439 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
4438 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
4440 """identify the working directory or specified revision
4439 """identify the working directory or specified revision
4441
4440
4442 Print a summary identifying the repository state at REV using one or
4441 Print a summary identifying the repository state at REV using one or
4443 two parent hash identifiers, followed by a "+" if the working
4442 two parent hash identifiers, followed by a "+" if the working
4444 directory has uncommitted changes, the branch name (if not default),
4443 directory has uncommitted changes, the branch name (if not default),
4445 a list of tags, and a list of bookmarks.
4444 a list of tags, and a list of bookmarks.
4446
4445
4447 When REV is not given, print a summary of the current state of the
4446 When REV is not given, print a summary of the current state of the
4448 repository.
4447 repository.
4449
4448
4450 Specifying a path to a repository root or Mercurial bundle will
4449 Specifying a path to a repository root or Mercurial bundle will
4451 cause lookup to operate on that repository/bundle.
4450 cause lookup to operate on that repository/bundle.
4452
4451
4453 .. container:: verbose
4452 .. container:: verbose
4454
4453
4455 Examples:
4454 Examples:
4456
4455
4457 - generate a build identifier for the working directory::
4456 - generate a build identifier for the working directory::
4458
4457
4459 hg id --id > build-id.dat
4458 hg id --id > build-id.dat
4460
4459
4461 - find the revision corresponding to a tag::
4460 - find the revision corresponding to a tag::
4462
4461
4463 hg id -n -r 1.3
4462 hg id -n -r 1.3
4464
4463
4465 - check the most recent revision of a remote repository::
4464 - check the most recent revision of a remote repository::
4466
4465
4467 hg id -r tip https://www.mercurial-scm.org/repo/hg/
4466 hg id -r tip https://www.mercurial-scm.org/repo/hg/
4468
4467
4469 See :hg:`log` for generating more information about specific revisions,
4468 See :hg:`log` for generating more information about specific revisions,
4470 including full hash identifiers.
4469 including full hash identifiers.
4471
4470
4472 Returns 0 if successful.
4471 Returns 0 if successful.
4473 """
4472 """
4474
4473
4475 if not repo and not source:
4474 if not repo and not source:
4476 raise error.Abort(_("there is no Mercurial repository here "
4475 raise error.Abort(_("there is no Mercurial repository here "
4477 "(.hg not found)"))
4476 "(.hg not found)"))
4478
4477
4479 if ui.debugflag:
4478 if ui.debugflag:
4480 hexfunc = hex
4479 hexfunc = hex
4481 else:
4480 else:
4482 hexfunc = short
4481 hexfunc = short
4483 default = not (num or id or branch or tags or bookmarks)
4482 default = not (num or id or branch or tags or bookmarks)
4484 output = []
4483 output = []
4485 revs = []
4484 revs = []
4486
4485
4487 if source:
4486 if source:
4488 source, branches = hg.parseurl(ui.expandpath(source))
4487 source, branches = hg.parseurl(ui.expandpath(source))
4489 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
4488 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
4490 repo = peer.local()
4489 repo = peer.local()
4491 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
4490 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
4492
4491
4493 if not repo:
4492 if not repo:
4494 if num or branch or tags:
4493 if num or branch or tags:
4495 raise error.Abort(
4494 raise error.Abort(
4496 _("can't query remote revision number, branch, or tags"))
4495 _("can't query remote revision number, branch, or tags"))
4497 if not rev and revs:
4496 if not rev and revs:
4498 rev = revs[0]
4497 rev = revs[0]
4499 if not rev:
4498 if not rev:
4500 rev = "tip"
4499 rev = "tip"
4501
4500
4502 remoterev = peer.lookup(rev)
4501 remoterev = peer.lookup(rev)
4503 if default or id:
4502 if default or id:
4504 output = [hexfunc(remoterev)]
4503 output = [hexfunc(remoterev)]
4505
4504
4506 def getbms():
4505 def getbms():
4507 bms = []
4506 bms = []
4508
4507
4509 if 'bookmarks' in peer.listkeys('namespaces'):
4508 if 'bookmarks' in peer.listkeys('namespaces'):
4510 hexremoterev = hex(remoterev)
4509 hexremoterev = hex(remoterev)
4511 bms = [bm for bm, bmr in peer.listkeys('bookmarks').iteritems()
4510 bms = [bm for bm, bmr in peer.listkeys('bookmarks').iteritems()
4512 if bmr == hexremoterev]
4511 if bmr == hexremoterev]
4513
4512
4514 return sorted(bms)
4513 return sorted(bms)
4515
4514
4516 if bookmarks:
4515 if bookmarks:
4517 output.extend(getbms())
4516 output.extend(getbms())
4518 elif default and not ui.quiet:
4517 elif default and not ui.quiet:
4519 # multiple bookmarks for a single parent separated by '/'
4518 # multiple bookmarks for a single parent separated by '/'
4520 bm = '/'.join(getbms())
4519 bm = '/'.join(getbms())
4521 if bm:
4520 if bm:
4522 output.append(bm)
4521 output.append(bm)
4523 else:
4522 else:
4524 ctx = scmutil.revsingle(repo, rev, None)
4523 ctx = scmutil.revsingle(repo, rev, None)
4525
4524
4526 if ctx.rev() is None:
4525 if ctx.rev() is None:
4527 ctx = repo[None]
4526 ctx = repo[None]
4528 parents = ctx.parents()
4527 parents = ctx.parents()
4529 taglist = []
4528 taglist = []
4530 for p in parents:
4529 for p in parents:
4531 taglist.extend(p.tags())
4530 taglist.extend(p.tags())
4532
4531
4533 changed = ""
4532 changed = ""
4534 if default or id or num:
4533 if default or id or num:
4535 if (any(repo.status())
4534 if (any(repo.status())
4536 or any(ctx.sub(s).dirty() for s in ctx.substate)):
4535 or any(ctx.sub(s).dirty() for s in ctx.substate)):
4537 changed = '+'
4536 changed = '+'
4538 if default or id:
4537 if default or id:
4539 output = ["%s%s" %
4538 output = ["%s%s" %
4540 ('+'.join([hexfunc(p.node()) for p in parents]), changed)]
4539 ('+'.join([hexfunc(p.node()) for p in parents]), changed)]
4541 if num:
4540 if num:
4542 output.append("%s%s" %
4541 output.append("%s%s" %
4543 ('+'.join([str(p.rev()) for p in parents]), changed))
4542 ('+'.join([str(p.rev()) for p in parents]), changed))
4544 else:
4543 else:
4545 if default or id:
4544 if default or id:
4546 output = [hexfunc(ctx.node())]
4545 output = [hexfunc(ctx.node())]
4547 if num:
4546 if num:
4548 output.append(str(ctx.rev()))
4547 output.append(str(ctx.rev()))
4549 taglist = ctx.tags()
4548 taglist = ctx.tags()
4550
4549
4551 if default and not ui.quiet:
4550 if default and not ui.quiet:
4552 b = ctx.branch()
4551 b = ctx.branch()
4553 if b != 'default':
4552 if b != 'default':
4554 output.append("(%s)" % b)
4553 output.append("(%s)" % b)
4555
4554
4556 # multiple tags for a single parent separated by '/'
4555 # multiple tags for a single parent separated by '/'
4557 t = '/'.join(taglist)
4556 t = '/'.join(taglist)
4558 if t:
4557 if t:
4559 output.append(t)
4558 output.append(t)
4560
4559
4561 # multiple bookmarks for a single parent separated by '/'
4560 # multiple bookmarks for a single parent separated by '/'
4562 bm = '/'.join(ctx.bookmarks())
4561 bm = '/'.join(ctx.bookmarks())
4563 if bm:
4562 if bm:
4564 output.append(bm)
4563 output.append(bm)
4565 else:
4564 else:
4566 if branch:
4565 if branch:
4567 output.append(ctx.branch())
4566 output.append(ctx.branch())
4568
4567
4569 if tags:
4568 if tags:
4570 output.extend(taglist)
4569 output.extend(taglist)
4571
4570
4572 if bookmarks:
4571 if bookmarks:
4573 output.extend(ctx.bookmarks())
4572 output.extend(ctx.bookmarks())
4574
4573
4575 ui.write("%s\n" % ' '.join(output))
4574 ui.write("%s\n" % ' '.join(output))
4576
4575
4577 @command('import|patch',
4576 @command('import|patch',
4578 [('p', 'strip', 1,
4577 [('p', 'strip', 1,
4579 _('directory strip option for patch. This has the same '
4578 _('directory strip option for patch. This has the same '
4580 'meaning as the corresponding patch option'), _('NUM')),
4579 'meaning as the corresponding patch option'), _('NUM')),
4581 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
4580 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
4582 ('e', 'edit', False, _('invoke editor on commit messages')),
4581 ('e', 'edit', False, _('invoke editor on commit messages')),
4583 ('f', 'force', None,
4582 ('f', 'force', None,
4584 _('skip check for outstanding uncommitted changes (DEPRECATED)')),
4583 _('skip check for outstanding uncommitted changes (DEPRECATED)')),
4585 ('', 'no-commit', None,
4584 ('', 'no-commit', None,
4586 _("don't commit, just update the working directory")),
4585 _("don't commit, just update the working directory")),
4587 ('', 'bypass', None,
4586 ('', 'bypass', None,
4588 _("apply patch without touching the working directory")),
4587 _("apply patch without touching the working directory")),
4589 ('', 'partial', None,
4588 ('', 'partial', None,
4590 _('commit even if some hunks fail')),
4589 _('commit even if some hunks fail')),
4591 ('', 'exact', None,
4590 ('', 'exact', None,
4592 _('abort if patch would apply lossily')),
4591 _('abort if patch would apply lossily')),
4593 ('', 'prefix', '',
4592 ('', 'prefix', '',
4594 _('apply patch to subdirectory'), _('DIR')),
4593 _('apply patch to subdirectory'), _('DIR')),
4595 ('', 'import-branch', None,
4594 ('', 'import-branch', None,
4596 _('use any branch information in patch (implied by --exact)'))] +
4595 _('use any branch information in patch (implied by --exact)'))] +
4597 commitopts + commitopts2 + similarityopts,
4596 commitopts + commitopts2 + similarityopts,
4598 _('[OPTION]... PATCH...'))
4597 _('[OPTION]... PATCH...'))
4599 def import_(ui, repo, patch1=None, *patches, **opts):
4598 def import_(ui, repo, patch1=None, *patches, **opts):
4600 """import an ordered set of patches
4599 """import an ordered set of patches
4601
4600
4602 Import a list of patches and commit them individually (unless
4601 Import a list of patches and commit them individually (unless
4603 --no-commit is specified).
4602 --no-commit is specified).
4604
4603
4605 To read a patch from standard input, use "-" as the patch name. If
4604 To read a patch from standard input, use "-" as the patch name. If
4606 a URL is specified, the patch will be downloaded from there.
4605 a URL is specified, the patch will be downloaded from there.
4607
4606
4608 Import first applies changes to the working directory (unless
4607 Import first applies changes to the working directory (unless
4609 --bypass is specified), import will abort if there are outstanding
4608 --bypass is specified), import will abort if there are outstanding
4610 changes.
4609 changes.
4611
4610
4612 Use --bypass to apply and commit patches directly to the
4611 Use --bypass to apply and commit patches directly to the
4613 repository, without affecting the working directory. Without
4612 repository, without affecting the working directory. Without
4614 --exact, patches will be applied on top of the working directory
4613 --exact, patches will be applied on top of the working directory
4615 parent revision.
4614 parent revision.
4616
4615
4617 You can import a patch straight from a mail message. Even patches
4616 You can import a patch straight from a mail message. Even patches
4618 as attachments work (to use the body part, it must have type
4617 as attachments work (to use the body part, it must have type
4619 text/plain or text/x-patch). From and Subject headers of email
4618 text/plain or text/x-patch). From and Subject headers of email
4620 message are used as default committer and commit message. All
4619 message are used as default committer and commit message. All
4621 text/plain body parts before first diff are added to the commit
4620 text/plain body parts before first diff are added to the commit
4622 message.
4621 message.
4623
4622
4624 If the imported patch was generated by :hg:`export`, user and
4623 If the imported patch was generated by :hg:`export`, user and
4625 description from patch override values from message headers and
4624 description from patch override values from message headers and
4626 body. Values given on command line with -m/--message and -u/--user
4625 body. Values given on command line with -m/--message and -u/--user
4627 override these.
4626 override these.
4628
4627
4629 If --exact is specified, import will set the working directory to
4628 If --exact is specified, import will set the working directory to
4630 the parent of each patch before applying it, and will abort if the
4629 the parent of each patch before applying it, and will abort if the
4631 resulting changeset has a different ID than the one recorded in
4630 resulting changeset has a different ID than the one recorded in
4632 the patch. This will guard against various ways that portable
4631 the patch. This will guard against various ways that portable
4633 patch formats and mail systems might fail to transfer Mercurial
4632 patch formats and mail systems might fail to transfer Mercurial
4634 data or metadata. See :hg:`bundle` for lossless transmission.
4633 data or metadata. See :hg:`bundle` for lossless transmission.
4635
4634
4636 Use --partial to ensure a changeset will be created from the patch
4635 Use --partial to ensure a changeset will be created from the patch
4637 even if some hunks fail to apply. Hunks that fail to apply will be
4636 even if some hunks fail to apply. Hunks that fail to apply will be
4638 written to a <target-file>.rej file. Conflicts can then be resolved
4637 written to a <target-file>.rej file. Conflicts can then be resolved
4639 by hand before :hg:`commit --amend` is run to update the created
4638 by hand before :hg:`commit --amend` is run to update the created
4640 changeset. This flag exists to let people import patches that
4639 changeset. This flag exists to let people import patches that
4641 partially apply without losing the associated metadata (author,
4640 partially apply without losing the associated metadata (author,
4642 date, description, ...).
4641 date, description, ...).
4643
4642
4644 .. note::
4643 .. note::
4645
4644
4646 When no hunks apply cleanly, :hg:`import --partial` will create
4645 When no hunks apply cleanly, :hg:`import --partial` will create
4647 an empty changeset, importing only the patch metadata.
4646 an empty changeset, importing only the patch metadata.
4648
4647
4649 With -s/--similarity, hg will attempt to discover renames and
4648 With -s/--similarity, hg will attempt to discover renames and
4650 copies in the patch in the same way as :hg:`addremove`.
4649 copies in the patch in the same way as :hg:`addremove`.
4651
4650
4652 It is possible to use external patch programs to perform the patch
4651 It is possible to use external patch programs to perform the patch
4653 by setting the ``ui.patch`` configuration option. For the default
4652 by setting the ``ui.patch`` configuration option. For the default
4654 internal tool, the fuzz can also be configured via ``patch.fuzz``.
4653 internal tool, the fuzz can also be configured via ``patch.fuzz``.
4655 See :hg:`help config` for more information about configuration
4654 See :hg:`help config` for more information about configuration
4656 files and how to use these options.
4655 files and how to use these options.
4657
4656
4658 See :hg:`help dates` for a list of formats valid for -d/--date.
4657 See :hg:`help dates` for a list of formats valid for -d/--date.
4659
4658
4660 .. container:: verbose
4659 .. container:: verbose
4661
4660
4662 Examples:
4661 Examples:
4663
4662
4664 - import a traditional patch from a website and detect renames::
4663 - import a traditional patch from a website and detect renames::
4665
4664
4666 hg import -s 80 http://example.com/bugfix.patch
4665 hg import -s 80 http://example.com/bugfix.patch
4667
4666
4668 - import a changeset from an hgweb server::
4667 - import a changeset from an hgweb server::
4669
4668
4670 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
4669 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
4671
4670
4672 - import all the patches in an Unix-style mbox::
4671 - import all the patches in an Unix-style mbox::
4673
4672
4674 hg import incoming-patches.mbox
4673 hg import incoming-patches.mbox
4675
4674
4676 - attempt to exactly restore an exported changeset (not always
4675 - attempt to exactly restore an exported changeset (not always
4677 possible)::
4676 possible)::
4678
4677
4679 hg import --exact proposed-fix.patch
4678 hg import --exact proposed-fix.patch
4680
4679
4681 - use an external tool to apply a patch which is too fuzzy for
4680 - use an external tool to apply a patch which is too fuzzy for
4682 the default internal tool.
4681 the default internal tool.
4683
4682
4684 hg import --config ui.patch="patch --merge" fuzzy.patch
4683 hg import --config ui.patch="patch --merge" fuzzy.patch
4685
4684
4686 - change the default fuzzing from 2 to a less strict 7
4685 - change the default fuzzing from 2 to a less strict 7
4687
4686
4688 hg import --config ui.fuzz=7 fuzz.patch
4687 hg import --config ui.fuzz=7 fuzz.patch
4689
4688
4690 Returns 0 on success, 1 on partial success (see --partial).
4689 Returns 0 on success, 1 on partial success (see --partial).
4691 """
4690 """
4692
4691
4693 if not patch1:
4692 if not patch1:
4694 raise error.Abort(_('need at least one patch to import'))
4693 raise error.Abort(_('need at least one patch to import'))
4695
4694
4696 patches = (patch1,) + patches
4695 patches = (patch1,) + patches
4697
4696
4698 date = opts.get('date')
4697 date = opts.get('date')
4699 if date:
4698 if date:
4700 opts['date'] = util.parsedate(date)
4699 opts['date'] = util.parsedate(date)
4701
4700
4702 exact = opts.get('exact')
4701 exact = opts.get('exact')
4703 update = not opts.get('bypass')
4702 update = not opts.get('bypass')
4704 if not update and opts.get('no_commit'):
4703 if not update and opts.get('no_commit'):
4705 raise error.Abort(_('cannot use --no-commit with --bypass'))
4704 raise error.Abort(_('cannot use --no-commit with --bypass'))
4706 try:
4705 try:
4707 sim = float(opts.get('similarity') or 0)
4706 sim = float(opts.get('similarity') or 0)
4708 except ValueError:
4707 except ValueError:
4709 raise error.Abort(_('similarity must be a number'))
4708 raise error.Abort(_('similarity must be a number'))
4710 if sim < 0 or sim > 100:
4709 if sim < 0 or sim > 100:
4711 raise error.Abort(_('similarity must be between 0 and 100'))
4710 raise error.Abort(_('similarity must be between 0 and 100'))
4712 if sim and not update:
4711 if sim and not update:
4713 raise error.Abort(_('cannot use --similarity with --bypass'))
4712 raise error.Abort(_('cannot use --similarity with --bypass'))
4714 if exact:
4713 if exact:
4715 if opts.get('edit'):
4714 if opts.get('edit'):
4716 raise error.Abort(_('cannot use --exact with --edit'))
4715 raise error.Abort(_('cannot use --exact with --edit'))
4717 if opts.get('prefix'):
4716 if opts.get('prefix'):
4718 raise error.Abort(_('cannot use --exact with --prefix'))
4717 raise error.Abort(_('cannot use --exact with --prefix'))
4719
4718
4720 base = opts["base"]
4719 base = opts["base"]
4721 wlock = dsguard = lock = tr = None
4720 wlock = dsguard = lock = tr = None
4722 msgs = []
4721 msgs = []
4723 ret = 0
4722 ret = 0
4724
4723
4725
4724
4726 try:
4725 try:
4727 wlock = repo.wlock()
4726 wlock = repo.wlock()
4728
4727
4729 if update:
4728 if update:
4730 cmdutil.checkunfinished(repo)
4729 cmdutil.checkunfinished(repo)
4731 if (exact or not opts.get('force')):
4730 if (exact or not opts.get('force')):
4732 cmdutil.bailifchanged(repo)
4731 cmdutil.bailifchanged(repo)
4733
4732
4734 if not opts.get('no_commit'):
4733 if not opts.get('no_commit'):
4735 lock = repo.lock()
4734 lock = repo.lock()
4736 tr = repo.transaction('import')
4735 tr = repo.transaction('import')
4737 else:
4736 else:
4738 dsguard = dirstateguard.dirstateguard(repo, 'import')
4737 dsguard = dirstateguard.dirstateguard(repo, 'import')
4739 parents = repo[None].parents()
4738 parents = repo[None].parents()
4740 for patchurl in patches:
4739 for patchurl in patches:
4741 if patchurl == '-':
4740 if patchurl == '-':
4742 ui.status(_('applying patch from stdin\n'))
4741 ui.status(_('applying patch from stdin\n'))
4743 patchfile = ui.fin
4742 patchfile = ui.fin
4744 patchurl = 'stdin' # for error message
4743 patchurl = 'stdin' # for error message
4745 else:
4744 else:
4746 patchurl = os.path.join(base, patchurl)
4745 patchurl = os.path.join(base, patchurl)
4747 ui.status(_('applying %s\n') % patchurl)
4746 ui.status(_('applying %s\n') % patchurl)
4748 patchfile = hg.openpath(ui, patchurl)
4747 patchfile = hg.openpath(ui, patchurl)
4749
4748
4750 haspatch = False
4749 haspatch = False
4751 for hunk in patch.split(patchfile):
4750 for hunk in patch.split(patchfile):
4752 (msg, node, rej) = cmdutil.tryimportone(ui, repo, hunk,
4751 (msg, node, rej) = cmdutil.tryimportone(ui, repo, hunk,
4753 parents, opts,
4752 parents, opts,
4754 msgs, hg.clean)
4753 msgs, hg.clean)
4755 if msg:
4754 if msg:
4756 haspatch = True
4755 haspatch = True
4757 ui.note(msg + '\n')
4756 ui.note(msg + '\n')
4758 if update or exact:
4757 if update or exact:
4759 parents = repo[None].parents()
4758 parents = repo[None].parents()
4760 else:
4759 else:
4761 parents = [repo[node]]
4760 parents = [repo[node]]
4762 if rej:
4761 if rej:
4763 ui.write_err(_("patch applied partially\n"))
4762 ui.write_err(_("patch applied partially\n"))
4764 ui.write_err(_("(fix the .rej files and run "
4763 ui.write_err(_("(fix the .rej files and run "
4765 "`hg commit --amend`)\n"))
4764 "`hg commit --amend`)\n"))
4766 ret = 1
4765 ret = 1
4767 break
4766 break
4768
4767
4769 if not haspatch:
4768 if not haspatch:
4770 raise error.Abort(_('%s: no diffs found') % patchurl)
4769 raise error.Abort(_('%s: no diffs found') % patchurl)
4771
4770
4772 if tr:
4771 if tr:
4773 tr.close()
4772 tr.close()
4774 if msgs:
4773 if msgs:
4775 repo.savecommitmessage('\n* * *\n'.join(msgs))
4774 repo.savecommitmessage('\n* * *\n'.join(msgs))
4776 if dsguard:
4775 if dsguard:
4777 dsguard.close()
4776 dsguard.close()
4778 return ret
4777 return ret
4779 finally:
4778 finally:
4780 if tr:
4779 if tr:
4781 tr.release()
4780 tr.release()
4782 release(lock, dsguard, wlock)
4781 release(lock, dsguard, wlock)
4783
4782
4784 @command('incoming|in',
4783 @command('incoming|in',
4785 [('f', 'force', None,
4784 [('f', 'force', None,
4786 _('run even if remote repository is unrelated')),
4785 _('run even if remote repository is unrelated')),
4787 ('n', 'newest-first', None, _('show newest record first')),
4786 ('n', 'newest-first', None, _('show newest record first')),
4788 ('', 'bundle', '',
4787 ('', 'bundle', '',
4789 _('file to store the bundles into'), _('FILE')),
4788 _('file to store the bundles into'), _('FILE')),
4790 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
4789 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
4791 ('B', 'bookmarks', False, _("compare bookmarks")),
4790 ('B', 'bookmarks', False, _("compare bookmarks")),
4792 ('b', 'branch', [],
4791 ('b', 'branch', [],
4793 _('a specific branch you would like to pull'), _('BRANCH')),
4792 _('a specific branch you would like to pull'), _('BRANCH')),
4794 ] + logopts + remoteopts + subrepoopts,
4793 ] + logopts + remoteopts + subrepoopts,
4795 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
4794 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
4796 def incoming(ui, repo, source="default", **opts):
4795 def incoming(ui, repo, source="default", **opts):
4797 """show new changesets found in source
4796 """show new changesets found in source
4798
4797
4799 Show new changesets found in the specified path/URL or the default
4798 Show new changesets found in the specified path/URL or the default
4800 pull location. These are the changesets that would have been pulled
4799 pull location. These are the changesets that would have been pulled
4801 if a pull at the time you issued this command.
4800 if a pull at the time you issued this command.
4802
4801
4803 See pull for valid source format details.
4802 See pull for valid source format details.
4804
4803
4805 .. container:: verbose
4804 .. container:: verbose
4806
4805
4807 With -B/--bookmarks, the result of bookmark comparison between
4806 With -B/--bookmarks, the result of bookmark comparison between
4808 local and remote repositories is displayed. With -v/--verbose,
4807 local and remote repositories is displayed. With -v/--verbose,
4809 status is also displayed for each bookmark like below::
4808 status is also displayed for each bookmark like below::
4810
4809
4811 BM1 01234567890a added
4810 BM1 01234567890a added
4812 BM2 1234567890ab advanced
4811 BM2 1234567890ab advanced
4813 BM3 234567890abc diverged
4812 BM3 234567890abc diverged
4814 BM4 34567890abcd changed
4813 BM4 34567890abcd changed
4815
4814
4816 The action taken locally when pulling depends on the
4815 The action taken locally when pulling depends on the
4817 status of each bookmark:
4816 status of each bookmark:
4818
4817
4819 :``added``: pull will create it
4818 :``added``: pull will create it
4820 :``advanced``: pull will update it
4819 :``advanced``: pull will update it
4821 :``diverged``: pull will create a divergent bookmark
4820 :``diverged``: pull will create a divergent bookmark
4822 :``changed``: result depends on remote changesets
4821 :``changed``: result depends on remote changesets
4823
4822
4824 From the point of view of pulling behavior, bookmark
4823 From the point of view of pulling behavior, bookmark
4825 existing only in the remote repository are treated as ``added``,
4824 existing only in the remote repository are treated as ``added``,
4826 even if it is in fact locally deleted.
4825 even if it is in fact locally deleted.
4827
4826
4828 .. container:: verbose
4827 .. container:: verbose
4829
4828
4830 For remote repository, using --bundle avoids downloading the
4829 For remote repository, using --bundle avoids downloading the
4831 changesets twice if the incoming is followed by a pull.
4830 changesets twice if the incoming is followed by a pull.
4832
4831
4833 Examples:
4832 Examples:
4834
4833
4835 - show incoming changes with patches and full description::
4834 - show incoming changes with patches and full description::
4836
4835
4837 hg incoming -vp
4836 hg incoming -vp
4838
4837
4839 - show incoming changes excluding merges, store a bundle::
4838 - show incoming changes excluding merges, store a bundle::
4840
4839
4841 hg in -vpM --bundle incoming.hg
4840 hg in -vpM --bundle incoming.hg
4842 hg pull incoming.hg
4841 hg pull incoming.hg
4843
4842
4844 - briefly list changes inside a bundle::
4843 - briefly list changes inside a bundle::
4845
4844
4846 hg in changes.hg -T "{desc|firstline}\\n"
4845 hg in changes.hg -T "{desc|firstline}\\n"
4847
4846
4848 Returns 0 if there are incoming changes, 1 otherwise.
4847 Returns 0 if there are incoming changes, 1 otherwise.
4849 """
4848 """
4850 if opts.get('graph'):
4849 if opts.get('graph'):
4851 cmdutil.checkunsupportedgraphflags([], opts)
4850 cmdutil.checkunsupportedgraphflags([], opts)
4852 def display(other, chlist, displayer):
4851 def display(other, chlist, displayer):
4853 revdag = cmdutil.graphrevs(other, chlist, opts)
4852 revdag = cmdutil.graphrevs(other, chlist, opts)
4854 cmdutil.displaygraph(ui, repo, revdag, displayer,
4853 cmdutil.displaygraph(ui, repo, revdag, displayer,
4855 graphmod.asciiedges)
4854 graphmod.asciiedges)
4856
4855
4857 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
4856 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
4858 return 0
4857 return 0
4859
4858
4860 if opts.get('bundle') and opts.get('subrepos'):
4859 if opts.get('bundle') and opts.get('subrepos'):
4861 raise error.Abort(_('cannot combine --bundle and --subrepos'))
4860 raise error.Abort(_('cannot combine --bundle and --subrepos'))
4862
4861
4863 if opts.get('bookmarks'):
4862 if opts.get('bookmarks'):
4864 source, branches = hg.parseurl(ui.expandpath(source),
4863 source, branches = hg.parseurl(ui.expandpath(source),
4865 opts.get('branch'))
4864 opts.get('branch'))
4866 other = hg.peer(repo, opts, source)
4865 other = hg.peer(repo, opts, source)
4867 if 'bookmarks' not in other.listkeys('namespaces'):
4866 if 'bookmarks' not in other.listkeys('namespaces'):
4868 ui.warn(_("remote doesn't support bookmarks\n"))
4867 ui.warn(_("remote doesn't support bookmarks\n"))
4869 return 0
4868 return 0
4870 ui.status(_('comparing with %s\n') % util.hidepassword(source))
4869 ui.status(_('comparing with %s\n') % util.hidepassword(source))
4871 return bookmarks.incoming(ui, repo, other)
4870 return bookmarks.incoming(ui, repo, other)
4872
4871
4873 repo._subtoppath = ui.expandpath(source)
4872 repo._subtoppath = ui.expandpath(source)
4874 try:
4873 try:
4875 return hg.incoming(ui, repo, source, opts)
4874 return hg.incoming(ui, repo, source, opts)
4876 finally:
4875 finally:
4877 del repo._subtoppath
4876 del repo._subtoppath
4878
4877
4879
4878
4880 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'),
4879 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'),
4881 norepo=True)
4880 norepo=True)
4882 def init(ui, dest=".", **opts):
4881 def init(ui, dest=".", **opts):
4883 """create a new repository in the given directory
4882 """create a new repository in the given directory
4884
4883
4885 Initialize a new repository in the given directory. If the given
4884 Initialize a new repository in the given directory. If the given
4886 directory does not exist, it will be created.
4885 directory does not exist, it will be created.
4887
4886
4888 If no directory is given, the current directory is used.
4887 If no directory is given, the current directory is used.
4889
4888
4890 It is possible to specify an ``ssh://`` URL as the destination.
4889 It is possible to specify an ``ssh://`` URL as the destination.
4891 See :hg:`help urls` for more information.
4890 See :hg:`help urls` for more information.
4892
4891
4893 Returns 0 on success.
4892 Returns 0 on success.
4894 """
4893 """
4895 hg.peer(ui, opts, ui.expandpath(dest), create=True)
4894 hg.peer(ui, opts, ui.expandpath(dest), create=True)
4896
4895
4897 @command('locate',
4896 @command('locate',
4898 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
4897 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
4899 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
4898 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
4900 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
4899 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
4901 ] + walkopts,
4900 ] + walkopts,
4902 _('[OPTION]... [PATTERN]...'))
4901 _('[OPTION]... [PATTERN]...'))
4903 def locate(ui, repo, *pats, **opts):
4902 def locate(ui, repo, *pats, **opts):
4904 """locate files matching specific patterns (DEPRECATED)
4903 """locate files matching specific patterns (DEPRECATED)
4905
4904
4906 Print files under Mercurial control in the working directory whose
4905 Print files under Mercurial control in the working directory whose
4907 names match the given patterns.
4906 names match the given patterns.
4908
4907
4909 By default, this command searches all directories in the working
4908 By default, this command searches all directories in the working
4910 directory. To search just the current directory and its
4909 directory. To search just the current directory and its
4911 subdirectories, use "--include .".
4910 subdirectories, use "--include .".
4912
4911
4913 If no patterns are given to match, this command prints the names
4912 If no patterns are given to match, this command prints the names
4914 of all files under Mercurial control in the working directory.
4913 of all files under Mercurial control in the working directory.
4915
4914
4916 If you want to feed the output of this command into the "xargs"
4915 If you want to feed the output of this command into the "xargs"
4917 command, use the -0 option to both this command and "xargs". This
4916 command, use the -0 option to both this command and "xargs". This
4918 will avoid the problem of "xargs" treating single filenames that
4917 will avoid the problem of "xargs" treating single filenames that
4919 contain whitespace as multiple filenames.
4918 contain whitespace as multiple filenames.
4920
4919
4921 See :hg:`help files` for a more versatile command.
4920 See :hg:`help files` for a more versatile command.
4922
4921
4923 Returns 0 if a match is found, 1 otherwise.
4922 Returns 0 if a match is found, 1 otherwise.
4924 """
4923 """
4925 if opts.get('print0'):
4924 if opts.get('print0'):
4926 end = '\0'
4925 end = '\0'
4927 else:
4926 else:
4928 end = '\n'
4927 end = '\n'
4929 rev = scmutil.revsingle(repo, opts.get('rev'), None).node()
4928 rev = scmutil.revsingle(repo, opts.get('rev'), None).node()
4930
4929
4931 ret = 1
4930 ret = 1
4932 ctx = repo[rev]
4931 ctx = repo[rev]
4933 m = scmutil.match(ctx, pats, opts, default='relglob',
4932 m = scmutil.match(ctx, pats, opts, default='relglob',
4934 badfn=lambda x, y: False)
4933 badfn=lambda x, y: False)
4935
4934
4936 for abs in ctx.matches(m):
4935 for abs in ctx.matches(m):
4937 if opts.get('fullpath'):
4936 if opts.get('fullpath'):
4938 ui.write(repo.wjoin(abs), end)
4937 ui.write(repo.wjoin(abs), end)
4939 else:
4938 else:
4940 ui.write(((pats and m.rel(abs)) or abs), end)
4939 ui.write(((pats and m.rel(abs)) or abs), end)
4941 ret = 0
4940 ret = 0
4942
4941
4943 return ret
4942 return ret
4944
4943
4945 @command('^log|history',
4944 @command('^log|history',
4946 [('f', 'follow', None,
4945 [('f', 'follow', None,
4947 _('follow changeset history, or file history across copies and renames')),
4946 _('follow changeset history, or file history across copies and renames')),
4948 ('', 'follow-first', None,
4947 ('', 'follow-first', None,
4949 _('only follow the first parent of merge changesets (DEPRECATED)')),
4948 _('only follow the first parent of merge changesets (DEPRECATED)')),
4950 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
4949 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
4951 ('C', 'copies', None, _('show copied files')),
4950 ('C', 'copies', None, _('show copied files')),
4952 ('k', 'keyword', [],
4951 ('k', 'keyword', [],
4953 _('do case-insensitive search for a given text'), _('TEXT')),
4952 _('do case-insensitive search for a given text'), _('TEXT')),
4954 ('r', 'rev', [], _('show the specified revision or revset'), _('REV')),
4953 ('r', 'rev', [], _('show the specified revision or revset'), _('REV')),
4955 ('', 'removed', None, _('include revisions where files were removed')),
4954 ('', 'removed', None, _('include revisions where files were removed')),
4956 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
4955 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
4957 ('u', 'user', [], _('revisions committed by user'), _('USER')),
4956 ('u', 'user', [], _('revisions committed by user'), _('USER')),
4958 ('', 'only-branch', [],
4957 ('', 'only-branch', [],
4959 _('show only changesets within the given named branch (DEPRECATED)'),
4958 _('show only changesets within the given named branch (DEPRECATED)'),
4960 _('BRANCH')),
4959 _('BRANCH')),
4961 ('b', 'branch', [],
4960 ('b', 'branch', [],
4962 _('show changesets within the given named branch'), _('BRANCH')),
4961 _('show changesets within the given named branch'), _('BRANCH')),
4963 ('P', 'prune', [],
4962 ('P', 'prune', [],
4964 _('do not display revision or any of its ancestors'), _('REV')),
4963 _('do not display revision or any of its ancestors'), _('REV')),
4965 ] + logopts + walkopts,
4964 ] + logopts + walkopts,
4966 _('[OPTION]... [FILE]'),
4965 _('[OPTION]... [FILE]'),
4967 inferrepo=True)
4966 inferrepo=True)
4968 def log(ui, repo, *pats, **opts):
4967 def log(ui, repo, *pats, **opts):
4969 """show revision history of entire repository or files
4968 """show revision history of entire repository or files
4970
4969
4971 Print the revision history of the specified files or the entire
4970 Print the revision history of the specified files or the entire
4972 project.
4971 project.
4973
4972
4974 If no revision range is specified, the default is ``tip:0`` unless
4973 If no revision range is specified, the default is ``tip:0`` unless
4975 --follow is set, in which case the working directory parent is
4974 --follow is set, in which case the working directory parent is
4976 used as the starting revision.
4975 used as the starting revision.
4977
4976
4978 File history is shown without following rename or copy history of
4977 File history is shown without following rename or copy history of
4979 files. Use -f/--follow with a filename to follow history across
4978 files. Use -f/--follow with a filename to follow history across
4980 renames and copies. --follow without a filename will only show
4979 renames and copies. --follow without a filename will only show
4981 ancestors or descendants of the starting revision.
4980 ancestors or descendants of the starting revision.
4982
4981
4983 By default this command prints revision number and changeset id,
4982 By default this command prints revision number and changeset id,
4984 tags, non-trivial parents, user, date and time, and a summary for
4983 tags, non-trivial parents, user, date and time, and a summary for
4985 each commit. When the -v/--verbose switch is used, the list of
4984 each commit. When the -v/--verbose switch is used, the list of
4986 changed files and full commit message are shown.
4985 changed files and full commit message are shown.
4987
4986
4988 With --graph the revisions are shown as an ASCII art DAG with the most
4987 With --graph the revisions are shown as an ASCII art DAG with the most
4989 recent changeset at the top.
4988 recent changeset at the top.
4990 'o' is a changeset, '@' is a working directory parent, 'x' is obsolete,
4989 'o' is a changeset, '@' is a working directory parent, 'x' is obsolete,
4991 and '+' represents a fork where the changeset from the lines below is a
4990 and '+' represents a fork where the changeset from the lines below is a
4992 parent of the 'o' merge on the same line.
4991 parent of the 'o' merge on the same line.
4993
4992
4994 .. note::
4993 .. note::
4995
4994
4996 :hg:`log --patch` may generate unexpected diff output for merge
4995 :hg:`log --patch` may generate unexpected diff output for merge
4997 changesets, as it will only compare the merge changeset against
4996 changesets, as it will only compare the merge changeset against
4998 its first parent. Also, only files different from BOTH parents
4997 its first parent. Also, only files different from BOTH parents
4999 will appear in files:.
4998 will appear in files:.
5000
4999
5001 .. note::
5000 .. note::
5002
5001
5003 For performance reasons, :hg:`log FILE` may omit duplicate changes
5002 For performance reasons, :hg:`log FILE` may omit duplicate changes
5004 made on branches and will not show removals or mode changes. To
5003 made on branches and will not show removals or mode changes. To
5005 see all such changes, use the --removed switch.
5004 see all such changes, use the --removed switch.
5006
5005
5007 .. container:: verbose
5006 .. container:: verbose
5008
5007
5009 Some examples:
5008 Some examples:
5010
5009
5011 - changesets with full descriptions and file lists::
5010 - changesets with full descriptions and file lists::
5012
5011
5013 hg log -v
5012 hg log -v
5014
5013
5015 - changesets ancestral to the working directory::
5014 - changesets ancestral to the working directory::
5016
5015
5017 hg log -f
5016 hg log -f
5018
5017
5019 - last 10 commits on the current branch::
5018 - last 10 commits on the current branch::
5020
5019
5021 hg log -l 10 -b .
5020 hg log -l 10 -b .
5022
5021
5023 - changesets showing all modifications of a file, including removals::
5022 - changesets showing all modifications of a file, including removals::
5024
5023
5025 hg log --removed file.c
5024 hg log --removed file.c
5026
5025
5027 - all changesets that touch a directory, with diffs, excluding merges::
5026 - all changesets that touch a directory, with diffs, excluding merges::
5028
5027
5029 hg log -Mp lib/
5028 hg log -Mp lib/
5030
5029
5031 - all revision numbers that match a keyword::
5030 - all revision numbers that match a keyword::
5032
5031
5033 hg log -k bug --template "{rev}\\n"
5032 hg log -k bug --template "{rev}\\n"
5034
5033
5035 - the full hash identifier of the working directory parent::
5034 - the full hash identifier of the working directory parent::
5036
5035
5037 hg log -r . --template "{node}\\n"
5036 hg log -r . --template "{node}\\n"
5038
5037
5039 - list available log templates::
5038 - list available log templates::
5040
5039
5041 hg log -T list
5040 hg log -T list
5042
5041
5043 - check if a given changeset is included in a tagged release::
5042 - check if a given changeset is included in a tagged release::
5044
5043
5045 hg log -r "a21ccf and ancestor(1.9)"
5044 hg log -r "a21ccf and ancestor(1.9)"
5046
5045
5047 - find all changesets by some user in a date range::
5046 - find all changesets by some user in a date range::
5048
5047
5049 hg log -k alice -d "may 2008 to jul 2008"
5048 hg log -k alice -d "may 2008 to jul 2008"
5050
5049
5051 - summary of all changesets after the last tag::
5050 - summary of all changesets after the last tag::
5052
5051
5053 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
5052 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
5054
5053
5055 See :hg:`help dates` for a list of formats valid for -d/--date.
5054 See :hg:`help dates` for a list of formats valid for -d/--date.
5056
5055
5057 See :hg:`help revisions` and :hg:`help revsets` for more about
5056 See :hg:`help revisions` and :hg:`help revsets` for more about
5058 specifying and ordering revisions.
5057 specifying and ordering revisions.
5059
5058
5060 See :hg:`help templates` for more about pre-packaged styles and
5059 See :hg:`help templates` for more about pre-packaged styles and
5061 specifying custom templates.
5060 specifying custom templates.
5062
5061
5063 Returns 0 on success.
5062 Returns 0 on success.
5064
5063
5065 """
5064 """
5066 if opts.get('follow') and opts.get('rev'):
5065 if opts.get('follow') and opts.get('rev'):
5067 opts['rev'] = [revset.formatspec('reverse(::%lr)', opts.get('rev'))]
5066 opts['rev'] = [revset.formatspec('reverse(::%lr)', opts.get('rev'))]
5068 del opts['follow']
5067 del opts['follow']
5069
5068
5070 if opts.get('graph'):
5069 if opts.get('graph'):
5071 return cmdutil.graphlog(ui, repo, *pats, **opts)
5070 return cmdutil.graphlog(ui, repo, *pats, **opts)
5072
5071
5073 revs, expr, filematcher = cmdutil.getlogrevs(repo, pats, opts)
5072 revs, expr, filematcher = cmdutil.getlogrevs(repo, pats, opts)
5074 limit = cmdutil.loglimit(opts)
5073 limit = cmdutil.loglimit(opts)
5075 count = 0
5074 count = 0
5076
5075
5077 getrenamed = None
5076 getrenamed = None
5078 if opts.get('copies'):
5077 if opts.get('copies'):
5079 endrev = None
5078 endrev = None
5080 if opts.get('rev'):
5079 if opts.get('rev'):
5081 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
5080 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
5082 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
5081 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
5083
5082
5084 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
5083 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
5085 for rev in revs:
5084 for rev in revs:
5086 if count == limit:
5085 if count == limit:
5087 break
5086 break
5088 ctx = repo[rev]
5087 ctx = repo[rev]
5089 copies = None
5088 copies = None
5090 if getrenamed is not None and rev:
5089 if getrenamed is not None and rev:
5091 copies = []
5090 copies = []
5092 for fn in ctx.files():
5091 for fn in ctx.files():
5093 rename = getrenamed(fn, rev)
5092 rename = getrenamed(fn, rev)
5094 if rename:
5093 if rename:
5095 copies.append((fn, rename[0]))
5094 copies.append((fn, rename[0]))
5096 if filematcher:
5095 if filematcher:
5097 revmatchfn = filematcher(ctx.rev())
5096 revmatchfn = filematcher(ctx.rev())
5098 else:
5097 else:
5099 revmatchfn = None
5098 revmatchfn = None
5100 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
5099 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
5101 if displayer.flush(ctx):
5100 if displayer.flush(ctx):
5102 count += 1
5101 count += 1
5103
5102
5104 displayer.close()
5103 displayer.close()
5105
5104
5106 @command('manifest',
5105 @command('manifest',
5107 [('r', 'rev', '', _('revision to display'), _('REV')),
5106 [('r', 'rev', '', _('revision to display'), _('REV')),
5108 ('', 'all', False, _("list files from all revisions"))]
5107 ('', 'all', False, _("list files from all revisions"))]
5109 + formatteropts,
5108 + formatteropts,
5110 _('[-r REV]'))
5109 _('[-r REV]'))
5111 def manifest(ui, repo, node=None, rev=None, **opts):
5110 def manifest(ui, repo, node=None, rev=None, **opts):
5112 """output the current or given revision of the project manifest
5111 """output the current or given revision of the project manifest
5113
5112
5114 Print a list of version controlled files for the given revision.
5113 Print a list of version controlled files for the given revision.
5115 If no revision is given, the first parent of the working directory
5114 If no revision is given, the first parent of the working directory
5116 is used, or the null revision if no revision is checked out.
5115 is used, or the null revision if no revision is checked out.
5117
5116
5118 With -v, print file permissions, symlink and executable bits.
5117 With -v, print file permissions, symlink and executable bits.
5119 With --debug, print file revision hashes.
5118 With --debug, print file revision hashes.
5120
5119
5121 If option --all is specified, the list of all files from all revisions
5120 If option --all is specified, the list of all files from all revisions
5122 is printed. This includes deleted and renamed files.
5121 is printed. This includes deleted and renamed files.
5123
5122
5124 Returns 0 on success.
5123 Returns 0 on success.
5125 """
5124 """
5126
5125
5127 fm = ui.formatter('manifest', opts)
5126 fm = ui.formatter('manifest', opts)
5128
5127
5129 if opts.get('all'):
5128 if opts.get('all'):
5130 if rev or node:
5129 if rev or node:
5131 raise error.Abort(_("can't specify a revision with --all"))
5130 raise error.Abort(_("can't specify a revision with --all"))
5132
5131
5133 res = []
5132 res = []
5134 prefix = "data/"
5133 prefix = "data/"
5135 suffix = ".i"
5134 suffix = ".i"
5136 plen = len(prefix)
5135 plen = len(prefix)
5137 slen = len(suffix)
5136 slen = len(suffix)
5138 with repo.lock():
5137 with repo.lock():
5139 for fn, b, size in repo.store.datafiles():
5138 for fn, b, size in repo.store.datafiles():
5140 if size != 0 and fn[-slen:] == suffix and fn[:plen] == prefix:
5139 if size != 0 and fn[-slen:] == suffix and fn[:plen] == prefix:
5141 res.append(fn[plen:-slen])
5140 res.append(fn[plen:-slen])
5142 for f in res:
5141 for f in res:
5143 fm.startitem()
5142 fm.startitem()
5144 fm.write("path", '%s\n', f)
5143 fm.write("path", '%s\n', f)
5145 fm.end()
5144 fm.end()
5146 return
5145 return
5147
5146
5148 if rev and node:
5147 if rev and node:
5149 raise error.Abort(_("please specify just one revision"))
5148 raise error.Abort(_("please specify just one revision"))
5150
5149
5151 if not node:
5150 if not node:
5152 node = rev
5151 node = rev
5153
5152
5154 char = {'l': '@', 'x': '*', '': ''}
5153 char = {'l': '@', 'x': '*', '': ''}
5155 mode = {'l': '644', 'x': '755', '': '644'}
5154 mode = {'l': '644', 'x': '755', '': '644'}
5156 ctx = scmutil.revsingle(repo, node)
5155 ctx = scmutil.revsingle(repo, node)
5157 mf = ctx.manifest()
5156 mf = ctx.manifest()
5158 for f in ctx:
5157 for f in ctx:
5159 fm.startitem()
5158 fm.startitem()
5160 fl = ctx[f].flags()
5159 fl = ctx[f].flags()
5161 fm.condwrite(ui.debugflag, 'hash', '%s ', hex(mf[f]))
5160 fm.condwrite(ui.debugflag, 'hash', '%s ', hex(mf[f]))
5162 fm.condwrite(ui.verbose, 'mode type', '%s %1s ', mode[fl], char[fl])
5161 fm.condwrite(ui.verbose, 'mode type', '%s %1s ', mode[fl], char[fl])
5163 fm.write('path', '%s\n', f)
5162 fm.write('path', '%s\n', f)
5164 fm.end()
5163 fm.end()
5165
5164
5166 @command('^merge',
5165 @command('^merge',
5167 [('f', 'force', None,
5166 [('f', 'force', None,
5168 _('force a merge including outstanding changes (DEPRECATED)')),
5167 _('force a merge including outstanding changes (DEPRECATED)')),
5169 ('r', 'rev', '', _('revision to merge'), _('REV')),
5168 ('r', 'rev', '', _('revision to merge'), _('REV')),
5170 ('P', 'preview', None,
5169 ('P', 'preview', None,
5171 _('review revisions to merge (no merge is performed)'))
5170 _('review revisions to merge (no merge is performed)'))
5172 ] + mergetoolopts,
5171 ] + mergetoolopts,
5173 _('[-P] [[-r] REV]'))
5172 _('[-P] [[-r] REV]'))
5174 def merge(ui, repo, node=None, **opts):
5173 def merge(ui, repo, node=None, **opts):
5175 """merge another revision into working directory
5174 """merge another revision into working directory
5176
5175
5177 The current working directory is updated with all changes made in
5176 The current working directory is updated with all changes made in
5178 the requested revision since the last common predecessor revision.
5177 the requested revision since the last common predecessor revision.
5179
5178
5180 Files that changed between either parent are marked as changed for
5179 Files that changed between either parent are marked as changed for
5181 the next commit and a commit must be performed before any further
5180 the next commit and a commit must be performed before any further
5182 updates to the repository are allowed. The next commit will have
5181 updates to the repository are allowed. The next commit will have
5183 two parents.
5182 two parents.
5184
5183
5185 ``--tool`` can be used to specify the merge tool used for file
5184 ``--tool`` can be used to specify the merge tool used for file
5186 merges. It overrides the HGMERGE environment variable and your
5185 merges. It overrides the HGMERGE environment variable and your
5187 configuration files. See :hg:`help merge-tools` for options.
5186 configuration files. See :hg:`help merge-tools` for options.
5188
5187
5189 If no revision is specified, the working directory's parent is a
5188 If no revision is specified, the working directory's parent is a
5190 head revision, and the current branch contains exactly one other
5189 head revision, and the current branch contains exactly one other
5191 head, the other head is merged with by default. Otherwise, an
5190 head, the other head is merged with by default. Otherwise, an
5192 explicit revision with which to merge with must be provided.
5191 explicit revision with which to merge with must be provided.
5193
5192
5194 See :hg:`help resolve` for information on handling file conflicts.
5193 See :hg:`help resolve` for information on handling file conflicts.
5195
5194
5196 To undo an uncommitted merge, use :hg:`update --clean .` which
5195 To undo an uncommitted merge, use :hg:`update --clean .` which
5197 will check out a clean copy of the original merge parent, losing
5196 will check out a clean copy of the original merge parent, losing
5198 all changes.
5197 all changes.
5199
5198
5200 Returns 0 on success, 1 if there are unresolved files.
5199 Returns 0 on success, 1 if there are unresolved files.
5201 """
5200 """
5202
5201
5203 if opts.get('rev') and node:
5202 if opts.get('rev') and node:
5204 raise error.Abort(_("please specify just one revision"))
5203 raise error.Abort(_("please specify just one revision"))
5205 if not node:
5204 if not node:
5206 node = opts.get('rev')
5205 node = opts.get('rev')
5207
5206
5208 if node:
5207 if node:
5209 node = scmutil.revsingle(repo, node).node()
5208 node = scmutil.revsingle(repo, node).node()
5210
5209
5211 if not node:
5210 if not node:
5212 node = repo[destutil.destmerge(repo)].node()
5211 node = repo[destutil.destmerge(repo)].node()
5213
5212
5214 if opts.get('preview'):
5213 if opts.get('preview'):
5215 # find nodes that are ancestors of p2 but not of p1
5214 # find nodes that are ancestors of p2 but not of p1
5216 p1 = repo.lookup('.')
5215 p1 = repo.lookup('.')
5217 p2 = repo.lookup(node)
5216 p2 = repo.lookup(node)
5218 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
5217 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
5219
5218
5220 displayer = cmdutil.show_changeset(ui, repo, opts)
5219 displayer = cmdutil.show_changeset(ui, repo, opts)
5221 for node in nodes:
5220 for node in nodes:
5222 displayer.show(repo[node])
5221 displayer.show(repo[node])
5223 displayer.close()
5222 displayer.close()
5224 return 0
5223 return 0
5225
5224
5226 try:
5225 try:
5227 # ui.forcemerge is an internal variable, do not document
5226 # ui.forcemerge is an internal variable, do not document
5228 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''), 'merge')
5227 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''), 'merge')
5229 force = opts.get('force')
5228 force = opts.get('force')
5230 labels = ['working copy', 'merge rev']
5229 labels = ['working copy', 'merge rev']
5231 return hg.merge(repo, node, force=force, mergeforce=force,
5230 return hg.merge(repo, node, force=force, mergeforce=force,
5232 labels=labels)
5231 labels=labels)
5233 finally:
5232 finally:
5234 ui.setconfig('ui', 'forcemerge', '', 'merge')
5233 ui.setconfig('ui', 'forcemerge', '', 'merge')
5235
5234
5236 @command('outgoing|out',
5235 @command('outgoing|out',
5237 [('f', 'force', None, _('run even when the destination is unrelated')),
5236 [('f', 'force', None, _('run even when the destination is unrelated')),
5238 ('r', 'rev', [],
5237 ('r', 'rev', [],
5239 _('a changeset intended to be included in the destination'), _('REV')),
5238 _('a changeset intended to be included in the destination'), _('REV')),
5240 ('n', 'newest-first', None, _('show newest record first')),
5239 ('n', 'newest-first', None, _('show newest record first')),
5241 ('B', 'bookmarks', False, _('compare bookmarks')),
5240 ('B', 'bookmarks', False, _('compare bookmarks')),
5242 ('b', 'branch', [], _('a specific branch you would like to push'),
5241 ('b', 'branch', [], _('a specific branch you would like to push'),
5243 _('BRANCH')),
5242 _('BRANCH')),
5244 ] + logopts + remoteopts + subrepoopts,
5243 ] + logopts + remoteopts + subrepoopts,
5245 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
5244 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
5246 def outgoing(ui, repo, dest=None, **opts):
5245 def outgoing(ui, repo, dest=None, **opts):
5247 """show changesets not found in the destination
5246 """show changesets not found in the destination
5248
5247
5249 Show changesets not found in the specified destination repository
5248 Show changesets not found in the specified destination repository
5250 or the default push location. These are the changesets that would
5249 or the default push location. These are the changesets that would
5251 be pushed if a push was requested.
5250 be pushed if a push was requested.
5252
5251
5253 See pull for details of valid destination formats.
5252 See pull for details of valid destination formats.
5254
5253
5255 .. container:: verbose
5254 .. container:: verbose
5256
5255
5257 With -B/--bookmarks, the result of bookmark comparison between
5256 With -B/--bookmarks, the result of bookmark comparison between
5258 local and remote repositories is displayed. With -v/--verbose,
5257 local and remote repositories is displayed. With -v/--verbose,
5259 status is also displayed for each bookmark like below::
5258 status is also displayed for each bookmark like below::
5260
5259
5261 BM1 01234567890a added
5260 BM1 01234567890a added
5262 BM2 deleted
5261 BM2 deleted
5263 BM3 234567890abc advanced
5262 BM3 234567890abc advanced
5264 BM4 34567890abcd diverged
5263 BM4 34567890abcd diverged
5265 BM5 4567890abcde changed
5264 BM5 4567890abcde changed
5266
5265
5267 The action taken when pushing depends on the
5266 The action taken when pushing depends on the
5268 status of each bookmark:
5267 status of each bookmark:
5269
5268
5270 :``added``: push with ``-B`` will create it
5269 :``added``: push with ``-B`` will create it
5271 :``deleted``: push with ``-B`` will delete it
5270 :``deleted``: push with ``-B`` will delete it
5272 :``advanced``: push will update it
5271 :``advanced``: push will update it
5273 :``diverged``: push with ``-B`` will update it
5272 :``diverged``: push with ``-B`` will update it
5274 :``changed``: push with ``-B`` will update it
5273 :``changed``: push with ``-B`` will update it
5275
5274
5276 From the point of view of pushing behavior, bookmarks
5275 From the point of view of pushing behavior, bookmarks
5277 existing only in the remote repository are treated as
5276 existing only in the remote repository are treated as
5278 ``deleted``, even if it is in fact added remotely.
5277 ``deleted``, even if it is in fact added remotely.
5279
5278
5280 Returns 0 if there are outgoing changes, 1 otherwise.
5279 Returns 0 if there are outgoing changes, 1 otherwise.
5281 """
5280 """
5282 if opts.get('graph'):
5281 if opts.get('graph'):
5283 cmdutil.checkunsupportedgraphflags([], opts)
5282 cmdutil.checkunsupportedgraphflags([], opts)
5284 o, other = hg._outgoing(ui, repo, dest, opts)
5283 o, other = hg._outgoing(ui, repo, dest, opts)
5285 if not o:
5284 if not o:
5286 cmdutil.outgoinghooks(ui, repo, other, opts, o)
5285 cmdutil.outgoinghooks(ui, repo, other, opts, o)
5287 return
5286 return
5288
5287
5289 revdag = cmdutil.graphrevs(repo, o, opts)
5288 revdag = cmdutil.graphrevs(repo, o, opts)
5290 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
5289 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
5291 cmdutil.displaygraph(ui, repo, revdag, displayer, graphmod.asciiedges)
5290 cmdutil.displaygraph(ui, repo, revdag, displayer, graphmod.asciiedges)
5292 cmdutil.outgoinghooks(ui, repo, other, opts, o)
5291 cmdutil.outgoinghooks(ui, repo, other, opts, o)
5293 return 0
5292 return 0
5294
5293
5295 if opts.get('bookmarks'):
5294 if opts.get('bookmarks'):
5296 dest = ui.expandpath(dest or 'default-push', dest or 'default')
5295 dest = ui.expandpath(dest or 'default-push', dest or 'default')
5297 dest, branches = hg.parseurl(dest, opts.get('branch'))
5296 dest, branches = hg.parseurl(dest, opts.get('branch'))
5298 other = hg.peer(repo, opts, dest)
5297 other = hg.peer(repo, opts, dest)
5299 if 'bookmarks' not in other.listkeys('namespaces'):
5298 if 'bookmarks' not in other.listkeys('namespaces'):
5300 ui.warn(_("remote doesn't support bookmarks\n"))
5299 ui.warn(_("remote doesn't support bookmarks\n"))
5301 return 0
5300 return 0
5302 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
5301 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
5303 return bookmarks.outgoing(ui, repo, other)
5302 return bookmarks.outgoing(ui, repo, other)
5304
5303
5305 repo._subtoppath = ui.expandpath(dest or 'default-push', dest or 'default')
5304 repo._subtoppath = ui.expandpath(dest or 'default-push', dest or 'default')
5306 try:
5305 try:
5307 return hg.outgoing(ui, repo, dest, opts)
5306 return hg.outgoing(ui, repo, dest, opts)
5308 finally:
5307 finally:
5309 del repo._subtoppath
5308 del repo._subtoppath
5310
5309
5311 @command('parents',
5310 @command('parents',
5312 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
5311 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
5313 ] + templateopts,
5312 ] + templateopts,
5314 _('[-r REV] [FILE]'),
5313 _('[-r REV] [FILE]'),
5315 inferrepo=True)
5314 inferrepo=True)
5316 def parents(ui, repo, file_=None, **opts):
5315 def parents(ui, repo, file_=None, **opts):
5317 """show the parents of the working directory or revision (DEPRECATED)
5316 """show the parents of the working directory or revision (DEPRECATED)
5318
5317
5319 Print the working directory's parent revisions. If a revision is
5318 Print the working directory's parent revisions. If a revision is
5320 given via -r/--rev, the parent of that revision will be printed.
5319 given via -r/--rev, the parent of that revision will be printed.
5321 If a file argument is given, the revision in which the file was
5320 If a file argument is given, the revision in which the file was
5322 last changed (before the working directory revision or the
5321 last changed (before the working directory revision or the
5323 argument to --rev if given) is printed.
5322 argument to --rev if given) is printed.
5324
5323
5325 This command is equivalent to::
5324 This command is equivalent to::
5326
5325
5327 hg log -r "p1()+p2()" or
5326 hg log -r "p1()+p2()" or
5328 hg log -r "p1(REV)+p2(REV)" or
5327 hg log -r "p1(REV)+p2(REV)" or
5329 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
5328 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
5330 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
5329 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
5331
5330
5332 See :hg:`summary` and :hg:`help revsets` for related information.
5331 See :hg:`summary` and :hg:`help revsets` for related information.
5333
5332
5334 Returns 0 on success.
5333 Returns 0 on success.
5335 """
5334 """
5336
5335
5337 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
5336 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
5338
5337
5339 if file_:
5338 if file_:
5340 m = scmutil.match(ctx, (file_,), opts)
5339 m = scmutil.match(ctx, (file_,), opts)
5341 if m.anypats() or len(m.files()) != 1:
5340 if m.anypats() or len(m.files()) != 1:
5342 raise error.Abort(_('can only specify an explicit filename'))
5341 raise error.Abort(_('can only specify an explicit filename'))
5343 file_ = m.files()[0]
5342 file_ = m.files()[0]
5344 filenodes = []
5343 filenodes = []
5345 for cp in ctx.parents():
5344 for cp in ctx.parents():
5346 if not cp:
5345 if not cp:
5347 continue
5346 continue
5348 try:
5347 try:
5349 filenodes.append(cp.filenode(file_))
5348 filenodes.append(cp.filenode(file_))
5350 except error.LookupError:
5349 except error.LookupError:
5351 pass
5350 pass
5352 if not filenodes:
5351 if not filenodes:
5353 raise error.Abort(_("'%s' not found in manifest!") % file_)
5352 raise error.Abort(_("'%s' not found in manifest!") % file_)
5354 p = []
5353 p = []
5355 for fn in filenodes:
5354 for fn in filenodes:
5356 fctx = repo.filectx(file_, fileid=fn)
5355 fctx = repo.filectx(file_, fileid=fn)
5357 p.append(fctx.node())
5356 p.append(fctx.node())
5358 else:
5357 else:
5359 p = [cp.node() for cp in ctx.parents()]
5358 p = [cp.node() for cp in ctx.parents()]
5360
5359
5361 displayer = cmdutil.show_changeset(ui, repo, opts)
5360 displayer = cmdutil.show_changeset(ui, repo, opts)
5362 for n in p:
5361 for n in p:
5363 if n != nullid:
5362 if n != nullid:
5364 displayer.show(repo[n])
5363 displayer.show(repo[n])
5365 displayer.close()
5364 displayer.close()
5366
5365
5367 @command('paths', formatteropts, _('[NAME]'), optionalrepo=True)
5366 @command('paths', formatteropts, _('[NAME]'), optionalrepo=True)
5368 def paths(ui, repo, search=None, **opts):
5367 def paths(ui, repo, search=None, **opts):
5369 """show aliases for remote repositories
5368 """show aliases for remote repositories
5370
5369
5371 Show definition of symbolic path name NAME. If no name is given,
5370 Show definition of symbolic path name NAME. If no name is given,
5372 show definition of all available names.
5371 show definition of all available names.
5373
5372
5374 Option -q/--quiet suppresses all output when searching for NAME
5373 Option -q/--quiet suppresses all output when searching for NAME
5375 and shows only the path names when listing all definitions.
5374 and shows only the path names when listing all definitions.
5376
5375
5377 Path names are defined in the [paths] section of your
5376 Path names are defined in the [paths] section of your
5378 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
5377 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
5379 repository, ``.hg/hgrc`` is used, too.
5378 repository, ``.hg/hgrc`` is used, too.
5380
5379
5381 The path names ``default`` and ``default-push`` have a special
5380 The path names ``default`` and ``default-push`` have a special
5382 meaning. When performing a push or pull operation, they are used
5381 meaning. When performing a push or pull operation, they are used
5383 as fallbacks if no location is specified on the command-line.
5382 as fallbacks if no location is specified on the command-line.
5384 When ``default-push`` is set, it will be used for push and
5383 When ``default-push`` is set, it will be used for push and
5385 ``default`` will be used for pull; otherwise ``default`` is used
5384 ``default`` will be used for pull; otherwise ``default`` is used
5386 as the fallback for both. When cloning a repository, the clone
5385 as the fallback for both. When cloning a repository, the clone
5387 source is written as ``default`` in ``.hg/hgrc``.
5386 source is written as ``default`` in ``.hg/hgrc``.
5388
5387
5389 .. note::
5388 .. note::
5390
5389
5391 ``default`` and ``default-push`` apply to all inbound (e.g.
5390 ``default`` and ``default-push`` apply to all inbound (e.g.
5392 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
5391 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
5393 and :hg:`bundle`) operations.
5392 and :hg:`bundle`) operations.
5394
5393
5395 See :hg:`help urls` for more information.
5394 See :hg:`help urls` for more information.
5396
5395
5397 Returns 0 on success.
5396 Returns 0 on success.
5398 """
5397 """
5399 if search:
5398 if search:
5400 pathitems = [(name, path) for name, path in ui.paths.iteritems()
5399 pathitems = [(name, path) for name, path in ui.paths.iteritems()
5401 if name == search]
5400 if name == search]
5402 else:
5401 else:
5403 pathitems = sorted(ui.paths.iteritems())
5402 pathitems = sorted(ui.paths.iteritems())
5404
5403
5405 fm = ui.formatter('paths', opts)
5404 fm = ui.formatter('paths', opts)
5406 if fm.isplain():
5405 if fm.isplain():
5407 hidepassword = util.hidepassword
5406 hidepassword = util.hidepassword
5408 else:
5407 else:
5409 hidepassword = str
5408 hidepassword = str
5410 if ui.quiet:
5409 if ui.quiet:
5411 namefmt = '%s\n'
5410 namefmt = '%s\n'
5412 else:
5411 else:
5413 namefmt = '%s = '
5412 namefmt = '%s = '
5414 showsubopts = not search and not ui.quiet
5413 showsubopts = not search and not ui.quiet
5415
5414
5416 for name, path in pathitems:
5415 for name, path in pathitems:
5417 fm.startitem()
5416 fm.startitem()
5418 fm.condwrite(not search, 'name', namefmt, name)
5417 fm.condwrite(not search, 'name', namefmt, name)
5419 fm.condwrite(not ui.quiet, 'url', '%s\n', hidepassword(path.rawloc))
5418 fm.condwrite(not ui.quiet, 'url', '%s\n', hidepassword(path.rawloc))
5420 for subopt, value in sorted(path.suboptions.items()):
5419 for subopt, value in sorted(path.suboptions.items()):
5421 assert subopt not in ('name', 'url')
5420 assert subopt not in ('name', 'url')
5422 if showsubopts:
5421 if showsubopts:
5423 fm.plain('%s:%s = ' % (name, subopt))
5422 fm.plain('%s:%s = ' % (name, subopt))
5424 fm.condwrite(showsubopts, subopt, '%s\n', value)
5423 fm.condwrite(showsubopts, subopt, '%s\n', value)
5425
5424
5426 fm.end()
5425 fm.end()
5427
5426
5428 if search and not pathitems:
5427 if search and not pathitems:
5429 if not ui.quiet:
5428 if not ui.quiet:
5430 ui.warn(_("not found!\n"))
5429 ui.warn(_("not found!\n"))
5431 return 1
5430 return 1
5432 else:
5431 else:
5433 return 0
5432 return 0
5434
5433
5435 @command('phase',
5434 @command('phase',
5436 [('p', 'public', False, _('set changeset phase to public')),
5435 [('p', 'public', False, _('set changeset phase to public')),
5437 ('d', 'draft', False, _('set changeset phase to draft')),
5436 ('d', 'draft', False, _('set changeset phase to draft')),
5438 ('s', 'secret', False, _('set changeset phase to secret')),
5437 ('s', 'secret', False, _('set changeset phase to secret')),
5439 ('f', 'force', False, _('allow to move boundary backward')),
5438 ('f', 'force', False, _('allow to move boundary backward')),
5440 ('r', 'rev', [], _('target revision'), _('REV')),
5439 ('r', 'rev', [], _('target revision'), _('REV')),
5441 ],
5440 ],
5442 _('[-p|-d|-s] [-f] [-r] [REV...]'))
5441 _('[-p|-d|-s] [-f] [-r] [REV...]'))
5443 def phase(ui, repo, *revs, **opts):
5442 def phase(ui, repo, *revs, **opts):
5444 """set or show the current phase name
5443 """set or show the current phase name
5445
5444
5446 With no argument, show the phase name of the current revision(s).
5445 With no argument, show the phase name of the current revision(s).
5447
5446
5448 With one of -p/--public, -d/--draft or -s/--secret, change the
5447 With one of -p/--public, -d/--draft or -s/--secret, change the
5449 phase value of the specified revisions.
5448 phase value of the specified revisions.
5450
5449
5451 Unless -f/--force is specified, :hg:`phase` won't move changeset from a
5450 Unless -f/--force is specified, :hg:`phase` won't move changeset from a
5452 lower phase to an higher phase. Phases are ordered as follows::
5451 lower phase to an higher phase. Phases are ordered as follows::
5453
5452
5454 public < draft < secret
5453 public < draft < secret
5455
5454
5456 Returns 0 on success, 1 if some phases could not be changed.
5455 Returns 0 on success, 1 if some phases could not be changed.
5457
5456
5458 (For more information about the phases concept, see :hg:`help phases`.)
5457 (For more information about the phases concept, see :hg:`help phases`.)
5459 """
5458 """
5460 # search for a unique phase argument
5459 # search for a unique phase argument
5461 targetphase = None
5460 targetphase = None
5462 for idx, name in enumerate(phases.phasenames):
5461 for idx, name in enumerate(phases.phasenames):
5463 if opts[name]:
5462 if opts[name]:
5464 if targetphase is not None:
5463 if targetphase is not None:
5465 raise error.Abort(_('only one phase can be specified'))
5464 raise error.Abort(_('only one phase can be specified'))
5466 targetphase = idx
5465 targetphase = idx
5467
5466
5468 # look for specified revision
5467 # look for specified revision
5469 revs = list(revs)
5468 revs = list(revs)
5470 revs.extend(opts['rev'])
5469 revs.extend(opts['rev'])
5471 if not revs:
5470 if not revs:
5472 # display both parents as the second parent phase can influence
5471 # display both parents as the second parent phase can influence
5473 # the phase of a merge commit
5472 # the phase of a merge commit
5474 revs = [c.rev() for c in repo[None].parents()]
5473 revs = [c.rev() for c in repo[None].parents()]
5475
5474
5476 revs = scmutil.revrange(repo, revs)
5475 revs = scmutil.revrange(repo, revs)
5477
5476
5478 lock = None
5477 lock = None
5479 ret = 0
5478 ret = 0
5480 if targetphase is None:
5479 if targetphase is None:
5481 # display
5480 # display
5482 for r in revs:
5481 for r in revs:
5483 ctx = repo[r]
5482 ctx = repo[r]
5484 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
5483 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
5485 else:
5484 else:
5486 tr = None
5485 tr = None
5487 lock = repo.lock()
5486 lock = repo.lock()
5488 try:
5487 try:
5489 tr = repo.transaction("phase")
5488 tr = repo.transaction("phase")
5490 # set phase
5489 # set phase
5491 if not revs:
5490 if not revs:
5492 raise error.Abort(_('empty revision set'))
5491 raise error.Abort(_('empty revision set'))
5493 nodes = [repo[r].node() for r in revs]
5492 nodes = [repo[r].node() for r in revs]
5494 # moving revision from public to draft may hide them
5493 # moving revision from public to draft may hide them
5495 # We have to check result on an unfiltered repository
5494 # We have to check result on an unfiltered repository
5496 unfi = repo.unfiltered()
5495 unfi = repo.unfiltered()
5497 getphase = unfi._phasecache.phase
5496 getphase = unfi._phasecache.phase
5498 olddata = [getphase(unfi, r) for r in unfi]
5497 olddata = [getphase(unfi, r) for r in unfi]
5499 phases.advanceboundary(repo, tr, targetphase, nodes)
5498 phases.advanceboundary(repo, tr, targetphase, nodes)
5500 if opts['force']:
5499 if opts['force']:
5501 phases.retractboundary(repo, tr, targetphase, nodes)
5500 phases.retractboundary(repo, tr, targetphase, nodes)
5502 tr.close()
5501 tr.close()
5503 finally:
5502 finally:
5504 if tr is not None:
5503 if tr is not None:
5505 tr.release()
5504 tr.release()
5506 lock.release()
5505 lock.release()
5507 getphase = unfi._phasecache.phase
5506 getphase = unfi._phasecache.phase
5508 newdata = [getphase(unfi, r) for r in unfi]
5507 newdata = [getphase(unfi, r) for r in unfi]
5509 changes = sum(newdata[r] != olddata[r] for r in unfi)
5508 changes = sum(newdata[r] != olddata[r] for r in unfi)
5510 cl = unfi.changelog
5509 cl = unfi.changelog
5511 rejected = [n for n in nodes
5510 rejected = [n for n in nodes
5512 if newdata[cl.rev(n)] < targetphase]
5511 if newdata[cl.rev(n)] < targetphase]
5513 if rejected:
5512 if rejected:
5514 ui.warn(_('cannot move %i changesets to a higher '
5513 ui.warn(_('cannot move %i changesets to a higher '
5515 'phase, use --force\n') % len(rejected))
5514 'phase, use --force\n') % len(rejected))
5516 ret = 1
5515 ret = 1
5517 if changes:
5516 if changes:
5518 msg = _('phase changed for %i changesets\n') % changes
5517 msg = _('phase changed for %i changesets\n') % changes
5519 if ret:
5518 if ret:
5520 ui.status(msg)
5519 ui.status(msg)
5521 else:
5520 else:
5522 ui.note(msg)
5521 ui.note(msg)
5523 else:
5522 else:
5524 ui.warn(_('no phases changed\n'))
5523 ui.warn(_('no phases changed\n'))
5525 return ret
5524 return ret
5526
5525
5527 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
5526 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
5528 """Run after a changegroup has been added via pull/unbundle
5527 """Run after a changegroup has been added via pull/unbundle
5529
5528
5530 This takes arguments below:
5529 This takes arguments below:
5531
5530
5532 :modheads: change of heads by pull/unbundle
5531 :modheads: change of heads by pull/unbundle
5533 :optupdate: updating working directory is needed or not
5532 :optupdate: updating working directory is needed or not
5534 :checkout: update destination revision (or None to default destination)
5533 :checkout: update destination revision (or None to default destination)
5535 :brev: a name, which might be a bookmark to be activated after updating
5534 :brev: a name, which might be a bookmark to be activated after updating
5536 """
5535 """
5537 if modheads == 0:
5536 if modheads == 0:
5538 return
5537 return
5539 if optupdate:
5538 if optupdate:
5540 try:
5539 try:
5541 return hg.updatetotally(ui, repo, checkout, brev)
5540 return hg.updatetotally(ui, repo, checkout, brev)
5542 except error.UpdateAbort as inst:
5541 except error.UpdateAbort as inst:
5543 msg = _("not updating: %s") % str(inst)
5542 msg = _("not updating: %s") % str(inst)
5544 hint = inst.hint
5543 hint = inst.hint
5545 raise error.UpdateAbort(msg, hint=hint)
5544 raise error.UpdateAbort(msg, hint=hint)
5546 if modheads > 1:
5545 if modheads > 1:
5547 currentbranchheads = len(repo.branchheads())
5546 currentbranchheads = len(repo.branchheads())
5548 if currentbranchheads == modheads:
5547 if currentbranchheads == modheads:
5549 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
5548 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
5550 elif currentbranchheads > 1:
5549 elif currentbranchheads > 1:
5551 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to "
5550 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to "
5552 "merge)\n"))
5551 "merge)\n"))
5553 else:
5552 else:
5554 ui.status(_("(run 'hg heads' to see heads)\n"))
5553 ui.status(_("(run 'hg heads' to see heads)\n"))
5555 else:
5554 else:
5556 ui.status(_("(run 'hg update' to get a working copy)\n"))
5555 ui.status(_("(run 'hg update' to get a working copy)\n"))
5557
5556
5558 @command('^pull',
5557 @command('^pull',
5559 [('u', 'update', None,
5558 [('u', 'update', None,
5560 _('update to new branch head if changesets were pulled')),
5559 _('update to new branch head if changesets were pulled')),
5561 ('f', 'force', None, _('run even when remote repository is unrelated')),
5560 ('f', 'force', None, _('run even when remote repository is unrelated')),
5562 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
5561 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
5563 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
5562 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
5564 ('b', 'branch', [], _('a specific branch you would like to pull'),
5563 ('b', 'branch', [], _('a specific branch you would like to pull'),
5565 _('BRANCH')),
5564 _('BRANCH')),
5566 ] + remoteopts,
5565 ] + remoteopts,
5567 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
5566 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
5568 def pull(ui, repo, source="default", **opts):
5567 def pull(ui, repo, source="default", **opts):
5569 """pull changes from the specified source
5568 """pull changes from the specified source
5570
5569
5571 Pull changes from a remote repository to a local one.
5570 Pull changes from a remote repository to a local one.
5572
5571
5573 This finds all changes from the repository at the specified path
5572 This finds all changes from the repository at the specified path
5574 or URL and adds them to a local repository (the current one unless
5573 or URL and adds them to a local repository (the current one unless
5575 -R is specified). By default, this does not update the copy of the
5574 -R is specified). By default, this does not update the copy of the
5576 project in the working directory.
5575 project in the working directory.
5577
5576
5578 Use :hg:`incoming` if you want to see what would have been added
5577 Use :hg:`incoming` if you want to see what would have been added
5579 by a pull at the time you issued this command. If you then decide
5578 by a pull at the time you issued this command. If you then decide
5580 to add those changes to the repository, you should use :hg:`pull
5579 to add those changes to the repository, you should use :hg:`pull
5581 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
5580 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
5582
5581
5583 If SOURCE is omitted, the 'default' path will be used.
5582 If SOURCE is omitted, the 'default' path will be used.
5584 See :hg:`help urls` for more information.
5583 See :hg:`help urls` for more information.
5585
5584
5586 Specifying bookmark as ``.`` is equivalent to specifying the active
5585 Specifying bookmark as ``.`` is equivalent to specifying the active
5587 bookmark's name.
5586 bookmark's name.
5588
5587
5589 Returns 0 on success, 1 if an update had unresolved files.
5588 Returns 0 on success, 1 if an update had unresolved files.
5590 """
5589 """
5591 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
5590 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
5592 ui.status(_('pulling from %s\n') % util.hidepassword(source))
5591 ui.status(_('pulling from %s\n') % util.hidepassword(source))
5593 other = hg.peer(repo, opts, source)
5592 other = hg.peer(repo, opts, source)
5594 try:
5593 try:
5595 revs, checkout = hg.addbranchrevs(repo, other, branches,
5594 revs, checkout = hg.addbranchrevs(repo, other, branches,
5596 opts.get('rev'))
5595 opts.get('rev'))
5597
5596
5598
5597
5599 pullopargs = {}
5598 pullopargs = {}
5600 if opts.get('bookmark'):
5599 if opts.get('bookmark'):
5601 if not revs:
5600 if not revs:
5602 revs = []
5601 revs = []
5603 # The list of bookmark used here is not the one used to actually
5602 # The list of bookmark used here is not the one used to actually
5604 # update the bookmark name. This can result in the revision pulled
5603 # update the bookmark name. This can result in the revision pulled
5605 # not ending up with the name of the bookmark because of a race
5604 # not ending up with the name of the bookmark because of a race
5606 # condition on the server. (See issue 4689 for details)
5605 # condition on the server. (See issue 4689 for details)
5607 remotebookmarks = other.listkeys('bookmarks')
5606 remotebookmarks = other.listkeys('bookmarks')
5608 pullopargs['remotebookmarks'] = remotebookmarks
5607 pullopargs['remotebookmarks'] = remotebookmarks
5609 for b in opts['bookmark']:
5608 for b in opts['bookmark']:
5610 b = repo._bookmarks.expandname(b)
5609 b = repo._bookmarks.expandname(b)
5611 if b not in remotebookmarks:
5610 if b not in remotebookmarks:
5612 raise error.Abort(_('remote bookmark %s not found!') % b)
5611 raise error.Abort(_('remote bookmark %s not found!') % b)
5613 revs.append(remotebookmarks[b])
5612 revs.append(remotebookmarks[b])
5614
5613
5615 if revs:
5614 if revs:
5616 try:
5615 try:
5617 # When 'rev' is a bookmark name, we cannot guarantee that it
5616 # When 'rev' is a bookmark name, we cannot guarantee that it
5618 # will be updated with that name because of a race condition
5617 # will be updated with that name because of a race condition
5619 # server side. (See issue 4689 for details)
5618 # server side. (See issue 4689 for details)
5620 oldrevs = revs
5619 oldrevs = revs
5621 revs = [] # actually, nodes
5620 revs = [] # actually, nodes
5622 for r in oldrevs:
5621 for r in oldrevs:
5623 node = other.lookup(r)
5622 node = other.lookup(r)
5624 revs.append(node)
5623 revs.append(node)
5625 if r == checkout:
5624 if r == checkout:
5626 checkout = node
5625 checkout = node
5627 except error.CapabilityError:
5626 except error.CapabilityError:
5628 err = _("other repository doesn't support revision lookup, "
5627 err = _("other repository doesn't support revision lookup, "
5629 "so a rev cannot be specified.")
5628 "so a rev cannot be specified.")
5630 raise error.Abort(err)
5629 raise error.Abort(err)
5631
5630
5632 pullopargs.update(opts.get('opargs', {}))
5631 pullopargs.update(opts.get('opargs', {}))
5633 modheads = exchange.pull(repo, other, heads=revs,
5632 modheads = exchange.pull(repo, other, heads=revs,
5634 force=opts.get('force'),
5633 force=opts.get('force'),
5635 bookmarks=opts.get('bookmark', ()),
5634 bookmarks=opts.get('bookmark', ()),
5636 opargs=pullopargs).cgresult
5635 opargs=pullopargs).cgresult
5637
5636
5638 # brev is a name, which might be a bookmark to be activated at
5637 # brev is a name, which might be a bookmark to be activated at
5639 # the end of the update. In other words, it is an explicit
5638 # the end of the update. In other words, it is an explicit
5640 # destination of the update
5639 # destination of the update
5641 brev = None
5640 brev = None
5642
5641
5643 if checkout:
5642 if checkout:
5644 checkout = str(repo.changelog.rev(checkout))
5643 checkout = str(repo.changelog.rev(checkout))
5645
5644
5646 # order below depends on implementation of
5645 # order below depends on implementation of
5647 # hg.addbranchrevs(). opts['bookmark'] is ignored,
5646 # hg.addbranchrevs(). opts['bookmark'] is ignored,
5648 # because 'checkout' is determined without it.
5647 # because 'checkout' is determined without it.
5649 if opts.get('rev'):
5648 if opts.get('rev'):
5650 brev = opts['rev'][0]
5649 brev = opts['rev'][0]
5651 elif opts.get('branch'):
5650 elif opts.get('branch'):
5652 brev = opts['branch'][0]
5651 brev = opts['branch'][0]
5653 else:
5652 else:
5654 brev = branches[0]
5653 brev = branches[0]
5655 repo._subtoppath = source
5654 repo._subtoppath = source
5656 try:
5655 try:
5657 ret = postincoming(ui, repo, modheads, opts.get('update'),
5656 ret = postincoming(ui, repo, modheads, opts.get('update'),
5658 checkout, brev)
5657 checkout, brev)
5659
5658
5660 finally:
5659 finally:
5661 del repo._subtoppath
5660 del repo._subtoppath
5662
5661
5663 finally:
5662 finally:
5664 other.close()
5663 other.close()
5665 return ret
5664 return ret
5666
5665
5667 @command('^push',
5666 @command('^push',
5668 [('f', 'force', None, _('force push')),
5667 [('f', 'force', None, _('force push')),
5669 ('r', 'rev', [],
5668 ('r', 'rev', [],
5670 _('a changeset intended to be included in the destination'),
5669 _('a changeset intended to be included in the destination'),
5671 _('REV')),
5670 _('REV')),
5672 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
5671 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
5673 ('b', 'branch', [],
5672 ('b', 'branch', [],
5674 _('a specific branch you would like to push'), _('BRANCH')),
5673 _('a specific branch you would like to push'), _('BRANCH')),
5675 ('', 'new-branch', False, _('allow pushing a new branch')),
5674 ('', 'new-branch', False, _('allow pushing a new branch')),
5676 ] + remoteopts,
5675 ] + remoteopts,
5677 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
5676 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
5678 def push(ui, repo, dest=None, **opts):
5677 def push(ui, repo, dest=None, **opts):
5679 """push changes to the specified destination
5678 """push changes to the specified destination
5680
5679
5681 Push changesets from the local repository to the specified
5680 Push changesets from the local repository to the specified
5682 destination.
5681 destination.
5683
5682
5684 This operation is symmetrical to pull: it is identical to a pull
5683 This operation is symmetrical to pull: it is identical to a pull
5685 in the destination repository from the current one.
5684 in the destination repository from the current one.
5686
5685
5687 By default, push will not allow creation of new heads at the
5686 By default, push will not allow creation of new heads at the
5688 destination, since multiple heads would make it unclear which head
5687 destination, since multiple heads would make it unclear which head
5689 to use. In this situation, it is recommended to pull and merge
5688 to use. In this situation, it is recommended to pull and merge
5690 before pushing.
5689 before pushing.
5691
5690
5692 Use --new-branch if you want to allow push to create a new named
5691 Use --new-branch if you want to allow push to create a new named
5693 branch that is not present at the destination. This allows you to
5692 branch that is not present at the destination. This allows you to
5694 only create a new branch without forcing other changes.
5693 only create a new branch without forcing other changes.
5695
5694
5696 .. note::
5695 .. note::
5697
5696
5698 Extra care should be taken with the -f/--force option,
5697 Extra care should be taken with the -f/--force option,
5699 which will push all new heads on all branches, an action which will
5698 which will push all new heads on all branches, an action which will
5700 almost always cause confusion for collaborators.
5699 almost always cause confusion for collaborators.
5701
5700
5702 If -r/--rev is used, the specified revision and all its ancestors
5701 If -r/--rev is used, the specified revision and all its ancestors
5703 will be pushed to the remote repository.
5702 will be pushed to the remote repository.
5704
5703
5705 If -B/--bookmark is used, the specified bookmarked revision, its
5704 If -B/--bookmark is used, the specified bookmarked revision, its
5706 ancestors, and the bookmark will be pushed to the remote
5705 ancestors, and the bookmark will be pushed to the remote
5707 repository. Specifying ``.`` is equivalent to specifying the active
5706 repository. Specifying ``.`` is equivalent to specifying the active
5708 bookmark's name.
5707 bookmark's name.
5709
5708
5710 Please see :hg:`help urls` for important details about ``ssh://``
5709 Please see :hg:`help urls` for important details about ``ssh://``
5711 URLs. If DESTINATION is omitted, a default path will be used.
5710 URLs. If DESTINATION is omitted, a default path will be used.
5712
5711
5713 Returns 0 if push was successful, 1 if nothing to push.
5712 Returns 0 if push was successful, 1 if nothing to push.
5714 """
5713 """
5715
5714
5716 if opts.get('bookmark'):
5715 if opts.get('bookmark'):
5717 ui.setconfig('bookmarks', 'pushing', opts['bookmark'], 'push')
5716 ui.setconfig('bookmarks', 'pushing', opts['bookmark'], 'push')
5718 for b in opts['bookmark']:
5717 for b in opts['bookmark']:
5719 # translate -B options to -r so changesets get pushed
5718 # translate -B options to -r so changesets get pushed
5720 b = repo._bookmarks.expandname(b)
5719 b = repo._bookmarks.expandname(b)
5721 if b in repo._bookmarks:
5720 if b in repo._bookmarks:
5722 opts.setdefault('rev', []).append(b)
5721 opts.setdefault('rev', []).append(b)
5723 else:
5722 else:
5724 # if we try to push a deleted bookmark, translate it to null
5723 # if we try to push a deleted bookmark, translate it to null
5725 # this lets simultaneous -r, -b options continue working
5724 # this lets simultaneous -r, -b options continue working
5726 opts.setdefault('rev', []).append("null")
5725 opts.setdefault('rev', []).append("null")
5727
5726
5728 path = ui.paths.getpath(dest, default=('default-push', 'default'))
5727 path = ui.paths.getpath(dest, default=('default-push', 'default'))
5729 if not path:
5728 if not path:
5730 raise error.Abort(_('default repository not configured!'),
5729 raise error.Abort(_('default repository not configured!'),
5731 hint=_("see 'hg help config.paths'"))
5730 hint=_("see 'hg help config.paths'"))
5732 dest = path.pushloc or path.loc
5731 dest = path.pushloc or path.loc
5733 branches = (path.branch, opts.get('branch') or [])
5732 branches = (path.branch, opts.get('branch') or [])
5734 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
5733 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
5735 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
5734 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
5736 other = hg.peer(repo, opts, dest)
5735 other = hg.peer(repo, opts, dest)
5737
5736
5738 if revs:
5737 if revs:
5739 revs = [repo.lookup(r) for r in scmutil.revrange(repo, revs)]
5738 revs = [repo.lookup(r) for r in scmutil.revrange(repo, revs)]
5740 if not revs:
5739 if not revs:
5741 raise error.Abort(_("specified revisions evaluate to an empty set"),
5740 raise error.Abort(_("specified revisions evaluate to an empty set"),
5742 hint=_("use different revision arguments"))
5741 hint=_("use different revision arguments"))
5743 elif path.pushrev:
5742 elif path.pushrev:
5744 # It doesn't make any sense to specify ancestor revisions. So limit
5743 # It doesn't make any sense to specify ancestor revisions. So limit
5745 # to DAG heads to make discovery simpler.
5744 # to DAG heads to make discovery simpler.
5746 expr = revset.formatspec('heads(%r)', path.pushrev)
5745 expr = revset.formatspec('heads(%r)', path.pushrev)
5747 revs = scmutil.revrange(repo, [expr])
5746 revs = scmutil.revrange(repo, [expr])
5748 revs = [repo[rev].node() for rev in revs]
5747 revs = [repo[rev].node() for rev in revs]
5749 if not revs:
5748 if not revs:
5750 raise error.Abort(_('default push revset for path evaluates to an '
5749 raise error.Abort(_('default push revset for path evaluates to an '
5751 'empty set'))
5750 'empty set'))
5752
5751
5753 repo._subtoppath = dest
5752 repo._subtoppath = dest
5754 try:
5753 try:
5755 # push subrepos depth-first for coherent ordering
5754 # push subrepos depth-first for coherent ordering
5756 c = repo['']
5755 c = repo['']
5757 subs = c.substate # only repos that are committed
5756 subs = c.substate # only repos that are committed
5758 for s in sorted(subs):
5757 for s in sorted(subs):
5759 result = c.sub(s).push(opts)
5758 result = c.sub(s).push(opts)
5760 if result == 0:
5759 if result == 0:
5761 return not result
5760 return not result
5762 finally:
5761 finally:
5763 del repo._subtoppath
5762 del repo._subtoppath
5764 pushop = exchange.push(repo, other, opts.get('force'), revs=revs,
5763 pushop = exchange.push(repo, other, opts.get('force'), revs=revs,
5765 newbranch=opts.get('new_branch'),
5764 newbranch=opts.get('new_branch'),
5766 bookmarks=opts.get('bookmark', ()),
5765 bookmarks=opts.get('bookmark', ()),
5767 opargs=opts.get('opargs'))
5766 opargs=opts.get('opargs'))
5768
5767
5769 result = not pushop.cgresult
5768 result = not pushop.cgresult
5770
5769
5771 if pushop.bkresult is not None:
5770 if pushop.bkresult is not None:
5772 if pushop.bkresult == 2:
5771 if pushop.bkresult == 2:
5773 result = 2
5772 result = 2
5774 elif not result and pushop.bkresult:
5773 elif not result and pushop.bkresult:
5775 result = 2
5774 result = 2
5776
5775
5777 return result
5776 return result
5778
5777
5779 @command('recover', [])
5778 @command('recover', [])
5780 def recover(ui, repo):
5779 def recover(ui, repo):
5781 """roll back an interrupted transaction
5780 """roll back an interrupted transaction
5782
5781
5783 Recover from an interrupted commit or pull.
5782 Recover from an interrupted commit or pull.
5784
5783
5785 This command tries to fix the repository status after an
5784 This command tries to fix the repository status after an
5786 interrupted operation. It should only be necessary when Mercurial
5785 interrupted operation. It should only be necessary when Mercurial
5787 suggests it.
5786 suggests it.
5788
5787
5789 Returns 0 if successful, 1 if nothing to recover or verify fails.
5788 Returns 0 if successful, 1 if nothing to recover or verify fails.
5790 """
5789 """
5791 if repo.recover():
5790 if repo.recover():
5792 return hg.verify(repo)
5791 return hg.verify(repo)
5793 return 1
5792 return 1
5794
5793
5795 @command('^remove|rm',
5794 @command('^remove|rm',
5796 [('A', 'after', None, _('record delete for missing files')),
5795 [('A', 'after', None, _('record delete for missing files')),
5797 ('f', 'force', None,
5796 ('f', 'force', None,
5798 _('forget added files, delete modified files')),
5797 _('forget added files, delete modified files')),
5799 ] + subrepoopts + walkopts,
5798 ] + subrepoopts + walkopts,
5800 _('[OPTION]... FILE...'),
5799 _('[OPTION]... FILE...'),
5801 inferrepo=True)
5800 inferrepo=True)
5802 def remove(ui, repo, *pats, **opts):
5801 def remove(ui, repo, *pats, **opts):
5803 """remove the specified files on the next commit
5802 """remove the specified files on the next commit
5804
5803
5805 Schedule the indicated files for removal from the current branch.
5804 Schedule the indicated files for removal from the current branch.
5806
5805
5807 This command schedules the files to be removed at the next commit.
5806 This command schedules the files to be removed at the next commit.
5808 To undo a remove before that, see :hg:`revert`. To undo added
5807 To undo a remove before that, see :hg:`revert`. To undo added
5809 files, see :hg:`forget`.
5808 files, see :hg:`forget`.
5810
5809
5811 .. container:: verbose
5810 .. container:: verbose
5812
5811
5813 -A/--after can be used to remove only files that have already
5812 -A/--after can be used to remove only files that have already
5814 been deleted, -f/--force can be used to force deletion, and -Af
5813 been deleted, -f/--force can be used to force deletion, and -Af
5815 can be used to remove files from the next revision without
5814 can be used to remove files from the next revision without
5816 deleting them from the working directory.
5815 deleting them from the working directory.
5817
5816
5818 The following table details the behavior of remove for different
5817 The following table details the behavior of remove for different
5819 file states (columns) and option combinations (rows). The file
5818 file states (columns) and option combinations (rows). The file
5820 states are Added [A], Clean [C], Modified [M] and Missing [!]
5819 states are Added [A], Clean [C], Modified [M] and Missing [!]
5821 (as reported by :hg:`status`). The actions are Warn, Remove
5820 (as reported by :hg:`status`). The actions are Warn, Remove
5822 (from branch) and Delete (from disk):
5821 (from branch) and Delete (from disk):
5823
5822
5824 ========= == == == ==
5823 ========= == == == ==
5825 opt/state A C M !
5824 opt/state A C M !
5826 ========= == == == ==
5825 ========= == == == ==
5827 none W RD W R
5826 none W RD W R
5828 -f R RD RD R
5827 -f R RD RD R
5829 -A W W W R
5828 -A W W W R
5830 -Af R R R R
5829 -Af R R R R
5831 ========= == == == ==
5830 ========= == == == ==
5832
5831
5833 .. note::
5832 .. note::
5834
5833
5835 :hg:`remove` never deletes files in Added [A] state from the
5834 :hg:`remove` never deletes files in Added [A] state from the
5836 working directory, not even if ``--force`` is specified.
5835 working directory, not even if ``--force`` is specified.
5837
5836
5838 Returns 0 on success, 1 if any warnings encountered.
5837 Returns 0 on success, 1 if any warnings encountered.
5839 """
5838 """
5840
5839
5841 after, force = opts.get('after'), opts.get('force')
5840 after, force = opts.get('after'), opts.get('force')
5842 if not pats and not after:
5841 if not pats and not after:
5843 raise error.Abort(_('no files specified'))
5842 raise error.Abort(_('no files specified'))
5844
5843
5845 m = scmutil.match(repo[None], pats, opts)
5844 m = scmutil.match(repo[None], pats, opts)
5846 subrepos = opts.get('subrepos')
5845 subrepos = opts.get('subrepos')
5847 return cmdutil.remove(ui, repo, m, "", after, force, subrepos)
5846 return cmdutil.remove(ui, repo, m, "", after, force, subrepos)
5848
5847
5849 @command('rename|move|mv',
5848 @command('rename|move|mv',
5850 [('A', 'after', None, _('record a rename that has already occurred')),
5849 [('A', 'after', None, _('record a rename that has already occurred')),
5851 ('f', 'force', None, _('forcibly copy over an existing managed file')),
5850 ('f', 'force', None, _('forcibly copy over an existing managed file')),
5852 ] + walkopts + dryrunopts,
5851 ] + walkopts + dryrunopts,
5853 _('[OPTION]... SOURCE... DEST'))
5852 _('[OPTION]... SOURCE... DEST'))
5854 def rename(ui, repo, *pats, **opts):
5853 def rename(ui, repo, *pats, **opts):
5855 """rename files; equivalent of copy + remove
5854 """rename files; equivalent of copy + remove
5856
5855
5857 Mark dest as copies of sources; mark sources for deletion. If dest
5856 Mark dest as copies of sources; mark sources for deletion. If dest
5858 is a directory, copies are put in that directory. If dest is a
5857 is a directory, copies are put in that directory. If dest is a
5859 file, there can only be one source.
5858 file, there can only be one source.
5860
5859
5861 By default, this command copies the contents of files as they
5860 By default, this command copies the contents of files as they
5862 exist in the working directory. If invoked with -A/--after, the
5861 exist in the working directory. If invoked with -A/--after, the
5863 operation is recorded, but no copying is performed.
5862 operation is recorded, but no copying is performed.
5864
5863
5865 This command takes effect at the next commit. To undo a rename
5864 This command takes effect at the next commit. To undo a rename
5866 before that, see :hg:`revert`.
5865 before that, see :hg:`revert`.
5867
5866
5868 Returns 0 on success, 1 if errors are encountered.
5867 Returns 0 on success, 1 if errors are encountered.
5869 """
5868 """
5870 with repo.wlock(False):
5869 with repo.wlock(False):
5871 return cmdutil.copy(ui, repo, pats, opts, rename=True)
5870 return cmdutil.copy(ui, repo, pats, opts, rename=True)
5872
5871
5873 @command('resolve',
5872 @command('resolve',
5874 [('a', 'all', None, _('select all unresolved files')),
5873 [('a', 'all', None, _('select all unresolved files')),
5875 ('l', 'list', None, _('list state of files needing merge')),
5874 ('l', 'list', None, _('list state of files needing merge')),
5876 ('m', 'mark', None, _('mark files as resolved')),
5875 ('m', 'mark', None, _('mark files as resolved')),
5877 ('u', 'unmark', None, _('mark files as unresolved')),
5876 ('u', 'unmark', None, _('mark files as unresolved')),
5878 ('n', 'no-status', None, _('hide status prefix'))]
5877 ('n', 'no-status', None, _('hide status prefix'))]
5879 + mergetoolopts + walkopts + formatteropts,
5878 + mergetoolopts + walkopts + formatteropts,
5880 _('[OPTION]... [FILE]...'),
5879 _('[OPTION]... [FILE]...'),
5881 inferrepo=True)
5880 inferrepo=True)
5882 def resolve(ui, repo, *pats, **opts):
5881 def resolve(ui, repo, *pats, **opts):
5883 """redo merges or set/view the merge status of files
5882 """redo merges or set/view the merge status of files
5884
5883
5885 Merges with unresolved conflicts are often the result of
5884 Merges with unresolved conflicts are often the result of
5886 non-interactive merging using the ``internal:merge`` configuration
5885 non-interactive merging using the ``internal:merge`` configuration
5887 setting, or a command-line merge tool like ``diff3``. The resolve
5886 setting, or a command-line merge tool like ``diff3``. The resolve
5888 command is used to manage the files involved in a merge, after
5887 command is used to manage the files involved in a merge, after
5889 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
5888 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
5890 working directory must have two parents). See :hg:`help
5889 working directory must have two parents). See :hg:`help
5891 merge-tools` for information on configuring merge tools.
5890 merge-tools` for information on configuring merge tools.
5892
5891
5893 The resolve command can be used in the following ways:
5892 The resolve command can be used in the following ways:
5894
5893
5895 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
5894 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
5896 files, discarding any previous merge attempts. Re-merging is not
5895 files, discarding any previous merge attempts. Re-merging is not
5897 performed for files already marked as resolved. Use ``--all/-a``
5896 performed for files already marked as resolved. Use ``--all/-a``
5898 to select all unresolved files. ``--tool`` can be used to specify
5897 to select all unresolved files. ``--tool`` can be used to specify
5899 the merge tool used for the given files. It overrides the HGMERGE
5898 the merge tool used for the given files. It overrides the HGMERGE
5900 environment variable and your configuration files. Previous file
5899 environment variable and your configuration files. Previous file
5901 contents are saved with a ``.orig`` suffix.
5900 contents are saved with a ``.orig`` suffix.
5902
5901
5903 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
5902 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
5904 (e.g. after having manually fixed-up the files). The default is
5903 (e.g. after having manually fixed-up the files). The default is
5905 to mark all unresolved files.
5904 to mark all unresolved files.
5906
5905
5907 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
5906 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
5908 default is to mark all resolved files.
5907 default is to mark all resolved files.
5909
5908
5910 - :hg:`resolve -l`: list files which had or still have conflicts.
5909 - :hg:`resolve -l`: list files which had or still have conflicts.
5911 In the printed list, ``U`` = unresolved and ``R`` = resolved.
5910 In the printed list, ``U`` = unresolved and ``R`` = resolved.
5912
5911
5913 .. note::
5912 .. note::
5914
5913
5915 Mercurial will not let you commit files with unresolved merge
5914 Mercurial will not let you commit files with unresolved merge
5916 conflicts. You must use :hg:`resolve -m ...` before you can
5915 conflicts. You must use :hg:`resolve -m ...` before you can
5917 commit after a conflicting merge.
5916 commit after a conflicting merge.
5918
5917
5919 Returns 0 on success, 1 if any files fail a resolve attempt.
5918 Returns 0 on success, 1 if any files fail a resolve attempt.
5920 """
5919 """
5921
5920
5922 flaglist = 'all mark unmark list no_status'.split()
5921 flaglist = 'all mark unmark list no_status'.split()
5923 all, mark, unmark, show, nostatus = \
5922 all, mark, unmark, show, nostatus = \
5924 [opts.get(o) for o in flaglist]
5923 [opts.get(o) for o in flaglist]
5925
5924
5926 if (show and (mark or unmark)) or (mark and unmark):
5925 if (show and (mark or unmark)) or (mark and unmark):
5927 raise error.Abort(_("too many options specified"))
5926 raise error.Abort(_("too many options specified"))
5928 if pats and all:
5927 if pats and all:
5929 raise error.Abort(_("can't specify --all and patterns"))
5928 raise error.Abort(_("can't specify --all and patterns"))
5930 if not (all or pats or show or mark or unmark):
5929 if not (all or pats or show or mark or unmark):
5931 raise error.Abort(_('no files or directories specified'),
5930 raise error.Abort(_('no files or directories specified'),
5932 hint=('use --all to re-merge all unresolved files'))
5931 hint=('use --all to re-merge all unresolved files'))
5933
5932
5934 if show:
5933 if show:
5935 fm = ui.formatter('resolve', opts)
5934 fm = ui.formatter('resolve', opts)
5936 ms = mergemod.mergestate.read(repo)
5935 ms = mergemod.mergestate.read(repo)
5937 m = scmutil.match(repo[None], pats, opts)
5936 m = scmutil.match(repo[None], pats, opts)
5938 for f in ms:
5937 for f in ms:
5939 if not m(f):
5938 if not m(f):
5940 continue
5939 continue
5941 l = 'resolve.' + {'u': 'unresolved', 'r': 'resolved',
5940 l = 'resolve.' + {'u': 'unresolved', 'r': 'resolved',
5942 'd': 'driverresolved'}[ms[f]]
5941 'd': 'driverresolved'}[ms[f]]
5943 fm.startitem()
5942 fm.startitem()
5944 fm.condwrite(not nostatus, 'status', '%s ', ms[f].upper(), label=l)
5943 fm.condwrite(not nostatus, 'status', '%s ', ms[f].upper(), label=l)
5945 fm.write('path', '%s\n', f, label=l)
5944 fm.write('path', '%s\n', f, label=l)
5946 fm.end()
5945 fm.end()
5947 return 0
5946 return 0
5948
5947
5949 with repo.wlock():
5948 with repo.wlock():
5950 ms = mergemod.mergestate.read(repo)
5949 ms = mergemod.mergestate.read(repo)
5951
5950
5952 if not (ms.active() or repo.dirstate.p2() != nullid):
5951 if not (ms.active() or repo.dirstate.p2() != nullid):
5953 raise error.Abort(
5952 raise error.Abort(
5954 _('resolve command not applicable when not merging'))
5953 _('resolve command not applicable when not merging'))
5955
5954
5956 wctx = repo[None]
5955 wctx = repo[None]
5957
5956
5958 if ms.mergedriver and ms.mdstate() == 'u':
5957 if ms.mergedriver and ms.mdstate() == 'u':
5959 proceed = mergemod.driverpreprocess(repo, ms, wctx)
5958 proceed = mergemod.driverpreprocess(repo, ms, wctx)
5960 ms.commit()
5959 ms.commit()
5961 # allow mark and unmark to go through
5960 # allow mark and unmark to go through
5962 if not mark and not unmark and not proceed:
5961 if not mark and not unmark and not proceed:
5963 return 1
5962 return 1
5964
5963
5965 m = scmutil.match(wctx, pats, opts)
5964 m = scmutil.match(wctx, pats, opts)
5966 ret = 0
5965 ret = 0
5967 didwork = False
5966 didwork = False
5968 runconclude = False
5967 runconclude = False
5969
5968
5970 tocomplete = []
5969 tocomplete = []
5971 for f in ms:
5970 for f in ms:
5972 if not m(f):
5971 if not m(f):
5973 continue
5972 continue
5974
5973
5975 didwork = True
5974 didwork = True
5976
5975
5977 # don't let driver-resolved files be marked, and run the conclude
5976 # don't let driver-resolved files be marked, and run the conclude
5978 # step if asked to resolve
5977 # step if asked to resolve
5979 if ms[f] == "d":
5978 if ms[f] == "d":
5980 exact = m.exact(f)
5979 exact = m.exact(f)
5981 if mark:
5980 if mark:
5982 if exact:
5981 if exact:
5983 ui.warn(_('not marking %s as it is driver-resolved\n')
5982 ui.warn(_('not marking %s as it is driver-resolved\n')
5984 % f)
5983 % f)
5985 elif unmark:
5984 elif unmark:
5986 if exact:
5985 if exact:
5987 ui.warn(_('not unmarking %s as it is driver-resolved\n')
5986 ui.warn(_('not unmarking %s as it is driver-resolved\n')
5988 % f)
5987 % f)
5989 else:
5988 else:
5990 runconclude = True
5989 runconclude = True
5991 continue
5990 continue
5992
5991
5993 if mark:
5992 if mark:
5994 ms.mark(f, "r")
5993 ms.mark(f, "r")
5995 elif unmark:
5994 elif unmark:
5996 ms.mark(f, "u")
5995 ms.mark(f, "u")
5997 else:
5996 else:
5998 # backup pre-resolve (merge uses .orig for its own purposes)
5997 # backup pre-resolve (merge uses .orig for its own purposes)
5999 a = repo.wjoin(f)
5998 a = repo.wjoin(f)
6000 try:
5999 try:
6001 util.copyfile(a, a + ".resolve")
6000 util.copyfile(a, a + ".resolve")
6002 except (IOError, OSError) as inst:
6001 except (IOError, OSError) as inst:
6003 if inst.errno != errno.ENOENT:
6002 if inst.errno != errno.ENOENT:
6004 raise
6003 raise
6005
6004
6006 try:
6005 try:
6007 # preresolve file
6006 # preresolve file
6008 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
6007 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
6009 'resolve')
6008 'resolve')
6010 complete, r = ms.preresolve(f, wctx)
6009 complete, r = ms.preresolve(f, wctx)
6011 if not complete:
6010 if not complete:
6012 tocomplete.append(f)
6011 tocomplete.append(f)
6013 elif r:
6012 elif r:
6014 ret = 1
6013 ret = 1
6015 finally:
6014 finally:
6016 ui.setconfig('ui', 'forcemerge', '', 'resolve')
6015 ui.setconfig('ui', 'forcemerge', '', 'resolve')
6017 ms.commit()
6016 ms.commit()
6018
6017
6019 # replace filemerge's .orig file with our resolve file, but only
6018 # replace filemerge's .orig file with our resolve file, but only
6020 # for merges that are complete
6019 # for merges that are complete
6021 if complete:
6020 if complete:
6022 try:
6021 try:
6023 util.rename(a + ".resolve",
6022 util.rename(a + ".resolve",
6024 scmutil.origpath(ui, repo, a))
6023 scmutil.origpath(ui, repo, a))
6025 except OSError as inst:
6024 except OSError as inst:
6026 if inst.errno != errno.ENOENT:
6025 if inst.errno != errno.ENOENT:
6027 raise
6026 raise
6028
6027
6029 for f in tocomplete:
6028 for f in tocomplete:
6030 try:
6029 try:
6031 # resolve file
6030 # resolve file
6032 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
6031 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
6033 'resolve')
6032 'resolve')
6034 r = ms.resolve(f, wctx)
6033 r = ms.resolve(f, wctx)
6035 if r:
6034 if r:
6036 ret = 1
6035 ret = 1
6037 finally:
6036 finally:
6038 ui.setconfig('ui', 'forcemerge', '', 'resolve')
6037 ui.setconfig('ui', 'forcemerge', '', 'resolve')
6039 ms.commit()
6038 ms.commit()
6040
6039
6041 # replace filemerge's .orig file with our resolve file
6040 # replace filemerge's .orig file with our resolve file
6042 a = repo.wjoin(f)
6041 a = repo.wjoin(f)
6043 try:
6042 try:
6044 util.rename(a + ".resolve", scmutil.origpath(ui, repo, a))
6043 util.rename(a + ".resolve", scmutil.origpath(ui, repo, a))
6045 except OSError as inst:
6044 except OSError as inst:
6046 if inst.errno != errno.ENOENT:
6045 if inst.errno != errno.ENOENT:
6047 raise
6046 raise
6048
6047
6049 ms.commit()
6048 ms.commit()
6050 ms.recordactions()
6049 ms.recordactions()
6051
6050
6052 if not didwork and pats:
6051 if not didwork and pats:
6053 hint = None
6052 hint = None
6054 if not any([p for p in pats if p.find(':') >= 0]):
6053 if not any([p for p in pats if p.find(':') >= 0]):
6055 pats = ['path:%s' % p for p in pats]
6054 pats = ['path:%s' % p for p in pats]
6056 m = scmutil.match(wctx, pats, opts)
6055 m = scmutil.match(wctx, pats, opts)
6057 for f in ms:
6056 for f in ms:
6058 if not m(f):
6057 if not m(f):
6059 continue
6058 continue
6060 flags = ''.join(['-%s ' % o[0] for o in flaglist
6059 flags = ''.join(['-%s ' % o[0] for o in flaglist
6061 if opts.get(o)])
6060 if opts.get(o)])
6062 hint = _("(try: hg resolve %s%s)\n") % (
6061 hint = _("(try: hg resolve %s%s)\n") % (
6063 flags,
6062 flags,
6064 ' '.join(pats))
6063 ' '.join(pats))
6065 break
6064 break
6066 ui.warn(_("arguments do not match paths that need resolving\n"))
6065 ui.warn(_("arguments do not match paths that need resolving\n"))
6067 if hint:
6066 if hint:
6068 ui.warn(hint)
6067 ui.warn(hint)
6069 elif ms.mergedriver and ms.mdstate() != 's':
6068 elif ms.mergedriver and ms.mdstate() != 's':
6070 # run conclude step when either a driver-resolved file is requested
6069 # run conclude step when either a driver-resolved file is requested
6071 # or there are no driver-resolved files
6070 # or there are no driver-resolved files
6072 # we can't use 'ret' to determine whether any files are unresolved
6071 # we can't use 'ret' to determine whether any files are unresolved
6073 # because we might not have tried to resolve some
6072 # because we might not have tried to resolve some
6074 if ((runconclude or not list(ms.driverresolved()))
6073 if ((runconclude or not list(ms.driverresolved()))
6075 and not list(ms.unresolved())):
6074 and not list(ms.unresolved())):
6076 proceed = mergemod.driverconclude(repo, ms, wctx)
6075 proceed = mergemod.driverconclude(repo, ms, wctx)
6077 ms.commit()
6076 ms.commit()
6078 if not proceed:
6077 if not proceed:
6079 return 1
6078 return 1
6080
6079
6081 # Nudge users into finishing an unfinished operation
6080 # Nudge users into finishing an unfinished operation
6082 unresolvedf = list(ms.unresolved())
6081 unresolvedf = list(ms.unresolved())
6083 driverresolvedf = list(ms.driverresolved())
6082 driverresolvedf = list(ms.driverresolved())
6084 if not unresolvedf and not driverresolvedf:
6083 if not unresolvedf and not driverresolvedf:
6085 ui.status(_('(no more unresolved files)\n'))
6084 ui.status(_('(no more unresolved files)\n'))
6086 cmdutil.checkafterresolved(repo)
6085 cmdutil.checkafterresolved(repo)
6087 elif not unresolvedf:
6086 elif not unresolvedf:
6088 ui.status(_('(no more unresolved files -- '
6087 ui.status(_('(no more unresolved files -- '
6089 'run "hg resolve --all" to conclude)\n'))
6088 'run "hg resolve --all" to conclude)\n'))
6090
6089
6091 return ret
6090 return ret
6092
6091
6093 @command('revert',
6092 @command('revert',
6094 [('a', 'all', None, _('revert all changes when no arguments given')),
6093 [('a', 'all', None, _('revert all changes when no arguments given')),
6095 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
6094 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
6096 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
6095 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
6097 ('C', 'no-backup', None, _('do not save backup copies of files')),
6096 ('C', 'no-backup', None, _('do not save backup copies of files')),
6098 ('i', 'interactive', None,
6097 ('i', 'interactive', None,
6099 _('interactively select the changes (EXPERIMENTAL)')),
6098 _('interactively select the changes (EXPERIMENTAL)')),
6100 ] + walkopts + dryrunopts,
6099 ] + walkopts + dryrunopts,
6101 _('[OPTION]... [-r REV] [NAME]...'))
6100 _('[OPTION]... [-r REV] [NAME]...'))
6102 def revert(ui, repo, *pats, **opts):
6101 def revert(ui, repo, *pats, **opts):
6103 """restore files to their checkout state
6102 """restore files to their checkout state
6104
6103
6105 .. note::
6104 .. note::
6106
6105
6107 To check out earlier revisions, you should use :hg:`update REV`.
6106 To check out earlier revisions, you should use :hg:`update REV`.
6108 To cancel an uncommitted merge (and lose your changes),
6107 To cancel an uncommitted merge (and lose your changes),
6109 use :hg:`update --clean .`.
6108 use :hg:`update --clean .`.
6110
6109
6111 With no revision specified, revert the specified files or directories
6110 With no revision specified, revert the specified files or directories
6112 to the contents they had in the parent of the working directory.
6111 to the contents they had in the parent of the working directory.
6113 This restores the contents of files to an unmodified
6112 This restores the contents of files to an unmodified
6114 state and unschedules adds, removes, copies, and renames. If the
6113 state and unschedules adds, removes, copies, and renames. If the
6115 working directory has two parents, you must explicitly specify a
6114 working directory has two parents, you must explicitly specify a
6116 revision.
6115 revision.
6117
6116
6118 Using the -r/--rev or -d/--date options, revert the given files or
6117 Using the -r/--rev or -d/--date options, revert the given files or
6119 directories to their states as of a specific revision. Because
6118 directories to their states as of a specific revision. Because
6120 revert does not change the working directory parents, this will
6119 revert does not change the working directory parents, this will
6121 cause these files to appear modified. This can be helpful to "back
6120 cause these files to appear modified. This can be helpful to "back
6122 out" some or all of an earlier change. See :hg:`backout` for a
6121 out" some or all of an earlier change. See :hg:`backout` for a
6123 related method.
6122 related method.
6124
6123
6125 Modified files are saved with a .orig suffix before reverting.
6124 Modified files are saved with a .orig suffix before reverting.
6126 To disable these backups, use --no-backup. It is possible to store
6125 To disable these backups, use --no-backup. It is possible to store
6127 the backup files in a custom directory relative to the root of the
6126 the backup files in a custom directory relative to the root of the
6128 repository by setting the ``ui.origbackuppath`` configuration
6127 repository by setting the ``ui.origbackuppath`` configuration
6129 option.
6128 option.
6130
6129
6131 See :hg:`help dates` for a list of formats valid for -d/--date.
6130 See :hg:`help dates` for a list of formats valid for -d/--date.
6132
6131
6133 See :hg:`help backout` for a way to reverse the effect of an
6132 See :hg:`help backout` for a way to reverse the effect of an
6134 earlier changeset.
6133 earlier changeset.
6135
6134
6136 Returns 0 on success.
6135 Returns 0 on success.
6137 """
6136 """
6138
6137
6139 if opts.get("date"):
6138 if opts.get("date"):
6140 if opts.get("rev"):
6139 if opts.get("rev"):
6141 raise error.Abort(_("you can't specify a revision and a date"))
6140 raise error.Abort(_("you can't specify a revision and a date"))
6142 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
6141 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
6143
6142
6144 parent, p2 = repo.dirstate.parents()
6143 parent, p2 = repo.dirstate.parents()
6145 if not opts.get('rev') and p2 != nullid:
6144 if not opts.get('rev') and p2 != nullid:
6146 # revert after merge is a trap for new users (issue2915)
6145 # revert after merge is a trap for new users (issue2915)
6147 raise error.Abort(_('uncommitted merge with no revision specified'),
6146 raise error.Abort(_('uncommitted merge with no revision specified'),
6148 hint=_("use 'hg update' or see 'hg help revert'"))
6147 hint=_("use 'hg update' or see 'hg help revert'"))
6149
6148
6150 ctx = scmutil.revsingle(repo, opts.get('rev'))
6149 ctx = scmutil.revsingle(repo, opts.get('rev'))
6151
6150
6152 if (not (pats or opts.get('include') or opts.get('exclude') or
6151 if (not (pats or opts.get('include') or opts.get('exclude') or
6153 opts.get('all') or opts.get('interactive'))):
6152 opts.get('all') or opts.get('interactive'))):
6154 msg = _("no files or directories specified")
6153 msg = _("no files or directories specified")
6155 if p2 != nullid:
6154 if p2 != nullid:
6156 hint = _("uncommitted merge, use --all to discard all changes,"
6155 hint = _("uncommitted merge, use --all to discard all changes,"
6157 " or 'hg update -C .' to abort the merge")
6156 " or 'hg update -C .' to abort the merge")
6158 raise error.Abort(msg, hint=hint)
6157 raise error.Abort(msg, hint=hint)
6159 dirty = any(repo.status())
6158 dirty = any(repo.status())
6160 node = ctx.node()
6159 node = ctx.node()
6161 if node != parent:
6160 if node != parent:
6162 if dirty:
6161 if dirty:
6163 hint = _("uncommitted changes, use --all to discard all"
6162 hint = _("uncommitted changes, use --all to discard all"
6164 " changes, or 'hg update %s' to update") % ctx.rev()
6163 " changes, or 'hg update %s' to update") % ctx.rev()
6165 else:
6164 else:
6166 hint = _("use --all to revert all files,"
6165 hint = _("use --all to revert all files,"
6167 " or 'hg update %s' to update") % ctx.rev()
6166 " or 'hg update %s' to update") % ctx.rev()
6168 elif dirty:
6167 elif dirty:
6169 hint = _("uncommitted changes, use --all to discard all changes")
6168 hint = _("uncommitted changes, use --all to discard all changes")
6170 else:
6169 else:
6171 hint = _("use --all to revert all files")
6170 hint = _("use --all to revert all files")
6172 raise error.Abort(msg, hint=hint)
6171 raise error.Abort(msg, hint=hint)
6173
6172
6174 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats, **opts)
6173 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats, **opts)
6175
6174
6176 @command('rollback', dryrunopts +
6175 @command('rollback', dryrunopts +
6177 [('f', 'force', False, _('ignore safety measures'))])
6176 [('f', 'force', False, _('ignore safety measures'))])
6178 def rollback(ui, repo, **opts):
6177 def rollback(ui, repo, **opts):
6179 """roll back the last transaction (DANGEROUS) (DEPRECATED)
6178 """roll back the last transaction (DANGEROUS) (DEPRECATED)
6180
6179
6181 Please use :hg:`commit --amend` instead of rollback to correct
6180 Please use :hg:`commit --amend` instead of rollback to correct
6182 mistakes in the last commit.
6181 mistakes in the last commit.
6183
6182
6184 This command should be used with care. There is only one level of
6183 This command should be used with care. There is only one level of
6185 rollback, and there is no way to undo a rollback. It will also
6184 rollback, and there is no way to undo a rollback. It will also
6186 restore the dirstate at the time of the last transaction, losing
6185 restore the dirstate at the time of the last transaction, losing
6187 any dirstate changes since that time. This command does not alter
6186 any dirstate changes since that time. This command does not alter
6188 the working directory.
6187 the working directory.
6189
6188
6190 Transactions are used to encapsulate the effects of all commands
6189 Transactions are used to encapsulate the effects of all commands
6191 that create new changesets or propagate existing changesets into a
6190 that create new changesets or propagate existing changesets into a
6192 repository.
6191 repository.
6193
6192
6194 .. container:: verbose
6193 .. container:: verbose
6195
6194
6196 For example, the following commands are transactional, and their
6195 For example, the following commands are transactional, and their
6197 effects can be rolled back:
6196 effects can be rolled back:
6198
6197
6199 - commit
6198 - commit
6200 - import
6199 - import
6201 - pull
6200 - pull
6202 - push (with this repository as the destination)
6201 - push (with this repository as the destination)
6203 - unbundle
6202 - unbundle
6204
6203
6205 To avoid permanent data loss, rollback will refuse to rollback a
6204 To avoid permanent data loss, rollback will refuse to rollback a
6206 commit transaction if it isn't checked out. Use --force to
6205 commit transaction if it isn't checked out. Use --force to
6207 override this protection.
6206 override this protection.
6208
6207
6209 The rollback command can be entirely disabled by setting the
6208 The rollback command can be entirely disabled by setting the
6210 ``ui.rollback`` configuration setting to false. If you're here
6209 ``ui.rollback`` configuration setting to false. If you're here
6211 because you want to use rollback and it's disabled, you can
6210 because you want to use rollback and it's disabled, you can
6212 re-enable the command by setting ``ui.rollback`` to true.
6211 re-enable the command by setting ``ui.rollback`` to true.
6213
6212
6214 This command is not intended for use on public repositories. Once
6213 This command is not intended for use on public repositories. Once
6215 changes are visible for pull by other users, rolling a transaction
6214 changes are visible for pull by other users, rolling a transaction
6216 back locally is ineffective (someone else may already have pulled
6215 back locally is ineffective (someone else may already have pulled
6217 the changes). Furthermore, a race is possible with readers of the
6216 the changes). Furthermore, a race is possible with readers of the
6218 repository; for example an in-progress pull from the repository
6217 repository; for example an in-progress pull from the repository
6219 may fail if a rollback is performed.
6218 may fail if a rollback is performed.
6220
6219
6221 Returns 0 on success, 1 if no rollback data is available.
6220 Returns 0 on success, 1 if no rollback data is available.
6222 """
6221 """
6223 if not ui.configbool('ui', 'rollback', True):
6222 if not ui.configbool('ui', 'rollback', True):
6224 raise error.Abort(_('rollback is disabled because it is unsafe'),
6223 raise error.Abort(_('rollback is disabled because it is unsafe'),
6225 hint=('see `hg help -v rollback` for information'))
6224 hint=('see `hg help -v rollback` for information'))
6226 return repo.rollback(dryrun=opts.get('dry_run'),
6225 return repo.rollback(dryrun=opts.get('dry_run'),
6227 force=opts.get('force'))
6226 force=opts.get('force'))
6228
6227
6229 @command('root', [])
6228 @command('root', [])
6230 def root(ui, repo):
6229 def root(ui, repo):
6231 """print the root (top) of the current working directory
6230 """print the root (top) of the current working directory
6232
6231
6233 Print the root directory of the current repository.
6232 Print the root directory of the current repository.
6234
6233
6235 Returns 0 on success.
6234 Returns 0 on success.
6236 """
6235 """
6237 ui.write(repo.root + "\n")
6236 ui.write(repo.root + "\n")
6238
6237
6239 @command('^serve',
6238 @command('^serve',
6240 [('A', 'accesslog', '', _('name of access log file to write to'),
6239 [('A', 'accesslog', '', _('name of access log file to write to'),
6241 _('FILE')),
6240 _('FILE')),
6242 ('d', 'daemon', None, _('run server in background')),
6241 ('d', 'daemon', None, _('run server in background')),
6243 ('', 'daemon-postexec', [], _('used internally by daemon mode')),
6242 ('', 'daemon-postexec', [], _('used internally by daemon mode')),
6244 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
6243 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
6245 # use string type, then we can check if something was passed
6244 # use string type, then we can check if something was passed
6246 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
6245 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
6247 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
6246 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
6248 _('ADDR')),
6247 _('ADDR')),
6249 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
6248 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
6250 _('PREFIX')),
6249 _('PREFIX')),
6251 ('n', 'name', '',
6250 ('n', 'name', '',
6252 _('name to show in web pages (default: working directory)'), _('NAME')),
6251 _('name to show in web pages (default: working directory)'), _('NAME')),
6253 ('', 'web-conf', '',
6252 ('', 'web-conf', '',
6254 _("name of the hgweb config file (see 'hg help hgweb')"), _('FILE')),
6253 _("name of the hgweb config file (see 'hg help hgweb')"), _('FILE')),
6255 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
6254 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
6256 _('FILE')),
6255 _('FILE')),
6257 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
6256 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
6258 ('', 'stdio', None, _('for remote clients')),
6257 ('', 'stdio', None, _('for remote clients')),
6259 ('', 'cmdserver', '', _('for remote clients'), _('MODE')),
6258 ('', 'cmdserver', '', _('for remote clients'), _('MODE')),
6260 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
6259 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
6261 ('', 'style', '', _('template style to use'), _('STYLE')),
6260 ('', 'style', '', _('template style to use'), _('STYLE')),
6262 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
6261 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
6263 ('', 'certificate', '', _('SSL certificate file'), _('FILE'))],
6262 ('', 'certificate', '', _('SSL certificate file'), _('FILE'))],
6264 _('[OPTION]...'),
6263 _('[OPTION]...'),
6265 optionalrepo=True)
6264 optionalrepo=True)
6266 def serve(ui, repo, **opts):
6265 def serve(ui, repo, **opts):
6267 """start stand-alone webserver
6266 """start stand-alone webserver
6268
6267
6269 Start a local HTTP repository browser and pull server. You can use
6268 Start a local HTTP repository browser and pull server. You can use
6270 this for ad-hoc sharing and browsing of repositories. It is
6269 this for ad-hoc sharing and browsing of repositories. It is
6271 recommended to use a real web server to serve a repository for
6270 recommended to use a real web server to serve a repository for
6272 longer periods of time.
6271 longer periods of time.
6273
6272
6274 Please note that the server does not implement access control.
6273 Please note that the server does not implement access control.
6275 This means that, by default, anybody can read from the server and
6274 This means that, by default, anybody can read from the server and
6276 nobody can write to it by default. Set the ``web.allow_push``
6275 nobody can write to it by default. Set the ``web.allow_push``
6277 option to ``*`` to allow everybody to push to the server. You
6276 option to ``*`` to allow everybody to push to the server. You
6278 should use a real web server if you need to authenticate users.
6277 should use a real web server if you need to authenticate users.
6279
6278
6280 By default, the server logs accesses to stdout and errors to
6279 By default, the server logs accesses to stdout and errors to
6281 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
6280 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
6282 files.
6281 files.
6283
6282
6284 To have the server choose a free port number to listen on, specify
6283 To have the server choose a free port number to listen on, specify
6285 a port number of 0; in this case, the server will print the port
6284 a port number of 0; in this case, the server will print the port
6286 number it uses.
6285 number it uses.
6287
6286
6288 Returns 0 on success.
6287 Returns 0 on success.
6289 """
6288 """
6290
6289
6291 if opts["stdio"] and opts["cmdserver"]:
6290 if opts["stdio"] and opts["cmdserver"]:
6292 raise error.Abort(_("cannot use --stdio with --cmdserver"))
6291 raise error.Abort(_("cannot use --stdio with --cmdserver"))
6293
6292
6294 if opts["stdio"]:
6293 if opts["stdio"]:
6295 if repo is None:
6294 if repo is None:
6296 raise error.RepoError(_("there is no Mercurial repository here"
6295 raise error.RepoError(_("there is no Mercurial repository here"
6297 " (.hg not found)"))
6296 " (.hg not found)"))
6298 s = sshserver.sshserver(ui, repo)
6297 s = sshserver.sshserver(ui, repo)
6299 s.serve_forever()
6298 s.serve_forever()
6300
6299
6301 if opts["cmdserver"]:
6300 if opts["cmdserver"]:
6302 service = commandserver.createservice(ui, repo, opts)
6301 service = server.createcmdservice(ui, repo, opts)
6303 else:
6302 else:
6304 service = hgweb.createservice(ui, repo, opts)
6303 service = hgweb.createservice(ui, repo, opts)
6305 return server.runservice(opts, initfn=service.init, runfn=service.run)
6304 return server.runservice(opts, initfn=service.init, runfn=service.run)
6306
6305
6307 @command('^status|st',
6306 @command('^status|st',
6308 [('A', 'all', None, _('show status of all files')),
6307 [('A', 'all', None, _('show status of all files')),
6309 ('m', 'modified', None, _('show only modified files')),
6308 ('m', 'modified', None, _('show only modified files')),
6310 ('a', 'added', None, _('show only added files')),
6309 ('a', 'added', None, _('show only added files')),
6311 ('r', 'removed', None, _('show only removed files')),
6310 ('r', 'removed', None, _('show only removed files')),
6312 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
6311 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
6313 ('c', 'clean', None, _('show only files without changes')),
6312 ('c', 'clean', None, _('show only files without changes')),
6314 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
6313 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
6315 ('i', 'ignored', None, _('show only ignored files')),
6314 ('i', 'ignored', None, _('show only ignored files')),
6316 ('n', 'no-status', None, _('hide status prefix')),
6315 ('n', 'no-status', None, _('hide status prefix')),
6317 ('C', 'copies', None, _('show source of copied files')),
6316 ('C', 'copies', None, _('show source of copied files')),
6318 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
6317 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
6319 ('', 'rev', [], _('show difference from revision'), _('REV')),
6318 ('', 'rev', [], _('show difference from revision'), _('REV')),
6320 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
6319 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
6321 ] + walkopts + subrepoopts + formatteropts,
6320 ] + walkopts + subrepoopts + formatteropts,
6322 _('[OPTION]... [FILE]...'),
6321 _('[OPTION]... [FILE]...'),
6323 inferrepo=True)
6322 inferrepo=True)
6324 def status(ui, repo, *pats, **opts):
6323 def status(ui, repo, *pats, **opts):
6325 """show changed files in the working directory
6324 """show changed files in the working directory
6326
6325
6327 Show status of files in the repository. If names are given, only
6326 Show status of files in the repository. If names are given, only
6328 files that match are shown. Files that are clean or ignored or
6327 files that match are shown. Files that are clean or ignored or
6329 the source of a copy/move operation, are not listed unless
6328 the source of a copy/move operation, are not listed unless
6330 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
6329 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
6331 Unless options described with "show only ..." are given, the
6330 Unless options described with "show only ..." are given, the
6332 options -mardu are used.
6331 options -mardu are used.
6333
6332
6334 Option -q/--quiet hides untracked (unknown and ignored) files
6333 Option -q/--quiet hides untracked (unknown and ignored) files
6335 unless explicitly requested with -u/--unknown or -i/--ignored.
6334 unless explicitly requested with -u/--unknown or -i/--ignored.
6336
6335
6337 .. note::
6336 .. note::
6338
6337
6339 :hg:`status` may appear to disagree with diff if permissions have
6338 :hg:`status` may appear to disagree with diff if permissions have
6340 changed or a merge has occurred. The standard diff format does
6339 changed or a merge has occurred. The standard diff format does
6341 not report permission changes and diff only reports changes
6340 not report permission changes and diff only reports changes
6342 relative to one merge parent.
6341 relative to one merge parent.
6343
6342
6344 If one revision is given, it is used as the base revision.
6343 If one revision is given, it is used as the base revision.
6345 If two revisions are given, the differences between them are
6344 If two revisions are given, the differences between them are
6346 shown. The --change option can also be used as a shortcut to list
6345 shown. The --change option can also be used as a shortcut to list
6347 the changed files of a revision from its first parent.
6346 the changed files of a revision from its first parent.
6348
6347
6349 The codes used to show the status of files are::
6348 The codes used to show the status of files are::
6350
6349
6351 M = modified
6350 M = modified
6352 A = added
6351 A = added
6353 R = removed
6352 R = removed
6354 C = clean
6353 C = clean
6355 ! = missing (deleted by non-hg command, but still tracked)
6354 ! = missing (deleted by non-hg command, but still tracked)
6356 ? = not tracked
6355 ? = not tracked
6357 I = ignored
6356 I = ignored
6358 = origin of the previous file (with --copies)
6357 = origin of the previous file (with --copies)
6359
6358
6360 .. container:: verbose
6359 .. container:: verbose
6361
6360
6362 Examples:
6361 Examples:
6363
6362
6364 - show changes in the working directory relative to a
6363 - show changes in the working directory relative to a
6365 changeset::
6364 changeset::
6366
6365
6367 hg status --rev 9353
6366 hg status --rev 9353
6368
6367
6369 - show changes in the working directory relative to the
6368 - show changes in the working directory relative to the
6370 current directory (see :hg:`help patterns` for more information)::
6369 current directory (see :hg:`help patterns` for more information)::
6371
6370
6372 hg status re:
6371 hg status re:
6373
6372
6374 - show all changes including copies in an existing changeset::
6373 - show all changes including copies in an existing changeset::
6375
6374
6376 hg status --copies --change 9353
6375 hg status --copies --change 9353
6377
6376
6378 - get a NUL separated list of added files, suitable for xargs::
6377 - get a NUL separated list of added files, suitable for xargs::
6379
6378
6380 hg status -an0
6379 hg status -an0
6381
6380
6382 Returns 0 on success.
6381 Returns 0 on success.
6383 """
6382 """
6384
6383
6385 revs = opts.get('rev')
6384 revs = opts.get('rev')
6386 change = opts.get('change')
6385 change = opts.get('change')
6387
6386
6388 if revs and change:
6387 if revs and change:
6389 msg = _('cannot specify --rev and --change at the same time')
6388 msg = _('cannot specify --rev and --change at the same time')
6390 raise error.Abort(msg)
6389 raise error.Abort(msg)
6391 elif change:
6390 elif change:
6392 node2 = scmutil.revsingle(repo, change, None).node()
6391 node2 = scmutil.revsingle(repo, change, None).node()
6393 node1 = repo[node2].p1().node()
6392 node1 = repo[node2].p1().node()
6394 else:
6393 else:
6395 node1, node2 = scmutil.revpair(repo, revs)
6394 node1, node2 = scmutil.revpair(repo, revs)
6396
6395
6397 if pats:
6396 if pats:
6398 cwd = repo.getcwd()
6397 cwd = repo.getcwd()
6399 else:
6398 else:
6400 cwd = ''
6399 cwd = ''
6401
6400
6402 if opts.get('print0'):
6401 if opts.get('print0'):
6403 end = '\0'
6402 end = '\0'
6404 else:
6403 else:
6405 end = '\n'
6404 end = '\n'
6406 copy = {}
6405 copy = {}
6407 states = 'modified added removed deleted unknown ignored clean'.split()
6406 states = 'modified added removed deleted unknown ignored clean'.split()
6408 show = [k for k in states if opts.get(k)]
6407 show = [k for k in states if opts.get(k)]
6409 if opts.get('all'):
6408 if opts.get('all'):
6410 show += ui.quiet and (states[:4] + ['clean']) or states
6409 show += ui.quiet and (states[:4] + ['clean']) or states
6411 if not show:
6410 if not show:
6412 if ui.quiet:
6411 if ui.quiet:
6413 show = states[:4]
6412 show = states[:4]
6414 else:
6413 else:
6415 show = states[:5]
6414 show = states[:5]
6416
6415
6417 m = scmutil.match(repo[node2], pats, opts)
6416 m = scmutil.match(repo[node2], pats, opts)
6418 stat = repo.status(node1, node2, m,
6417 stat = repo.status(node1, node2, m,
6419 'ignored' in show, 'clean' in show, 'unknown' in show,
6418 'ignored' in show, 'clean' in show, 'unknown' in show,
6420 opts.get('subrepos'))
6419 opts.get('subrepos'))
6421 changestates = zip(states, 'MAR!?IC', stat)
6420 changestates = zip(states, 'MAR!?IC', stat)
6422
6421
6423 if (opts.get('all') or opts.get('copies')
6422 if (opts.get('all') or opts.get('copies')
6424 or ui.configbool('ui', 'statuscopies')) and not opts.get('no_status'):
6423 or ui.configbool('ui', 'statuscopies')) and not opts.get('no_status'):
6425 copy = copies.pathcopies(repo[node1], repo[node2], m)
6424 copy = copies.pathcopies(repo[node1], repo[node2], m)
6426
6425
6427 fm = ui.formatter('status', opts)
6426 fm = ui.formatter('status', opts)
6428 fmt = '%s' + end
6427 fmt = '%s' + end
6429 showchar = not opts.get('no_status')
6428 showchar = not opts.get('no_status')
6430
6429
6431 for state, char, files in changestates:
6430 for state, char, files in changestates:
6432 if state in show:
6431 if state in show:
6433 label = 'status.' + state
6432 label = 'status.' + state
6434 for f in files:
6433 for f in files:
6435 fm.startitem()
6434 fm.startitem()
6436 fm.condwrite(showchar, 'status', '%s ', char, label=label)
6435 fm.condwrite(showchar, 'status', '%s ', char, label=label)
6437 fm.write('path', fmt, repo.pathto(f, cwd), label=label)
6436 fm.write('path', fmt, repo.pathto(f, cwd), label=label)
6438 if f in copy:
6437 if f in copy:
6439 fm.write("copy", ' %s' + end, repo.pathto(copy[f], cwd),
6438 fm.write("copy", ' %s' + end, repo.pathto(copy[f], cwd),
6440 label='status.copied')
6439 label='status.copied')
6441 fm.end()
6440 fm.end()
6442
6441
6443 @command('^summary|sum',
6442 @command('^summary|sum',
6444 [('', 'remote', None, _('check for push and pull'))], '[--remote]')
6443 [('', 'remote', None, _('check for push and pull'))], '[--remote]')
6445 def summary(ui, repo, **opts):
6444 def summary(ui, repo, **opts):
6446 """summarize working directory state
6445 """summarize working directory state
6447
6446
6448 This generates a brief summary of the working directory state,
6447 This generates a brief summary of the working directory state,
6449 including parents, branch, commit status, phase and available updates.
6448 including parents, branch, commit status, phase and available updates.
6450
6449
6451 With the --remote option, this will check the default paths for
6450 With the --remote option, this will check the default paths for
6452 incoming and outgoing changes. This can be time-consuming.
6451 incoming and outgoing changes. This can be time-consuming.
6453
6452
6454 Returns 0 on success.
6453 Returns 0 on success.
6455 """
6454 """
6456
6455
6457 ctx = repo[None]
6456 ctx = repo[None]
6458 parents = ctx.parents()
6457 parents = ctx.parents()
6459 pnode = parents[0].node()
6458 pnode = parents[0].node()
6460 marks = []
6459 marks = []
6461
6460
6462 ms = None
6461 ms = None
6463 try:
6462 try:
6464 ms = mergemod.mergestate.read(repo)
6463 ms = mergemod.mergestate.read(repo)
6465 except error.UnsupportedMergeRecords as e:
6464 except error.UnsupportedMergeRecords as e:
6466 s = ' '.join(e.recordtypes)
6465 s = ' '.join(e.recordtypes)
6467 ui.warn(
6466 ui.warn(
6468 _('warning: merge state has unsupported record types: %s\n') % s)
6467 _('warning: merge state has unsupported record types: %s\n') % s)
6469 unresolved = 0
6468 unresolved = 0
6470 else:
6469 else:
6471 unresolved = [f for f in ms if ms[f] == 'u']
6470 unresolved = [f for f in ms if ms[f] == 'u']
6472
6471
6473 for p in parents:
6472 for p in parents:
6474 # label with log.changeset (instead of log.parent) since this
6473 # label with log.changeset (instead of log.parent) since this
6475 # shows a working directory parent *changeset*:
6474 # shows a working directory parent *changeset*:
6476 # i18n: column positioning for "hg summary"
6475 # i18n: column positioning for "hg summary"
6477 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
6476 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
6478 label='log.changeset changeset.%s' % p.phasestr())
6477 label='log.changeset changeset.%s' % p.phasestr())
6479 ui.write(' '.join(p.tags()), label='log.tag')
6478 ui.write(' '.join(p.tags()), label='log.tag')
6480 if p.bookmarks():
6479 if p.bookmarks():
6481 marks.extend(p.bookmarks())
6480 marks.extend(p.bookmarks())
6482 if p.rev() == -1:
6481 if p.rev() == -1:
6483 if not len(repo):
6482 if not len(repo):
6484 ui.write(_(' (empty repository)'))
6483 ui.write(_(' (empty repository)'))
6485 else:
6484 else:
6486 ui.write(_(' (no revision checked out)'))
6485 ui.write(_(' (no revision checked out)'))
6487 ui.write('\n')
6486 ui.write('\n')
6488 if p.description():
6487 if p.description():
6489 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
6488 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
6490 label='log.summary')
6489 label='log.summary')
6491
6490
6492 branch = ctx.branch()
6491 branch = ctx.branch()
6493 bheads = repo.branchheads(branch)
6492 bheads = repo.branchheads(branch)
6494 # i18n: column positioning for "hg summary"
6493 # i18n: column positioning for "hg summary"
6495 m = _('branch: %s\n') % branch
6494 m = _('branch: %s\n') % branch
6496 if branch != 'default':
6495 if branch != 'default':
6497 ui.write(m, label='log.branch')
6496 ui.write(m, label='log.branch')
6498 else:
6497 else:
6499 ui.status(m, label='log.branch')
6498 ui.status(m, label='log.branch')
6500
6499
6501 if marks:
6500 if marks:
6502 active = repo._activebookmark
6501 active = repo._activebookmark
6503 # i18n: column positioning for "hg summary"
6502 # i18n: column positioning for "hg summary"
6504 ui.write(_('bookmarks:'), label='log.bookmark')
6503 ui.write(_('bookmarks:'), label='log.bookmark')
6505 if active is not None:
6504 if active is not None:
6506 if active in marks:
6505 if active in marks:
6507 ui.write(' *' + active, label=activebookmarklabel)
6506 ui.write(' *' + active, label=activebookmarklabel)
6508 marks.remove(active)
6507 marks.remove(active)
6509 else:
6508 else:
6510 ui.write(' [%s]' % active, label=activebookmarklabel)
6509 ui.write(' [%s]' % active, label=activebookmarklabel)
6511 for m in marks:
6510 for m in marks:
6512 ui.write(' ' + m, label='log.bookmark')
6511 ui.write(' ' + m, label='log.bookmark')
6513 ui.write('\n', label='log.bookmark')
6512 ui.write('\n', label='log.bookmark')
6514
6513
6515 status = repo.status(unknown=True)
6514 status = repo.status(unknown=True)
6516
6515
6517 c = repo.dirstate.copies()
6516 c = repo.dirstate.copies()
6518 copied, renamed = [], []
6517 copied, renamed = [], []
6519 for d, s in c.iteritems():
6518 for d, s in c.iteritems():
6520 if s in status.removed:
6519 if s in status.removed:
6521 status.removed.remove(s)
6520 status.removed.remove(s)
6522 renamed.append(d)
6521 renamed.append(d)
6523 else:
6522 else:
6524 copied.append(d)
6523 copied.append(d)
6525 if d in status.added:
6524 if d in status.added:
6526 status.added.remove(d)
6525 status.added.remove(d)
6527
6526
6528 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
6527 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
6529
6528
6530 labels = [(ui.label(_('%d modified'), 'status.modified'), status.modified),
6529 labels = [(ui.label(_('%d modified'), 'status.modified'), status.modified),
6531 (ui.label(_('%d added'), 'status.added'), status.added),
6530 (ui.label(_('%d added'), 'status.added'), status.added),
6532 (ui.label(_('%d removed'), 'status.removed'), status.removed),
6531 (ui.label(_('%d removed'), 'status.removed'), status.removed),
6533 (ui.label(_('%d renamed'), 'status.copied'), renamed),
6532 (ui.label(_('%d renamed'), 'status.copied'), renamed),
6534 (ui.label(_('%d copied'), 'status.copied'), copied),
6533 (ui.label(_('%d copied'), 'status.copied'), copied),
6535 (ui.label(_('%d deleted'), 'status.deleted'), status.deleted),
6534 (ui.label(_('%d deleted'), 'status.deleted'), status.deleted),
6536 (ui.label(_('%d unknown'), 'status.unknown'), status.unknown),
6535 (ui.label(_('%d unknown'), 'status.unknown'), status.unknown),
6537 (ui.label(_('%d unresolved'), 'resolve.unresolved'), unresolved),
6536 (ui.label(_('%d unresolved'), 'resolve.unresolved'), unresolved),
6538 (ui.label(_('%d subrepos'), 'status.modified'), subs)]
6537 (ui.label(_('%d subrepos'), 'status.modified'), subs)]
6539 t = []
6538 t = []
6540 for l, s in labels:
6539 for l, s in labels:
6541 if s:
6540 if s:
6542 t.append(l % len(s))
6541 t.append(l % len(s))
6543
6542
6544 t = ', '.join(t)
6543 t = ', '.join(t)
6545 cleanworkdir = False
6544 cleanworkdir = False
6546
6545
6547 if repo.vfs.exists('graftstate'):
6546 if repo.vfs.exists('graftstate'):
6548 t += _(' (graft in progress)')
6547 t += _(' (graft in progress)')
6549 if repo.vfs.exists('updatestate'):
6548 if repo.vfs.exists('updatestate'):
6550 t += _(' (interrupted update)')
6549 t += _(' (interrupted update)')
6551 elif len(parents) > 1:
6550 elif len(parents) > 1:
6552 t += _(' (merge)')
6551 t += _(' (merge)')
6553 elif branch != parents[0].branch():
6552 elif branch != parents[0].branch():
6554 t += _(' (new branch)')
6553 t += _(' (new branch)')
6555 elif (parents[0].closesbranch() and
6554 elif (parents[0].closesbranch() and
6556 pnode in repo.branchheads(branch, closed=True)):
6555 pnode in repo.branchheads(branch, closed=True)):
6557 t += _(' (head closed)')
6556 t += _(' (head closed)')
6558 elif not (status.modified or status.added or status.removed or renamed or
6557 elif not (status.modified or status.added or status.removed or renamed or
6559 copied or subs):
6558 copied or subs):
6560 t += _(' (clean)')
6559 t += _(' (clean)')
6561 cleanworkdir = True
6560 cleanworkdir = True
6562 elif pnode not in bheads:
6561 elif pnode not in bheads:
6563 t += _(' (new branch head)')
6562 t += _(' (new branch head)')
6564
6563
6565 if parents:
6564 if parents:
6566 pendingphase = max(p.phase() for p in parents)
6565 pendingphase = max(p.phase() for p in parents)
6567 else:
6566 else:
6568 pendingphase = phases.public
6567 pendingphase = phases.public
6569
6568
6570 if pendingphase > phases.newcommitphase(ui):
6569 if pendingphase > phases.newcommitphase(ui):
6571 t += ' (%s)' % phases.phasenames[pendingphase]
6570 t += ' (%s)' % phases.phasenames[pendingphase]
6572
6571
6573 if cleanworkdir:
6572 if cleanworkdir:
6574 # i18n: column positioning for "hg summary"
6573 # i18n: column positioning for "hg summary"
6575 ui.status(_('commit: %s\n') % t.strip())
6574 ui.status(_('commit: %s\n') % t.strip())
6576 else:
6575 else:
6577 # i18n: column positioning for "hg summary"
6576 # i18n: column positioning for "hg summary"
6578 ui.write(_('commit: %s\n') % t.strip())
6577 ui.write(_('commit: %s\n') % t.strip())
6579
6578
6580 # all ancestors of branch heads - all ancestors of parent = new csets
6579 # all ancestors of branch heads - all ancestors of parent = new csets
6581 new = len(repo.changelog.findmissing([pctx.node() for pctx in parents],
6580 new = len(repo.changelog.findmissing([pctx.node() for pctx in parents],
6582 bheads))
6581 bheads))
6583
6582
6584 if new == 0:
6583 if new == 0:
6585 # i18n: column positioning for "hg summary"
6584 # i18n: column positioning for "hg summary"
6586 ui.status(_('update: (current)\n'))
6585 ui.status(_('update: (current)\n'))
6587 elif pnode not in bheads:
6586 elif pnode not in bheads:
6588 # i18n: column positioning for "hg summary"
6587 # i18n: column positioning for "hg summary"
6589 ui.write(_('update: %d new changesets (update)\n') % new)
6588 ui.write(_('update: %d new changesets (update)\n') % new)
6590 else:
6589 else:
6591 # i18n: column positioning for "hg summary"
6590 # i18n: column positioning for "hg summary"
6592 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
6591 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
6593 (new, len(bheads)))
6592 (new, len(bheads)))
6594
6593
6595 t = []
6594 t = []
6596 draft = len(repo.revs('draft()'))
6595 draft = len(repo.revs('draft()'))
6597 if draft:
6596 if draft:
6598 t.append(_('%d draft') % draft)
6597 t.append(_('%d draft') % draft)
6599 secret = len(repo.revs('secret()'))
6598 secret = len(repo.revs('secret()'))
6600 if secret:
6599 if secret:
6601 t.append(_('%d secret') % secret)
6600 t.append(_('%d secret') % secret)
6602
6601
6603 if draft or secret:
6602 if draft or secret:
6604 ui.status(_('phases: %s\n') % ', '.join(t))
6603 ui.status(_('phases: %s\n') % ', '.join(t))
6605
6604
6606 if obsolete.isenabled(repo, obsolete.createmarkersopt):
6605 if obsolete.isenabled(repo, obsolete.createmarkersopt):
6607 for trouble in ("unstable", "divergent", "bumped"):
6606 for trouble in ("unstable", "divergent", "bumped"):
6608 numtrouble = len(repo.revs(trouble + "()"))
6607 numtrouble = len(repo.revs(trouble + "()"))
6609 # We write all the possibilities to ease translation
6608 # We write all the possibilities to ease translation
6610 troublemsg = {
6609 troublemsg = {
6611 "unstable": _("unstable: %d changesets"),
6610 "unstable": _("unstable: %d changesets"),
6612 "divergent": _("divergent: %d changesets"),
6611 "divergent": _("divergent: %d changesets"),
6613 "bumped": _("bumped: %d changesets"),
6612 "bumped": _("bumped: %d changesets"),
6614 }
6613 }
6615 if numtrouble > 0:
6614 if numtrouble > 0:
6616 ui.status(troublemsg[trouble] % numtrouble + "\n")
6615 ui.status(troublemsg[trouble] % numtrouble + "\n")
6617
6616
6618 cmdutil.summaryhooks(ui, repo)
6617 cmdutil.summaryhooks(ui, repo)
6619
6618
6620 if opts.get('remote'):
6619 if opts.get('remote'):
6621 needsincoming, needsoutgoing = True, True
6620 needsincoming, needsoutgoing = True, True
6622 else:
6621 else:
6623 needsincoming, needsoutgoing = False, False
6622 needsincoming, needsoutgoing = False, False
6624 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
6623 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
6625 if i:
6624 if i:
6626 needsincoming = True
6625 needsincoming = True
6627 if o:
6626 if o:
6628 needsoutgoing = True
6627 needsoutgoing = True
6629 if not needsincoming and not needsoutgoing:
6628 if not needsincoming and not needsoutgoing:
6630 return
6629 return
6631
6630
6632 def getincoming():
6631 def getincoming():
6633 source, branches = hg.parseurl(ui.expandpath('default'))
6632 source, branches = hg.parseurl(ui.expandpath('default'))
6634 sbranch = branches[0]
6633 sbranch = branches[0]
6635 try:
6634 try:
6636 other = hg.peer(repo, {}, source)
6635 other = hg.peer(repo, {}, source)
6637 except error.RepoError:
6636 except error.RepoError:
6638 if opts.get('remote'):
6637 if opts.get('remote'):
6639 raise
6638 raise
6640 return source, sbranch, None, None, None
6639 return source, sbranch, None, None, None
6641 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
6640 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
6642 if revs:
6641 if revs:
6643 revs = [other.lookup(rev) for rev in revs]
6642 revs = [other.lookup(rev) for rev in revs]
6644 ui.debug('comparing with %s\n' % util.hidepassword(source))
6643 ui.debug('comparing with %s\n' % util.hidepassword(source))
6645 repo.ui.pushbuffer()
6644 repo.ui.pushbuffer()
6646 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
6645 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
6647 repo.ui.popbuffer()
6646 repo.ui.popbuffer()
6648 return source, sbranch, other, commoninc, commoninc[1]
6647 return source, sbranch, other, commoninc, commoninc[1]
6649
6648
6650 if needsincoming:
6649 if needsincoming:
6651 source, sbranch, sother, commoninc, incoming = getincoming()
6650 source, sbranch, sother, commoninc, incoming = getincoming()
6652 else:
6651 else:
6653 source = sbranch = sother = commoninc = incoming = None
6652 source = sbranch = sother = commoninc = incoming = None
6654
6653
6655 def getoutgoing():
6654 def getoutgoing():
6656 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
6655 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
6657 dbranch = branches[0]
6656 dbranch = branches[0]
6658 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
6657 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
6659 if source != dest:
6658 if source != dest:
6660 try:
6659 try:
6661 dother = hg.peer(repo, {}, dest)
6660 dother = hg.peer(repo, {}, dest)
6662 except error.RepoError:
6661 except error.RepoError:
6663 if opts.get('remote'):
6662 if opts.get('remote'):
6664 raise
6663 raise
6665 return dest, dbranch, None, None
6664 return dest, dbranch, None, None
6666 ui.debug('comparing with %s\n' % util.hidepassword(dest))
6665 ui.debug('comparing with %s\n' % util.hidepassword(dest))
6667 elif sother is None:
6666 elif sother is None:
6668 # there is no explicit destination peer, but source one is invalid
6667 # there is no explicit destination peer, but source one is invalid
6669 return dest, dbranch, None, None
6668 return dest, dbranch, None, None
6670 else:
6669 else:
6671 dother = sother
6670 dother = sother
6672 if (source != dest or (sbranch is not None and sbranch != dbranch)):
6671 if (source != dest or (sbranch is not None and sbranch != dbranch)):
6673 common = None
6672 common = None
6674 else:
6673 else:
6675 common = commoninc
6674 common = commoninc
6676 if revs:
6675 if revs:
6677 revs = [repo.lookup(rev) for rev in revs]
6676 revs = [repo.lookup(rev) for rev in revs]
6678 repo.ui.pushbuffer()
6677 repo.ui.pushbuffer()
6679 outgoing = discovery.findcommonoutgoing(repo, dother, onlyheads=revs,
6678 outgoing = discovery.findcommonoutgoing(repo, dother, onlyheads=revs,
6680 commoninc=common)
6679 commoninc=common)
6681 repo.ui.popbuffer()
6680 repo.ui.popbuffer()
6682 return dest, dbranch, dother, outgoing
6681 return dest, dbranch, dother, outgoing
6683
6682
6684 if needsoutgoing:
6683 if needsoutgoing:
6685 dest, dbranch, dother, outgoing = getoutgoing()
6684 dest, dbranch, dother, outgoing = getoutgoing()
6686 else:
6685 else:
6687 dest = dbranch = dother = outgoing = None
6686 dest = dbranch = dother = outgoing = None
6688
6687
6689 if opts.get('remote'):
6688 if opts.get('remote'):
6690 t = []
6689 t = []
6691 if incoming:
6690 if incoming:
6692 t.append(_('1 or more incoming'))
6691 t.append(_('1 or more incoming'))
6693 o = outgoing.missing
6692 o = outgoing.missing
6694 if o:
6693 if o:
6695 t.append(_('%d outgoing') % len(o))
6694 t.append(_('%d outgoing') % len(o))
6696 other = dother or sother
6695 other = dother or sother
6697 if 'bookmarks' in other.listkeys('namespaces'):
6696 if 'bookmarks' in other.listkeys('namespaces'):
6698 counts = bookmarks.summary(repo, other)
6697 counts = bookmarks.summary(repo, other)
6699 if counts[0] > 0:
6698 if counts[0] > 0:
6700 t.append(_('%d incoming bookmarks') % counts[0])
6699 t.append(_('%d incoming bookmarks') % counts[0])
6701 if counts[1] > 0:
6700 if counts[1] > 0:
6702 t.append(_('%d outgoing bookmarks') % counts[1])
6701 t.append(_('%d outgoing bookmarks') % counts[1])
6703
6702
6704 if t:
6703 if t:
6705 # i18n: column positioning for "hg summary"
6704 # i18n: column positioning for "hg summary"
6706 ui.write(_('remote: %s\n') % (', '.join(t)))
6705 ui.write(_('remote: %s\n') % (', '.join(t)))
6707 else:
6706 else:
6708 # i18n: column positioning for "hg summary"
6707 # i18n: column positioning for "hg summary"
6709 ui.status(_('remote: (synced)\n'))
6708 ui.status(_('remote: (synced)\n'))
6710
6709
6711 cmdutil.summaryremotehooks(ui, repo, opts,
6710 cmdutil.summaryremotehooks(ui, repo, opts,
6712 ((source, sbranch, sother, commoninc),
6711 ((source, sbranch, sother, commoninc),
6713 (dest, dbranch, dother, outgoing)))
6712 (dest, dbranch, dother, outgoing)))
6714
6713
6715 @command('tag',
6714 @command('tag',
6716 [('f', 'force', None, _('force tag')),
6715 [('f', 'force', None, _('force tag')),
6717 ('l', 'local', None, _('make the tag local')),
6716 ('l', 'local', None, _('make the tag local')),
6718 ('r', 'rev', '', _('revision to tag'), _('REV')),
6717 ('r', 'rev', '', _('revision to tag'), _('REV')),
6719 ('', 'remove', None, _('remove a tag')),
6718 ('', 'remove', None, _('remove a tag')),
6720 # -l/--local is already there, commitopts cannot be used
6719 # -l/--local is already there, commitopts cannot be used
6721 ('e', 'edit', None, _('invoke editor on commit messages')),
6720 ('e', 'edit', None, _('invoke editor on commit messages')),
6722 ('m', 'message', '', _('use text as commit message'), _('TEXT')),
6721 ('m', 'message', '', _('use text as commit message'), _('TEXT')),
6723 ] + commitopts2,
6722 ] + commitopts2,
6724 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
6723 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
6725 def tag(ui, repo, name1, *names, **opts):
6724 def tag(ui, repo, name1, *names, **opts):
6726 """add one or more tags for the current or given revision
6725 """add one or more tags for the current or given revision
6727
6726
6728 Name a particular revision using <name>.
6727 Name a particular revision using <name>.
6729
6728
6730 Tags are used to name particular revisions of the repository and are
6729 Tags are used to name particular revisions of the repository and are
6731 very useful to compare different revisions, to go back to significant
6730 very useful to compare different revisions, to go back to significant
6732 earlier versions or to mark branch points as releases, etc. Changing
6731 earlier versions or to mark branch points as releases, etc. Changing
6733 an existing tag is normally disallowed; use -f/--force to override.
6732 an existing tag is normally disallowed; use -f/--force to override.
6734
6733
6735 If no revision is given, the parent of the working directory is
6734 If no revision is given, the parent of the working directory is
6736 used.
6735 used.
6737
6736
6738 To facilitate version control, distribution, and merging of tags,
6737 To facilitate version control, distribution, and merging of tags,
6739 they are stored as a file named ".hgtags" which is managed similarly
6738 they are stored as a file named ".hgtags" which is managed similarly
6740 to other project files and can be hand-edited if necessary. This
6739 to other project files and can be hand-edited if necessary. This
6741 also means that tagging creates a new commit. The file
6740 also means that tagging creates a new commit. The file
6742 ".hg/localtags" is used for local tags (not shared among
6741 ".hg/localtags" is used for local tags (not shared among
6743 repositories).
6742 repositories).
6744
6743
6745 Tag commits are usually made at the head of a branch. If the parent
6744 Tag commits are usually made at the head of a branch. If the parent
6746 of the working directory is not a branch head, :hg:`tag` aborts; use
6745 of the working directory is not a branch head, :hg:`tag` aborts; use
6747 -f/--force to force the tag commit to be based on a non-head
6746 -f/--force to force the tag commit to be based on a non-head
6748 changeset.
6747 changeset.
6749
6748
6750 See :hg:`help dates` for a list of formats valid for -d/--date.
6749 See :hg:`help dates` for a list of formats valid for -d/--date.
6751
6750
6752 Since tag names have priority over branch names during revision
6751 Since tag names have priority over branch names during revision
6753 lookup, using an existing branch name as a tag name is discouraged.
6752 lookup, using an existing branch name as a tag name is discouraged.
6754
6753
6755 Returns 0 on success.
6754 Returns 0 on success.
6756 """
6755 """
6757 wlock = lock = None
6756 wlock = lock = None
6758 try:
6757 try:
6759 wlock = repo.wlock()
6758 wlock = repo.wlock()
6760 lock = repo.lock()
6759 lock = repo.lock()
6761 rev_ = "."
6760 rev_ = "."
6762 names = [t.strip() for t in (name1,) + names]
6761 names = [t.strip() for t in (name1,) + names]
6763 if len(names) != len(set(names)):
6762 if len(names) != len(set(names)):
6764 raise error.Abort(_('tag names must be unique'))
6763 raise error.Abort(_('tag names must be unique'))
6765 for n in names:
6764 for n in names:
6766 scmutil.checknewlabel(repo, n, 'tag')
6765 scmutil.checknewlabel(repo, n, 'tag')
6767 if not n:
6766 if not n:
6768 raise error.Abort(_('tag names cannot consist entirely of '
6767 raise error.Abort(_('tag names cannot consist entirely of '
6769 'whitespace'))
6768 'whitespace'))
6770 if opts.get('rev') and opts.get('remove'):
6769 if opts.get('rev') and opts.get('remove'):
6771 raise error.Abort(_("--rev and --remove are incompatible"))
6770 raise error.Abort(_("--rev and --remove are incompatible"))
6772 if opts.get('rev'):
6771 if opts.get('rev'):
6773 rev_ = opts['rev']
6772 rev_ = opts['rev']
6774 message = opts.get('message')
6773 message = opts.get('message')
6775 if opts.get('remove'):
6774 if opts.get('remove'):
6776 if opts.get('local'):
6775 if opts.get('local'):
6777 expectedtype = 'local'
6776 expectedtype = 'local'
6778 else:
6777 else:
6779 expectedtype = 'global'
6778 expectedtype = 'global'
6780
6779
6781 for n in names:
6780 for n in names:
6782 if not repo.tagtype(n):
6781 if not repo.tagtype(n):
6783 raise error.Abort(_("tag '%s' does not exist") % n)
6782 raise error.Abort(_("tag '%s' does not exist") % n)
6784 if repo.tagtype(n) != expectedtype:
6783 if repo.tagtype(n) != expectedtype:
6785 if expectedtype == 'global':
6784 if expectedtype == 'global':
6786 raise error.Abort(_("tag '%s' is not a global tag") % n)
6785 raise error.Abort(_("tag '%s' is not a global tag") % n)
6787 else:
6786 else:
6788 raise error.Abort(_("tag '%s' is not a local tag") % n)
6787 raise error.Abort(_("tag '%s' is not a local tag") % n)
6789 rev_ = 'null'
6788 rev_ = 'null'
6790 if not message:
6789 if not message:
6791 # we don't translate commit messages
6790 # we don't translate commit messages
6792 message = 'Removed tag %s' % ', '.join(names)
6791 message = 'Removed tag %s' % ', '.join(names)
6793 elif not opts.get('force'):
6792 elif not opts.get('force'):
6794 for n in names:
6793 for n in names:
6795 if n in repo.tags():
6794 if n in repo.tags():
6796 raise error.Abort(_("tag '%s' already exists "
6795 raise error.Abort(_("tag '%s' already exists "
6797 "(use -f to force)") % n)
6796 "(use -f to force)") % n)
6798 if not opts.get('local'):
6797 if not opts.get('local'):
6799 p1, p2 = repo.dirstate.parents()
6798 p1, p2 = repo.dirstate.parents()
6800 if p2 != nullid:
6799 if p2 != nullid:
6801 raise error.Abort(_('uncommitted merge'))
6800 raise error.Abort(_('uncommitted merge'))
6802 bheads = repo.branchheads()
6801 bheads = repo.branchheads()
6803 if not opts.get('force') and bheads and p1 not in bheads:
6802 if not opts.get('force') and bheads and p1 not in bheads:
6804 raise error.Abort(_('working directory is not at a branch head '
6803 raise error.Abort(_('working directory is not at a branch head '
6805 '(use -f to force)'))
6804 '(use -f to force)'))
6806 r = scmutil.revsingle(repo, rev_).node()
6805 r = scmutil.revsingle(repo, rev_).node()
6807
6806
6808 if not message:
6807 if not message:
6809 # we don't translate commit messages
6808 # we don't translate commit messages
6810 message = ('Added tag %s for changeset %s' %
6809 message = ('Added tag %s for changeset %s' %
6811 (', '.join(names), short(r)))
6810 (', '.join(names), short(r)))
6812
6811
6813 date = opts.get('date')
6812 date = opts.get('date')
6814 if date:
6813 if date:
6815 date = util.parsedate(date)
6814 date = util.parsedate(date)
6816
6815
6817 if opts.get('remove'):
6816 if opts.get('remove'):
6818 editform = 'tag.remove'
6817 editform = 'tag.remove'
6819 else:
6818 else:
6820 editform = 'tag.add'
6819 editform = 'tag.add'
6821 editor = cmdutil.getcommiteditor(editform=editform, **opts)
6820 editor = cmdutil.getcommiteditor(editform=editform, **opts)
6822
6821
6823 # don't allow tagging the null rev
6822 # don't allow tagging the null rev
6824 if (not opts.get('remove') and
6823 if (not opts.get('remove') and
6825 scmutil.revsingle(repo, rev_).rev() == nullrev):
6824 scmutil.revsingle(repo, rev_).rev() == nullrev):
6826 raise error.Abort(_("cannot tag null revision"))
6825 raise error.Abort(_("cannot tag null revision"))
6827
6826
6828 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date,
6827 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date,
6829 editor=editor)
6828 editor=editor)
6830 finally:
6829 finally:
6831 release(lock, wlock)
6830 release(lock, wlock)
6832
6831
6833 @command('tags', formatteropts, '')
6832 @command('tags', formatteropts, '')
6834 def tags(ui, repo, **opts):
6833 def tags(ui, repo, **opts):
6835 """list repository tags
6834 """list repository tags
6836
6835
6837 This lists both regular and local tags. When the -v/--verbose
6836 This lists both regular and local tags. When the -v/--verbose
6838 switch is used, a third column "local" is printed for local tags.
6837 switch is used, a third column "local" is printed for local tags.
6839 When the -q/--quiet switch is used, only the tag name is printed.
6838 When the -q/--quiet switch is used, only the tag name is printed.
6840
6839
6841 Returns 0 on success.
6840 Returns 0 on success.
6842 """
6841 """
6843
6842
6844 fm = ui.formatter('tags', opts)
6843 fm = ui.formatter('tags', opts)
6845 hexfunc = fm.hexfunc
6844 hexfunc = fm.hexfunc
6846 tagtype = ""
6845 tagtype = ""
6847
6846
6848 for t, n in reversed(repo.tagslist()):
6847 for t, n in reversed(repo.tagslist()):
6849 hn = hexfunc(n)
6848 hn = hexfunc(n)
6850 label = 'tags.normal'
6849 label = 'tags.normal'
6851 tagtype = ''
6850 tagtype = ''
6852 if repo.tagtype(t) == 'local':
6851 if repo.tagtype(t) == 'local':
6853 label = 'tags.local'
6852 label = 'tags.local'
6854 tagtype = 'local'
6853 tagtype = 'local'
6855
6854
6856 fm.startitem()
6855 fm.startitem()
6857 fm.write('tag', '%s', t, label=label)
6856 fm.write('tag', '%s', t, label=label)
6858 fmt = " " * (30 - encoding.colwidth(t)) + ' %5d:%s'
6857 fmt = " " * (30 - encoding.colwidth(t)) + ' %5d:%s'
6859 fm.condwrite(not ui.quiet, 'rev node', fmt,
6858 fm.condwrite(not ui.quiet, 'rev node', fmt,
6860 repo.changelog.rev(n), hn, label=label)
6859 repo.changelog.rev(n), hn, label=label)
6861 fm.condwrite(ui.verbose and tagtype, 'type', ' %s',
6860 fm.condwrite(ui.verbose and tagtype, 'type', ' %s',
6862 tagtype, label=label)
6861 tagtype, label=label)
6863 fm.plain('\n')
6862 fm.plain('\n')
6864 fm.end()
6863 fm.end()
6865
6864
6866 @command('tip',
6865 @command('tip',
6867 [('p', 'patch', None, _('show patch')),
6866 [('p', 'patch', None, _('show patch')),
6868 ('g', 'git', None, _('use git extended diff format')),
6867 ('g', 'git', None, _('use git extended diff format')),
6869 ] + templateopts,
6868 ] + templateopts,
6870 _('[-p] [-g]'))
6869 _('[-p] [-g]'))
6871 def tip(ui, repo, **opts):
6870 def tip(ui, repo, **opts):
6872 """show the tip revision (DEPRECATED)
6871 """show the tip revision (DEPRECATED)
6873
6872
6874 The tip revision (usually just called the tip) is the changeset
6873 The tip revision (usually just called the tip) is the changeset
6875 most recently added to the repository (and therefore the most
6874 most recently added to the repository (and therefore the most
6876 recently changed head).
6875 recently changed head).
6877
6876
6878 If you have just made a commit, that commit will be the tip. If
6877 If you have just made a commit, that commit will be the tip. If
6879 you have just pulled changes from another repository, the tip of
6878 you have just pulled changes from another repository, the tip of
6880 that repository becomes the current tip. The "tip" tag is special
6879 that repository becomes the current tip. The "tip" tag is special
6881 and cannot be renamed or assigned to a different changeset.
6880 and cannot be renamed or assigned to a different changeset.
6882
6881
6883 This command is deprecated, please use :hg:`heads` instead.
6882 This command is deprecated, please use :hg:`heads` instead.
6884
6883
6885 Returns 0 on success.
6884 Returns 0 on success.
6886 """
6885 """
6887 displayer = cmdutil.show_changeset(ui, repo, opts)
6886 displayer = cmdutil.show_changeset(ui, repo, opts)
6888 displayer.show(repo['tip'])
6887 displayer.show(repo['tip'])
6889 displayer.close()
6888 displayer.close()
6890
6889
6891 @command('unbundle',
6890 @command('unbundle',
6892 [('u', 'update', None,
6891 [('u', 'update', None,
6893 _('update to new branch head if changesets were unbundled'))],
6892 _('update to new branch head if changesets were unbundled'))],
6894 _('[-u] FILE...'))
6893 _('[-u] FILE...'))
6895 def unbundle(ui, repo, fname1, *fnames, **opts):
6894 def unbundle(ui, repo, fname1, *fnames, **opts):
6896 """apply one or more changegroup files
6895 """apply one or more changegroup files
6897
6896
6898 Apply one or more compressed changegroup files generated by the
6897 Apply one or more compressed changegroup files generated by the
6899 bundle command.
6898 bundle command.
6900
6899
6901 Returns 0 on success, 1 if an update has unresolved files.
6900 Returns 0 on success, 1 if an update has unresolved files.
6902 """
6901 """
6903 fnames = (fname1,) + fnames
6902 fnames = (fname1,) + fnames
6904
6903
6905 with repo.lock():
6904 with repo.lock():
6906 for fname in fnames:
6905 for fname in fnames:
6907 f = hg.openpath(ui, fname)
6906 f = hg.openpath(ui, fname)
6908 gen = exchange.readbundle(ui, f, fname)
6907 gen = exchange.readbundle(ui, f, fname)
6909 if isinstance(gen, bundle2.unbundle20):
6908 if isinstance(gen, bundle2.unbundle20):
6910 tr = repo.transaction('unbundle')
6909 tr = repo.transaction('unbundle')
6911 try:
6910 try:
6912 op = bundle2.applybundle(repo, gen, tr, source='unbundle',
6911 op = bundle2.applybundle(repo, gen, tr, source='unbundle',
6913 url='bundle:' + fname)
6912 url='bundle:' + fname)
6914 tr.close()
6913 tr.close()
6915 except error.BundleUnknownFeatureError as exc:
6914 except error.BundleUnknownFeatureError as exc:
6916 raise error.Abort(_('%s: unknown bundle feature, %s')
6915 raise error.Abort(_('%s: unknown bundle feature, %s')
6917 % (fname, exc),
6916 % (fname, exc),
6918 hint=_("see https://mercurial-scm.org/"
6917 hint=_("see https://mercurial-scm.org/"
6919 "wiki/BundleFeature for more "
6918 "wiki/BundleFeature for more "
6920 "information"))
6919 "information"))
6921 finally:
6920 finally:
6922 if tr:
6921 if tr:
6923 tr.release()
6922 tr.release()
6924 changes = [r.get('return', 0)
6923 changes = [r.get('return', 0)
6925 for r in op.records['changegroup']]
6924 for r in op.records['changegroup']]
6926 modheads = changegroup.combineresults(changes)
6925 modheads = changegroup.combineresults(changes)
6927 elif isinstance(gen, streamclone.streamcloneapplier):
6926 elif isinstance(gen, streamclone.streamcloneapplier):
6928 raise error.Abort(
6927 raise error.Abort(
6929 _('packed bundles cannot be applied with '
6928 _('packed bundles cannot be applied with '
6930 '"hg unbundle"'),
6929 '"hg unbundle"'),
6931 hint=_('use "hg debugapplystreamclonebundle"'))
6930 hint=_('use "hg debugapplystreamclonebundle"'))
6932 else:
6931 else:
6933 modheads = gen.apply(repo, 'unbundle', 'bundle:' + fname)
6932 modheads = gen.apply(repo, 'unbundle', 'bundle:' + fname)
6934
6933
6935 return postincoming(ui, repo, modheads, opts.get('update'), None, None)
6934 return postincoming(ui, repo, modheads, opts.get('update'), None, None)
6936
6935
6937 @command('^update|up|checkout|co',
6936 @command('^update|up|checkout|co',
6938 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
6937 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
6939 ('c', 'check', None, _('require clean working directory')),
6938 ('c', 'check', None, _('require clean working directory')),
6940 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
6939 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
6941 ('r', 'rev', '', _('revision'), _('REV'))
6940 ('r', 'rev', '', _('revision'), _('REV'))
6942 ] + mergetoolopts,
6941 ] + mergetoolopts,
6943 _('[-c] [-C] [-d DATE] [[-r] REV]'))
6942 _('[-c] [-C] [-d DATE] [[-r] REV]'))
6944 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False,
6943 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False,
6945 tool=None):
6944 tool=None):
6946 """update working directory (or switch revisions)
6945 """update working directory (or switch revisions)
6947
6946
6948 Update the repository's working directory to the specified
6947 Update the repository's working directory to the specified
6949 changeset. If no changeset is specified, update to the tip of the
6948 changeset. If no changeset is specified, update to the tip of the
6950 current named branch and move the active bookmark (see :hg:`help
6949 current named branch and move the active bookmark (see :hg:`help
6951 bookmarks`).
6950 bookmarks`).
6952
6951
6953 Update sets the working directory's parent revision to the specified
6952 Update sets the working directory's parent revision to the specified
6954 changeset (see :hg:`help parents`).
6953 changeset (see :hg:`help parents`).
6955
6954
6956 If the changeset is not a descendant or ancestor of the working
6955 If the changeset is not a descendant or ancestor of the working
6957 directory's parent, the update is aborted. With the -c/--check
6956 directory's parent, the update is aborted. With the -c/--check
6958 option, the working directory is checked for uncommitted changes; if
6957 option, the working directory is checked for uncommitted changes; if
6959 none are found, the working directory is updated to the specified
6958 none are found, the working directory is updated to the specified
6960 changeset.
6959 changeset.
6961
6960
6962 .. container:: verbose
6961 .. container:: verbose
6963
6962
6964 The following rules apply when the working directory contains
6963 The following rules apply when the working directory contains
6965 uncommitted changes:
6964 uncommitted changes:
6966
6965
6967 1. If neither -c/--check nor -C/--clean is specified, and if
6966 1. If neither -c/--check nor -C/--clean is specified, and if
6968 the requested changeset is an ancestor or descendant of
6967 the requested changeset is an ancestor or descendant of
6969 the working directory's parent, the uncommitted changes
6968 the working directory's parent, the uncommitted changes
6970 are merged into the requested changeset and the merged
6969 are merged into the requested changeset and the merged
6971 result is left uncommitted. If the requested changeset is
6970 result is left uncommitted. If the requested changeset is
6972 not an ancestor or descendant (that is, it is on another
6971 not an ancestor or descendant (that is, it is on another
6973 branch), the update is aborted and the uncommitted changes
6972 branch), the update is aborted and the uncommitted changes
6974 are preserved.
6973 are preserved.
6975
6974
6976 2. With the -c/--check option, the update is aborted and the
6975 2. With the -c/--check option, the update is aborted and the
6977 uncommitted changes are preserved.
6976 uncommitted changes are preserved.
6978
6977
6979 3. With the -C/--clean option, uncommitted changes are discarded and
6978 3. With the -C/--clean option, uncommitted changes are discarded and
6980 the working directory is updated to the requested changeset.
6979 the working directory is updated to the requested changeset.
6981
6980
6982 To cancel an uncommitted merge (and lose your changes), use
6981 To cancel an uncommitted merge (and lose your changes), use
6983 :hg:`update --clean .`.
6982 :hg:`update --clean .`.
6984
6983
6985 Use null as the changeset to remove the working directory (like
6984 Use null as the changeset to remove the working directory (like
6986 :hg:`clone -U`).
6985 :hg:`clone -U`).
6987
6986
6988 If you want to revert just one file to an older revision, use
6987 If you want to revert just one file to an older revision, use
6989 :hg:`revert [-r REV] NAME`.
6988 :hg:`revert [-r REV] NAME`.
6990
6989
6991 See :hg:`help dates` for a list of formats valid for -d/--date.
6990 See :hg:`help dates` for a list of formats valid for -d/--date.
6992
6991
6993 Returns 0 on success, 1 if there are unresolved files.
6992 Returns 0 on success, 1 if there are unresolved files.
6994 """
6993 """
6995 if rev and node:
6994 if rev and node:
6996 raise error.Abort(_("please specify just one revision"))
6995 raise error.Abort(_("please specify just one revision"))
6997
6996
6998 if rev is None or rev == '':
6997 if rev is None or rev == '':
6999 rev = node
6998 rev = node
7000
6999
7001 if date and rev is not None:
7000 if date and rev is not None:
7002 raise error.Abort(_("you can't specify a revision and a date"))
7001 raise error.Abort(_("you can't specify a revision and a date"))
7003
7002
7004 if check and clean:
7003 if check and clean:
7005 raise error.Abort(_("cannot specify both -c/--check and -C/--clean"))
7004 raise error.Abort(_("cannot specify both -c/--check and -C/--clean"))
7006
7005
7007 with repo.wlock():
7006 with repo.wlock():
7008 cmdutil.clearunfinished(repo)
7007 cmdutil.clearunfinished(repo)
7009
7008
7010 if date:
7009 if date:
7011 rev = cmdutil.finddate(ui, repo, date)
7010 rev = cmdutil.finddate(ui, repo, date)
7012
7011
7013 # if we defined a bookmark, we have to remember the original name
7012 # if we defined a bookmark, we have to remember the original name
7014 brev = rev
7013 brev = rev
7015 rev = scmutil.revsingle(repo, rev, rev).rev()
7014 rev = scmutil.revsingle(repo, rev, rev).rev()
7016
7015
7017 if check:
7016 if check:
7018 cmdutil.bailifchanged(repo, merge=False)
7017 cmdutil.bailifchanged(repo, merge=False)
7019
7018
7020 repo.ui.setconfig('ui', 'forcemerge', tool, 'update')
7019 repo.ui.setconfig('ui', 'forcemerge', tool, 'update')
7021
7020
7022 return hg.updatetotally(ui, repo, rev, brev, clean=clean, check=check)
7021 return hg.updatetotally(ui, repo, rev, brev, clean=clean, check=check)
7023
7022
7024 @command('verify', [])
7023 @command('verify', [])
7025 def verify(ui, repo):
7024 def verify(ui, repo):
7026 """verify the integrity of the repository
7025 """verify the integrity of the repository
7027
7026
7028 Verify the integrity of the current repository.
7027 Verify the integrity of the current repository.
7029
7028
7030 This will perform an extensive check of the repository's
7029 This will perform an extensive check of the repository's
7031 integrity, validating the hashes and checksums of each entry in
7030 integrity, validating the hashes and checksums of each entry in
7032 the changelog, manifest, and tracked files, as well as the
7031 the changelog, manifest, and tracked files, as well as the
7033 integrity of their crosslinks and indices.
7032 integrity of their crosslinks and indices.
7034
7033
7035 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
7034 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
7036 for more information about recovery from corruption of the
7035 for more information about recovery from corruption of the
7037 repository.
7036 repository.
7038
7037
7039 Returns 0 on success, 1 if errors are encountered.
7038 Returns 0 on success, 1 if errors are encountered.
7040 """
7039 """
7041 return hg.verify(repo)
7040 return hg.verify(repo)
7042
7041
7043 @command('version', [] + formatteropts, norepo=True)
7042 @command('version', [] + formatteropts, norepo=True)
7044 def version_(ui, **opts):
7043 def version_(ui, **opts):
7045 """output version and copyright information"""
7044 """output version and copyright information"""
7046 fm = ui.formatter("version", opts)
7045 fm = ui.formatter("version", opts)
7047 fm.startitem()
7046 fm.startitem()
7048 fm.write("ver", _("Mercurial Distributed SCM (version %s)\n"),
7047 fm.write("ver", _("Mercurial Distributed SCM (version %s)\n"),
7049 util.version())
7048 util.version())
7050 license = _(
7049 license = _(
7051 "(see https://mercurial-scm.org for more information)\n"
7050 "(see https://mercurial-scm.org for more information)\n"
7052 "\nCopyright (C) 2005-2016 Matt Mackall and others\n"
7051 "\nCopyright (C) 2005-2016 Matt Mackall and others\n"
7053 "This is free software; see the source for copying conditions. "
7052 "This is free software; see the source for copying conditions. "
7054 "There is NO\nwarranty; "
7053 "There is NO\nwarranty; "
7055 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
7054 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
7056 )
7055 )
7057 if not ui.quiet:
7056 if not ui.quiet:
7058 fm.plain(license)
7057 fm.plain(license)
7059
7058
7060 if ui.verbose:
7059 if ui.verbose:
7061 fm.plain(_("\nEnabled extensions:\n\n"))
7060 fm.plain(_("\nEnabled extensions:\n\n"))
7062 # format names and versions into columns
7061 # format names and versions into columns
7063 names = []
7062 names = []
7064 vers = []
7063 vers = []
7065 isinternals = []
7064 isinternals = []
7066 for name, module in extensions.extensions():
7065 for name, module in extensions.extensions():
7067 names.append(name)
7066 names.append(name)
7068 vers.append(extensions.moduleversion(module) or None)
7067 vers.append(extensions.moduleversion(module) or None)
7069 isinternals.append(extensions.ismoduleinternal(module))
7068 isinternals.append(extensions.ismoduleinternal(module))
7070 fn = fm.nested("extensions")
7069 fn = fm.nested("extensions")
7071 if names:
7070 if names:
7072 namefmt = " %%-%ds " % max(len(n) for n in names)
7071 namefmt = " %%-%ds " % max(len(n) for n in names)
7073 places = [_("external"), _("internal")]
7072 places = [_("external"), _("internal")]
7074 for n, v, p in zip(names, vers, isinternals):
7073 for n, v, p in zip(names, vers, isinternals):
7075 fn.startitem()
7074 fn.startitem()
7076 fn.condwrite(ui.verbose, "name", namefmt, n)
7075 fn.condwrite(ui.verbose, "name", namefmt, n)
7077 if ui.verbose:
7076 if ui.verbose:
7078 fn.plain("%s " % places[p])
7077 fn.plain("%s " % places[p])
7079 fn.data(bundled=p)
7078 fn.data(bundled=p)
7080 fn.condwrite(ui.verbose and v, "ver", "%s", v)
7079 fn.condwrite(ui.verbose and v, "ver", "%s", v)
7081 if ui.verbose:
7080 if ui.verbose:
7082 fn.plain("\n")
7081 fn.plain("\n")
7083 fn.end()
7082 fn.end()
7084 fm.end()
7083 fm.end()
7085
7084
7086 def loadcmdtable(ui, name, cmdtable):
7085 def loadcmdtable(ui, name, cmdtable):
7087 """Load command functions from specified cmdtable
7086 """Load command functions from specified cmdtable
7088 """
7087 """
7089 overrides = [cmd for cmd in cmdtable if cmd in table]
7088 overrides = [cmd for cmd in cmdtable if cmd in table]
7090 if overrides:
7089 if overrides:
7091 ui.warn(_("extension '%s' overrides commands: %s\n")
7090 ui.warn(_("extension '%s' overrides commands: %s\n")
7092 % (name, " ".join(overrides)))
7091 % (name, " ".join(overrides)))
7093 table.update(cmdtable)
7092 table.update(cmdtable)
@@ -1,543 +1,531 b''
1 # commandserver.py - communicate with Mercurial's API over a pipe
1 # commandserver.py - communicate with Mercurial's API over a pipe
2 #
2 #
3 # Copyright Matt Mackall <mpm@selenic.com>
3 # Copyright 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 errno
10 import errno
11 import gc
11 import gc
12 import os
12 import os
13 import random
13 import random
14 import select
14 import select
15 import signal
15 import signal
16 import socket
16 import socket
17 import struct
17 import struct
18 import traceback
18 import traceback
19
19
20 from .i18n import _
20 from .i18n import _
21 from . import (
21 from . import (
22 encoding,
22 encoding,
23 error,
23 error,
24 util,
24 util,
25 )
25 )
26
26
27 logfile = None
27 logfile = None
28
28
29 def log(*args):
29 def log(*args):
30 if not logfile:
30 if not logfile:
31 return
31 return
32
32
33 for a in args:
33 for a in args:
34 logfile.write(str(a))
34 logfile.write(str(a))
35
35
36 logfile.flush()
36 logfile.flush()
37
37
38 class channeledoutput(object):
38 class channeledoutput(object):
39 """
39 """
40 Write data to out in the following format:
40 Write data to out in the following format:
41
41
42 data length (unsigned int),
42 data length (unsigned int),
43 data
43 data
44 """
44 """
45 def __init__(self, out, channel):
45 def __init__(self, out, channel):
46 self.out = out
46 self.out = out
47 self.channel = channel
47 self.channel = channel
48
48
49 @property
49 @property
50 def name(self):
50 def name(self):
51 return '<%c-channel>' % self.channel
51 return '<%c-channel>' % self.channel
52
52
53 def write(self, data):
53 def write(self, data):
54 if not data:
54 if not data:
55 return
55 return
56 # single write() to guarantee the same atomicity as the underlying file
56 # single write() to guarantee the same atomicity as the underlying file
57 self.out.write(struct.pack('>cI', self.channel, len(data)) + data)
57 self.out.write(struct.pack('>cI', self.channel, len(data)) + data)
58 self.out.flush()
58 self.out.flush()
59
59
60 def __getattr__(self, attr):
60 def __getattr__(self, attr):
61 if attr in ('isatty', 'fileno', 'tell', 'seek'):
61 if attr in ('isatty', 'fileno', 'tell', 'seek'):
62 raise AttributeError(attr)
62 raise AttributeError(attr)
63 return getattr(self.out, attr)
63 return getattr(self.out, attr)
64
64
65 class channeledinput(object):
65 class channeledinput(object):
66 """
66 """
67 Read data from in_.
67 Read data from in_.
68
68
69 Requests for input are written to out in the following format:
69 Requests for input are written to out in the following format:
70 channel identifier - 'I' for plain input, 'L' line based (1 byte)
70 channel identifier - 'I' for plain input, 'L' line based (1 byte)
71 how many bytes to send at most (unsigned int),
71 how many bytes to send at most (unsigned int),
72
72
73 The client replies with:
73 The client replies with:
74 data length (unsigned int), 0 meaning EOF
74 data length (unsigned int), 0 meaning EOF
75 data
75 data
76 """
76 """
77
77
78 maxchunksize = 4 * 1024
78 maxchunksize = 4 * 1024
79
79
80 def __init__(self, in_, out, channel):
80 def __init__(self, in_, out, channel):
81 self.in_ = in_
81 self.in_ = in_
82 self.out = out
82 self.out = out
83 self.channel = channel
83 self.channel = channel
84
84
85 @property
85 @property
86 def name(self):
86 def name(self):
87 return '<%c-channel>' % self.channel
87 return '<%c-channel>' % self.channel
88
88
89 def read(self, size=-1):
89 def read(self, size=-1):
90 if size < 0:
90 if size < 0:
91 # if we need to consume all the clients input, ask for 4k chunks
91 # if we need to consume all the clients input, ask for 4k chunks
92 # so the pipe doesn't fill up risking a deadlock
92 # so the pipe doesn't fill up risking a deadlock
93 size = self.maxchunksize
93 size = self.maxchunksize
94 s = self._read(size, self.channel)
94 s = self._read(size, self.channel)
95 buf = s
95 buf = s
96 while s:
96 while s:
97 s = self._read(size, self.channel)
97 s = self._read(size, self.channel)
98 buf += s
98 buf += s
99
99
100 return buf
100 return buf
101 else:
101 else:
102 return self._read(size, self.channel)
102 return self._read(size, self.channel)
103
103
104 def _read(self, size, channel):
104 def _read(self, size, channel):
105 if not size:
105 if not size:
106 return ''
106 return ''
107 assert size > 0
107 assert size > 0
108
108
109 # tell the client we need at most size bytes
109 # tell the client we need at most size bytes
110 self.out.write(struct.pack('>cI', channel, size))
110 self.out.write(struct.pack('>cI', channel, size))
111 self.out.flush()
111 self.out.flush()
112
112
113 length = self.in_.read(4)
113 length = self.in_.read(4)
114 length = struct.unpack('>I', length)[0]
114 length = struct.unpack('>I', length)[0]
115 if not length:
115 if not length:
116 return ''
116 return ''
117 else:
117 else:
118 return self.in_.read(length)
118 return self.in_.read(length)
119
119
120 def readline(self, size=-1):
120 def readline(self, size=-1):
121 if size < 0:
121 if size < 0:
122 size = self.maxchunksize
122 size = self.maxchunksize
123 s = self._read(size, 'L')
123 s = self._read(size, 'L')
124 buf = s
124 buf = s
125 # keep asking for more until there's either no more or
125 # keep asking for more until there's either no more or
126 # we got a full line
126 # we got a full line
127 while s and s[-1] != '\n':
127 while s and s[-1] != '\n':
128 s = self._read(size, 'L')
128 s = self._read(size, 'L')
129 buf += s
129 buf += s
130
130
131 return buf
131 return buf
132 else:
132 else:
133 return self._read(size, 'L')
133 return self._read(size, 'L')
134
134
135 def __iter__(self):
135 def __iter__(self):
136 return self
136 return self
137
137
138 def next(self):
138 def next(self):
139 l = self.readline()
139 l = self.readline()
140 if not l:
140 if not l:
141 raise StopIteration
141 raise StopIteration
142 return l
142 return l
143
143
144 def __getattr__(self, attr):
144 def __getattr__(self, attr):
145 if attr in ('isatty', 'fileno', 'tell', 'seek'):
145 if attr in ('isatty', 'fileno', 'tell', 'seek'):
146 raise AttributeError(attr)
146 raise AttributeError(attr)
147 return getattr(self.in_, attr)
147 return getattr(self.in_, attr)
148
148
149 class server(object):
149 class server(object):
150 """
150 """
151 Listens for commands on fin, runs them and writes the output on a channel
151 Listens for commands on fin, runs them and writes the output on a channel
152 based stream to fout.
152 based stream to fout.
153 """
153 """
154 def __init__(self, ui, repo, fin, fout):
154 def __init__(self, ui, repo, fin, fout):
155 self.cwd = os.getcwd()
155 self.cwd = os.getcwd()
156
156
157 # developer config: cmdserver.log
157 # developer config: cmdserver.log
158 logpath = ui.config("cmdserver", "log", None)
158 logpath = ui.config("cmdserver", "log", None)
159 if logpath:
159 if logpath:
160 global logfile
160 global logfile
161 if logpath == '-':
161 if logpath == '-':
162 # write log on a special 'd' (debug) channel
162 # write log on a special 'd' (debug) channel
163 logfile = channeledoutput(fout, 'd')
163 logfile = channeledoutput(fout, 'd')
164 else:
164 else:
165 logfile = open(logpath, 'a')
165 logfile = open(logpath, 'a')
166
166
167 if repo:
167 if repo:
168 # the ui here is really the repo ui so take its baseui so we don't
168 # the ui here is really the repo ui so take its baseui so we don't
169 # end up with its local configuration
169 # end up with its local configuration
170 self.ui = repo.baseui
170 self.ui = repo.baseui
171 self.repo = repo
171 self.repo = repo
172 self.repoui = repo.ui
172 self.repoui = repo.ui
173 else:
173 else:
174 self.ui = ui
174 self.ui = ui
175 self.repo = self.repoui = None
175 self.repo = self.repoui = None
176
176
177 self.cerr = channeledoutput(fout, 'e')
177 self.cerr = channeledoutput(fout, 'e')
178 self.cout = channeledoutput(fout, 'o')
178 self.cout = channeledoutput(fout, 'o')
179 self.cin = channeledinput(fin, fout, 'I')
179 self.cin = channeledinput(fin, fout, 'I')
180 self.cresult = channeledoutput(fout, 'r')
180 self.cresult = channeledoutput(fout, 'r')
181
181
182 self.client = fin
182 self.client = fin
183
183
184 def cleanup(self):
184 def cleanup(self):
185 """release and restore resources taken during server session"""
185 """release and restore resources taken during server session"""
186 pass
186 pass
187
187
188 def _read(self, size):
188 def _read(self, size):
189 if not size:
189 if not size:
190 return ''
190 return ''
191
191
192 data = self.client.read(size)
192 data = self.client.read(size)
193
193
194 # is the other end closed?
194 # is the other end closed?
195 if not data:
195 if not data:
196 raise EOFError
196 raise EOFError
197
197
198 return data
198 return data
199
199
200 def _readstr(self):
200 def _readstr(self):
201 """read a string from the channel
201 """read a string from the channel
202
202
203 format:
203 format:
204 data length (uint32), data
204 data length (uint32), data
205 """
205 """
206 length = struct.unpack('>I', self._read(4))[0]
206 length = struct.unpack('>I', self._read(4))[0]
207 if not length:
207 if not length:
208 return ''
208 return ''
209 return self._read(length)
209 return self._read(length)
210
210
211 def _readlist(self):
211 def _readlist(self):
212 """read a list of NULL separated strings from the channel"""
212 """read a list of NULL separated strings from the channel"""
213 s = self._readstr()
213 s = self._readstr()
214 if s:
214 if s:
215 return s.split('\0')
215 return s.split('\0')
216 else:
216 else:
217 return []
217 return []
218
218
219 def runcommand(self):
219 def runcommand(self):
220 """ reads a list of \0 terminated arguments, executes
220 """ reads a list of \0 terminated arguments, executes
221 and writes the return code to the result channel """
221 and writes the return code to the result channel """
222 from . import dispatch # avoid cycle
222 from . import dispatch # avoid cycle
223
223
224 args = self._readlist()
224 args = self._readlist()
225
225
226 # copy the uis so changes (e.g. --config or --verbose) don't
226 # copy the uis so changes (e.g. --config or --verbose) don't
227 # persist between requests
227 # persist between requests
228 copiedui = self.ui.copy()
228 copiedui = self.ui.copy()
229 uis = [copiedui]
229 uis = [copiedui]
230 if self.repo:
230 if self.repo:
231 self.repo.baseui = copiedui
231 self.repo.baseui = copiedui
232 # clone ui without using ui.copy because this is protected
232 # clone ui without using ui.copy because this is protected
233 repoui = self.repoui.__class__(self.repoui)
233 repoui = self.repoui.__class__(self.repoui)
234 repoui.copy = copiedui.copy # redo copy protection
234 repoui.copy = copiedui.copy # redo copy protection
235 uis.append(repoui)
235 uis.append(repoui)
236 self.repo.ui = self.repo.dirstate._ui = repoui
236 self.repo.ui = self.repo.dirstate._ui = repoui
237 self.repo.invalidateall()
237 self.repo.invalidateall()
238
238
239 for ui in uis:
239 for ui in uis:
240 ui.resetstate()
240 ui.resetstate()
241 # any kind of interaction must use server channels, but chg may
241 # any kind of interaction must use server channels, but chg may
242 # replace channels by fully functional tty files. so nontty is
242 # replace channels by fully functional tty files. so nontty is
243 # enforced only if cin is a channel.
243 # enforced only if cin is a channel.
244 if not util.safehasattr(self.cin, 'fileno'):
244 if not util.safehasattr(self.cin, 'fileno'):
245 ui.setconfig('ui', 'nontty', 'true', 'commandserver')
245 ui.setconfig('ui', 'nontty', 'true', 'commandserver')
246
246
247 req = dispatch.request(args[:], copiedui, self.repo, self.cin,
247 req = dispatch.request(args[:], copiedui, self.repo, self.cin,
248 self.cout, self.cerr)
248 self.cout, self.cerr)
249
249
250 ret = (dispatch.dispatch(req) or 0) & 255 # might return None
250 ret = (dispatch.dispatch(req) or 0) & 255 # might return None
251
251
252 # restore old cwd
252 # restore old cwd
253 if '--cwd' in args:
253 if '--cwd' in args:
254 os.chdir(self.cwd)
254 os.chdir(self.cwd)
255
255
256 self.cresult.write(struct.pack('>i', int(ret)))
256 self.cresult.write(struct.pack('>i', int(ret)))
257
257
258 def getencoding(self):
258 def getencoding(self):
259 """ writes the current encoding to the result channel """
259 """ writes the current encoding to the result channel """
260 self.cresult.write(encoding.encoding)
260 self.cresult.write(encoding.encoding)
261
261
262 def serveone(self):
262 def serveone(self):
263 cmd = self.client.readline()[:-1]
263 cmd = self.client.readline()[:-1]
264 if cmd:
264 if cmd:
265 handler = self.capabilities.get(cmd)
265 handler = self.capabilities.get(cmd)
266 if handler:
266 if handler:
267 handler(self)
267 handler(self)
268 else:
268 else:
269 # clients are expected to check what commands are supported by
269 # clients are expected to check what commands are supported by
270 # looking at the servers capabilities
270 # looking at the servers capabilities
271 raise error.Abort(_('unknown command %s') % cmd)
271 raise error.Abort(_('unknown command %s') % cmd)
272
272
273 return cmd != ''
273 return cmd != ''
274
274
275 capabilities = {'runcommand' : runcommand,
275 capabilities = {'runcommand' : runcommand,
276 'getencoding' : getencoding}
276 'getencoding' : getencoding}
277
277
278 def serve(self):
278 def serve(self):
279 hellomsg = 'capabilities: ' + ' '.join(sorted(self.capabilities))
279 hellomsg = 'capabilities: ' + ' '.join(sorted(self.capabilities))
280 hellomsg += '\n'
280 hellomsg += '\n'
281 hellomsg += 'encoding: ' + encoding.encoding
281 hellomsg += 'encoding: ' + encoding.encoding
282 hellomsg += '\n'
282 hellomsg += '\n'
283 hellomsg += 'pid: %d' % util.getpid()
283 hellomsg += 'pid: %d' % util.getpid()
284 if util.safehasattr(os, 'getpgid'):
284 if util.safehasattr(os, 'getpgid'):
285 hellomsg += '\n'
285 hellomsg += '\n'
286 hellomsg += 'pgid: %d' % os.getpgid(0)
286 hellomsg += 'pgid: %d' % os.getpgid(0)
287
287
288 # write the hello msg in -one- chunk
288 # write the hello msg in -one- chunk
289 self.cout.write(hellomsg)
289 self.cout.write(hellomsg)
290
290
291 try:
291 try:
292 while self.serveone():
292 while self.serveone():
293 pass
293 pass
294 except EOFError:
294 except EOFError:
295 # we'll get here if the client disconnected while we were reading
295 # we'll get here if the client disconnected while we were reading
296 # its request
296 # its request
297 return 1
297 return 1
298
298
299 return 0
299 return 0
300
300
301 def _protectio(ui):
301 def _protectio(ui):
302 """ duplicates streams and redirect original to null if ui uses stdio """
302 """ duplicates streams and redirect original to null if ui uses stdio """
303 ui.flush()
303 ui.flush()
304 newfiles = []
304 newfiles = []
305 nullfd = os.open(os.devnull, os.O_RDWR)
305 nullfd = os.open(os.devnull, os.O_RDWR)
306 for f, sysf, mode in [(ui.fin, util.stdin, 'rb'),
306 for f, sysf, mode in [(ui.fin, util.stdin, 'rb'),
307 (ui.fout, util.stdout, 'wb')]:
307 (ui.fout, util.stdout, 'wb')]:
308 if f is sysf:
308 if f is sysf:
309 newfd = os.dup(f.fileno())
309 newfd = os.dup(f.fileno())
310 os.dup2(nullfd, f.fileno())
310 os.dup2(nullfd, f.fileno())
311 f = os.fdopen(newfd, mode)
311 f = os.fdopen(newfd, mode)
312 newfiles.append(f)
312 newfiles.append(f)
313 os.close(nullfd)
313 os.close(nullfd)
314 return tuple(newfiles)
314 return tuple(newfiles)
315
315
316 def _restoreio(ui, fin, fout):
316 def _restoreio(ui, fin, fout):
317 """ restores streams from duplicated ones """
317 """ restores streams from duplicated ones """
318 ui.flush()
318 ui.flush()
319 for f, uif in [(fin, ui.fin), (fout, ui.fout)]:
319 for f, uif in [(fin, ui.fin), (fout, ui.fout)]:
320 if f is not uif:
320 if f is not uif:
321 os.dup2(f.fileno(), uif.fileno())
321 os.dup2(f.fileno(), uif.fileno())
322 f.close()
322 f.close()
323
323
324 class pipeservice(object):
324 class pipeservice(object):
325 def __init__(self, ui, repo, opts):
325 def __init__(self, ui, repo, opts):
326 self.ui = ui
326 self.ui = ui
327 self.repo = repo
327 self.repo = repo
328
328
329 def init(self):
329 def init(self):
330 pass
330 pass
331
331
332 def run(self):
332 def run(self):
333 ui = self.ui
333 ui = self.ui
334 # redirect stdio to null device so that broken extensions or in-process
334 # redirect stdio to null device so that broken extensions or in-process
335 # hooks will never cause corruption of channel protocol.
335 # hooks will never cause corruption of channel protocol.
336 fin, fout = _protectio(ui)
336 fin, fout = _protectio(ui)
337 try:
337 try:
338 sv = server(ui, self.repo, fin, fout)
338 sv = server(ui, self.repo, fin, fout)
339 return sv.serve()
339 return sv.serve()
340 finally:
340 finally:
341 sv.cleanup()
341 sv.cleanup()
342 _restoreio(ui, fin, fout)
342 _restoreio(ui, fin, fout)
343
343
344 def _initworkerprocess():
344 def _initworkerprocess():
345 # use a different process group from the master process, in order to:
345 # use a different process group from the master process, in order to:
346 # 1. make the current process group no longer "orphaned" (because the
346 # 1. make the current process group no longer "orphaned" (because the
347 # parent of this process is in a different process group while
347 # parent of this process is in a different process group while
348 # remains in a same session)
348 # remains in a same session)
349 # according to POSIX 2.2.2.52, orphaned process group will ignore
349 # according to POSIX 2.2.2.52, orphaned process group will ignore
350 # terminal-generated stop signals like SIGTSTP (Ctrl+Z), which will
350 # terminal-generated stop signals like SIGTSTP (Ctrl+Z), which will
351 # cause trouble for things like ncurses.
351 # cause trouble for things like ncurses.
352 # 2. the client can use kill(-pgid, sig) to simulate terminal-generated
352 # 2. the client can use kill(-pgid, sig) to simulate terminal-generated
353 # SIGINT (Ctrl+C) and process-exit-generated SIGHUP. our child
353 # SIGINT (Ctrl+C) and process-exit-generated SIGHUP. our child
354 # processes like ssh will be killed properly, without affecting
354 # processes like ssh will be killed properly, without affecting
355 # unrelated processes.
355 # unrelated processes.
356 os.setpgid(0, 0)
356 os.setpgid(0, 0)
357 # change random state otherwise forked request handlers would have a
357 # change random state otherwise forked request handlers would have a
358 # same state inherited from parent.
358 # same state inherited from parent.
359 random.seed()
359 random.seed()
360
360
361 def _serverequest(ui, repo, conn, createcmdserver):
361 def _serverequest(ui, repo, conn, createcmdserver):
362 fin = conn.makefile('rb')
362 fin = conn.makefile('rb')
363 fout = conn.makefile('wb')
363 fout = conn.makefile('wb')
364 sv = None
364 sv = None
365 try:
365 try:
366 sv = createcmdserver(repo, conn, fin, fout)
366 sv = createcmdserver(repo, conn, fin, fout)
367 try:
367 try:
368 sv.serve()
368 sv.serve()
369 # handle exceptions that may be raised by command server. most of
369 # handle exceptions that may be raised by command server. most of
370 # known exceptions are caught by dispatch.
370 # known exceptions are caught by dispatch.
371 except error.Abort as inst:
371 except error.Abort as inst:
372 ui.warn(_('abort: %s\n') % inst)
372 ui.warn(_('abort: %s\n') % inst)
373 except IOError as inst:
373 except IOError as inst:
374 if inst.errno != errno.EPIPE:
374 if inst.errno != errno.EPIPE:
375 raise
375 raise
376 except KeyboardInterrupt:
376 except KeyboardInterrupt:
377 pass
377 pass
378 finally:
378 finally:
379 sv.cleanup()
379 sv.cleanup()
380 except: # re-raises
380 except: # re-raises
381 # also write traceback to error channel. otherwise client cannot
381 # also write traceback to error channel. otherwise client cannot
382 # see it because it is written to server's stderr by default.
382 # see it because it is written to server's stderr by default.
383 if sv:
383 if sv:
384 cerr = sv.cerr
384 cerr = sv.cerr
385 else:
385 else:
386 cerr = channeledoutput(fout, 'e')
386 cerr = channeledoutput(fout, 'e')
387 traceback.print_exc(file=cerr)
387 traceback.print_exc(file=cerr)
388 raise
388 raise
389 finally:
389 finally:
390 fin.close()
390 fin.close()
391 try:
391 try:
392 fout.close() # implicit flush() may cause another EPIPE
392 fout.close() # implicit flush() may cause another EPIPE
393 except IOError as inst:
393 except IOError as inst:
394 if inst.errno != errno.EPIPE:
394 if inst.errno != errno.EPIPE:
395 raise
395 raise
396
396
397 class unixservicehandler(object):
397 class unixservicehandler(object):
398 """Set of pluggable operations for unix-mode services
398 """Set of pluggable operations for unix-mode services
399
399
400 Almost all methods except for createcmdserver() are called in the main
400 Almost all methods except for createcmdserver() are called in the main
401 process. You can't pass mutable resource back from createcmdserver().
401 process. You can't pass mutable resource back from createcmdserver().
402 """
402 """
403
403
404 pollinterval = None
404 pollinterval = None
405
405
406 def __init__(self, ui):
406 def __init__(self, ui):
407 self.ui = ui
407 self.ui = ui
408
408
409 def bindsocket(self, sock, address):
409 def bindsocket(self, sock, address):
410 util.bindunixsocket(sock, address)
410 util.bindunixsocket(sock, address)
411
411
412 def unlinksocket(self, address):
412 def unlinksocket(self, address):
413 os.unlink(address)
413 os.unlink(address)
414
414
415 def printbanner(self, address):
415 def printbanner(self, address):
416 self.ui.status(_('listening at %s\n') % address)
416 self.ui.status(_('listening at %s\n') % address)
417 self.ui.flush() # avoid buffering of status message
417 self.ui.flush() # avoid buffering of status message
418
418
419 def shouldexit(self):
419 def shouldexit(self):
420 """True if server should shut down; checked per pollinterval"""
420 """True if server should shut down; checked per pollinterval"""
421 return False
421 return False
422
422
423 def newconnection(self):
423 def newconnection(self):
424 """Called when main process notices new connection"""
424 """Called when main process notices new connection"""
425 pass
425 pass
426
426
427 def createcmdserver(self, repo, conn, fin, fout):
427 def createcmdserver(self, repo, conn, fin, fout):
428 """Create new command server instance; called in the process that
428 """Create new command server instance; called in the process that
429 serves for the current connection"""
429 serves for the current connection"""
430 return server(self.ui, repo, fin, fout)
430 return server(self.ui, repo, fin, fout)
431
431
432 class unixforkingservice(object):
432 class unixforkingservice(object):
433 """
433 """
434 Listens on unix domain socket and forks server per connection
434 Listens on unix domain socket and forks server per connection
435 """
435 """
436
436
437 def __init__(self, ui, repo, opts, handler=None):
437 def __init__(self, ui, repo, opts, handler=None):
438 self.ui = ui
438 self.ui = ui
439 self.repo = repo
439 self.repo = repo
440 self.address = opts['address']
440 self.address = opts['address']
441 if not util.safehasattr(socket, 'AF_UNIX'):
441 if not util.safehasattr(socket, 'AF_UNIX'):
442 raise error.Abort(_('unsupported platform'))
442 raise error.Abort(_('unsupported platform'))
443 if not self.address:
443 if not self.address:
444 raise error.Abort(_('no socket path specified with --address'))
444 raise error.Abort(_('no socket path specified with --address'))
445 self._servicehandler = handler or unixservicehandler(ui)
445 self._servicehandler = handler or unixservicehandler(ui)
446 self._sock = None
446 self._sock = None
447 self._oldsigchldhandler = None
447 self._oldsigchldhandler = None
448 self._workerpids = set() # updated by signal handler; do not iterate
448 self._workerpids = set() # updated by signal handler; do not iterate
449
449
450 def init(self):
450 def init(self):
451 self._sock = socket.socket(socket.AF_UNIX)
451 self._sock = socket.socket(socket.AF_UNIX)
452 self._servicehandler.bindsocket(self._sock, self.address)
452 self._servicehandler.bindsocket(self._sock, self.address)
453 self._sock.listen(socket.SOMAXCONN)
453 self._sock.listen(socket.SOMAXCONN)
454 o = signal.signal(signal.SIGCHLD, self._sigchldhandler)
454 o = signal.signal(signal.SIGCHLD, self._sigchldhandler)
455 self._oldsigchldhandler = o
455 self._oldsigchldhandler = o
456 self._servicehandler.printbanner(self.address)
456 self._servicehandler.printbanner(self.address)
457
457
458 def _cleanup(self):
458 def _cleanup(self):
459 signal.signal(signal.SIGCHLD, self._oldsigchldhandler)
459 signal.signal(signal.SIGCHLD, self._oldsigchldhandler)
460 self._sock.close()
460 self._sock.close()
461 self._servicehandler.unlinksocket(self.address)
461 self._servicehandler.unlinksocket(self.address)
462 # don't kill child processes as they have active clients, just wait
462 # don't kill child processes as they have active clients, just wait
463 self._reapworkers(0)
463 self._reapworkers(0)
464
464
465 def run(self):
465 def run(self):
466 try:
466 try:
467 self._mainloop()
467 self._mainloop()
468 finally:
468 finally:
469 self._cleanup()
469 self._cleanup()
470
470
471 def _mainloop(self):
471 def _mainloop(self):
472 h = self._servicehandler
472 h = self._servicehandler
473 while not h.shouldexit():
473 while not h.shouldexit():
474 try:
474 try:
475 ready = select.select([self._sock], [], [], h.pollinterval)[0]
475 ready = select.select([self._sock], [], [], h.pollinterval)[0]
476 if not ready:
476 if not ready:
477 continue
477 continue
478 conn, _addr = self._sock.accept()
478 conn, _addr = self._sock.accept()
479 except (select.error, socket.error) as inst:
479 except (select.error, socket.error) as inst:
480 if inst.args[0] == errno.EINTR:
480 if inst.args[0] == errno.EINTR:
481 continue
481 continue
482 raise
482 raise
483
483
484 pid = os.fork()
484 pid = os.fork()
485 if pid:
485 if pid:
486 try:
486 try:
487 self.ui.debug('forked worker process (pid=%d)\n' % pid)
487 self.ui.debug('forked worker process (pid=%d)\n' % pid)
488 self._workerpids.add(pid)
488 self._workerpids.add(pid)
489 h.newconnection()
489 h.newconnection()
490 finally:
490 finally:
491 conn.close() # release handle in parent process
491 conn.close() # release handle in parent process
492 else:
492 else:
493 try:
493 try:
494 self._runworker(conn)
494 self._runworker(conn)
495 conn.close()
495 conn.close()
496 os._exit(0)
496 os._exit(0)
497 except: # never return, hence no re-raises
497 except: # never return, hence no re-raises
498 try:
498 try:
499 self.ui.traceback(force=True)
499 self.ui.traceback(force=True)
500 finally:
500 finally:
501 os._exit(255)
501 os._exit(255)
502
502
503 def _sigchldhandler(self, signal, frame):
503 def _sigchldhandler(self, signal, frame):
504 self._reapworkers(os.WNOHANG)
504 self._reapworkers(os.WNOHANG)
505
505
506 def _reapworkers(self, options):
506 def _reapworkers(self, options):
507 while self._workerpids:
507 while self._workerpids:
508 try:
508 try:
509 pid, _status = os.waitpid(-1, options)
509 pid, _status = os.waitpid(-1, options)
510 except OSError as inst:
510 except OSError as inst:
511 if inst.errno == errno.EINTR:
511 if inst.errno == errno.EINTR:
512 continue
512 continue
513 if inst.errno != errno.ECHILD:
513 if inst.errno != errno.ECHILD:
514 raise
514 raise
515 # no child processes at all (reaped by other waitpid()?)
515 # no child processes at all (reaped by other waitpid()?)
516 self._workerpids.clear()
516 self._workerpids.clear()
517 return
517 return
518 if pid == 0:
518 if pid == 0:
519 # no waitable child processes
519 # no waitable child processes
520 return
520 return
521 self.ui.debug('worker process exited (pid=%d)\n' % pid)
521 self.ui.debug('worker process exited (pid=%d)\n' % pid)
522 self._workerpids.discard(pid)
522 self._workerpids.discard(pid)
523
523
524 def _runworker(self, conn):
524 def _runworker(self, conn):
525 signal.signal(signal.SIGCHLD, self._oldsigchldhandler)
525 signal.signal(signal.SIGCHLD, self._oldsigchldhandler)
526 _initworkerprocess()
526 _initworkerprocess()
527 h = self._servicehandler
527 h = self._servicehandler
528 try:
528 try:
529 _serverequest(self.ui, self.repo, conn, h.createcmdserver)
529 _serverequest(self.ui, self.repo, conn, h.createcmdserver)
530 finally:
530 finally:
531 gc.collect() # trigger __del__ since worker process uses os._exit
531 gc.collect() # trigger __del__ since worker process uses os._exit
532
533 _servicemap = {
534 'pipe': pipeservice,
535 'unix': unixforkingservice,
536 }
537
538 def createservice(ui, repo, opts):
539 mode = opts['cmdserver']
540 try:
541 return _servicemap[mode](ui, repo, opts)
542 except KeyError:
543 raise error.Abort(_('unknown mode %s') % mode)
@@ -1,107 +1,120 b''
1 # server.py - utility and factory of server
1 # server.py - utility and factory of server
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 errno
10 import errno
11 import os
11 import os
12 import sys
12 import sys
13 import tempfile
13 import tempfile
14
14
15 from .i18n import _
15 from .i18n import _
16
16
17 from . import (
17 from . import (
18 commandserver,
18 error,
19 error,
19 util,
20 util,
20 )
21 )
21
22
22 def runservice(opts, parentfn=None, initfn=None, runfn=None, logfile=None,
23 def runservice(opts, parentfn=None, initfn=None, runfn=None, logfile=None,
23 runargs=None, appendpid=False):
24 runargs=None, appendpid=False):
24 '''Run a command as a service.'''
25 '''Run a command as a service.'''
25
26
26 def writepid(pid):
27 def writepid(pid):
27 if opts['pid_file']:
28 if opts['pid_file']:
28 if appendpid:
29 if appendpid:
29 mode = 'a'
30 mode = 'a'
30 else:
31 else:
31 mode = 'w'
32 mode = 'w'
32 fp = open(opts['pid_file'], mode)
33 fp = open(opts['pid_file'], mode)
33 fp.write(str(pid) + '\n')
34 fp.write(str(pid) + '\n')
34 fp.close()
35 fp.close()
35
36
36 if opts['daemon'] and not opts['daemon_postexec']:
37 if opts['daemon'] and not opts['daemon_postexec']:
37 # Signal child process startup with file removal
38 # Signal child process startup with file removal
38 lockfd, lockpath = tempfile.mkstemp(prefix='hg-service-')
39 lockfd, lockpath = tempfile.mkstemp(prefix='hg-service-')
39 os.close(lockfd)
40 os.close(lockfd)
40 try:
41 try:
41 if not runargs:
42 if not runargs:
42 runargs = util.hgcmd() + sys.argv[1:]
43 runargs = util.hgcmd() + sys.argv[1:]
43 runargs.append('--daemon-postexec=unlink:%s' % lockpath)
44 runargs.append('--daemon-postexec=unlink:%s' % lockpath)
44 # Don't pass --cwd to the child process, because we've already
45 # Don't pass --cwd to the child process, because we've already
45 # changed directory.
46 # changed directory.
46 for i in xrange(1, len(runargs)):
47 for i in xrange(1, len(runargs)):
47 if runargs[i].startswith('--cwd='):
48 if runargs[i].startswith('--cwd='):
48 del runargs[i]
49 del runargs[i]
49 break
50 break
50 elif runargs[i].startswith('--cwd'):
51 elif runargs[i].startswith('--cwd'):
51 del runargs[i:i + 2]
52 del runargs[i:i + 2]
52 break
53 break
53 def condfn():
54 def condfn():
54 return not os.path.exists(lockpath)
55 return not os.path.exists(lockpath)
55 pid = util.rundetached(runargs, condfn)
56 pid = util.rundetached(runargs, condfn)
56 if pid < 0:
57 if pid < 0:
57 raise error.Abort(_('child process failed to start'))
58 raise error.Abort(_('child process failed to start'))
58 writepid(pid)
59 writepid(pid)
59 finally:
60 finally:
60 try:
61 try:
61 os.unlink(lockpath)
62 os.unlink(lockpath)
62 except OSError as e:
63 except OSError as e:
63 if e.errno != errno.ENOENT:
64 if e.errno != errno.ENOENT:
64 raise
65 raise
65 if parentfn:
66 if parentfn:
66 return parentfn(pid)
67 return parentfn(pid)
67 else:
68 else:
68 return
69 return
69
70
70 if initfn:
71 if initfn:
71 initfn()
72 initfn()
72
73
73 if not opts['daemon']:
74 if not opts['daemon']:
74 writepid(util.getpid())
75 writepid(util.getpid())
75
76
76 if opts['daemon_postexec']:
77 if opts['daemon_postexec']:
77 try:
78 try:
78 os.setsid()
79 os.setsid()
79 except AttributeError:
80 except AttributeError:
80 pass
81 pass
81 for inst in opts['daemon_postexec']:
82 for inst in opts['daemon_postexec']:
82 if inst.startswith('unlink:'):
83 if inst.startswith('unlink:'):
83 lockpath = inst[7:]
84 lockpath = inst[7:]
84 os.unlink(lockpath)
85 os.unlink(lockpath)
85 elif inst.startswith('chdir:'):
86 elif inst.startswith('chdir:'):
86 os.chdir(inst[6:])
87 os.chdir(inst[6:])
87 elif inst != 'none':
88 elif inst != 'none':
88 raise error.Abort(_('invalid value for --daemon-postexec: %s')
89 raise error.Abort(_('invalid value for --daemon-postexec: %s')
89 % inst)
90 % inst)
90 util.hidewindow()
91 util.hidewindow()
91 util.stdout.flush()
92 util.stdout.flush()
92 util.stderr.flush()
93 util.stderr.flush()
93
94
94 nullfd = os.open(os.devnull, os.O_RDWR)
95 nullfd = os.open(os.devnull, os.O_RDWR)
95 logfilefd = nullfd
96 logfilefd = nullfd
96 if logfile:
97 if logfile:
97 logfilefd = os.open(logfile, os.O_RDWR | os.O_CREAT | os.O_APPEND)
98 logfilefd = os.open(logfile, os.O_RDWR | os.O_CREAT | os.O_APPEND)
98 os.dup2(nullfd, 0)
99 os.dup2(nullfd, 0)
99 os.dup2(logfilefd, 1)
100 os.dup2(logfilefd, 1)
100 os.dup2(logfilefd, 2)
101 os.dup2(logfilefd, 2)
101 if nullfd not in (0, 1, 2):
102 if nullfd not in (0, 1, 2):
102 os.close(nullfd)
103 os.close(nullfd)
103 if logfile and logfilefd not in (0, 1, 2):
104 if logfile and logfilefd not in (0, 1, 2):
104 os.close(logfilefd)
105 os.close(logfilefd)
105
106
106 if runfn:
107 if runfn:
107 return runfn()
108 return runfn()
109
110 _cmdservicemap = {
111 'pipe': commandserver.pipeservice,
112 'unix': commandserver.unixforkingservice,
113 }
114
115 def createcmdservice(ui, repo, opts):
116 mode = opts['cmdserver']
117 try:
118 return _cmdservicemap[mode](ui, repo, opts)
119 except KeyError:
120 raise error.Abort(_('unknown mode %s') % mode)
General Comments 0
You need to be logged in to leave comments. Login now