##// END OF EJS Templates
py3: have a bytes version of shlex.split()...
Pulkit Goyal -
r30678:caf7e1c5 default
parent child Browse files
Show More
@@ -1,392 +1,392 b''
1 1 # extdiff.py - external diff program support for mercurial
2 2 #
3 3 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 '''command to allow external programs to compare revisions
9 9
10 10 The extdiff Mercurial extension allows you to use external programs
11 11 to compare revisions, or revision with working directory. The external
12 12 diff programs are called with a configurable set of options and two
13 13 non-option arguments: paths to directories containing snapshots of
14 14 files to compare.
15 15
16 16 The extdiff extension also allows you to configure new diff commands, so
17 17 you do not need to type :hg:`extdiff -p kdiff3` always. ::
18 18
19 19 [extdiff]
20 20 # add new command that runs GNU diff(1) in 'context diff' mode
21 21 cdiff = gdiff -Nprc5
22 22 ## or the old way:
23 23 #cmd.cdiff = gdiff
24 24 #opts.cdiff = -Nprc5
25 25
26 26 # add new command called meld, runs meld (no need to name twice). If
27 27 # the meld executable is not available, the meld tool in [merge-tools]
28 28 # will be used, if available
29 29 meld =
30 30
31 31 # add new command called vimdiff, runs gvimdiff with DirDiff plugin
32 32 # (see http://www.vim.org/scripts/script.php?script_id=102) Non
33 33 # English user, be sure to put "let g:DirDiffDynamicDiffText = 1" in
34 34 # your .vimrc
35 35 vimdiff = gvim -f "+next" \\
36 36 "+execute 'DirDiff' fnameescape(argv(0)) fnameescape(argv(1))"
37 37
38 38 Tool arguments can include variables that are expanded at runtime::
39 39
40 40 $parent1, $plabel1 - filename, descriptive label of first parent
41 41 $child, $clabel - filename, descriptive label of child revision
42 42 $parent2, $plabel2 - filename, descriptive label of second parent
43 43 $root - repository root
44 44 $parent is an alias for $parent1.
45 45
46 46 The extdiff extension will look in your [diff-tools] and [merge-tools]
47 47 sections for diff tool arguments, when none are specified in [extdiff].
48 48
49 49 ::
50 50
51 51 [extdiff]
52 52 kdiff3 =
53 53
54 54 [diff-tools]
55 55 kdiff3.diffargs=--L1 '$plabel1' --L2 '$clabel' $parent $child
56 56
57 57 You can use -I/-X and list of file or directory names like normal
58 58 :hg:`diff` command. The extdiff extension makes snapshots of only
59 59 needed files, so running the external diff program will actually be
60 60 pretty fast (at least faster than having to compare the entire tree).
61 61 '''
62 62
63 63 from __future__ import absolute_import
64 64
65 65 import os
66 66 import re
67 import shlex
68 67 import shutil
69 68 import tempfile
70 69 from mercurial.i18n import _
71 70 from mercurial.node import (
72 71 nullid,
73 72 short,
74 73 )
75 74 from mercurial import (
76 75 archival,
77 76 cmdutil,
78 77 commands,
79 78 error,
80 79 filemerge,
80 pycompat,
81 81 scmutil,
82 82 util,
83 83 )
84 84
85 85 cmdtable = {}
86 86 command = cmdutil.command(cmdtable)
87 87 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
88 88 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
89 89 # be specifying the version(s) of Mercurial they are tested with, or
90 90 # leave the attribute unspecified.
91 91 testedwith = 'ships-with-hg-core'
92 92
93 93 def snapshot(ui, repo, files, node, tmproot, listsubrepos):
94 94 '''snapshot files as of some revision
95 95 if not using snapshot, -I/-X does not work and recursive diff
96 96 in tools like kdiff3 and meld displays too many files.'''
97 97 dirname = os.path.basename(repo.root)
98 98 if dirname == "":
99 99 dirname = "root"
100 100 if node is not None:
101 101 dirname = '%s.%s' % (dirname, short(node))
102 102 base = os.path.join(tmproot, dirname)
103 103 os.mkdir(base)
104 104 fns_and_mtime = []
105 105
106 106 if node is not None:
107 107 ui.note(_('making snapshot of %d files from rev %s\n') %
108 108 (len(files), short(node)))
109 109 else:
110 110 ui.note(_('making snapshot of %d files from working directory\n') %
111 111 (len(files)))
112 112
113 113 if files:
114 114 repo.ui.setconfig("ui", "archivemeta", False)
115 115
116 116 archival.archive(repo, base, node, 'files',
117 117 matchfn=scmutil.matchfiles(repo, files),
118 118 subrepos=listsubrepos)
119 119
120 120 for fn in sorted(files):
121 121 wfn = util.pconvert(fn)
122 122 ui.note(' %s\n' % wfn)
123 123
124 124 if node is None:
125 125 dest = os.path.join(base, wfn)
126 126
127 127 fns_and_mtime.append((dest, repo.wjoin(fn),
128 128 os.lstat(dest).st_mtime))
129 129 return dirname, fns_and_mtime
130 130
131 131 def dodiff(ui, repo, cmdline, pats, opts):
132 132 '''Do the actual diff:
133 133
134 134 - copy to a temp structure if diffing 2 internal revisions
135 135 - copy to a temp structure if diffing working revision with
136 136 another one and more than 1 file is changed
137 137 - just invoke the diff for a single file in the working dir
138 138 '''
139 139
140 140 revs = opts.get('rev')
141 141 change = opts.get('change')
142 142 do3way = '$parent2' in cmdline
143 143
144 144 if revs and change:
145 145 msg = _('cannot specify --rev and --change at the same time')
146 146 raise error.Abort(msg)
147 147 elif change:
148 148 node2 = scmutil.revsingle(repo, change, None).node()
149 149 node1a, node1b = repo.changelog.parents(node2)
150 150 else:
151 151 node1a, node2 = scmutil.revpair(repo, revs)
152 152 if not revs:
153 153 node1b = repo.dirstate.p2()
154 154 else:
155 155 node1b = nullid
156 156
157 157 # Disable 3-way merge if there is only one parent
158 158 if do3way:
159 159 if node1b == nullid:
160 160 do3way = False
161 161
162 162 subrepos=opts.get('subrepos')
163 163
164 164 matcher = scmutil.match(repo[node2], pats, opts)
165 165
166 166 if opts.get('patch'):
167 167 if subrepos:
168 168 raise error.Abort(_('--patch cannot be used with --subrepos'))
169 169 if node2 is None:
170 170 raise error.Abort(_('--patch requires two revisions'))
171 171 else:
172 172 mod_a, add_a, rem_a = map(set, repo.status(node1a, node2, matcher,
173 173 listsubrepos=subrepos)[:3])
174 174 if do3way:
175 175 mod_b, add_b, rem_b = map(set,
176 176 repo.status(node1b, node2, matcher,
177 177 listsubrepos=subrepos)[:3])
178 178 else:
179 179 mod_b, add_b, rem_b = set(), set(), set()
180 180 modadd = mod_a | add_a | mod_b | add_b
181 181 common = modadd | rem_a | rem_b
182 182 if not common:
183 183 return 0
184 184
185 185 tmproot = tempfile.mkdtemp(prefix='extdiff.')
186 186 try:
187 187 if not opts.get('patch'):
188 188 # Always make a copy of node1a (and node1b, if applicable)
189 189 dir1a_files = mod_a | rem_a | ((mod_b | add_b) - add_a)
190 190 dir1a = snapshot(ui, repo, dir1a_files, node1a, tmproot,
191 191 subrepos)[0]
192 192 rev1a = '@%d' % repo[node1a].rev()
193 193 if do3way:
194 194 dir1b_files = mod_b | rem_b | ((mod_a | add_a) - add_b)
195 195 dir1b = snapshot(ui, repo, dir1b_files, node1b, tmproot,
196 196 subrepos)[0]
197 197 rev1b = '@%d' % repo[node1b].rev()
198 198 else:
199 199 dir1b = None
200 200 rev1b = ''
201 201
202 202 fns_and_mtime = []
203 203
204 204 # If node2 in not the wc or there is >1 change, copy it
205 205 dir2root = ''
206 206 rev2 = ''
207 207 if node2:
208 208 dir2 = snapshot(ui, repo, modadd, node2, tmproot, subrepos)[0]
209 209 rev2 = '@%d' % repo[node2].rev()
210 210 elif len(common) > 1:
211 211 #we only actually need to get the files to copy back to
212 212 #the working dir in this case (because the other cases
213 213 #are: diffing 2 revisions or single file -- in which case
214 214 #the file is already directly passed to the diff tool).
215 215 dir2, fns_and_mtime = snapshot(ui, repo, modadd, None, tmproot,
216 216 subrepos)
217 217 else:
218 218 # This lets the diff tool open the changed file directly
219 219 dir2 = ''
220 220 dir2root = repo.root
221 221
222 222 label1a = rev1a
223 223 label1b = rev1b
224 224 label2 = rev2
225 225
226 226 # If only one change, diff the files instead of the directories
227 227 # Handle bogus modifies correctly by checking if the files exist
228 228 if len(common) == 1:
229 229 common_file = util.localpath(common.pop())
230 230 dir1a = os.path.join(tmproot, dir1a, common_file)
231 231 label1a = common_file + rev1a
232 232 if not os.path.isfile(dir1a):
233 233 dir1a = os.devnull
234 234 if do3way:
235 235 dir1b = os.path.join(tmproot, dir1b, common_file)
236 236 label1b = common_file + rev1b
237 237 if not os.path.isfile(dir1b):
238 238 dir1b = os.devnull
239 239 dir2 = os.path.join(dir2root, dir2, common_file)
240 240 label2 = common_file + rev2
241 241 else:
242 242 template = 'hg-%h.patch'
243 243 cmdutil.export(repo, [repo[node1a].rev(), repo[node2].rev()],
244 244 template=repo.vfs.reljoin(tmproot, template),
245 245 match=matcher)
246 246 label1a = cmdutil.makefilename(repo, template, node1a)
247 247 label2 = cmdutil.makefilename(repo, template, node2)
248 248 dir1a = repo.vfs.reljoin(tmproot, label1a)
249 249 dir2 = repo.vfs.reljoin(tmproot, label2)
250 250 dir1b = None
251 251 label1b = None
252 252 fns_and_mtime = []
253 253
254 254 # Function to quote file/dir names in the argument string.
255 255 # When not operating in 3-way mode, an empty string is
256 256 # returned for parent2
257 257 replace = {'parent': dir1a, 'parent1': dir1a, 'parent2': dir1b,
258 258 'plabel1': label1a, 'plabel2': label1b,
259 259 'clabel': label2, 'child': dir2,
260 260 'root': repo.root}
261 261 def quote(match):
262 262 pre = match.group(2)
263 263 key = match.group(3)
264 264 if not do3way and key == 'parent2':
265 265 return pre
266 266 return pre + util.shellquote(replace[key])
267 267
268 268 # Match parent2 first, so 'parent1?' will match both parent1 and parent
269 269 regex = (r'''(['"]?)([^\s'"$]*)'''
270 270 r'\$(parent2|parent1?|child|plabel1|plabel2|clabel|root)\1')
271 271 if not do3way and not re.search(regex, cmdline):
272 272 cmdline += ' $parent1 $child'
273 273 cmdline = re.sub(regex, quote, cmdline)
274 274
275 275 ui.debug('running %r in %s\n' % (cmdline, tmproot))
276 276 ui.system(cmdline, cwd=tmproot)
277 277
278 278 for copy_fn, working_fn, mtime in fns_and_mtime:
279 279 if os.lstat(copy_fn).st_mtime != mtime:
280 280 ui.debug('file changed while diffing. '
281 281 'Overwriting: %s (src: %s)\n' % (working_fn, copy_fn))
282 282 util.copyfile(copy_fn, working_fn)
283 283
284 284 return 1
285 285 finally:
286 286 ui.note(_('cleaning up temp directory\n'))
287 287 shutil.rmtree(tmproot)
288 288
289 289 extdiffopts = [
290 290 ('o', 'option', [],
291 291 _('pass option to comparison program'), _('OPT')),
292 292 ('r', 'rev', [], _('revision'), _('REV')),
293 293 ('c', 'change', '', _('change made by revision'), _('REV')),
294 294 ('', 'patch', None, _('compare patches for two revisions'))
295 295 ] + commands.walkopts + commands.subrepoopts
296 296
297 297 @command('extdiff',
298 298 [('p', 'program', '', _('comparison program to run'), _('CMD')),
299 299 ] + extdiffopts,
300 300 _('hg extdiff [OPT]... [FILE]...'),
301 301 inferrepo=True)
302 302 def extdiff(ui, repo, *pats, **opts):
303 303 '''use external program to diff repository (or selected files)
304 304
305 305 Show differences between revisions for the specified files, using
306 306 an external program. The default program used is diff, with
307 307 default options "-Npru".
308 308
309 309 To select a different program, use the -p/--program option. The
310 310 program will be passed the names of two directories to compare. To
311 311 pass additional options to the program, use -o/--option. These
312 312 will be passed before the names of the directories to compare.
313 313
314 314 When two revision arguments are given, then changes are shown
315 315 between those revisions. If only one revision is specified then
316 316 that revision is compared to the working directory, and, when no
317 317 revisions are specified, the working directory files are compared
318 318 to its parent.'''
319 319 program = opts.get('program')
320 320 option = opts.get('option')
321 321 if not program:
322 322 program = 'diff'
323 323 option = option or ['-Npru']
324 324 cmdline = ' '.join(map(util.shellquote, [program] + option))
325 325 return dodiff(ui, repo, cmdline, pats, opts)
326 326
327 327 class savedcmd(object):
328 328 """use external program to diff repository (or selected files)
329 329
330 330 Show differences between revisions for the specified files, using
331 331 the following program::
332 332
333 333 %(path)s
334 334
335 335 When two revision arguments are given, then changes are shown
336 336 between those revisions. If only one revision is specified then
337 337 that revision is compared to the working directory, and, when no
338 338 revisions are specified, the working directory files are compared
339 339 to its parent.
340 340 """
341 341
342 342 def __init__(self, path, cmdline):
343 343 # We can't pass non-ASCII through docstrings (and path is
344 344 # in an unknown encoding anyway)
345 345 docpath = path.encode("string-escape")
346 346 self.__doc__ = self.__doc__ % {'path': util.uirepr(docpath)}
347 347 self._cmdline = cmdline
348 348
349 349 def __call__(self, ui, repo, *pats, **opts):
350 350 options = ' '.join(map(util.shellquote, opts['option']))
351 351 if options:
352 352 options = ' ' + options
353 353 return dodiff(ui, repo, self._cmdline + options, pats, opts)
354 354
355 355 def uisetup(ui):
356 356 for cmd, path in ui.configitems('extdiff'):
357 357 path = util.expandpath(path)
358 358 if cmd.startswith('cmd.'):
359 359 cmd = cmd[4:]
360 360 if not path:
361 361 path = util.findexe(cmd)
362 362 if path is None:
363 363 path = filemerge.findexternaltool(ui, cmd) or cmd
364 364 diffopts = ui.config('extdiff', 'opts.' + cmd, '')
365 365 cmdline = util.shellquote(path)
366 366 if diffopts:
367 367 cmdline += ' ' + diffopts
368 368 elif cmd.startswith('opts.'):
369 369 continue
370 370 else:
371 371 if path:
372 372 # case "cmd = path opts"
373 373 cmdline = path
374 diffopts = len(shlex.split(cmdline)) > 1
374 diffopts = len(pycompat.shlexsplit(cmdline)) > 1
375 375 else:
376 376 # case "cmd ="
377 377 path = util.findexe(cmd)
378 378 if path is None:
379 379 path = filemerge.findexternaltool(ui, cmd) or cmd
380 380 cmdline = util.shellquote(path)
381 381 diffopts = False
382 382 # look for diff arguments in [diff-tools] then [merge-tools]
383 383 if not diffopts:
384 384 args = ui.config('diff-tools', cmd+'.diffargs') or \
385 385 ui.config('merge-tools', cmd+'.diffargs')
386 386 if args:
387 387 cmdline += ' ' + args
388 388 command(cmd, extdiffopts[:], _('hg %s [OPTION]... [FILE]...') % cmd,
389 389 inferrepo=True)(savedcmd(path, cmdline))
390 390
391 391 # tell hggettext to extract docstrings from these functions:
392 392 i18nfunctions = [savedcmd]
@@ -1,6601 +1,6600 b''
1 1 # commands.py - command processing for mercurial
2 2 #
3 3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from __future__ import absolute_import
9 9
10 10 import difflib
11 11 import errno
12 12 import os
13 13 import re
14 import shlex
15 14 import socket
16 15 import string
17 16 import sys
18 17 import tempfile
19 18 import time
20 19
21 20 from .i18n import _
22 21 from .node import (
23 22 bin,
24 23 hex,
25 24 nullhex,
26 25 nullid,
27 26 nullrev,
28 27 short,
29 28 )
30 29 from . import (
31 30 archival,
32 31 bookmarks,
33 32 bundle2,
34 33 changegroup,
35 34 cmdutil,
36 35 copies,
37 36 destutil,
38 37 dirstateguard,
39 38 discovery,
40 39 encoding,
41 40 error,
42 41 exchange,
43 42 extensions,
44 43 formatter,
45 44 graphmod,
46 45 hbisect,
47 46 help,
48 47 hg,
49 48 lock as lockmod,
50 49 merge as mergemod,
51 50 minirst,
52 51 obsolete,
53 52 patch,
54 53 phases,
55 54 policy,
56 55 pvec,
57 56 pycompat,
58 57 repair,
59 58 revlog,
60 59 revset,
61 60 scmutil,
62 61 server,
63 62 sshserver,
64 63 sslutil,
65 64 streamclone,
66 65 templatekw,
67 66 templater,
68 67 ui as uimod,
69 68 util,
70 69 )
71 70
72 71 release = lockmod.release
73 72
74 73 table = {}
75 74
76 75 command = cmdutil.command(table)
77 76
78 77 # label constants
79 78 # until 3.5, bookmarks.current was the advertised name, not
80 79 # bookmarks.active, so we must use both to avoid breaking old
81 80 # custom styles
82 81 activebookmarklabel = 'bookmarks.active bookmarks.current'
83 82
84 83 # common command options
85 84
86 85 globalopts = [
87 86 ('R', 'repository', '',
88 87 _('repository root directory or name of overlay bundle file'),
89 88 _('REPO')),
90 89 ('', 'cwd', '',
91 90 _('change working directory'), _('DIR')),
92 91 ('y', 'noninteractive', None,
93 92 _('do not prompt, automatically pick the first choice for all prompts')),
94 93 ('q', 'quiet', None, _('suppress output')),
95 94 ('v', 'verbose', None, _('enable additional output')),
96 95 ('', 'config', [],
97 96 _('set/override config option (use \'section.name=value\')'),
98 97 _('CONFIG')),
99 98 ('', 'debug', None, _('enable debugging output')),
100 99 ('', 'debugger', None, _('start debugger')),
101 100 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
102 101 _('ENCODE')),
103 102 ('', 'encodingmode', encoding.encodingmode,
104 103 _('set the charset encoding mode'), _('MODE')),
105 104 ('', 'traceback', None, _('always print a traceback on exception')),
106 105 ('', 'time', None, _('time how long the command takes')),
107 106 ('', 'profile', None, _('print command execution profile')),
108 107 ('', 'version', None, _('output version information and exit')),
109 108 ('h', 'help', None, _('display help and exit')),
110 109 ('', 'hidden', False, _('consider hidden changesets')),
111 110 ]
112 111
113 112 dryrunopts = [('n', 'dry-run', None,
114 113 _('do not perform actions, just print output'))]
115 114
116 115 remoteopts = [
117 116 ('e', 'ssh', '',
118 117 _('specify ssh command to use'), _('CMD')),
119 118 ('', 'remotecmd', '',
120 119 _('specify hg command to run on the remote side'), _('CMD')),
121 120 ('', 'insecure', None,
122 121 _('do not verify server certificate (ignoring web.cacerts config)')),
123 122 ]
124 123
125 124 walkopts = [
126 125 ('I', 'include', [],
127 126 _('include names matching the given patterns'), _('PATTERN')),
128 127 ('X', 'exclude', [],
129 128 _('exclude names matching the given patterns'), _('PATTERN')),
130 129 ]
131 130
132 131 commitopts = [
133 132 ('m', 'message', '',
134 133 _('use text as commit message'), _('TEXT')),
135 134 ('l', 'logfile', '',
136 135 _('read commit message from file'), _('FILE')),
137 136 ]
138 137
139 138 commitopts2 = [
140 139 ('d', 'date', '',
141 140 _('record the specified date as commit date'), _('DATE')),
142 141 ('u', 'user', '',
143 142 _('record the specified user as committer'), _('USER')),
144 143 ]
145 144
146 145 # hidden for now
147 146 formatteropts = [
148 147 ('T', 'template', '',
149 148 _('display with template (EXPERIMENTAL)'), _('TEMPLATE')),
150 149 ]
151 150
152 151 templateopts = [
153 152 ('', 'style', '',
154 153 _('display using template map file (DEPRECATED)'), _('STYLE')),
155 154 ('T', 'template', '',
156 155 _('display with template'), _('TEMPLATE')),
157 156 ]
158 157
159 158 logopts = [
160 159 ('p', 'patch', None, _('show patch')),
161 160 ('g', 'git', None, _('use git extended diff format')),
162 161 ('l', 'limit', '',
163 162 _('limit number of changes displayed'), _('NUM')),
164 163 ('M', 'no-merges', None, _('do not show merges')),
165 164 ('', 'stat', None, _('output diffstat-style summary of changes')),
166 165 ('G', 'graph', None, _("show the revision DAG")),
167 166 ] + templateopts
168 167
169 168 diffopts = [
170 169 ('a', 'text', None, _('treat all files as text')),
171 170 ('g', 'git', None, _('use git extended diff format')),
172 171 ('', 'nodates', None, _('omit dates from diff headers'))
173 172 ]
174 173
175 174 diffwsopts = [
176 175 ('w', 'ignore-all-space', None,
177 176 _('ignore white space when comparing lines')),
178 177 ('b', 'ignore-space-change', None,
179 178 _('ignore changes in the amount of white space')),
180 179 ('B', 'ignore-blank-lines', None,
181 180 _('ignore changes whose lines are all blank')),
182 181 ]
183 182
184 183 diffopts2 = [
185 184 ('', 'noprefix', None, _('omit a/ and b/ prefixes from filenames')),
186 185 ('p', 'show-function', None, _('show which function each change is in')),
187 186 ('', 'reverse', None, _('produce a diff that undoes the changes')),
188 187 ] + diffwsopts + [
189 188 ('U', 'unified', '',
190 189 _('number of lines of context to show'), _('NUM')),
191 190 ('', 'stat', None, _('output diffstat-style summary of changes')),
192 191 ('', 'root', '', _('produce diffs relative to subdirectory'), _('DIR')),
193 192 ]
194 193
195 194 mergetoolopts = [
196 195 ('t', 'tool', '', _('specify merge tool')),
197 196 ]
198 197
199 198 similarityopts = [
200 199 ('s', 'similarity', '',
201 200 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
202 201 ]
203 202
204 203 subrepoopts = [
205 204 ('S', 'subrepos', None,
206 205 _('recurse into subrepositories'))
207 206 ]
208 207
209 208 debugrevlogopts = [
210 209 ('c', 'changelog', False, _('open changelog')),
211 210 ('m', 'manifest', False, _('open manifest')),
212 211 ('', 'dir', '', _('open directory manifest')),
213 212 ]
214 213
215 214 # Commands start here, listed alphabetically
216 215
217 216 @command('^add',
218 217 walkopts + subrepoopts + dryrunopts,
219 218 _('[OPTION]... [FILE]...'),
220 219 inferrepo=True)
221 220 def add(ui, repo, *pats, **opts):
222 221 """add the specified files on the next commit
223 222
224 223 Schedule files to be version controlled and added to the
225 224 repository.
226 225
227 226 The files will be added to the repository at the next commit. To
228 227 undo an add before that, see :hg:`forget`.
229 228
230 229 If no names are given, add all files to the repository (except
231 230 files matching ``.hgignore``).
232 231
233 232 .. container:: verbose
234 233
235 234 Examples:
236 235
237 236 - New (unknown) files are added
238 237 automatically by :hg:`add`::
239 238
240 239 $ ls
241 240 foo.c
242 241 $ hg status
243 242 ? foo.c
244 243 $ hg add
245 244 adding foo.c
246 245 $ hg status
247 246 A foo.c
248 247
249 248 - Specific files to be added can be specified::
250 249
251 250 $ ls
252 251 bar.c foo.c
253 252 $ hg status
254 253 ? bar.c
255 254 ? foo.c
256 255 $ hg add bar.c
257 256 $ hg status
258 257 A bar.c
259 258 ? foo.c
260 259
261 260 Returns 0 if all files are successfully added.
262 261 """
263 262
264 263 m = scmutil.match(repo[None], pats, opts)
265 264 rejected = cmdutil.add(ui, repo, m, "", False, **opts)
266 265 return rejected and 1 or 0
267 266
268 267 @command('addremove',
269 268 similarityopts + subrepoopts + walkopts + dryrunopts,
270 269 _('[OPTION]... [FILE]...'),
271 270 inferrepo=True)
272 271 def addremove(ui, repo, *pats, **opts):
273 272 """add all new files, delete all missing files
274 273
275 274 Add all new files and remove all missing files from the
276 275 repository.
277 276
278 277 Unless names are given, new files are ignored if they match any of
279 278 the patterns in ``.hgignore``. As with add, these changes take
280 279 effect at the next commit.
281 280
282 281 Use the -s/--similarity option to detect renamed files. This
283 282 option takes a percentage between 0 (disabled) and 100 (files must
284 283 be identical) as its parameter. With a parameter greater than 0,
285 284 this compares every removed file with every added file and records
286 285 those similar enough as renames. Detecting renamed files this way
287 286 can be expensive. After using this option, :hg:`status -C` can be
288 287 used to check which files were identified as moved or renamed. If
289 288 not specified, -s/--similarity defaults to 100 and only renames of
290 289 identical files are detected.
291 290
292 291 .. container:: verbose
293 292
294 293 Examples:
295 294
296 295 - A number of files (bar.c and foo.c) are new,
297 296 while foobar.c has been removed (without using :hg:`remove`)
298 297 from the repository::
299 298
300 299 $ ls
301 300 bar.c foo.c
302 301 $ hg status
303 302 ! foobar.c
304 303 ? bar.c
305 304 ? foo.c
306 305 $ hg addremove
307 306 adding bar.c
308 307 adding foo.c
309 308 removing foobar.c
310 309 $ hg status
311 310 A bar.c
312 311 A foo.c
313 312 R foobar.c
314 313
315 314 - A file foobar.c was moved to foo.c without using :hg:`rename`.
316 315 Afterwards, it was edited slightly::
317 316
318 317 $ ls
319 318 foo.c
320 319 $ hg status
321 320 ! foobar.c
322 321 ? foo.c
323 322 $ hg addremove --similarity 90
324 323 removing foobar.c
325 324 adding foo.c
326 325 recording removal of foobar.c as rename to foo.c (94% similar)
327 326 $ hg status -C
328 327 A foo.c
329 328 foobar.c
330 329 R foobar.c
331 330
332 331 Returns 0 if all files are successfully added.
333 332 """
334 333 try:
335 334 sim = float(opts.get('similarity') or 100)
336 335 except ValueError:
337 336 raise error.Abort(_('similarity must be a number'))
338 337 if sim < 0 or sim > 100:
339 338 raise error.Abort(_('similarity must be between 0 and 100'))
340 339 matcher = scmutil.match(repo[None], pats, opts)
341 340 return scmutil.addremove(repo, matcher, "", opts, similarity=sim / 100.0)
342 341
343 342 @command('^annotate|blame',
344 343 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
345 344 ('', 'follow', None,
346 345 _('follow copies/renames and list the filename (DEPRECATED)')),
347 346 ('', 'no-follow', None, _("don't follow copies and renames")),
348 347 ('a', 'text', None, _('treat all files as text')),
349 348 ('u', 'user', None, _('list the author (long with -v)')),
350 349 ('f', 'file', None, _('list the filename')),
351 350 ('d', 'date', None, _('list the date (short with -q)')),
352 351 ('n', 'number', None, _('list the revision number (default)')),
353 352 ('c', 'changeset', None, _('list the changeset')),
354 353 ('l', 'line-number', None, _('show line number at the first appearance'))
355 354 ] + diffwsopts + walkopts + formatteropts,
356 355 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
357 356 inferrepo=True)
358 357 def annotate(ui, repo, *pats, **opts):
359 358 """show changeset information by line for each file
360 359
361 360 List changes in files, showing the revision id responsible for
362 361 each line.
363 362
364 363 This command is useful for discovering when a change was made and
365 364 by whom.
366 365
367 366 If you include --file, --user, or --date, the revision number is
368 367 suppressed unless you also include --number.
369 368
370 369 Without the -a/--text option, annotate will avoid processing files
371 370 it detects as binary. With -a, annotate will annotate the file
372 371 anyway, although the results will probably be neither useful
373 372 nor desirable.
374 373
375 374 Returns 0 on success.
376 375 """
377 376 if not pats:
378 377 raise error.Abort(_('at least one filename or pattern is required'))
379 378
380 379 if opts.get('follow'):
381 380 # --follow is deprecated and now just an alias for -f/--file
382 381 # to mimic the behavior of Mercurial before version 1.5
383 382 opts['file'] = True
384 383
385 384 ctx = scmutil.revsingle(repo, opts.get('rev'))
386 385
387 386 fm = ui.formatter('annotate', opts)
388 387 if ui.quiet:
389 388 datefunc = util.shortdate
390 389 else:
391 390 datefunc = util.datestr
392 391 if ctx.rev() is None:
393 392 def hexfn(node):
394 393 if node is None:
395 394 return None
396 395 else:
397 396 return fm.hexfunc(node)
398 397 if opts.get('changeset'):
399 398 # omit "+" suffix which is appended to node hex
400 399 def formatrev(rev):
401 400 if rev is None:
402 401 return '%d' % ctx.p1().rev()
403 402 else:
404 403 return '%d' % rev
405 404 else:
406 405 def formatrev(rev):
407 406 if rev is None:
408 407 return '%d+' % ctx.p1().rev()
409 408 else:
410 409 return '%d ' % rev
411 410 def formathex(hex):
412 411 if hex is None:
413 412 return '%s+' % fm.hexfunc(ctx.p1().node())
414 413 else:
415 414 return '%s ' % hex
416 415 else:
417 416 hexfn = fm.hexfunc
418 417 formatrev = formathex = str
419 418
420 419 opmap = [('user', ' ', lambda x: x[0].user(), ui.shortuser),
421 420 ('number', ' ', lambda x: x[0].rev(), formatrev),
422 421 ('changeset', ' ', lambda x: hexfn(x[0].node()), formathex),
423 422 ('date', ' ', lambda x: x[0].date(), util.cachefunc(datefunc)),
424 423 ('file', ' ', lambda x: x[0].path(), str),
425 424 ('line_number', ':', lambda x: x[1], str),
426 425 ]
427 426 fieldnamemap = {'number': 'rev', 'changeset': 'node'}
428 427
429 428 if (not opts.get('user') and not opts.get('changeset')
430 429 and not opts.get('date') and not opts.get('file')):
431 430 opts['number'] = True
432 431
433 432 linenumber = opts.get('line_number') is not None
434 433 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
435 434 raise error.Abort(_('at least one of -n/-c is required for -l'))
436 435
437 436 if fm.isplain():
438 437 def makefunc(get, fmt):
439 438 return lambda x: fmt(get(x))
440 439 else:
441 440 def makefunc(get, fmt):
442 441 return get
443 442 funcmap = [(makefunc(get, fmt), sep) for op, sep, get, fmt in opmap
444 443 if opts.get(op)]
445 444 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
446 445 fields = ' '.join(fieldnamemap.get(op, op) for op, sep, get, fmt in opmap
447 446 if opts.get(op))
448 447
449 448 def bad(x, y):
450 449 raise error.Abort("%s: %s" % (x, y))
451 450
452 451 m = scmutil.match(ctx, pats, opts, badfn=bad)
453 452
454 453 follow = not opts.get('no_follow')
455 454 diffopts = patch.difffeatureopts(ui, opts, section='annotate',
456 455 whitespace=True)
457 456 for abs in ctx.walk(m):
458 457 fctx = ctx[abs]
459 458 if not opts.get('text') and util.binary(fctx.data()):
460 459 fm.plain(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
461 460 continue
462 461
463 462 lines = fctx.annotate(follow=follow, linenumber=linenumber,
464 463 diffopts=diffopts)
465 464 if not lines:
466 465 continue
467 466 formats = []
468 467 pieces = []
469 468
470 469 for f, sep in funcmap:
471 470 l = [f(n) for n, dummy in lines]
472 471 if fm.isplain():
473 472 sizes = [encoding.colwidth(x) for x in l]
474 473 ml = max(sizes)
475 474 formats.append([sep + ' ' * (ml - w) + '%s' for w in sizes])
476 475 else:
477 476 formats.append(['%s' for x in l])
478 477 pieces.append(l)
479 478
480 479 for f, p, l in zip(zip(*formats), zip(*pieces), lines):
481 480 fm.startitem()
482 481 fm.write(fields, "".join(f), *p)
483 482 fm.write('line', ": %s", l[1])
484 483
485 484 if not lines[-1][1].endswith('\n'):
486 485 fm.plain('\n')
487 486
488 487 fm.end()
489 488
490 489 @command('archive',
491 490 [('', 'no-decode', None, _('do not pass files through decoders')),
492 491 ('p', 'prefix', '', _('directory prefix for files in archive'),
493 492 _('PREFIX')),
494 493 ('r', 'rev', '', _('revision to distribute'), _('REV')),
495 494 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
496 495 ] + subrepoopts + walkopts,
497 496 _('[OPTION]... DEST'))
498 497 def archive(ui, repo, dest, **opts):
499 498 '''create an unversioned archive of a repository revision
500 499
501 500 By default, the revision used is the parent of the working
502 501 directory; use -r/--rev to specify a different revision.
503 502
504 503 The archive type is automatically detected based on file
505 504 extension (to override, use -t/--type).
506 505
507 506 .. container:: verbose
508 507
509 508 Examples:
510 509
511 510 - create a zip file containing the 1.0 release::
512 511
513 512 hg archive -r 1.0 project-1.0.zip
514 513
515 514 - create a tarball excluding .hg files::
516 515
517 516 hg archive project.tar.gz -X ".hg*"
518 517
519 518 Valid types are:
520 519
521 520 :``files``: a directory full of files (default)
522 521 :``tar``: tar archive, uncompressed
523 522 :``tbz2``: tar archive, compressed using bzip2
524 523 :``tgz``: tar archive, compressed using gzip
525 524 :``uzip``: zip archive, uncompressed
526 525 :``zip``: zip archive, compressed using deflate
527 526
528 527 The exact name of the destination archive or directory is given
529 528 using a format string; see :hg:`help export` for details.
530 529
531 530 Each member added to an archive file has a directory prefix
532 531 prepended. Use -p/--prefix to specify a format string for the
533 532 prefix. The default is the basename of the archive, with suffixes
534 533 removed.
535 534
536 535 Returns 0 on success.
537 536 '''
538 537
539 538 ctx = scmutil.revsingle(repo, opts.get('rev'))
540 539 if not ctx:
541 540 raise error.Abort(_('no working directory: please specify a revision'))
542 541 node = ctx.node()
543 542 dest = cmdutil.makefilename(repo, dest, node)
544 543 if os.path.realpath(dest) == repo.root:
545 544 raise error.Abort(_('repository root cannot be destination'))
546 545
547 546 kind = opts.get('type') or archival.guesskind(dest) or 'files'
548 547 prefix = opts.get('prefix')
549 548
550 549 if dest == '-':
551 550 if kind == 'files':
552 551 raise error.Abort(_('cannot archive plain files to stdout'))
553 552 dest = cmdutil.makefileobj(repo, dest)
554 553 if not prefix:
555 554 prefix = os.path.basename(repo.root) + '-%h'
556 555
557 556 prefix = cmdutil.makefilename(repo, prefix, node)
558 557 matchfn = scmutil.match(ctx, [], opts)
559 558 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
560 559 matchfn, prefix, subrepos=opts.get('subrepos'))
561 560
562 561 @command('backout',
563 562 [('', 'merge', None, _('merge with old dirstate parent after backout')),
564 563 ('', 'commit', None,
565 564 _('commit if no conflicts were encountered (DEPRECATED)')),
566 565 ('', 'no-commit', None, _('do not commit')),
567 566 ('', 'parent', '',
568 567 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
569 568 ('r', 'rev', '', _('revision to backout'), _('REV')),
570 569 ('e', 'edit', False, _('invoke editor on commit messages')),
571 570 ] + mergetoolopts + walkopts + commitopts + commitopts2,
572 571 _('[OPTION]... [-r] REV'))
573 572 def backout(ui, repo, node=None, rev=None, **opts):
574 573 '''reverse effect of earlier changeset
575 574
576 575 Prepare a new changeset with the effect of REV undone in the
577 576 current working directory. If no conflicts were encountered,
578 577 it will be committed immediately.
579 578
580 579 If REV is the parent of the working directory, then this new changeset
581 580 is committed automatically (unless --no-commit is specified).
582 581
583 582 .. note::
584 583
585 584 :hg:`backout` cannot be used to fix either an unwanted or
586 585 incorrect merge.
587 586
588 587 .. container:: verbose
589 588
590 589 Examples:
591 590
592 591 - Reverse the effect of the parent of the working directory.
593 592 This backout will be committed immediately::
594 593
595 594 hg backout -r .
596 595
597 596 - Reverse the effect of previous bad revision 23::
598 597
599 598 hg backout -r 23
600 599
601 600 - Reverse the effect of previous bad revision 23 and
602 601 leave changes uncommitted::
603 602
604 603 hg backout -r 23 --no-commit
605 604 hg commit -m "Backout revision 23"
606 605
607 606 By default, the pending changeset will have one parent,
608 607 maintaining a linear history. With --merge, the pending
609 608 changeset will instead have two parents: the old parent of the
610 609 working directory and a new child of REV that simply undoes REV.
611 610
612 611 Before version 1.7, the behavior without --merge was equivalent
613 612 to specifying --merge followed by :hg:`update --clean .` to
614 613 cancel the merge and leave the child of REV as a head to be
615 614 merged separately.
616 615
617 616 See :hg:`help dates` for a list of formats valid for -d/--date.
618 617
619 618 See :hg:`help revert` for a way to restore files to the state
620 619 of another revision.
621 620
622 621 Returns 0 on success, 1 if nothing to backout or there are unresolved
623 622 files.
624 623 '''
625 624 wlock = lock = None
626 625 try:
627 626 wlock = repo.wlock()
628 627 lock = repo.lock()
629 628 return _dobackout(ui, repo, node, rev, **opts)
630 629 finally:
631 630 release(lock, wlock)
632 631
633 632 def _dobackout(ui, repo, node=None, rev=None, **opts):
634 633 if opts.get('commit') and opts.get('no_commit'):
635 634 raise error.Abort(_("cannot use --commit with --no-commit"))
636 635 if opts.get('merge') and opts.get('no_commit'):
637 636 raise error.Abort(_("cannot use --merge with --no-commit"))
638 637
639 638 if rev and node:
640 639 raise error.Abort(_("please specify just one revision"))
641 640
642 641 if not rev:
643 642 rev = node
644 643
645 644 if not rev:
646 645 raise error.Abort(_("please specify a revision to backout"))
647 646
648 647 date = opts.get('date')
649 648 if date:
650 649 opts['date'] = util.parsedate(date)
651 650
652 651 cmdutil.checkunfinished(repo)
653 652 cmdutil.bailifchanged(repo)
654 653 node = scmutil.revsingle(repo, rev).node()
655 654
656 655 op1, op2 = repo.dirstate.parents()
657 656 if not repo.changelog.isancestor(node, op1):
658 657 raise error.Abort(_('cannot backout change that is not an ancestor'))
659 658
660 659 p1, p2 = repo.changelog.parents(node)
661 660 if p1 == nullid:
662 661 raise error.Abort(_('cannot backout a change with no parents'))
663 662 if p2 != nullid:
664 663 if not opts.get('parent'):
665 664 raise error.Abort(_('cannot backout a merge changeset'))
666 665 p = repo.lookup(opts['parent'])
667 666 if p not in (p1, p2):
668 667 raise error.Abort(_('%s is not a parent of %s') %
669 668 (short(p), short(node)))
670 669 parent = p
671 670 else:
672 671 if opts.get('parent'):
673 672 raise error.Abort(_('cannot use --parent on non-merge changeset'))
674 673 parent = p1
675 674
676 675 # the backout should appear on the same branch
677 676 branch = repo.dirstate.branch()
678 677 bheads = repo.branchheads(branch)
679 678 rctx = scmutil.revsingle(repo, hex(parent))
680 679 if not opts.get('merge') and op1 != node:
681 680 dsguard = dirstateguard.dirstateguard(repo, 'backout')
682 681 try:
683 682 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
684 683 'backout')
685 684 stats = mergemod.update(repo, parent, True, True, node, False)
686 685 repo.setparents(op1, op2)
687 686 dsguard.close()
688 687 hg._showstats(repo, stats)
689 688 if stats[3]:
690 689 repo.ui.status(_("use 'hg resolve' to retry unresolved "
691 690 "file merges\n"))
692 691 return 1
693 692 finally:
694 693 ui.setconfig('ui', 'forcemerge', '', '')
695 694 lockmod.release(dsguard)
696 695 else:
697 696 hg.clean(repo, node, show_stats=False)
698 697 repo.dirstate.setbranch(branch)
699 698 cmdutil.revert(ui, repo, rctx, repo.dirstate.parents())
700 699
701 700 if opts.get('no_commit'):
702 701 msg = _("changeset %s backed out, "
703 702 "don't forget to commit.\n")
704 703 ui.status(msg % short(node))
705 704 return 0
706 705
707 706 def commitfunc(ui, repo, message, match, opts):
708 707 editform = 'backout'
709 708 e = cmdutil.getcommiteditor(editform=editform, **opts)
710 709 if not message:
711 710 # we don't translate commit messages
712 711 message = "Backed out changeset %s" % short(node)
713 712 e = cmdutil.getcommiteditor(edit=True, editform=editform)
714 713 return repo.commit(message, opts.get('user'), opts.get('date'),
715 714 match, editor=e)
716 715 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
717 716 if not newnode:
718 717 ui.status(_("nothing changed\n"))
719 718 return 1
720 719 cmdutil.commitstatus(repo, newnode, branch, bheads)
721 720
722 721 def nice(node):
723 722 return '%d:%s' % (repo.changelog.rev(node), short(node))
724 723 ui.status(_('changeset %s backs out changeset %s\n') %
725 724 (nice(repo.changelog.tip()), nice(node)))
726 725 if opts.get('merge') and op1 != node:
727 726 hg.clean(repo, op1, show_stats=False)
728 727 ui.status(_('merging with changeset %s\n')
729 728 % nice(repo.changelog.tip()))
730 729 try:
731 730 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
732 731 'backout')
733 732 return hg.merge(repo, hex(repo.changelog.tip()))
734 733 finally:
735 734 ui.setconfig('ui', 'forcemerge', '', '')
736 735 return 0
737 736
738 737 @command('bisect',
739 738 [('r', 'reset', False, _('reset bisect state')),
740 739 ('g', 'good', False, _('mark changeset good')),
741 740 ('b', 'bad', False, _('mark changeset bad')),
742 741 ('s', 'skip', False, _('skip testing changeset')),
743 742 ('e', 'extend', False, _('extend the bisect range')),
744 743 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
745 744 ('U', 'noupdate', False, _('do not update to target'))],
746 745 _("[-gbsr] [-U] [-c CMD] [REV]"))
747 746 def bisect(ui, repo, rev=None, extra=None, command=None,
748 747 reset=None, good=None, bad=None, skip=None, extend=None,
749 748 noupdate=None):
750 749 """subdivision search of changesets
751 750
752 751 This command helps to find changesets which introduce problems. To
753 752 use, mark the earliest changeset you know exhibits the problem as
754 753 bad, then mark the latest changeset which is free from the problem
755 754 as good. Bisect will update your working directory to a revision
756 755 for testing (unless the -U/--noupdate option is specified). Once
757 756 you have performed tests, mark the working directory as good or
758 757 bad, and bisect will either update to another candidate changeset
759 758 or announce that it has found the bad revision.
760 759
761 760 As a shortcut, you can also use the revision argument to mark a
762 761 revision as good or bad without checking it out first.
763 762
764 763 If you supply a command, it will be used for automatic bisection.
765 764 The environment variable HG_NODE will contain the ID of the
766 765 changeset being tested. The exit status of the command will be
767 766 used to mark revisions as good or bad: status 0 means good, 125
768 767 means to skip the revision, 127 (command not found) will abort the
769 768 bisection, and any other non-zero exit status means the revision
770 769 is bad.
771 770
772 771 .. container:: verbose
773 772
774 773 Some examples:
775 774
776 775 - start a bisection with known bad revision 34, and good revision 12::
777 776
778 777 hg bisect --bad 34
779 778 hg bisect --good 12
780 779
781 780 - advance the current bisection by marking current revision as good or
782 781 bad::
783 782
784 783 hg bisect --good
785 784 hg bisect --bad
786 785
787 786 - mark the current revision, or a known revision, to be skipped (e.g. if
788 787 that revision is not usable because of another issue)::
789 788
790 789 hg bisect --skip
791 790 hg bisect --skip 23
792 791
793 792 - skip all revisions that do not touch directories ``foo`` or ``bar``::
794 793
795 794 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
796 795
797 796 - forget the current bisection::
798 797
799 798 hg bisect --reset
800 799
801 800 - use 'make && make tests' to automatically find the first broken
802 801 revision::
803 802
804 803 hg bisect --reset
805 804 hg bisect --bad 34
806 805 hg bisect --good 12
807 806 hg bisect --command "make && make tests"
808 807
809 808 - see all changesets whose states are already known in the current
810 809 bisection::
811 810
812 811 hg log -r "bisect(pruned)"
813 812
814 813 - see the changeset currently being bisected (especially useful
815 814 if running with -U/--noupdate)::
816 815
817 816 hg log -r "bisect(current)"
818 817
819 818 - see all changesets that took part in the current bisection::
820 819
821 820 hg log -r "bisect(range)"
822 821
823 822 - you can even get a nice graph::
824 823
825 824 hg log --graph -r "bisect(range)"
826 825
827 826 See :hg:`help revsets` for more about the `bisect()` keyword.
828 827
829 828 Returns 0 on success.
830 829 """
831 830 # backward compatibility
832 831 if rev in "good bad reset init".split():
833 832 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
834 833 cmd, rev, extra = rev, extra, None
835 834 if cmd == "good":
836 835 good = True
837 836 elif cmd == "bad":
838 837 bad = True
839 838 else:
840 839 reset = True
841 840 elif extra or good + bad + skip + reset + extend + bool(command) > 1:
842 841 raise error.Abort(_('incompatible arguments'))
843 842
844 843 cmdutil.checkunfinished(repo)
845 844
846 845 if reset:
847 846 hbisect.resetstate(repo)
848 847 return
849 848
850 849 state = hbisect.load_state(repo)
851 850
852 851 # update state
853 852 if good or bad or skip:
854 853 if rev:
855 854 nodes = [repo.lookup(i) for i in scmutil.revrange(repo, [rev])]
856 855 else:
857 856 nodes = [repo.lookup('.')]
858 857 if good:
859 858 state['good'] += nodes
860 859 elif bad:
861 860 state['bad'] += nodes
862 861 elif skip:
863 862 state['skip'] += nodes
864 863 hbisect.save_state(repo, state)
865 864 if not (state['good'] and state['bad']):
866 865 return
867 866
868 867 def mayupdate(repo, node, show_stats=True):
869 868 """common used update sequence"""
870 869 if noupdate:
871 870 return
872 871 cmdutil.bailifchanged(repo)
873 872 return hg.clean(repo, node, show_stats=show_stats)
874 873
875 874 displayer = cmdutil.show_changeset(ui, repo, {})
876 875
877 876 if command:
878 877 changesets = 1
879 878 if noupdate:
880 879 try:
881 880 node = state['current'][0]
882 881 except LookupError:
883 882 raise error.Abort(_('current bisect revision is unknown - '
884 883 'start a new bisect to fix'))
885 884 else:
886 885 node, p2 = repo.dirstate.parents()
887 886 if p2 != nullid:
888 887 raise error.Abort(_('current bisect revision is a merge'))
889 888 if rev:
890 889 node = repo[scmutil.revsingle(repo, rev, node)].node()
891 890 try:
892 891 while changesets:
893 892 # update state
894 893 state['current'] = [node]
895 894 hbisect.save_state(repo, state)
896 895 status = ui.system(command, environ={'HG_NODE': hex(node)})
897 896 if status == 125:
898 897 transition = "skip"
899 898 elif status == 0:
900 899 transition = "good"
901 900 # status < 0 means process was killed
902 901 elif status == 127:
903 902 raise error.Abort(_("failed to execute %s") % command)
904 903 elif status < 0:
905 904 raise error.Abort(_("%s killed") % command)
906 905 else:
907 906 transition = "bad"
908 907 state[transition].append(node)
909 908 ctx = repo[node]
910 909 ui.status(_('changeset %d:%s: %s\n') % (ctx, ctx, transition))
911 910 hbisect.checkstate(state)
912 911 # bisect
913 912 nodes, changesets, bgood = hbisect.bisect(repo.changelog, state)
914 913 # update to next check
915 914 node = nodes[0]
916 915 mayupdate(repo, node, show_stats=False)
917 916 finally:
918 917 state['current'] = [node]
919 918 hbisect.save_state(repo, state)
920 919 hbisect.printresult(ui, repo, state, displayer, nodes, bgood)
921 920 return
922 921
923 922 hbisect.checkstate(state)
924 923
925 924 # actually bisect
926 925 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
927 926 if extend:
928 927 if not changesets:
929 928 extendnode = hbisect.extendrange(repo, state, nodes, good)
930 929 if extendnode is not None:
931 930 ui.write(_("Extending search to changeset %d:%s\n")
932 931 % (extendnode.rev(), extendnode))
933 932 state['current'] = [extendnode.node()]
934 933 hbisect.save_state(repo, state)
935 934 return mayupdate(repo, extendnode.node())
936 935 raise error.Abort(_("nothing to extend"))
937 936
938 937 if changesets == 0:
939 938 hbisect.printresult(ui, repo, state, displayer, nodes, good)
940 939 else:
941 940 assert len(nodes) == 1 # only a single node can be tested next
942 941 node = nodes[0]
943 942 # compute the approximate number of remaining tests
944 943 tests, size = 0, 2
945 944 while size <= changesets:
946 945 tests, size = tests + 1, size * 2
947 946 rev = repo.changelog.rev(node)
948 947 ui.write(_("Testing changeset %d:%s "
949 948 "(%d changesets remaining, ~%d tests)\n")
950 949 % (rev, short(node), changesets, tests))
951 950 state['current'] = [node]
952 951 hbisect.save_state(repo, state)
953 952 return mayupdate(repo, node)
954 953
955 954 @command('bookmarks|bookmark',
956 955 [('f', 'force', False, _('force')),
957 956 ('r', 'rev', '', _('revision for bookmark action'), _('REV')),
958 957 ('d', 'delete', False, _('delete a given bookmark')),
959 958 ('m', 'rename', '', _('rename a given bookmark'), _('OLD')),
960 959 ('i', 'inactive', False, _('mark a bookmark inactive')),
961 960 ] + formatteropts,
962 961 _('hg bookmarks [OPTIONS]... [NAME]...'))
963 962 def bookmark(ui, repo, *names, **opts):
964 963 '''create a new bookmark or list existing bookmarks
965 964
966 965 Bookmarks are labels on changesets to help track lines of development.
967 966 Bookmarks are unversioned and can be moved, renamed and deleted.
968 967 Deleting or moving a bookmark has no effect on the associated changesets.
969 968
970 969 Creating or updating to a bookmark causes it to be marked as 'active'.
971 970 The active bookmark is indicated with a '*'.
972 971 When a commit is made, the active bookmark will advance to the new commit.
973 972 A plain :hg:`update` will also advance an active bookmark, if possible.
974 973 Updating away from a bookmark will cause it to be deactivated.
975 974
976 975 Bookmarks can be pushed and pulled between repositories (see
977 976 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
978 977 diverged, a new 'divergent bookmark' of the form 'name@path' will
979 978 be created. Using :hg:`merge` will resolve the divergence.
980 979
981 980 A bookmark named '@' has the special property that :hg:`clone` will
982 981 check it out by default if it exists.
983 982
984 983 .. container:: verbose
985 984
986 985 Examples:
987 986
988 987 - create an active bookmark for a new line of development::
989 988
990 989 hg book new-feature
991 990
992 991 - create an inactive bookmark as a place marker::
993 992
994 993 hg book -i reviewed
995 994
996 995 - create an inactive bookmark on another changeset::
997 996
998 997 hg book -r .^ tested
999 998
1000 999 - rename bookmark turkey to dinner::
1001 1000
1002 1001 hg book -m turkey dinner
1003 1002
1004 1003 - move the '@' bookmark from another branch::
1005 1004
1006 1005 hg book -f @
1007 1006 '''
1008 1007 force = opts.get('force')
1009 1008 rev = opts.get('rev')
1010 1009 delete = opts.get('delete')
1011 1010 rename = opts.get('rename')
1012 1011 inactive = opts.get('inactive')
1013 1012
1014 1013 def checkformat(mark):
1015 1014 mark = mark.strip()
1016 1015 if not mark:
1017 1016 raise error.Abort(_("bookmark names cannot consist entirely of "
1018 1017 "whitespace"))
1019 1018 scmutil.checknewlabel(repo, mark, 'bookmark')
1020 1019 return mark
1021 1020
1022 1021 def checkconflict(repo, mark, cur, force=False, target=None):
1023 1022 if mark in marks and not force:
1024 1023 if target:
1025 1024 if marks[mark] == target and target == cur:
1026 1025 # re-activating a bookmark
1027 1026 return
1028 1027 anc = repo.changelog.ancestors([repo[target].rev()])
1029 1028 bmctx = repo[marks[mark]]
1030 1029 divs = [repo[b].node() for b in marks
1031 1030 if b.split('@', 1)[0] == mark.split('@', 1)[0]]
1032 1031
1033 1032 # allow resolving a single divergent bookmark even if moving
1034 1033 # the bookmark across branches when a revision is specified
1035 1034 # that contains a divergent bookmark
1036 1035 if bmctx.rev() not in anc and target in divs:
1037 1036 bookmarks.deletedivergent(repo, [target], mark)
1038 1037 return
1039 1038
1040 1039 deletefrom = [b for b in divs
1041 1040 if repo[b].rev() in anc or b == target]
1042 1041 bookmarks.deletedivergent(repo, deletefrom, mark)
1043 1042 if bookmarks.validdest(repo, bmctx, repo[target]):
1044 1043 ui.status(_("moving bookmark '%s' forward from %s\n") %
1045 1044 (mark, short(bmctx.node())))
1046 1045 return
1047 1046 raise error.Abort(_("bookmark '%s' already exists "
1048 1047 "(use -f to force)") % mark)
1049 1048 if ((mark in repo.branchmap() or mark == repo.dirstate.branch())
1050 1049 and not force):
1051 1050 raise error.Abort(
1052 1051 _("a bookmark cannot have the name of an existing branch"))
1053 1052
1054 1053 if delete and rename:
1055 1054 raise error.Abort(_("--delete and --rename are incompatible"))
1056 1055 if delete and rev:
1057 1056 raise error.Abort(_("--rev is incompatible with --delete"))
1058 1057 if rename and rev:
1059 1058 raise error.Abort(_("--rev is incompatible with --rename"))
1060 1059 if not names and (delete or rev):
1061 1060 raise error.Abort(_("bookmark name required"))
1062 1061
1063 1062 if delete or rename or names or inactive:
1064 1063 wlock = lock = tr = None
1065 1064 try:
1066 1065 wlock = repo.wlock()
1067 1066 lock = repo.lock()
1068 1067 cur = repo.changectx('.').node()
1069 1068 marks = repo._bookmarks
1070 1069 if delete:
1071 1070 tr = repo.transaction('bookmark')
1072 1071 for mark in names:
1073 1072 if mark not in marks:
1074 1073 raise error.Abort(_("bookmark '%s' does not exist") %
1075 1074 mark)
1076 1075 if mark == repo._activebookmark:
1077 1076 bookmarks.deactivate(repo)
1078 1077 del marks[mark]
1079 1078
1080 1079 elif rename:
1081 1080 tr = repo.transaction('bookmark')
1082 1081 if not names:
1083 1082 raise error.Abort(_("new bookmark name required"))
1084 1083 elif len(names) > 1:
1085 1084 raise error.Abort(_("only one new bookmark name allowed"))
1086 1085 mark = checkformat(names[0])
1087 1086 if rename not in marks:
1088 1087 raise error.Abort(_("bookmark '%s' does not exist")
1089 1088 % rename)
1090 1089 checkconflict(repo, mark, cur, force)
1091 1090 marks[mark] = marks[rename]
1092 1091 if repo._activebookmark == rename and not inactive:
1093 1092 bookmarks.activate(repo, mark)
1094 1093 del marks[rename]
1095 1094 elif names:
1096 1095 tr = repo.transaction('bookmark')
1097 1096 newact = None
1098 1097 for mark in names:
1099 1098 mark = checkformat(mark)
1100 1099 if newact is None:
1101 1100 newact = mark
1102 1101 if inactive and mark == repo._activebookmark:
1103 1102 bookmarks.deactivate(repo)
1104 1103 return
1105 1104 tgt = cur
1106 1105 if rev:
1107 1106 tgt = scmutil.revsingle(repo, rev).node()
1108 1107 checkconflict(repo, mark, cur, force, tgt)
1109 1108 marks[mark] = tgt
1110 1109 if not inactive and cur == marks[newact] and not rev:
1111 1110 bookmarks.activate(repo, newact)
1112 1111 elif cur != tgt and newact == repo._activebookmark:
1113 1112 bookmarks.deactivate(repo)
1114 1113 elif inactive:
1115 1114 if len(marks) == 0:
1116 1115 ui.status(_("no bookmarks set\n"))
1117 1116 elif not repo._activebookmark:
1118 1117 ui.status(_("no active bookmark\n"))
1119 1118 else:
1120 1119 bookmarks.deactivate(repo)
1121 1120 if tr is not None:
1122 1121 marks.recordchange(tr)
1123 1122 tr.close()
1124 1123 finally:
1125 1124 lockmod.release(tr, lock, wlock)
1126 1125 else: # show bookmarks
1127 1126 fm = ui.formatter('bookmarks', opts)
1128 1127 hexfn = fm.hexfunc
1129 1128 marks = repo._bookmarks
1130 1129 if len(marks) == 0 and fm.isplain():
1131 1130 ui.status(_("no bookmarks set\n"))
1132 1131 for bmark, n in sorted(marks.iteritems()):
1133 1132 active = repo._activebookmark
1134 1133 if bmark == active:
1135 1134 prefix, label = '*', activebookmarklabel
1136 1135 else:
1137 1136 prefix, label = ' ', ''
1138 1137
1139 1138 fm.startitem()
1140 1139 if not ui.quiet:
1141 1140 fm.plain(' %s ' % prefix, label=label)
1142 1141 fm.write('bookmark', '%s', bmark, label=label)
1143 1142 pad = " " * (25 - encoding.colwidth(bmark))
1144 1143 fm.condwrite(not ui.quiet, 'rev node', pad + ' %d:%s',
1145 1144 repo.changelog.rev(n), hexfn(n), label=label)
1146 1145 fm.data(active=(bmark == active))
1147 1146 fm.plain('\n')
1148 1147 fm.end()
1149 1148
1150 1149 @command('branch',
1151 1150 [('f', 'force', None,
1152 1151 _('set branch name even if it shadows an existing branch')),
1153 1152 ('C', 'clean', None, _('reset branch name to parent branch name'))],
1154 1153 _('[-fC] [NAME]'))
1155 1154 def branch(ui, repo, label=None, **opts):
1156 1155 """set or show the current branch name
1157 1156
1158 1157 .. note::
1159 1158
1160 1159 Branch names are permanent and global. Use :hg:`bookmark` to create a
1161 1160 light-weight bookmark instead. See :hg:`help glossary` for more
1162 1161 information about named branches and bookmarks.
1163 1162
1164 1163 With no argument, show the current branch name. With one argument,
1165 1164 set the working directory branch name (the branch will not exist
1166 1165 in the repository until the next commit). Standard practice
1167 1166 recommends that primary development take place on the 'default'
1168 1167 branch.
1169 1168
1170 1169 Unless -f/--force is specified, branch will not let you set a
1171 1170 branch name that already exists.
1172 1171
1173 1172 Use -C/--clean to reset the working directory branch to that of
1174 1173 the parent of the working directory, negating a previous branch
1175 1174 change.
1176 1175
1177 1176 Use the command :hg:`update` to switch to an existing branch. Use
1178 1177 :hg:`commit --close-branch` to mark this branch head as closed.
1179 1178 When all heads of a branch are closed, the branch will be
1180 1179 considered closed.
1181 1180
1182 1181 Returns 0 on success.
1183 1182 """
1184 1183 if label:
1185 1184 label = label.strip()
1186 1185
1187 1186 if not opts.get('clean') and not label:
1188 1187 ui.write("%s\n" % repo.dirstate.branch())
1189 1188 return
1190 1189
1191 1190 with repo.wlock():
1192 1191 if opts.get('clean'):
1193 1192 label = repo[None].p1().branch()
1194 1193 repo.dirstate.setbranch(label)
1195 1194 ui.status(_('reset working directory to branch %s\n') % label)
1196 1195 elif label:
1197 1196 if not opts.get('force') and label in repo.branchmap():
1198 1197 if label not in [p.branch() for p in repo[None].parents()]:
1199 1198 raise error.Abort(_('a branch of the same name already'
1200 1199 ' exists'),
1201 1200 # i18n: "it" refers to an existing branch
1202 1201 hint=_("use 'hg update' to switch to it"))
1203 1202 scmutil.checknewlabel(repo, label, 'branch')
1204 1203 repo.dirstate.setbranch(label)
1205 1204 ui.status(_('marked working directory as branch %s\n') % label)
1206 1205
1207 1206 # find any open named branches aside from default
1208 1207 others = [n for n, h, t, c in repo.branchmap().iterbranches()
1209 1208 if n != "default" and not c]
1210 1209 if not others:
1211 1210 ui.status(_('(branches are permanent and global, '
1212 1211 'did you want a bookmark?)\n'))
1213 1212
1214 1213 @command('branches',
1215 1214 [('a', 'active', False,
1216 1215 _('show only branches that have unmerged heads (DEPRECATED)')),
1217 1216 ('c', 'closed', False, _('show normal and closed branches')),
1218 1217 ] + formatteropts,
1219 1218 _('[-c]'))
1220 1219 def branches(ui, repo, active=False, closed=False, **opts):
1221 1220 """list repository named branches
1222 1221
1223 1222 List the repository's named branches, indicating which ones are
1224 1223 inactive. If -c/--closed is specified, also list branches which have
1225 1224 been marked closed (see :hg:`commit --close-branch`).
1226 1225
1227 1226 Use the command :hg:`update` to switch to an existing branch.
1228 1227
1229 1228 Returns 0.
1230 1229 """
1231 1230
1232 1231 fm = ui.formatter('branches', opts)
1233 1232 hexfunc = fm.hexfunc
1234 1233
1235 1234 allheads = set(repo.heads())
1236 1235 branches = []
1237 1236 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1238 1237 isactive = not isclosed and bool(set(heads) & allheads)
1239 1238 branches.append((tag, repo[tip], isactive, not isclosed))
1240 1239 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]),
1241 1240 reverse=True)
1242 1241
1243 1242 for tag, ctx, isactive, isopen in branches:
1244 1243 if active and not isactive:
1245 1244 continue
1246 1245 if isactive:
1247 1246 label = 'branches.active'
1248 1247 notice = ''
1249 1248 elif not isopen:
1250 1249 if not closed:
1251 1250 continue
1252 1251 label = 'branches.closed'
1253 1252 notice = _(' (closed)')
1254 1253 else:
1255 1254 label = 'branches.inactive'
1256 1255 notice = _(' (inactive)')
1257 1256 current = (tag == repo.dirstate.branch())
1258 1257 if current:
1259 1258 label = 'branches.current'
1260 1259
1261 1260 fm.startitem()
1262 1261 fm.write('branch', '%s', tag, label=label)
1263 1262 rev = ctx.rev()
1264 1263 padsize = max(31 - len(str(rev)) - encoding.colwidth(tag), 0)
1265 1264 fmt = ' ' * padsize + ' %d:%s'
1266 1265 fm.condwrite(not ui.quiet, 'rev node', fmt, rev, hexfunc(ctx.node()),
1267 1266 label='log.changeset changeset.%s' % ctx.phasestr())
1268 1267 fm.data(active=isactive, closed=not isopen, current=current)
1269 1268 if not ui.quiet:
1270 1269 fm.plain(notice)
1271 1270 fm.plain('\n')
1272 1271 fm.end()
1273 1272
1274 1273 @command('bundle',
1275 1274 [('f', 'force', None, _('run even when the destination is unrelated')),
1276 1275 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
1277 1276 _('REV')),
1278 1277 ('b', 'branch', [], _('a specific branch you would like to bundle'),
1279 1278 _('BRANCH')),
1280 1279 ('', 'base', [],
1281 1280 _('a base changeset assumed to be available at the destination'),
1282 1281 _('REV')),
1283 1282 ('a', 'all', None, _('bundle all changesets in the repository')),
1284 1283 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
1285 1284 ] + remoteopts,
1286 1285 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]'))
1287 1286 def bundle(ui, repo, fname, dest=None, **opts):
1288 1287 """create a changegroup file
1289 1288
1290 1289 Generate a changegroup file collecting changesets to be added
1291 1290 to a repository.
1292 1291
1293 1292 To create a bundle containing all changesets, use -a/--all
1294 1293 (or --base null). Otherwise, hg assumes the destination will have
1295 1294 all the nodes you specify with --base parameters. Otherwise, hg
1296 1295 will assume the repository has all the nodes in destination, or
1297 1296 default-push/default if no destination is specified.
1298 1297
1299 1298 You can change bundle format with the -t/--type option. You can
1300 1299 specify a compression, a bundle version or both using a dash
1301 1300 (comp-version). The available compression methods are: none, bzip2,
1302 1301 and gzip (by default, bundles are compressed using bzip2). The
1303 1302 available formats are: v1, v2 (default to most suitable).
1304 1303
1305 1304 The bundle file can then be transferred using conventional means
1306 1305 and applied to another repository with the unbundle or pull
1307 1306 command. This is useful when direct push and pull are not
1308 1307 available or when exporting an entire repository is undesirable.
1309 1308
1310 1309 Applying bundles preserves all changeset contents including
1311 1310 permissions, copy/rename information, and revision history.
1312 1311
1313 1312 Returns 0 on success, 1 if no changes found.
1314 1313 """
1315 1314 revs = None
1316 1315 if 'rev' in opts:
1317 1316 revstrings = opts['rev']
1318 1317 revs = scmutil.revrange(repo, revstrings)
1319 1318 if revstrings and not revs:
1320 1319 raise error.Abort(_('no commits to bundle'))
1321 1320
1322 1321 bundletype = opts.get('type', 'bzip2').lower()
1323 1322 try:
1324 1323 bcompression, cgversion, params = exchange.parsebundlespec(
1325 1324 repo, bundletype, strict=False)
1326 1325 except error.UnsupportedBundleSpecification as e:
1327 1326 raise error.Abort(str(e),
1328 1327 hint=_("see 'hg help bundle' for supported "
1329 1328 "values for --type"))
1330 1329
1331 1330 # Packed bundles are a pseudo bundle format for now.
1332 1331 if cgversion == 's1':
1333 1332 raise error.Abort(_('packed bundles cannot be produced by "hg bundle"'),
1334 1333 hint=_("use 'hg debugcreatestreamclonebundle'"))
1335 1334
1336 1335 if opts.get('all'):
1337 1336 if dest:
1338 1337 raise error.Abort(_("--all is incompatible with specifying "
1339 1338 "a destination"))
1340 1339 if opts.get('base'):
1341 1340 ui.warn(_("ignoring --base because --all was specified\n"))
1342 1341 base = ['null']
1343 1342 else:
1344 1343 base = scmutil.revrange(repo, opts.get('base'))
1345 1344 # TODO: get desired bundlecaps from command line.
1346 1345 bundlecaps = None
1347 1346 if cgversion not in changegroup.supportedoutgoingversions(repo):
1348 1347 raise error.Abort(_("repository does not support bundle version %s") %
1349 1348 cgversion)
1350 1349
1351 1350 if base:
1352 1351 if dest:
1353 1352 raise error.Abort(_("--base is incompatible with specifying "
1354 1353 "a destination"))
1355 1354 common = [repo.lookup(rev) for rev in base]
1356 1355 heads = revs and map(repo.lookup, revs) or None
1357 1356 outgoing = discovery.outgoing(repo, common, heads)
1358 1357 cg = changegroup.getchangegroup(repo, 'bundle', outgoing,
1359 1358 bundlecaps=bundlecaps,
1360 1359 version=cgversion)
1361 1360 outgoing = None
1362 1361 else:
1363 1362 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1364 1363 dest, branches = hg.parseurl(dest, opts.get('branch'))
1365 1364 other = hg.peer(repo, opts, dest)
1366 1365 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1367 1366 heads = revs and map(repo.lookup, revs) or revs
1368 1367 outgoing = discovery.findcommonoutgoing(repo, other,
1369 1368 onlyheads=heads,
1370 1369 force=opts.get('force'),
1371 1370 portable=True)
1372 1371 cg = changegroup.getlocalchangegroup(repo, 'bundle', outgoing,
1373 1372 bundlecaps, version=cgversion)
1374 1373 if not cg:
1375 1374 scmutil.nochangesfound(ui, repo, outgoing and outgoing.excluded)
1376 1375 return 1
1377 1376
1378 1377 if cgversion == '01': #bundle1
1379 1378 if bcompression is None:
1380 1379 bcompression = 'UN'
1381 1380 bversion = 'HG10' + bcompression
1382 1381 bcompression = None
1383 1382 else:
1384 1383 assert cgversion == '02'
1385 1384 bversion = 'HG20'
1386 1385
1387 1386 bundle2.writebundle(ui, cg, fname, bversion, compression=bcompression)
1388 1387
1389 1388 @command('cat',
1390 1389 [('o', 'output', '',
1391 1390 _('print output to file with formatted name'), _('FORMAT')),
1392 1391 ('r', 'rev', '', _('print the given revision'), _('REV')),
1393 1392 ('', 'decode', None, _('apply any matching decode filter')),
1394 1393 ] + walkopts,
1395 1394 _('[OPTION]... FILE...'),
1396 1395 inferrepo=True)
1397 1396 def cat(ui, repo, file1, *pats, **opts):
1398 1397 """output the current or given revision of files
1399 1398
1400 1399 Print the specified files as they were at the given revision. If
1401 1400 no revision is given, the parent of the working directory is used.
1402 1401
1403 1402 Output may be to a file, in which case the name of the file is
1404 1403 given using a format string. The formatting rules as follows:
1405 1404
1406 1405 :``%%``: literal "%" character
1407 1406 :``%s``: basename of file being printed
1408 1407 :``%d``: dirname of file being printed, or '.' if in repository root
1409 1408 :``%p``: root-relative path name of file being printed
1410 1409 :``%H``: changeset hash (40 hexadecimal digits)
1411 1410 :``%R``: changeset revision number
1412 1411 :``%h``: short-form changeset hash (12 hexadecimal digits)
1413 1412 :``%r``: zero-padded changeset revision number
1414 1413 :``%b``: basename of the exporting repository
1415 1414
1416 1415 Returns 0 on success.
1417 1416 """
1418 1417 ctx = scmutil.revsingle(repo, opts.get('rev'))
1419 1418 m = scmutil.match(ctx, (file1,) + pats, opts)
1420 1419
1421 1420 return cmdutil.cat(ui, repo, ctx, m, '', **opts)
1422 1421
1423 1422 @command('^clone',
1424 1423 [('U', 'noupdate', None, _('the clone will include an empty working '
1425 1424 'directory (only a repository)')),
1426 1425 ('u', 'updaterev', '', _('revision, tag, or branch to check out'),
1427 1426 _('REV')),
1428 1427 ('r', 'rev', [], _('include the specified changeset'), _('REV')),
1429 1428 ('b', 'branch', [], _('clone only the specified branch'), _('BRANCH')),
1430 1429 ('', 'pull', None, _('use pull protocol to copy metadata')),
1431 1430 ('', 'uncompressed', None, _('use uncompressed transfer (fast over LAN)')),
1432 1431 ] + remoteopts,
1433 1432 _('[OPTION]... SOURCE [DEST]'),
1434 1433 norepo=True)
1435 1434 def clone(ui, source, dest=None, **opts):
1436 1435 """make a copy of an existing repository
1437 1436
1438 1437 Create a copy of an existing repository in a new directory.
1439 1438
1440 1439 If no destination directory name is specified, it defaults to the
1441 1440 basename of the source.
1442 1441
1443 1442 The location of the source is added to the new repository's
1444 1443 ``.hg/hgrc`` file, as the default to be used for future pulls.
1445 1444
1446 1445 Only local paths and ``ssh://`` URLs are supported as
1447 1446 destinations. For ``ssh://`` destinations, no working directory or
1448 1447 ``.hg/hgrc`` will be created on the remote side.
1449 1448
1450 1449 If the source repository has a bookmark called '@' set, that
1451 1450 revision will be checked out in the new repository by default.
1452 1451
1453 1452 To check out a particular version, use -u/--update, or
1454 1453 -U/--noupdate to create a clone with no working directory.
1455 1454
1456 1455 To pull only a subset of changesets, specify one or more revisions
1457 1456 identifiers with -r/--rev or branches with -b/--branch. The
1458 1457 resulting clone will contain only the specified changesets and
1459 1458 their ancestors. These options (or 'clone src#rev dest') imply
1460 1459 --pull, even for local source repositories.
1461 1460
1462 1461 .. note::
1463 1462
1464 1463 Specifying a tag will include the tagged changeset but not the
1465 1464 changeset containing the tag.
1466 1465
1467 1466 .. container:: verbose
1468 1467
1469 1468 For efficiency, hardlinks are used for cloning whenever the
1470 1469 source and destination are on the same filesystem (note this
1471 1470 applies only to the repository data, not to the working
1472 1471 directory). Some filesystems, such as AFS, implement hardlinking
1473 1472 incorrectly, but do not report errors. In these cases, use the
1474 1473 --pull option to avoid hardlinking.
1475 1474
1476 1475 In some cases, you can clone repositories and the working
1477 1476 directory using full hardlinks with ::
1478 1477
1479 1478 $ cp -al REPO REPOCLONE
1480 1479
1481 1480 This is the fastest way to clone, but it is not always safe. The
1482 1481 operation is not atomic (making sure REPO is not modified during
1483 1482 the operation is up to you) and you have to make sure your
1484 1483 editor breaks hardlinks (Emacs and most Linux Kernel tools do
1485 1484 so). Also, this is not compatible with certain extensions that
1486 1485 place their metadata under the .hg directory, such as mq.
1487 1486
1488 1487 Mercurial will update the working directory to the first applicable
1489 1488 revision from this list:
1490 1489
1491 1490 a) null if -U or the source repository has no changesets
1492 1491 b) if -u . and the source repository is local, the first parent of
1493 1492 the source repository's working directory
1494 1493 c) the changeset specified with -u (if a branch name, this means the
1495 1494 latest head of that branch)
1496 1495 d) the changeset specified with -r
1497 1496 e) the tipmost head specified with -b
1498 1497 f) the tipmost head specified with the url#branch source syntax
1499 1498 g) the revision marked with the '@' bookmark, if present
1500 1499 h) the tipmost head of the default branch
1501 1500 i) tip
1502 1501
1503 1502 When cloning from servers that support it, Mercurial may fetch
1504 1503 pre-generated data from a server-advertised URL. When this is done,
1505 1504 hooks operating on incoming changesets and changegroups may fire twice,
1506 1505 once for the bundle fetched from the URL and another for any additional
1507 1506 data not fetched from this URL. In addition, if an error occurs, the
1508 1507 repository may be rolled back to a partial clone. This behavior may
1509 1508 change in future releases. See :hg:`help -e clonebundles` for more.
1510 1509
1511 1510 Examples:
1512 1511
1513 1512 - clone a remote repository to a new directory named hg/::
1514 1513
1515 1514 hg clone https://www.mercurial-scm.org/repo/hg/
1516 1515
1517 1516 - create a lightweight local clone::
1518 1517
1519 1518 hg clone project/ project-feature/
1520 1519
1521 1520 - clone from an absolute path on an ssh server (note double-slash)::
1522 1521
1523 1522 hg clone ssh://user@server//home/projects/alpha/
1524 1523
1525 1524 - do a high-speed clone over a LAN while checking out a
1526 1525 specified version::
1527 1526
1528 1527 hg clone --uncompressed http://server/repo -u 1.5
1529 1528
1530 1529 - create a repository without changesets after a particular revision::
1531 1530
1532 1531 hg clone -r 04e544 experimental/ good/
1533 1532
1534 1533 - clone (and track) a particular named branch::
1535 1534
1536 1535 hg clone https://www.mercurial-scm.org/repo/hg/#stable
1537 1536
1538 1537 See :hg:`help urls` for details on specifying URLs.
1539 1538
1540 1539 Returns 0 on success.
1541 1540 """
1542 1541 if opts.get('noupdate') and opts.get('updaterev'):
1543 1542 raise error.Abort(_("cannot specify both --noupdate and --updaterev"))
1544 1543
1545 1544 r = hg.clone(ui, opts, source, dest,
1546 1545 pull=opts.get('pull'),
1547 1546 stream=opts.get('uncompressed'),
1548 1547 rev=opts.get('rev'),
1549 1548 update=opts.get('updaterev') or not opts.get('noupdate'),
1550 1549 branch=opts.get('branch'),
1551 1550 shareopts=opts.get('shareopts'))
1552 1551
1553 1552 return r is None
1554 1553
1555 1554 @command('^commit|ci',
1556 1555 [('A', 'addremove', None,
1557 1556 _('mark new/missing files as added/removed before committing')),
1558 1557 ('', 'close-branch', None,
1559 1558 _('mark a branch head as closed')),
1560 1559 ('', 'amend', None, _('amend the parent of the working directory')),
1561 1560 ('s', 'secret', None, _('use the secret phase for committing')),
1562 1561 ('e', 'edit', None, _('invoke editor on commit messages')),
1563 1562 ('i', 'interactive', None, _('use interactive mode')),
1564 1563 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1565 1564 _('[OPTION]... [FILE]...'),
1566 1565 inferrepo=True)
1567 1566 def commit(ui, repo, *pats, **opts):
1568 1567 """commit the specified files or all outstanding changes
1569 1568
1570 1569 Commit changes to the given files into the repository. Unlike a
1571 1570 centralized SCM, this operation is a local operation. See
1572 1571 :hg:`push` for a way to actively distribute your changes.
1573 1572
1574 1573 If a list of files is omitted, all changes reported by :hg:`status`
1575 1574 will be committed.
1576 1575
1577 1576 If you are committing the result of a merge, do not provide any
1578 1577 filenames or -I/-X filters.
1579 1578
1580 1579 If no commit message is specified, Mercurial starts your
1581 1580 configured editor where you can enter a message. In case your
1582 1581 commit fails, you will find a backup of your message in
1583 1582 ``.hg/last-message.txt``.
1584 1583
1585 1584 The --close-branch flag can be used to mark the current branch
1586 1585 head closed. When all heads of a branch are closed, the branch
1587 1586 will be considered closed and no longer listed.
1588 1587
1589 1588 The --amend flag can be used to amend the parent of the
1590 1589 working directory with a new commit that contains the changes
1591 1590 in the parent in addition to those currently reported by :hg:`status`,
1592 1591 if there are any. The old commit is stored in a backup bundle in
1593 1592 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1594 1593 on how to restore it).
1595 1594
1596 1595 Message, user and date are taken from the amended commit unless
1597 1596 specified. When a message isn't specified on the command line,
1598 1597 the editor will open with the message of the amended commit.
1599 1598
1600 1599 It is not possible to amend public changesets (see :hg:`help phases`)
1601 1600 or changesets that have children.
1602 1601
1603 1602 See :hg:`help dates` for a list of formats valid for -d/--date.
1604 1603
1605 1604 Returns 0 on success, 1 if nothing changed.
1606 1605
1607 1606 .. container:: verbose
1608 1607
1609 1608 Examples:
1610 1609
1611 1610 - commit all files ending in .py::
1612 1611
1613 1612 hg commit --include "set:**.py"
1614 1613
1615 1614 - commit all non-binary files::
1616 1615
1617 1616 hg commit --exclude "set:binary()"
1618 1617
1619 1618 - amend the current commit and set the date to now::
1620 1619
1621 1620 hg commit --amend --date now
1622 1621 """
1623 1622 wlock = lock = None
1624 1623 try:
1625 1624 wlock = repo.wlock()
1626 1625 lock = repo.lock()
1627 1626 return _docommit(ui, repo, *pats, **opts)
1628 1627 finally:
1629 1628 release(lock, wlock)
1630 1629
1631 1630 def _docommit(ui, repo, *pats, **opts):
1632 1631 if opts.get('interactive'):
1633 1632 opts.pop('interactive')
1634 1633 ret = cmdutil.dorecord(ui, repo, commit, None, False,
1635 1634 cmdutil.recordfilter, *pats, **opts)
1636 1635 # ret can be 0 (no changes to record) or the value returned by
1637 1636 # commit(), 1 if nothing changed or None on success.
1638 1637 return 1 if ret == 0 else ret
1639 1638
1640 1639 if opts.get('subrepos'):
1641 1640 if opts.get('amend'):
1642 1641 raise error.Abort(_('cannot amend with --subrepos'))
1643 1642 # Let --subrepos on the command line override config setting.
1644 1643 ui.setconfig('ui', 'commitsubrepos', True, 'commit')
1645 1644
1646 1645 cmdutil.checkunfinished(repo, commit=True)
1647 1646
1648 1647 branch = repo[None].branch()
1649 1648 bheads = repo.branchheads(branch)
1650 1649
1651 1650 extra = {}
1652 1651 if opts.get('close_branch'):
1653 1652 extra['close'] = 1
1654 1653
1655 1654 if not bheads:
1656 1655 raise error.Abort(_('can only close branch heads'))
1657 1656 elif opts.get('amend'):
1658 1657 if repo[None].parents()[0].p1().branch() != branch and \
1659 1658 repo[None].parents()[0].p2().branch() != branch:
1660 1659 raise error.Abort(_('can only close branch heads'))
1661 1660
1662 1661 if opts.get('amend'):
1663 1662 if ui.configbool('ui', 'commitsubrepos'):
1664 1663 raise error.Abort(_('cannot amend with ui.commitsubrepos enabled'))
1665 1664
1666 1665 old = repo['.']
1667 1666 if not old.mutable():
1668 1667 raise error.Abort(_('cannot amend public changesets'))
1669 1668 if len(repo[None].parents()) > 1:
1670 1669 raise error.Abort(_('cannot amend while merging'))
1671 1670 allowunstable = obsolete.isenabled(repo, obsolete.allowunstableopt)
1672 1671 if not allowunstable and old.children():
1673 1672 raise error.Abort(_('cannot amend changeset with children'))
1674 1673
1675 1674 # Currently histedit gets confused if an amend happens while histedit
1676 1675 # is in progress. Since we have a checkunfinished command, we are
1677 1676 # temporarily honoring it.
1678 1677 #
1679 1678 # Note: eventually this guard will be removed. Please do not expect
1680 1679 # this behavior to remain.
1681 1680 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
1682 1681 cmdutil.checkunfinished(repo)
1683 1682
1684 1683 # commitfunc is used only for temporary amend commit by cmdutil.amend
1685 1684 def commitfunc(ui, repo, message, match, opts):
1686 1685 return repo.commit(message,
1687 1686 opts.get('user') or old.user(),
1688 1687 opts.get('date') or old.date(),
1689 1688 match,
1690 1689 extra=extra)
1691 1690
1692 1691 node = cmdutil.amend(ui, repo, commitfunc, old, extra, pats, opts)
1693 1692 if node == old.node():
1694 1693 ui.status(_("nothing changed\n"))
1695 1694 return 1
1696 1695 else:
1697 1696 def commitfunc(ui, repo, message, match, opts):
1698 1697 backup = ui.backupconfig('phases', 'new-commit')
1699 1698 baseui = repo.baseui
1700 1699 basebackup = baseui.backupconfig('phases', 'new-commit')
1701 1700 try:
1702 1701 if opts.get('secret'):
1703 1702 ui.setconfig('phases', 'new-commit', 'secret', 'commit')
1704 1703 # Propagate to subrepos
1705 1704 baseui.setconfig('phases', 'new-commit', 'secret', 'commit')
1706 1705
1707 1706 editform = cmdutil.mergeeditform(repo[None], 'commit.normal')
1708 1707 editor = cmdutil.getcommiteditor(editform=editform, **opts)
1709 1708 return repo.commit(message, opts.get('user'), opts.get('date'),
1710 1709 match,
1711 1710 editor=editor,
1712 1711 extra=extra)
1713 1712 finally:
1714 1713 ui.restoreconfig(backup)
1715 1714 repo.baseui.restoreconfig(basebackup)
1716 1715
1717 1716
1718 1717 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1719 1718
1720 1719 if not node:
1721 1720 stat = cmdutil.postcommitstatus(repo, pats, opts)
1722 1721 if stat[3]:
1723 1722 ui.status(_("nothing changed (%d missing files, see "
1724 1723 "'hg status')\n") % len(stat[3]))
1725 1724 else:
1726 1725 ui.status(_("nothing changed\n"))
1727 1726 return 1
1728 1727
1729 1728 cmdutil.commitstatus(repo, node, branch, bheads, opts)
1730 1729
1731 1730 @command('config|showconfig|debugconfig',
1732 1731 [('u', 'untrusted', None, _('show untrusted configuration options')),
1733 1732 ('e', 'edit', None, _('edit user config')),
1734 1733 ('l', 'local', None, _('edit repository config')),
1735 1734 ('g', 'global', None, _('edit global config'))] + formatteropts,
1736 1735 _('[-u] [NAME]...'),
1737 1736 optionalrepo=True)
1738 1737 def config(ui, repo, *values, **opts):
1739 1738 """show combined config settings from all hgrc files
1740 1739
1741 1740 With no arguments, print names and values of all config items.
1742 1741
1743 1742 With one argument of the form section.name, print just the value
1744 1743 of that config item.
1745 1744
1746 1745 With multiple arguments, print names and values of all config
1747 1746 items with matching section names.
1748 1747
1749 1748 With --edit, start an editor on the user-level config file. With
1750 1749 --global, edit the system-wide config file. With --local, edit the
1751 1750 repository-level config file.
1752 1751
1753 1752 With --debug, the source (filename and line number) is printed
1754 1753 for each config item.
1755 1754
1756 1755 See :hg:`help config` for more information about config files.
1757 1756
1758 1757 Returns 0 on success, 1 if NAME does not exist.
1759 1758
1760 1759 """
1761 1760
1762 1761 if opts.get('edit') or opts.get('local') or opts.get('global'):
1763 1762 if opts.get('local') and opts.get('global'):
1764 1763 raise error.Abort(_("can't use --local and --global together"))
1765 1764
1766 1765 if opts.get('local'):
1767 1766 if not repo:
1768 1767 raise error.Abort(_("can't use --local outside a repository"))
1769 1768 paths = [repo.join('hgrc')]
1770 1769 elif opts.get('global'):
1771 1770 paths = scmutil.systemrcpath()
1772 1771 else:
1773 1772 paths = scmutil.userrcpath()
1774 1773
1775 1774 for f in paths:
1776 1775 if os.path.exists(f):
1777 1776 break
1778 1777 else:
1779 1778 if opts.get('global'):
1780 1779 samplehgrc = uimod.samplehgrcs['global']
1781 1780 elif opts.get('local'):
1782 1781 samplehgrc = uimod.samplehgrcs['local']
1783 1782 else:
1784 1783 samplehgrc = uimod.samplehgrcs['user']
1785 1784
1786 1785 f = paths[0]
1787 1786 fp = open(f, "w")
1788 1787 fp.write(samplehgrc)
1789 1788 fp.close()
1790 1789
1791 1790 editor = ui.geteditor()
1792 1791 ui.system("%s \"%s\"" % (editor, f),
1793 1792 onerr=error.Abort, errprefix=_("edit failed"))
1794 1793 return
1795 1794
1796 1795 fm = ui.formatter('config', opts)
1797 1796 for f in scmutil.rcpath():
1798 1797 ui.debug('read config from: %s\n' % f)
1799 1798 untrusted = bool(opts.get('untrusted'))
1800 1799 if values:
1801 1800 sections = [v for v in values if '.' not in v]
1802 1801 items = [v for v in values if '.' in v]
1803 1802 if len(items) > 1 or items and sections:
1804 1803 raise error.Abort(_('only one config item permitted'))
1805 1804 matched = False
1806 1805 for section, name, value in ui.walkconfig(untrusted=untrusted):
1807 1806 source = ui.configsource(section, name, untrusted)
1808 1807 value = str(value)
1809 1808 if fm.isplain():
1810 1809 source = source or 'none'
1811 1810 value = value.replace('\n', '\\n')
1812 1811 entryname = section + '.' + name
1813 1812 if values:
1814 1813 for v in values:
1815 1814 if v == section:
1816 1815 fm.startitem()
1817 1816 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1818 1817 fm.write('name value', '%s=%s\n', entryname, value)
1819 1818 matched = True
1820 1819 elif v == entryname:
1821 1820 fm.startitem()
1822 1821 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1823 1822 fm.write('value', '%s\n', value)
1824 1823 fm.data(name=entryname)
1825 1824 matched = True
1826 1825 else:
1827 1826 fm.startitem()
1828 1827 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1829 1828 fm.write('name value', '%s=%s\n', entryname, value)
1830 1829 matched = True
1831 1830 fm.end()
1832 1831 if matched:
1833 1832 return 0
1834 1833 return 1
1835 1834
1836 1835 @command('copy|cp',
1837 1836 [('A', 'after', None, _('record a copy that has already occurred')),
1838 1837 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1839 1838 ] + walkopts + dryrunopts,
1840 1839 _('[OPTION]... [SOURCE]... DEST'))
1841 1840 def copy(ui, repo, *pats, **opts):
1842 1841 """mark files as copied for the next commit
1843 1842
1844 1843 Mark dest as having copies of source files. If dest is a
1845 1844 directory, copies are put in that directory. If dest is a file,
1846 1845 the source must be a single file.
1847 1846
1848 1847 By default, this command copies the contents of files as they
1849 1848 exist in the working directory. If invoked with -A/--after, the
1850 1849 operation is recorded, but no copying is performed.
1851 1850
1852 1851 This command takes effect with the next commit. To undo a copy
1853 1852 before that, see :hg:`revert`.
1854 1853
1855 1854 Returns 0 on success, 1 if errors are encountered.
1856 1855 """
1857 1856 with repo.wlock(False):
1858 1857 return cmdutil.copy(ui, repo, pats, opts)
1859 1858
1860 1859 @command('debuginstall', [] + formatteropts, '', norepo=True)
1861 1860 def debuginstall(ui, **opts):
1862 1861 '''test Mercurial installation
1863 1862
1864 1863 Returns 0 on success.
1865 1864 '''
1866 1865
1867 1866 def writetemp(contents):
1868 1867 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
1869 1868 f = os.fdopen(fd, "wb")
1870 1869 f.write(contents)
1871 1870 f.close()
1872 1871 return name
1873 1872
1874 1873 problems = 0
1875 1874
1876 1875 fm = ui.formatter('debuginstall', opts)
1877 1876 fm.startitem()
1878 1877
1879 1878 # encoding
1880 1879 fm.write('encoding', _("checking encoding (%s)...\n"), encoding.encoding)
1881 1880 err = None
1882 1881 try:
1883 1882 encoding.fromlocal("test")
1884 1883 except error.Abort as inst:
1885 1884 err = inst
1886 1885 problems += 1
1887 1886 fm.condwrite(err, 'encodingerror', _(" %s\n"
1888 1887 " (check that your locale is properly set)\n"), err)
1889 1888
1890 1889 # Python
1891 1890 fm.write('pythonexe', _("checking Python executable (%s)\n"),
1892 1891 pycompat.sysexecutable)
1893 1892 fm.write('pythonver', _("checking Python version (%s)\n"),
1894 1893 ("%d.%d.%d" % sys.version_info[:3]))
1895 1894 fm.write('pythonlib', _("checking Python lib (%s)...\n"),
1896 1895 os.path.dirname(os.__file__))
1897 1896
1898 1897 security = set(sslutil.supportedprotocols)
1899 1898 if sslutil.hassni:
1900 1899 security.add('sni')
1901 1900
1902 1901 fm.write('pythonsecurity', _("checking Python security support (%s)\n"),
1903 1902 fm.formatlist(sorted(security), name='protocol',
1904 1903 fmt='%s', sep=','))
1905 1904
1906 1905 # These are warnings, not errors. So don't increment problem count. This
1907 1906 # may change in the future.
1908 1907 if 'tls1.2' not in security:
1909 1908 fm.plain(_(' TLS 1.2 not supported by Python install; '
1910 1909 'network connections lack modern security\n'))
1911 1910 if 'sni' not in security:
1912 1911 fm.plain(_(' SNI not supported by Python install; may have '
1913 1912 'connectivity issues with some servers\n'))
1914 1913
1915 1914 # TODO print CA cert info
1916 1915
1917 1916 # hg version
1918 1917 hgver = util.version()
1919 1918 fm.write('hgver', _("checking Mercurial version (%s)\n"),
1920 1919 hgver.split('+')[0])
1921 1920 fm.write('hgverextra', _("checking Mercurial custom build (%s)\n"),
1922 1921 '+'.join(hgver.split('+')[1:]))
1923 1922
1924 1923 # compiled modules
1925 1924 fm.write('hgmodulepolicy', _("checking module policy (%s)\n"),
1926 1925 policy.policy)
1927 1926 fm.write('hgmodules', _("checking installed modules (%s)...\n"),
1928 1927 os.path.dirname(__file__))
1929 1928
1930 1929 err = None
1931 1930 try:
1932 1931 from . import (
1933 1932 base85,
1934 1933 bdiff,
1935 1934 mpatch,
1936 1935 osutil,
1937 1936 )
1938 1937 dir(bdiff), dir(mpatch), dir(base85), dir(osutil) # quiet pyflakes
1939 1938 except Exception as inst:
1940 1939 err = inst
1941 1940 problems += 1
1942 1941 fm.condwrite(err, 'extensionserror', " %s\n", err)
1943 1942
1944 1943 compengines = util.compengines._engines.values()
1945 1944 fm.write('compengines', _('checking registered compression engines (%s)\n'),
1946 1945 fm.formatlist(sorted(e.name() for e in compengines),
1947 1946 name='compengine', fmt='%s', sep=', '))
1948 1947 fm.write('compenginesavail', _('checking available compression engines '
1949 1948 '(%s)\n'),
1950 1949 fm.formatlist(sorted(e.name() for e in compengines
1951 1950 if e.available()),
1952 1951 name='compengine', fmt='%s', sep=', '))
1953 1952
1954 1953 # templates
1955 1954 p = templater.templatepaths()
1956 1955 fm.write('templatedirs', 'checking templates (%s)...\n', ' '.join(p))
1957 1956 fm.condwrite(not p, '', _(" no template directories found\n"))
1958 1957 if p:
1959 1958 m = templater.templatepath("map-cmdline.default")
1960 1959 if m:
1961 1960 # template found, check if it is working
1962 1961 err = None
1963 1962 try:
1964 1963 templater.templater.frommapfile(m)
1965 1964 except Exception as inst:
1966 1965 err = inst
1967 1966 p = None
1968 1967 fm.condwrite(err, 'defaulttemplateerror', " %s\n", err)
1969 1968 else:
1970 1969 p = None
1971 1970 fm.condwrite(p, 'defaulttemplate',
1972 1971 _("checking default template (%s)\n"), m)
1973 1972 fm.condwrite(not m, 'defaulttemplatenotfound',
1974 1973 _(" template '%s' not found\n"), "default")
1975 1974 if not p:
1976 1975 problems += 1
1977 1976 fm.condwrite(not p, '',
1978 1977 _(" (templates seem to have been installed incorrectly)\n"))
1979 1978
1980 1979 # editor
1981 1980 editor = ui.geteditor()
1982 1981 editor = util.expandpath(editor)
1983 1982 fm.write('editor', _("checking commit editor... (%s)\n"), editor)
1984 cmdpath = util.findexe(shlex.split(editor)[0])
1983 cmdpath = util.findexe(pycompat.shlexsplit(editor)[0])
1985 1984 fm.condwrite(not cmdpath and editor == 'vi', 'vinotfound',
1986 1985 _(" No commit editor set and can't find %s in PATH\n"
1987 1986 " (specify a commit editor in your configuration"
1988 1987 " file)\n"), not cmdpath and editor == 'vi' and editor)
1989 1988 fm.condwrite(not cmdpath and editor != 'vi', 'editornotfound',
1990 1989 _(" Can't find editor '%s' in PATH\n"
1991 1990 " (specify a commit editor in your configuration"
1992 1991 " file)\n"), not cmdpath and editor)
1993 1992 if not cmdpath and editor != 'vi':
1994 1993 problems += 1
1995 1994
1996 1995 # check username
1997 1996 username = None
1998 1997 err = None
1999 1998 try:
2000 1999 username = ui.username()
2001 2000 except error.Abort as e:
2002 2001 err = e
2003 2002 problems += 1
2004 2003
2005 2004 fm.condwrite(username, 'username', _("checking username (%s)\n"), username)
2006 2005 fm.condwrite(err, 'usernameerror', _("checking username...\n %s\n"
2007 2006 " (specify a username in your configuration file)\n"), err)
2008 2007
2009 2008 fm.condwrite(not problems, '',
2010 2009 _("no problems detected\n"))
2011 2010 if not problems:
2012 2011 fm.data(problems=problems)
2013 2012 fm.condwrite(problems, 'problems',
2014 2013 _("%d problems detected,"
2015 2014 " please check your install!\n"), problems)
2016 2015 fm.end()
2017 2016
2018 2017 return problems
2019 2018
2020 2019 @command('debugknown', [], _('REPO ID...'), norepo=True)
2021 2020 def debugknown(ui, repopath, *ids, **opts):
2022 2021 """test whether node ids are known to a repo
2023 2022
2024 2023 Every ID must be a full-length hex node id string. Returns a list of 0s
2025 2024 and 1s indicating unknown/known.
2026 2025 """
2027 2026 repo = hg.peer(ui, opts, repopath)
2028 2027 if not repo.capable('known'):
2029 2028 raise error.Abort("known() not supported by target repository")
2030 2029 flags = repo.known([bin(s) for s in ids])
2031 2030 ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
2032 2031
2033 2032 @command('debuglabelcomplete', [], _('LABEL...'))
2034 2033 def debuglabelcomplete(ui, repo, *args):
2035 2034 '''backwards compatibility with old bash completion scripts (DEPRECATED)'''
2036 2035 debugnamecomplete(ui, repo, *args)
2037 2036
2038 2037 @command('debugmergestate', [], '')
2039 2038 def debugmergestate(ui, repo, *args):
2040 2039 """print merge state
2041 2040
2042 2041 Use --verbose to print out information about whether v1 or v2 merge state
2043 2042 was chosen."""
2044 2043 def _hashornull(h):
2045 2044 if h == nullhex:
2046 2045 return 'null'
2047 2046 else:
2048 2047 return h
2049 2048
2050 2049 def printrecords(version):
2051 2050 ui.write(('* version %s records\n') % version)
2052 2051 if version == 1:
2053 2052 records = v1records
2054 2053 else:
2055 2054 records = v2records
2056 2055
2057 2056 for rtype, record in records:
2058 2057 # pretty print some record types
2059 2058 if rtype == 'L':
2060 2059 ui.write(('local: %s\n') % record)
2061 2060 elif rtype == 'O':
2062 2061 ui.write(('other: %s\n') % record)
2063 2062 elif rtype == 'm':
2064 2063 driver, mdstate = record.split('\0', 1)
2065 2064 ui.write(('merge driver: %s (state "%s")\n')
2066 2065 % (driver, mdstate))
2067 2066 elif rtype in 'FDC':
2068 2067 r = record.split('\0')
2069 2068 f, state, hash, lfile, afile, anode, ofile = r[0:7]
2070 2069 if version == 1:
2071 2070 onode = 'not stored in v1 format'
2072 2071 flags = r[7]
2073 2072 else:
2074 2073 onode, flags = r[7:9]
2075 2074 ui.write(('file: %s (record type "%s", state "%s", hash %s)\n')
2076 2075 % (f, rtype, state, _hashornull(hash)))
2077 2076 ui.write((' local path: %s (flags "%s")\n') % (lfile, flags))
2078 2077 ui.write((' ancestor path: %s (node %s)\n')
2079 2078 % (afile, _hashornull(anode)))
2080 2079 ui.write((' other path: %s (node %s)\n')
2081 2080 % (ofile, _hashornull(onode)))
2082 2081 elif rtype == 'f':
2083 2082 filename, rawextras = record.split('\0', 1)
2084 2083 extras = rawextras.split('\0')
2085 2084 i = 0
2086 2085 extrastrings = []
2087 2086 while i < len(extras):
2088 2087 extrastrings.append('%s = %s' % (extras[i], extras[i + 1]))
2089 2088 i += 2
2090 2089
2091 2090 ui.write(('file extras: %s (%s)\n')
2092 2091 % (filename, ', '.join(extrastrings)))
2093 2092 elif rtype == 'l':
2094 2093 labels = record.split('\0', 2)
2095 2094 labels = [l for l in labels if len(l) > 0]
2096 2095 ui.write(('labels:\n'))
2097 2096 ui.write((' local: %s\n' % labels[0]))
2098 2097 ui.write((' other: %s\n' % labels[1]))
2099 2098 if len(labels) > 2:
2100 2099 ui.write((' base: %s\n' % labels[2]))
2101 2100 else:
2102 2101 ui.write(('unrecognized entry: %s\t%s\n')
2103 2102 % (rtype, record.replace('\0', '\t')))
2104 2103
2105 2104 # Avoid mergestate.read() since it may raise an exception for unsupported
2106 2105 # merge state records. We shouldn't be doing this, but this is OK since this
2107 2106 # command is pretty low-level.
2108 2107 ms = mergemod.mergestate(repo)
2109 2108
2110 2109 # sort so that reasonable information is on top
2111 2110 v1records = ms._readrecordsv1()
2112 2111 v2records = ms._readrecordsv2()
2113 2112 order = 'LOml'
2114 2113 def key(r):
2115 2114 idx = order.find(r[0])
2116 2115 if idx == -1:
2117 2116 return (1, r[1])
2118 2117 else:
2119 2118 return (0, idx)
2120 2119 v1records.sort(key=key)
2121 2120 v2records.sort(key=key)
2122 2121
2123 2122 if not v1records and not v2records:
2124 2123 ui.write(('no merge state found\n'))
2125 2124 elif not v2records:
2126 2125 ui.note(('no version 2 merge state\n'))
2127 2126 printrecords(1)
2128 2127 elif ms._v1v2match(v1records, v2records):
2129 2128 ui.note(('v1 and v2 states match: using v2\n'))
2130 2129 printrecords(2)
2131 2130 else:
2132 2131 ui.note(('v1 and v2 states mismatch: using v1\n'))
2133 2132 printrecords(1)
2134 2133 if ui.verbose:
2135 2134 printrecords(2)
2136 2135
2137 2136 @command('debugnamecomplete', [], _('NAME...'))
2138 2137 def debugnamecomplete(ui, repo, *args):
2139 2138 '''complete "names" - tags, open branch names, bookmark names'''
2140 2139
2141 2140 names = set()
2142 2141 # since we previously only listed open branches, we will handle that
2143 2142 # specially (after this for loop)
2144 2143 for name, ns in repo.names.iteritems():
2145 2144 if name != 'branches':
2146 2145 names.update(ns.listnames(repo))
2147 2146 names.update(tag for (tag, heads, tip, closed)
2148 2147 in repo.branchmap().iterbranches() if not closed)
2149 2148 completions = set()
2150 2149 if not args:
2151 2150 args = ['']
2152 2151 for a in args:
2153 2152 completions.update(n for n in names if n.startswith(a))
2154 2153 ui.write('\n'.join(sorted(completions)))
2155 2154 ui.write('\n')
2156 2155
2157 2156 @command('debuglocks',
2158 2157 [('L', 'force-lock', None, _('free the store lock (DANGEROUS)')),
2159 2158 ('W', 'force-wlock', None,
2160 2159 _('free the working state lock (DANGEROUS)'))],
2161 2160 _('[OPTION]...'))
2162 2161 def debuglocks(ui, repo, **opts):
2163 2162 """show or modify state of locks
2164 2163
2165 2164 By default, this command will show which locks are held. This
2166 2165 includes the user and process holding the lock, the amount of time
2167 2166 the lock has been held, and the machine name where the process is
2168 2167 running if it's not local.
2169 2168
2170 2169 Locks protect the integrity of Mercurial's data, so should be
2171 2170 treated with care. System crashes or other interruptions may cause
2172 2171 locks to not be properly released, though Mercurial will usually
2173 2172 detect and remove such stale locks automatically.
2174 2173
2175 2174 However, detecting stale locks may not always be possible (for
2176 2175 instance, on a shared filesystem). Removing locks may also be
2177 2176 blocked by filesystem permissions.
2178 2177
2179 2178 Returns 0 if no locks are held.
2180 2179
2181 2180 """
2182 2181
2183 2182 if opts.get('force_lock'):
2184 2183 repo.svfs.unlink('lock')
2185 2184 if opts.get('force_wlock'):
2186 2185 repo.vfs.unlink('wlock')
2187 2186 if opts.get('force_lock') or opts.get('force_lock'):
2188 2187 return 0
2189 2188
2190 2189 now = time.time()
2191 2190 held = 0
2192 2191
2193 2192 def report(vfs, name, method):
2194 2193 # this causes stale locks to get reaped for more accurate reporting
2195 2194 try:
2196 2195 l = method(False)
2197 2196 except error.LockHeld:
2198 2197 l = None
2199 2198
2200 2199 if l:
2201 2200 l.release()
2202 2201 else:
2203 2202 try:
2204 2203 stat = vfs.lstat(name)
2205 2204 age = now - stat.st_mtime
2206 2205 user = util.username(stat.st_uid)
2207 2206 locker = vfs.readlock(name)
2208 2207 if ":" in locker:
2209 2208 host, pid = locker.split(':')
2210 2209 if host == socket.gethostname():
2211 2210 locker = 'user %s, process %s' % (user, pid)
2212 2211 else:
2213 2212 locker = 'user %s, process %s, host %s' \
2214 2213 % (user, pid, host)
2215 2214 ui.write(("%-6s %s (%ds)\n") % (name + ":", locker, age))
2216 2215 return 1
2217 2216 except OSError as e:
2218 2217 if e.errno != errno.ENOENT:
2219 2218 raise
2220 2219
2221 2220 ui.write(("%-6s free\n") % (name + ":"))
2222 2221 return 0
2223 2222
2224 2223 held += report(repo.svfs, "lock", repo.lock)
2225 2224 held += report(repo.vfs, "wlock", repo.wlock)
2226 2225
2227 2226 return held
2228 2227
2229 2228 @command('debugobsolete',
2230 2229 [('', 'flags', 0, _('markers flag')),
2231 2230 ('', 'record-parents', False,
2232 2231 _('record parent information for the precursor')),
2233 2232 ('r', 'rev', [], _('display markers relevant to REV')),
2234 2233 ('', 'index', False, _('display index of the marker')),
2235 2234 ('', 'delete', [], _('delete markers specified by indices')),
2236 2235 ] + commitopts2 + formatteropts,
2237 2236 _('[OBSOLETED [REPLACEMENT ...]]'))
2238 2237 def debugobsolete(ui, repo, precursor=None, *successors, **opts):
2239 2238 """create arbitrary obsolete marker
2240 2239
2241 2240 With no arguments, displays the list of obsolescence markers."""
2242 2241
2243 2242 def parsenodeid(s):
2244 2243 try:
2245 2244 # We do not use revsingle/revrange functions here to accept
2246 2245 # arbitrary node identifiers, possibly not present in the
2247 2246 # local repository.
2248 2247 n = bin(s)
2249 2248 if len(n) != len(nullid):
2250 2249 raise TypeError()
2251 2250 return n
2252 2251 except TypeError:
2253 2252 raise error.Abort('changeset references must be full hexadecimal '
2254 2253 'node identifiers')
2255 2254
2256 2255 if opts.get('delete'):
2257 2256 indices = []
2258 2257 for v in opts.get('delete'):
2259 2258 try:
2260 2259 indices.append(int(v))
2261 2260 except ValueError:
2262 2261 raise error.Abort(_('invalid index value: %r') % v,
2263 2262 hint=_('use integers for indices'))
2264 2263
2265 2264 if repo.currenttransaction():
2266 2265 raise error.Abort(_('cannot delete obsmarkers in the middle '
2267 2266 'of transaction.'))
2268 2267
2269 2268 with repo.lock():
2270 2269 n = repair.deleteobsmarkers(repo.obsstore, indices)
2271 2270 ui.write(_('deleted %i obsolescence markers\n') % n)
2272 2271
2273 2272 return
2274 2273
2275 2274 if precursor is not None:
2276 2275 if opts['rev']:
2277 2276 raise error.Abort('cannot select revision when creating marker')
2278 2277 metadata = {}
2279 2278 metadata['user'] = opts['user'] or ui.username()
2280 2279 succs = tuple(parsenodeid(succ) for succ in successors)
2281 2280 l = repo.lock()
2282 2281 try:
2283 2282 tr = repo.transaction('debugobsolete')
2284 2283 try:
2285 2284 date = opts.get('date')
2286 2285 if date:
2287 2286 date = util.parsedate(date)
2288 2287 else:
2289 2288 date = None
2290 2289 prec = parsenodeid(precursor)
2291 2290 parents = None
2292 2291 if opts['record_parents']:
2293 2292 if prec not in repo.unfiltered():
2294 2293 raise error.Abort('cannot used --record-parents on '
2295 2294 'unknown changesets')
2296 2295 parents = repo.unfiltered()[prec].parents()
2297 2296 parents = tuple(p.node() for p in parents)
2298 2297 repo.obsstore.create(tr, prec, succs, opts['flags'],
2299 2298 parents=parents, date=date,
2300 2299 metadata=metadata)
2301 2300 tr.close()
2302 2301 except ValueError as exc:
2303 2302 raise error.Abort(_('bad obsmarker input: %s') % exc)
2304 2303 finally:
2305 2304 tr.release()
2306 2305 finally:
2307 2306 l.release()
2308 2307 else:
2309 2308 if opts['rev']:
2310 2309 revs = scmutil.revrange(repo, opts['rev'])
2311 2310 nodes = [repo[r].node() for r in revs]
2312 2311 markers = list(obsolete.getmarkers(repo, nodes=nodes))
2313 2312 markers.sort(key=lambda x: x._data)
2314 2313 else:
2315 2314 markers = obsolete.getmarkers(repo)
2316 2315
2317 2316 markerstoiter = markers
2318 2317 isrelevant = lambda m: True
2319 2318 if opts.get('rev') and opts.get('index'):
2320 2319 markerstoiter = obsolete.getmarkers(repo)
2321 2320 markerset = set(markers)
2322 2321 isrelevant = lambda m: m in markerset
2323 2322
2324 2323 fm = ui.formatter('debugobsolete', opts)
2325 2324 for i, m in enumerate(markerstoiter):
2326 2325 if not isrelevant(m):
2327 2326 # marker can be irrelevant when we're iterating over a set
2328 2327 # of markers (markerstoiter) which is bigger than the set
2329 2328 # of markers we want to display (markers)
2330 2329 # this can happen if both --index and --rev options are
2331 2330 # provided and thus we need to iterate over all of the markers
2332 2331 # to get the correct indices, but only display the ones that
2333 2332 # are relevant to --rev value
2334 2333 continue
2335 2334 fm.startitem()
2336 2335 ind = i if opts.get('index') else None
2337 2336 cmdutil.showmarker(fm, m, index=ind)
2338 2337 fm.end()
2339 2338
2340 2339 @command('debugpathcomplete',
2341 2340 [('f', 'full', None, _('complete an entire path')),
2342 2341 ('n', 'normal', None, _('show only normal files')),
2343 2342 ('a', 'added', None, _('show only added files')),
2344 2343 ('r', 'removed', None, _('show only removed files'))],
2345 2344 _('FILESPEC...'))
2346 2345 def debugpathcomplete(ui, repo, *specs, **opts):
2347 2346 '''complete part or all of a tracked path
2348 2347
2349 2348 This command supports shells that offer path name completion. It
2350 2349 currently completes only files already known to the dirstate.
2351 2350
2352 2351 Completion extends only to the next path segment unless
2353 2352 --full is specified, in which case entire paths are used.'''
2354 2353
2355 2354 def complete(path, acceptable):
2356 2355 dirstate = repo.dirstate
2357 2356 spec = os.path.normpath(os.path.join(pycompat.getcwd(), path))
2358 2357 rootdir = repo.root + pycompat.ossep
2359 2358 if spec != repo.root and not spec.startswith(rootdir):
2360 2359 return [], []
2361 2360 if os.path.isdir(spec):
2362 2361 spec += '/'
2363 2362 spec = spec[len(rootdir):]
2364 2363 fixpaths = pycompat.ossep != '/'
2365 2364 if fixpaths:
2366 2365 spec = spec.replace(pycompat.ossep, '/')
2367 2366 speclen = len(spec)
2368 2367 fullpaths = opts['full']
2369 2368 files, dirs = set(), set()
2370 2369 adddir, addfile = dirs.add, files.add
2371 2370 for f, st in dirstate.iteritems():
2372 2371 if f.startswith(spec) and st[0] in acceptable:
2373 2372 if fixpaths:
2374 2373 f = f.replace('/', pycompat.ossep)
2375 2374 if fullpaths:
2376 2375 addfile(f)
2377 2376 continue
2378 2377 s = f.find(pycompat.ossep, speclen)
2379 2378 if s >= 0:
2380 2379 adddir(f[:s])
2381 2380 else:
2382 2381 addfile(f)
2383 2382 return files, dirs
2384 2383
2385 2384 acceptable = ''
2386 2385 if opts['normal']:
2387 2386 acceptable += 'nm'
2388 2387 if opts['added']:
2389 2388 acceptable += 'a'
2390 2389 if opts['removed']:
2391 2390 acceptable += 'r'
2392 2391 cwd = repo.getcwd()
2393 2392 if not specs:
2394 2393 specs = ['.']
2395 2394
2396 2395 files, dirs = set(), set()
2397 2396 for spec in specs:
2398 2397 f, d = complete(spec, acceptable or 'nmar')
2399 2398 files.update(f)
2400 2399 dirs.update(d)
2401 2400 files.update(dirs)
2402 2401 ui.write('\n'.join(repo.pathto(p, cwd) for p in sorted(files)))
2403 2402 ui.write('\n')
2404 2403
2405 2404 @command('debugpushkey', [], _('REPO NAMESPACE [KEY OLD NEW]'), norepo=True)
2406 2405 def debugpushkey(ui, repopath, namespace, *keyinfo, **opts):
2407 2406 '''access the pushkey key/value protocol
2408 2407
2409 2408 With two args, list the keys in the given namespace.
2410 2409
2411 2410 With five args, set a key to new if it currently is set to old.
2412 2411 Reports success or failure.
2413 2412 '''
2414 2413
2415 2414 target = hg.peer(ui, {}, repopath)
2416 2415 if keyinfo:
2417 2416 key, old, new = keyinfo
2418 2417 r = target.pushkey(namespace, key, old, new)
2419 2418 ui.status(str(r) + '\n')
2420 2419 return not r
2421 2420 else:
2422 2421 for k, v in sorted(target.listkeys(namespace).iteritems()):
2423 2422 ui.write("%s\t%s\n" % (k.encode('string-escape'),
2424 2423 v.encode('string-escape')))
2425 2424
2426 2425 @command('debugpvec', [], _('A B'))
2427 2426 def debugpvec(ui, repo, a, b=None):
2428 2427 ca = scmutil.revsingle(repo, a)
2429 2428 cb = scmutil.revsingle(repo, b)
2430 2429 pa = pvec.ctxpvec(ca)
2431 2430 pb = pvec.ctxpvec(cb)
2432 2431 if pa == pb:
2433 2432 rel = "="
2434 2433 elif pa > pb:
2435 2434 rel = ">"
2436 2435 elif pa < pb:
2437 2436 rel = "<"
2438 2437 elif pa | pb:
2439 2438 rel = "|"
2440 2439 ui.write(_("a: %s\n") % pa)
2441 2440 ui.write(_("b: %s\n") % pb)
2442 2441 ui.write(_("depth(a): %d depth(b): %d\n") % (pa._depth, pb._depth))
2443 2442 ui.write(_("delta: %d hdist: %d distance: %d relation: %s\n") %
2444 2443 (abs(pa._depth - pb._depth), pvec._hamming(pa._vec, pb._vec),
2445 2444 pa.distance(pb), rel))
2446 2445
2447 2446 @command('debugrebuilddirstate|debugrebuildstate',
2448 2447 [('r', 'rev', '', _('revision to rebuild to'), _('REV')),
2449 2448 ('', 'minimal', None, _('only rebuild files that are inconsistent with '
2450 2449 'the working copy parent')),
2451 2450 ],
2452 2451 _('[-r REV]'))
2453 2452 def debugrebuilddirstate(ui, repo, rev, **opts):
2454 2453 """rebuild the dirstate as it would look like for the given revision
2455 2454
2456 2455 If no revision is specified the first current parent will be used.
2457 2456
2458 2457 The dirstate will be set to the files of the given revision.
2459 2458 The actual working directory content or existing dirstate
2460 2459 information such as adds or removes is not considered.
2461 2460
2462 2461 ``minimal`` will only rebuild the dirstate status for files that claim to be
2463 2462 tracked but are not in the parent manifest, or that exist in the parent
2464 2463 manifest but are not in the dirstate. It will not change adds, removes, or
2465 2464 modified files that are in the working copy parent.
2466 2465
2467 2466 One use of this command is to make the next :hg:`status` invocation
2468 2467 check the actual file content.
2469 2468 """
2470 2469 ctx = scmutil.revsingle(repo, rev)
2471 2470 with repo.wlock():
2472 2471 dirstate = repo.dirstate
2473 2472 changedfiles = None
2474 2473 # See command doc for what minimal does.
2475 2474 if opts.get('minimal'):
2476 2475 manifestfiles = set(ctx.manifest().keys())
2477 2476 dirstatefiles = set(dirstate)
2478 2477 manifestonly = manifestfiles - dirstatefiles
2479 2478 dsonly = dirstatefiles - manifestfiles
2480 2479 dsnotadded = set(f for f in dsonly if dirstate[f] != 'a')
2481 2480 changedfiles = manifestonly | dsnotadded
2482 2481
2483 2482 dirstate.rebuild(ctx.node(), ctx.manifest(), changedfiles)
2484 2483
2485 2484 @command('debugrebuildfncache', [], '')
2486 2485 def debugrebuildfncache(ui, repo):
2487 2486 """rebuild the fncache file"""
2488 2487 repair.rebuildfncache(ui, repo)
2489 2488
2490 2489 @command('debugrename',
2491 2490 [('r', 'rev', '', _('revision to debug'), _('REV'))],
2492 2491 _('[-r REV] FILE'))
2493 2492 def debugrename(ui, repo, file1, *pats, **opts):
2494 2493 """dump rename information"""
2495 2494
2496 2495 ctx = scmutil.revsingle(repo, opts.get('rev'))
2497 2496 m = scmutil.match(ctx, (file1,) + pats, opts)
2498 2497 for abs in ctx.walk(m):
2499 2498 fctx = ctx[abs]
2500 2499 o = fctx.filelog().renamed(fctx.filenode())
2501 2500 rel = m.rel(abs)
2502 2501 if o:
2503 2502 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
2504 2503 else:
2505 2504 ui.write(_("%s not renamed\n") % rel)
2506 2505
2507 2506 @command('debugrevlog', debugrevlogopts +
2508 2507 [('d', 'dump', False, _('dump index data'))],
2509 2508 _('-c|-m|FILE'),
2510 2509 optionalrepo=True)
2511 2510 def debugrevlog(ui, repo, file_=None, **opts):
2512 2511 """show data and statistics about a revlog"""
2513 2512 r = cmdutil.openrevlog(repo, 'debugrevlog', file_, opts)
2514 2513
2515 2514 if opts.get("dump"):
2516 2515 numrevs = len(r)
2517 2516 ui.write(("# rev p1rev p2rev start end deltastart base p1 p2"
2518 2517 " rawsize totalsize compression heads chainlen\n"))
2519 2518 ts = 0
2520 2519 heads = set()
2521 2520
2522 2521 for rev in xrange(numrevs):
2523 2522 dbase = r.deltaparent(rev)
2524 2523 if dbase == -1:
2525 2524 dbase = rev
2526 2525 cbase = r.chainbase(rev)
2527 2526 clen = r.chainlen(rev)
2528 2527 p1, p2 = r.parentrevs(rev)
2529 2528 rs = r.rawsize(rev)
2530 2529 ts = ts + rs
2531 2530 heads -= set(r.parentrevs(rev))
2532 2531 heads.add(rev)
2533 2532 try:
2534 2533 compression = ts / r.end(rev)
2535 2534 except ZeroDivisionError:
2536 2535 compression = 0
2537 2536 ui.write("%5d %5d %5d %5d %5d %10d %4d %4d %4d %7d %9d "
2538 2537 "%11d %5d %8d\n" %
2539 2538 (rev, p1, p2, r.start(rev), r.end(rev),
2540 2539 r.start(dbase), r.start(cbase),
2541 2540 r.start(p1), r.start(p2),
2542 2541 rs, ts, compression, len(heads), clen))
2543 2542 return 0
2544 2543
2545 2544 v = r.version
2546 2545 format = v & 0xFFFF
2547 2546 flags = []
2548 2547 gdelta = False
2549 2548 if v & revlog.REVLOGNGINLINEDATA:
2550 2549 flags.append('inline')
2551 2550 if v & revlog.REVLOGGENERALDELTA:
2552 2551 gdelta = True
2553 2552 flags.append('generaldelta')
2554 2553 if not flags:
2555 2554 flags = ['(none)']
2556 2555
2557 2556 nummerges = 0
2558 2557 numfull = 0
2559 2558 numprev = 0
2560 2559 nump1 = 0
2561 2560 nump2 = 0
2562 2561 numother = 0
2563 2562 nump1prev = 0
2564 2563 nump2prev = 0
2565 2564 chainlengths = []
2566 2565
2567 2566 datasize = [None, 0, 0]
2568 2567 fullsize = [None, 0, 0]
2569 2568 deltasize = [None, 0, 0]
2570 2569 chunktypecounts = {}
2571 2570 chunktypesizes = {}
2572 2571
2573 2572 def addsize(size, l):
2574 2573 if l[0] is None or size < l[0]:
2575 2574 l[0] = size
2576 2575 if size > l[1]:
2577 2576 l[1] = size
2578 2577 l[2] += size
2579 2578
2580 2579 numrevs = len(r)
2581 2580 for rev in xrange(numrevs):
2582 2581 p1, p2 = r.parentrevs(rev)
2583 2582 delta = r.deltaparent(rev)
2584 2583 if format > 0:
2585 2584 addsize(r.rawsize(rev), datasize)
2586 2585 if p2 != nullrev:
2587 2586 nummerges += 1
2588 2587 size = r.length(rev)
2589 2588 if delta == nullrev:
2590 2589 chainlengths.append(0)
2591 2590 numfull += 1
2592 2591 addsize(size, fullsize)
2593 2592 else:
2594 2593 chainlengths.append(chainlengths[delta] + 1)
2595 2594 addsize(size, deltasize)
2596 2595 if delta == rev - 1:
2597 2596 numprev += 1
2598 2597 if delta == p1:
2599 2598 nump1prev += 1
2600 2599 elif delta == p2:
2601 2600 nump2prev += 1
2602 2601 elif delta == p1:
2603 2602 nump1 += 1
2604 2603 elif delta == p2:
2605 2604 nump2 += 1
2606 2605 elif delta != nullrev:
2607 2606 numother += 1
2608 2607
2609 2608 # Obtain data on the raw chunks in the revlog.
2610 2609 chunk = r._chunkraw(rev, rev)[1]
2611 2610 if chunk:
2612 2611 chunktype = chunk[0]
2613 2612 else:
2614 2613 chunktype = 'empty'
2615 2614
2616 2615 if chunktype not in chunktypecounts:
2617 2616 chunktypecounts[chunktype] = 0
2618 2617 chunktypesizes[chunktype] = 0
2619 2618
2620 2619 chunktypecounts[chunktype] += 1
2621 2620 chunktypesizes[chunktype] += size
2622 2621
2623 2622 # Adjust size min value for empty cases
2624 2623 for size in (datasize, fullsize, deltasize):
2625 2624 if size[0] is None:
2626 2625 size[0] = 0
2627 2626
2628 2627 numdeltas = numrevs - numfull
2629 2628 numoprev = numprev - nump1prev - nump2prev
2630 2629 totalrawsize = datasize[2]
2631 2630 datasize[2] /= numrevs
2632 2631 fulltotal = fullsize[2]
2633 2632 fullsize[2] /= numfull
2634 2633 deltatotal = deltasize[2]
2635 2634 if numrevs - numfull > 0:
2636 2635 deltasize[2] /= numrevs - numfull
2637 2636 totalsize = fulltotal + deltatotal
2638 2637 avgchainlen = sum(chainlengths) / numrevs
2639 2638 maxchainlen = max(chainlengths)
2640 2639 compratio = 1
2641 2640 if totalsize:
2642 2641 compratio = totalrawsize / totalsize
2643 2642
2644 2643 basedfmtstr = '%%%dd\n'
2645 2644 basepcfmtstr = '%%%dd %s(%%5.2f%%%%)\n'
2646 2645
2647 2646 def dfmtstr(max):
2648 2647 return basedfmtstr % len(str(max))
2649 2648 def pcfmtstr(max, padding=0):
2650 2649 return basepcfmtstr % (len(str(max)), ' ' * padding)
2651 2650
2652 2651 def pcfmt(value, total):
2653 2652 if total:
2654 2653 return (value, 100 * float(value) / total)
2655 2654 else:
2656 2655 return value, 100.0
2657 2656
2658 2657 ui.write(('format : %d\n') % format)
2659 2658 ui.write(('flags : %s\n') % ', '.join(flags))
2660 2659
2661 2660 ui.write('\n')
2662 2661 fmt = pcfmtstr(totalsize)
2663 2662 fmt2 = dfmtstr(totalsize)
2664 2663 ui.write(('revisions : ') + fmt2 % numrevs)
2665 2664 ui.write((' merges : ') + fmt % pcfmt(nummerges, numrevs))
2666 2665 ui.write((' normal : ') + fmt % pcfmt(numrevs - nummerges, numrevs))
2667 2666 ui.write(('revisions : ') + fmt2 % numrevs)
2668 2667 ui.write((' full : ') + fmt % pcfmt(numfull, numrevs))
2669 2668 ui.write((' deltas : ') + fmt % pcfmt(numdeltas, numrevs))
2670 2669 ui.write(('revision size : ') + fmt2 % totalsize)
2671 2670 ui.write((' full : ') + fmt % pcfmt(fulltotal, totalsize))
2672 2671 ui.write((' deltas : ') + fmt % pcfmt(deltatotal, totalsize))
2673 2672
2674 2673 def fmtchunktype(chunktype):
2675 2674 if chunktype == 'empty':
2676 2675 return ' %s : ' % chunktype
2677 2676 elif chunktype in string.ascii_letters:
2678 2677 return ' 0x%s (%s) : ' % (hex(chunktype), chunktype)
2679 2678 else:
2680 2679 return ' 0x%s : ' % hex(chunktype)
2681 2680
2682 2681 ui.write('\n')
2683 2682 ui.write(('chunks : ') + fmt2 % numrevs)
2684 2683 for chunktype in sorted(chunktypecounts):
2685 2684 ui.write(fmtchunktype(chunktype))
2686 2685 ui.write(fmt % pcfmt(chunktypecounts[chunktype], numrevs))
2687 2686 ui.write(('chunks size : ') + fmt2 % totalsize)
2688 2687 for chunktype in sorted(chunktypecounts):
2689 2688 ui.write(fmtchunktype(chunktype))
2690 2689 ui.write(fmt % pcfmt(chunktypesizes[chunktype], totalsize))
2691 2690
2692 2691 ui.write('\n')
2693 2692 fmt = dfmtstr(max(avgchainlen, compratio))
2694 2693 ui.write(('avg chain length : ') + fmt % avgchainlen)
2695 2694 ui.write(('max chain length : ') + fmt % maxchainlen)
2696 2695 ui.write(('compression ratio : ') + fmt % compratio)
2697 2696
2698 2697 if format > 0:
2699 2698 ui.write('\n')
2700 2699 ui.write(('uncompressed data size (min/max/avg) : %d / %d / %d\n')
2701 2700 % tuple(datasize))
2702 2701 ui.write(('full revision size (min/max/avg) : %d / %d / %d\n')
2703 2702 % tuple(fullsize))
2704 2703 ui.write(('delta size (min/max/avg) : %d / %d / %d\n')
2705 2704 % tuple(deltasize))
2706 2705
2707 2706 if numdeltas > 0:
2708 2707 ui.write('\n')
2709 2708 fmt = pcfmtstr(numdeltas)
2710 2709 fmt2 = pcfmtstr(numdeltas, 4)
2711 2710 ui.write(('deltas against prev : ') + fmt % pcfmt(numprev, numdeltas))
2712 2711 if numprev > 0:
2713 2712 ui.write((' where prev = p1 : ') + fmt2 % pcfmt(nump1prev,
2714 2713 numprev))
2715 2714 ui.write((' where prev = p2 : ') + fmt2 % pcfmt(nump2prev,
2716 2715 numprev))
2717 2716 ui.write((' other : ') + fmt2 % pcfmt(numoprev,
2718 2717 numprev))
2719 2718 if gdelta:
2720 2719 ui.write(('deltas against p1 : ')
2721 2720 + fmt % pcfmt(nump1, numdeltas))
2722 2721 ui.write(('deltas against p2 : ')
2723 2722 + fmt % pcfmt(nump2, numdeltas))
2724 2723 ui.write(('deltas against other : ') + fmt % pcfmt(numother,
2725 2724 numdeltas))
2726 2725
2727 2726 @command('debugrevspec',
2728 2727 [('', 'optimize', None,
2729 2728 _('print parsed tree after optimizing (DEPRECATED)')),
2730 2729 ('p', 'show-stage', [],
2731 2730 _('print parsed tree at the given stage'), _('NAME')),
2732 2731 ('', 'no-optimized', False, _('evaluate tree without optimization')),
2733 2732 ('', 'verify-optimized', False, _('verify optimized result')),
2734 2733 ],
2735 2734 ('REVSPEC'))
2736 2735 def debugrevspec(ui, repo, expr, **opts):
2737 2736 """parse and apply a revision specification
2738 2737
2739 2738 Use -p/--show-stage option to print the parsed tree at the given stages.
2740 2739 Use -p all to print tree at every stage.
2741 2740
2742 2741 Use --verify-optimized to compare the optimized result with the unoptimized
2743 2742 one. Returns 1 if the optimized result differs.
2744 2743 """
2745 2744 stages = [
2746 2745 ('parsed', lambda tree: tree),
2747 2746 ('expanded', lambda tree: revset.expandaliases(ui, tree)),
2748 2747 ('concatenated', revset.foldconcat),
2749 2748 ('analyzed', revset.analyze),
2750 2749 ('optimized', revset.optimize),
2751 2750 ]
2752 2751 if opts['no_optimized']:
2753 2752 stages = stages[:-1]
2754 2753 if opts['verify_optimized'] and opts['no_optimized']:
2755 2754 raise error.Abort(_('cannot use --verify-optimized with '
2756 2755 '--no-optimized'))
2757 2756 stagenames = set(n for n, f in stages)
2758 2757
2759 2758 showalways = set()
2760 2759 showchanged = set()
2761 2760 if ui.verbose and not opts['show_stage']:
2762 2761 # show parsed tree by --verbose (deprecated)
2763 2762 showalways.add('parsed')
2764 2763 showchanged.update(['expanded', 'concatenated'])
2765 2764 if opts['optimize']:
2766 2765 showalways.add('optimized')
2767 2766 if opts['show_stage'] and opts['optimize']:
2768 2767 raise error.Abort(_('cannot use --optimize with --show-stage'))
2769 2768 if opts['show_stage'] == ['all']:
2770 2769 showalways.update(stagenames)
2771 2770 else:
2772 2771 for n in opts['show_stage']:
2773 2772 if n not in stagenames:
2774 2773 raise error.Abort(_('invalid stage name: %s') % n)
2775 2774 showalways.update(opts['show_stage'])
2776 2775
2777 2776 treebystage = {}
2778 2777 printedtree = None
2779 2778 tree = revset.parse(expr, lookup=repo.__contains__)
2780 2779 for n, f in stages:
2781 2780 treebystage[n] = tree = f(tree)
2782 2781 if n in showalways or (n in showchanged and tree != printedtree):
2783 2782 if opts['show_stage'] or n != 'parsed':
2784 2783 ui.write(("* %s:\n") % n)
2785 2784 ui.write(revset.prettyformat(tree), "\n")
2786 2785 printedtree = tree
2787 2786
2788 2787 if opts['verify_optimized']:
2789 2788 arevs = revset.makematcher(treebystage['analyzed'])(repo)
2790 2789 brevs = revset.makematcher(treebystage['optimized'])(repo)
2791 2790 if ui.verbose:
2792 2791 ui.note(("* analyzed set:\n"), revset.prettyformatset(arevs), "\n")
2793 2792 ui.note(("* optimized set:\n"), revset.prettyformatset(brevs), "\n")
2794 2793 arevs = list(arevs)
2795 2794 brevs = list(brevs)
2796 2795 if arevs == brevs:
2797 2796 return 0
2798 2797 ui.write(('--- analyzed\n'), label='diff.file_a')
2799 2798 ui.write(('+++ optimized\n'), label='diff.file_b')
2800 2799 sm = difflib.SequenceMatcher(None, arevs, brevs)
2801 2800 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
2802 2801 if tag in ('delete', 'replace'):
2803 2802 for c in arevs[alo:ahi]:
2804 2803 ui.write('-%s\n' % c, label='diff.deleted')
2805 2804 if tag in ('insert', 'replace'):
2806 2805 for c in brevs[blo:bhi]:
2807 2806 ui.write('+%s\n' % c, label='diff.inserted')
2808 2807 if tag == 'equal':
2809 2808 for c in arevs[alo:ahi]:
2810 2809 ui.write(' %s\n' % c)
2811 2810 return 1
2812 2811
2813 2812 func = revset.makematcher(tree)
2814 2813 revs = func(repo)
2815 2814 if ui.verbose:
2816 2815 ui.note(("* set:\n"), revset.prettyformatset(revs), "\n")
2817 2816 for c in revs:
2818 2817 ui.write("%s\n" % c)
2819 2818
2820 2819 @command('debugsetparents', [], _('REV1 [REV2]'))
2821 2820 def debugsetparents(ui, repo, rev1, rev2=None):
2822 2821 """manually set the parents of the current working directory
2823 2822
2824 2823 This is useful for writing repository conversion tools, but should
2825 2824 be used with care. For example, neither the working directory nor the
2826 2825 dirstate is updated, so file status may be incorrect after running this
2827 2826 command.
2828 2827
2829 2828 Returns 0 on success.
2830 2829 """
2831 2830
2832 2831 r1 = scmutil.revsingle(repo, rev1).node()
2833 2832 r2 = scmutil.revsingle(repo, rev2, 'null').node()
2834 2833
2835 2834 with repo.wlock():
2836 2835 repo.setparents(r1, r2)
2837 2836
2838 2837 @command('debugdirstate|debugstate',
2839 2838 [('', 'nodates', None, _('do not display the saved mtime')),
2840 2839 ('', 'datesort', None, _('sort by saved mtime'))],
2841 2840 _('[OPTION]...'))
2842 2841 def debugstate(ui, repo, **opts):
2843 2842 """show the contents of the current dirstate"""
2844 2843
2845 2844 nodates = opts.get('nodates')
2846 2845 datesort = opts.get('datesort')
2847 2846
2848 2847 timestr = ""
2849 2848 if datesort:
2850 2849 keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
2851 2850 else:
2852 2851 keyfunc = None # sort by filename
2853 2852 for file_, ent in sorted(repo.dirstate._map.iteritems(), key=keyfunc):
2854 2853 if ent[3] == -1:
2855 2854 timestr = 'unset '
2856 2855 elif nodates:
2857 2856 timestr = 'set '
2858 2857 else:
2859 2858 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
2860 2859 time.localtime(ent[3]))
2861 2860 if ent[1] & 0o20000:
2862 2861 mode = 'lnk'
2863 2862 else:
2864 2863 mode = '%3o' % (ent[1] & 0o777 & ~util.umask)
2865 2864 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
2866 2865 for f in repo.dirstate.copies():
2867 2866 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
2868 2867
2869 2868 @command('debugsub',
2870 2869 [('r', 'rev', '',
2871 2870 _('revision to check'), _('REV'))],
2872 2871 _('[-r REV] [REV]'))
2873 2872 def debugsub(ui, repo, rev=None):
2874 2873 ctx = scmutil.revsingle(repo, rev, None)
2875 2874 for k, v in sorted(ctx.substate.items()):
2876 2875 ui.write(('path %s\n') % k)
2877 2876 ui.write((' source %s\n') % v[0])
2878 2877 ui.write((' revision %s\n') % v[1])
2879 2878
2880 2879 @command('debugsuccessorssets',
2881 2880 [],
2882 2881 _('[REV]'))
2883 2882 def debugsuccessorssets(ui, repo, *revs):
2884 2883 """show set of successors for revision
2885 2884
2886 2885 A successors set of changeset A is a consistent group of revisions that
2887 2886 succeed A. It contains non-obsolete changesets only.
2888 2887
2889 2888 In most cases a changeset A has a single successors set containing a single
2890 2889 successor (changeset A replaced by A').
2891 2890
2892 2891 A changeset that is made obsolete with no successors are called "pruned".
2893 2892 Such changesets have no successors sets at all.
2894 2893
2895 2894 A changeset that has been "split" will have a successors set containing
2896 2895 more than one successor.
2897 2896
2898 2897 A changeset that has been rewritten in multiple different ways is called
2899 2898 "divergent". Such changesets have multiple successor sets (each of which
2900 2899 may also be split, i.e. have multiple successors).
2901 2900
2902 2901 Results are displayed as follows::
2903 2902
2904 2903 <rev1>
2905 2904 <successors-1A>
2906 2905 <rev2>
2907 2906 <successors-2A>
2908 2907 <successors-2B1> <successors-2B2> <successors-2B3>
2909 2908
2910 2909 Here rev2 has two possible (i.e. divergent) successors sets. The first
2911 2910 holds one element, whereas the second holds three (i.e. the changeset has
2912 2911 been split).
2913 2912 """
2914 2913 # passed to successorssets caching computation from one call to another
2915 2914 cache = {}
2916 2915 ctx2str = str
2917 2916 node2str = short
2918 2917 if ui.debug():
2919 2918 def ctx2str(ctx):
2920 2919 return ctx.hex()
2921 2920 node2str = hex
2922 2921 for rev in scmutil.revrange(repo, revs):
2923 2922 ctx = repo[rev]
2924 2923 ui.write('%s\n'% ctx2str(ctx))
2925 2924 for succsset in obsolete.successorssets(repo, ctx.node(), cache):
2926 2925 if succsset:
2927 2926 ui.write(' ')
2928 2927 ui.write(node2str(succsset[0]))
2929 2928 for node in succsset[1:]:
2930 2929 ui.write(' ')
2931 2930 ui.write(node2str(node))
2932 2931 ui.write('\n')
2933 2932
2934 2933 @command('debugtemplate',
2935 2934 [('r', 'rev', [], _('apply template on changesets'), _('REV')),
2936 2935 ('D', 'define', [], _('define template keyword'), _('KEY=VALUE'))],
2937 2936 _('[-r REV]... [-D KEY=VALUE]... TEMPLATE'),
2938 2937 optionalrepo=True)
2939 2938 def debugtemplate(ui, repo, tmpl, **opts):
2940 2939 """parse and apply a template
2941 2940
2942 2941 If -r/--rev is given, the template is processed as a log template and
2943 2942 applied to the given changesets. Otherwise, it is processed as a generic
2944 2943 template.
2945 2944
2946 2945 Use --verbose to print the parsed tree.
2947 2946 """
2948 2947 revs = None
2949 2948 if opts['rev']:
2950 2949 if repo is None:
2951 2950 raise error.RepoError(_('there is no Mercurial repository here '
2952 2951 '(.hg not found)'))
2953 2952 revs = scmutil.revrange(repo, opts['rev'])
2954 2953
2955 2954 props = {}
2956 2955 for d in opts['define']:
2957 2956 try:
2958 2957 k, v = (e.strip() for e in d.split('=', 1))
2959 2958 if not k:
2960 2959 raise ValueError
2961 2960 props[k] = v
2962 2961 except ValueError:
2963 2962 raise error.Abort(_('malformed keyword definition: %s') % d)
2964 2963
2965 2964 if ui.verbose:
2966 2965 aliases = ui.configitems('templatealias')
2967 2966 tree = templater.parse(tmpl)
2968 2967 ui.note(templater.prettyformat(tree), '\n')
2969 2968 newtree = templater.expandaliases(tree, aliases)
2970 2969 if newtree != tree:
2971 2970 ui.note(("* expanded:\n"), templater.prettyformat(newtree), '\n')
2972 2971
2973 2972 mapfile = None
2974 2973 if revs is None:
2975 2974 k = 'debugtemplate'
2976 2975 t = formatter.maketemplater(ui, k, tmpl)
2977 2976 ui.write(templater.stringify(t(k, **props)))
2978 2977 else:
2979 2978 displayer = cmdutil.changeset_templater(ui, repo, None, opts, tmpl,
2980 2979 mapfile, buffered=False)
2981 2980 for r in revs:
2982 2981 displayer.show(repo[r], **props)
2983 2982 displayer.close()
2984 2983
2985 2984 @command('debugwalk', walkopts, _('[OPTION]... [FILE]...'), inferrepo=True)
2986 2985 def debugwalk(ui, repo, *pats, **opts):
2987 2986 """show how files match on given patterns"""
2988 2987 m = scmutil.match(repo[None], pats, opts)
2989 2988 items = list(repo.walk(m))
2990 2989 if not items:
2991 2990 return
2992 2991 f = lambda fn: fn
2993 2992 if ui.configbool('ui', 'slash') and pycompat.ossep != '/':
2994 2993 f = lambda fn: util.normpath(fn)
2995 2994 fmt = 'f %%-%ds %%-%ds %%s' % (
2996 2995 max([len(abs) for abs in items]),
2997 2996 max([len(m.rel(abs)) for abs in items]))
2998 2997 for abs in items:
2999 2998 line = fmt % (abs, f(m.rel(abs)), m.exact(abs) and 'exact' or '')
3000 2999 ui.write("%s\n" % line.rstrip())
3001 3000
3002 3001 @command('debugwireargs',
3003 3002 [('', 'three', '', 'three'),
3004 3003 ('', 'four', '', 'four'),
3005 3004 ('', 'five', '', 'five'),
3006 3005 ] + remoteopts,
3007 3006 _('REPO [OPTIONS]... [ONE [TWO]]'),
3008 3007 norepo=True)
3009 3008 def debugwireargs(ui, repopath, *vals, **opts):
3010 3009 repo = hg.peer(ui, opts, repopath)
3011 3010 for opt in remoteopts:
3012 3011 del opts[opt[1]]
3013 3012 args = {}
3014 3013 for k, v in opts.iteritems():
3015 3014 if v:
3016 3015 args[k] = v
3017 3016 # run twice to check that we don't mess up the stream for the next command
3018 3017 res1 = repo.debugwireargs(*vals, **args)
3019 3018 res2 = repo.debugwireargs(*vals, **args)
3020 3019 ui.write("%s\n" % res1)
3021 3020 if res1 != res2:
3022 3021 ui.warn("%s\n" % res2)
3023 3022
3024 3023 @command('^diff',
3025 3024 [('r', 'rev', [], _('revision'), _('REV')),
3026 3025 ('c', 'change', '', _('change made by revision'), _('REV'))
3027 3026 ] + diffopts + diffopts2 + walkopts + subrepoopts,
3028 3027 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
3029 3028 inferrepo=True)
3030 3029 def diff(ui, repo, *pats, **opts):
3031 3030 """diff repository (or selected files)
3032 3031
3033 3032 Show differences between revisions for the specified files.
3034 3033
3035 3034 Differences between files are shown using the unified diff format.
3036 3035
3037 3036 .. note::
3038 3037
3039 3038 :hg:`diff` may generate unexpected results for merges, as it will
3040 3039 default to comparing against the working directory's first
3041 3040 parent changeset if no revisions are specified.
3042 3041
3043 3042 When two revision arguments are given, then changes are shown
3044 3043 between those revisions. If only one revision is specified then
3045 3044 that revision is compared to the working directory, and, when no
3046 3045 revisions are specified, the working directory files are compared
3047 3046 to its first parent.
3048 3047
3049 3048 Alternatively you can specify -c/--change with a revision to see
3050 3049 the changes in that changeset relative to its first parent.
3051 3050
3052 3051 Without the -a/--text option, diff will avoid generating diffs of
3053 3052 files it detects as binary. With -a, diff will generate a diff
3054 3053 anyway, probably with undesirable results.
3055 3054
3056 3055 Use the -g/--git option to generate diffs in the git extended diff
3057 3056 format. For more information, read :hg:`help diffs`.
3058 3057
3059 3058 .. container:: verbose
3060 3059
3061 3060 Examples:
3062 3061
3063 3062 - compare a file in the current working directory to its parent::
3064 3063
3065 3064 hg diff foo.c
3066 3065
3067 3066 - compare two historical versions of a directory, with rename info::
3068 3067
3069 3068 hg diff --git -r 1.0:1.2 lib/
3070 3069
3071 3070 - get change stats relative to the last change on some date::
3072 3071
3073 3072 hg diff --stat -r "date('may 2')"
3074 3073
3075 3074 - diff all newly-added files that contain a keyword::
3076 3075
3077 3076 hg diff "set:added() and grep(GNU)"
3078 3077
3079 3078 - compare a revision and its parents::
3080 3079
3081 3080 hg diff -c 9353 # compare against first parent
3082 3081 hg diff -r 9353^:9353 # same using revset syntax
3083 3082 hg diff -r 9353^2:9353 # compare against the second parent
3084 3083
3085 3084 Returns 0 on success.
3086 3085 """
3087 3086
3088 3087 revs = opts.get('rev')
3089 3088 change = opts.get('change')
3090 3089 stat = opts.get('stat')
3091 3090 reverse = opts.get('reverse')
3092 3091
3093 3092 if revs and change:
3094 3093 msg = _('cannot specify --rev and --change at the same time')
3095 3094 raise error.Abort(msg)
3096 3095 elif change:
3097 3096 node2 = scmutil.revsingle(repo, change, None).node()
3098 3097 node1 = repo[node2].p1().node()
3099 3098 else:
3100 3099 node1, node2 = scmutil.revpair(repo, revs)
3101 3100
3102 3101 if reverse:
3103 3102 node1, node2 = node2, node1
3104 3103
3105 3104 diffopts = patch.diffallopts(ui, opts)
3106 3105 m = scmutil.match(repo[node2], pats, opts)
3107 3106 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
3108 3107 listsubrepos=opts.get('subrepos'),
3109 3108 root=opts.get('root'))
3110 3109
3111 3110 @command('^export',
3112 3111 [('o', 'output', '',
3113 3112 _('print output to file with formatted name'), _('FORMAT')),
3114 3113 ('', 'switch-parent', None, _('diff against the second parent')),
3115 3114 ('r', 'rev', [], _('revisions to export'), _('REV')),
3116 3115 ] + diffopts,
3117 3116 _('[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'))
3118 3117 def export(ui, repo, *changesets, **opts):
3119 3118 """dump the header and diffs for one or more changesets
3120 3119
3121 3120 Print the changeset header and diffs for one or more revisions.
3122 3121 If no revision is given, the parent of the working directory is used.
3123 3122
3124 3123 The information shown in the changeset header is: author, date,
3125 3124 branch name (if non-default), changeset hash, parent(s) and commit
3126 3125 comment.
3127 3126
3128 3127 .. note::
3129 3128
3130 3129 :hg:`export` may generate unexpected diff output for merge
3131 3130 changesets, as it will compare the merge changeset against its
3132 3131 first parent only.
3133 3132
3134 3133 Output may be to a file, in which case the name of the file is
3135 3134 given using a format string. The formatting rules are as follows:
3136 3135
3137 3136 :``%%``: literal "%" character
3138 3137 :``%H``: changeset hash (40 hexadecimal digits)
3139 3138 :``%N``: number of patches being generated
3140 3139 :``%R``: changeset revision number
3141 3140 :``%b``: basename of the exporting repository
3142 3141 :``%h``: short-form changeset hash (12 hexadecimal digits)
3143 3142 :``%m``: first line of the commit message (only alphanumeric characters)
3144 3143 :``%n``: zero-padded sequence number, starting at 1
3145 3144 :``%r``: zero-padded changeset revision number
3146 3145
3147 3146 Without the -a/--text option, export will avoid generating diffs
3148 3147 of files it detects as binary. With -a, export will generate a
3149 3148 diff anyway, probably with undesirable results.
3150 3149
3151 3150 Use the -g/--git option to generate diffs in the git extended diff
3152 3151 format. See :hg:`help diffs` for more information.
3153 3152
3154 3153 With the --switch-parent option, the diff will be against the
3155 3154 second parent. It can be useful to review a merge.
3156 3155
3157 3156 .. container:: verbose
3158 3157
3159 3158 Examples:
3160 3159
3161 3160 - use export and import to transplant a bugfix to the current
3162 3161 branch::
3163 3162
3164 3163 hg export -r 9353 | hg import -
3165 3164
3166 3165 - export all the changesets between two revisions to a file with
3167 3166 rename information::
3168 3167
3169 3168 hg export --git -r 123:150 > changes.txt
3170 3169
3171 3170 - split outgoing changes into a series of patches with
3172 3171 descriptive names::
3173 3172
3174 3173 hg export -r "outgoing()" -o "%n-%m.patch"
3175 3174
3176 3175 Returns 0 on success.
3177 3176 """
3178 3177 changesets += tuple(opts.get('rev', []))
3179 3178 if not changesets:
3180 3179 changesets = ['.']
3181 3180 revs = scmutil.revrange(repo, changesets)
3182 3181 if not revs:
3183 3182 raise error.Abort(_("export requires at least one changeset"))
3184 3183 if len(revs) > 1:
3185 3184 ui.note(_('exporting patches:\n'))
3186 3185 else:
3187 3186 ui.note(_('exporting patch:\n'))
3188 3187 cmdutil.export(repo, revs, template=opts.get('output'),
3189 3188 switch_parent=opts.get('switch_parent'),
3190 3189 opts=patch.diffallopts(ui, opts))
3191 3190
3192 3191 @command('files',
3193 3192 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3194 3193 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3195 3194 ] + walkopts + formatteropts + subrepoopts,
3196 3195 _('[OPTION]... [FILE]...'))
3197 3196 def files(ui, repo, *pats, **opts):
3198 3197 """list tracked files
3199 3198
3200 3199 Print files under Mercurial control in the working directory or
3201 3200 specified revision for given files (excluding removed files).
3202 3201 Files can be specified as filenames or filesets.
3203 3202
3204 3203 If no files are given to match, this command prints the names
3205 3204 of all files under Mercurial control.
3206 3205
3207 3206 .. container:: verbose
3208 3207
3209 3208 Examples:
3210 3209
3211 3210 - list all files under the current directory::
3212 3211
3213 3212 hg files .
3214 3213
3215 3214 - shows sizes and flags for current revision::
3216 3215
3217 3216 hg files -vr .
3218 3217
3219 3218 - list all files named README::
3220 3219
3221 3220 hg files -I "**/README"
3222 3221
3223 3222 - list all binary files::
3224 3223
3225 3224 hg files "set:binary()"
3226 3225
3227 3226 - find files containing a regular expression::
3228 3227
3229 3228 hg files "set:grep('bob')"
3230 3229
3231 3230 - search tracked file contents with xargs and grep::
3232 3231
3233 3232 hg files -0 | xargs -0 grep foo
3234 3233
3235 3234 See :hg:`help patterns` and :hg:`help filesets` for more information
3236 3235 on specifying file patterns.
3237 3236
3238 3237 Returns 0 if a match is found, 1 otherwise.
3239 3238
3240 3239 """
3241 3240 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
3242 3241
3243 3242 end = '\n'
3244 3243 if opts.get('print0'):
3245 3244 end = '\0'
3246 3245 fmt = '%s' + end
3247 3246
3248 3247 m = scmutil.match(ctx, pats, opts)
3249 3248 with ui.formatter('files', opts) as fm:
3250 3249 return cmdutil.files(ui, ctx, m, fm, fmt, opts.get('subrepos'))
3251 3250
3252 3251 @command('^forget', walkopts, _('[OPTION]... FILE...'), inferrepo=True)
3253 3252 def forget(ui, repo, *pats, **opts):
3254 3253 """forget the specified files on the next commit
3255 3254
3256 3255 Mark the specified files so they will no longer be tracked
3257 3256 after the next commit.
3258 3257
3259 3258 This only removes files from the current branch, not from the
3260 3259 entire project history, and it does not delete them from the
3261 3260 working directory.
3262 3261
3263 3262 To delete the file from the working directory, see :hg:`remove`.
3264 3263
3265 3264 To undo a forget before the next commit, see :hg:`add`.
3266 3265
3267 3266 .. container:: verbose
3268 3267
3269 3268 Examples:
3270 3269
3271 3270 - forget newly-added binary files::
3272 3271
3273 3272 hg forget "set:added() and binary()"
3274 3273
3275 3274 - forget files that would be excluded by .hgignore::
3276 3275
3277 3276 hg forget "set:hgignore()"
3278 3277
3279 3278 Returns 0 on success.
3280 3279 """
3281 3280
3282 3281 if not pats:
3283 3282 raise error.Abort(_('no files specified'))
3284 3283
3285 3284 m = scmutil.match(repo[None], pats, opts)
3286 3285 rejected = cmdutil.forget(ui, repo, m, prefix="", explicitonly=False)[0]
3287 3286 return rejected and 1 or 0
3288 3287
3289 3288 @command(
3290 3289 'graft',
3291 3290 [('r', 'rev', [], _('revisions to graft'), _('REV')),
3292 3291 ('c', 'continue', False, _('resume interrupted graft')),
3293 3292 ('e', 'edit', False, _('invoke editor on commit messages')),
3294 3293 ('', 'log', None, _('append graft info to log message')),
3295 3294 ('f', 'force', False, _('force graft')),
3296 3295 ('D', 'currentdate', False,
3297 3296 _('record the current date as commit date')),
3298 3297 ('U', 'currentuser', False,
3299 3298 _('record the current user as committer'), _('DATE'))]
3300 3299 + commitopts2 + mergetoolopts + dryrunopts,
3301 3300 _('[OPTION]... [-r REV]... REV...'))
3302 3301 def graft(ui, repo, *revs, **opts):
3303 3302 '''copy changes from other branches onto the current branch
3304 3303
3305 3304 This command uses Mercurial's merge logic to copy individual
3306 3305 changes from other branches without merging branches in the
3307 3306 history graph. This is sometimes known as 'backporting' or
3308 3307 'cherry-picking'. By default, graft will copy user, date, and
3309 3308 description from the source changesets.
3310 3309
3311 3310 Changesets that are ancestors of the current revision, that have
3312 3311 already been grafted, or that are merges will be skipped.
3313 3312
3314 3313 If --log is specified, log messages will have a comment appended
3315 3314 of the form::
3316 3315
3317 3316 (grafted from CHANGESETHASH)
3318 3317
3319 3318 If --force is specified, revisions will be grafted even if they
3320 3319 are already ancestors of or have been grafted to the destination.
3321 3320 This is useful when the revisions have since been backed out.
3322 3321
3323 3322 If a graft merge results in conflicts, the graft process is
3324 3323 interrupted so that the current merge can be manually resolved.
3325 3324 Once all conflicts are addressed, the graft process can be
3326 3325 continued with the -c/--continue option.
3327 3326
3328 3327 .. note::
3329 3328
3330 3329 The -c/--continue option does not reapply earlier options, except
3331 3330 for --force.
3332 3331
3333 3332 .. container:: verbose
3334 3333
3335 3334 Examples:
3336 3335
3337 3336 - copy a single change to the stable branch and edit its description::
3338 3337
3339 3338 hg update stable
3340 3339 hg graft --edit 9393
3341 3340
3342 3341 - graft a range of changesets with one exception, updating dates::
3343 3342
3344 3343 hg graft -D "2085::2093 and not 2091"
3345 3344
3346 3345 - continue a graft after resolving conflicts::
3347 3346
3348 3347 hg graft -c
3349 3348
3350 3349 - show the source of a grafted changeset::
3351 3350
3352 3351 hg log --debug -r .
3353 3352
3354 3353 - show revisions sorted by date::
3355 3354
3356 3355 hg log -r "sort(all(), date)"
3357 3356
3358 3357 See :hg:`help revisions` and :hg:`help revsets` for more about
3359 3358 specifying revisions.
3360 3359
3361 3360 Returns 0 on successful completion.
3362 3361 '''
3363 3362 with repo.wlock():
3364 3363 return _dograft(ui, repo, *revs, **opts)
3365 3364
3366 3365 def _dograft(ui, repo, *revs, **opts):
3367 3366 if revs and opts.get('rev'):
3368 3367 ui.warn(_('warning: inconsistent use of --rev might give unexpected '
3369 3368 'revision ordering!\n'))
3370 3369
3371 3370 revs = list(revs)
3372 3371 revs.extend(opts.get('rev'))
3373 3372
3374 3373 if not opts.get('user') and opts.get('currentuser'):
3375 3374 opts['user'] = ui.username()
3376 3375 if not opts.get('date') and opts.get('currentdate'):
3377 3376 opts['date'] = "%d %d" % util.makedate()
3378 3377
3379 3378 editor = cmdutil.getcommiteditor(editform='graft', **opts)
3380 3379
3381 3380 cont = False
3382 3381 if opts.get('continue'):
3383 3382 cont = True
3384 3383 if revs:
3385 3384 raise error.Abort(_("can't specify --continue and revisions"))
3386 3385 # read in unfinished revisions
3387 3386 try:
3388 3387 nodes = repo.vfs.read('graftstate').splitlines()
3389 3388 revs = [repo[node].rev() for node in nodes]
3390 3389 except IOError as inst:
3391 3390 if inst.errno != errno.ENOENT:
3392 3391 raise
3393 3392 cmdutil.wrongtooltocontinue(repo, _('graft'))
3394 3393 else:
3395 3394 cmdutil.checkunfinished(repo)
3396 3395 cmdutil.bailifchanged(repo)
3397 3396 if not revs:
3398 3397 raise error.Abort(_('no revisions specified'))
3399 3398 revs = scmutil.revrange(repo, revs)
3400 3399
3401 3400 skipped = set()
3402 3401 # check for merges
3403 3402 for rev in repo.revs('%ld and merge()', revs):
3404 3403 ui.warn(_('skipping ungraftable merge revision %s\n') % rev)
3405 3404 skipped.add(rev)
3406 3405 revs = [r for r in revs if r not in skipped]
3407 3406 if not revs:
3408 3407 return -1
3409 3408
3410 3409 # Don't check in the --continue case, in effect retaining --force across
3411 3410 # --continues. That's because without --force, any revisions we decided to
3412 3411 # skip would have been filtered out here, so they wouldn't have made their
3413 3412 # way to the graftstate. With --force, any revisions we would have otherwise
3414 3413 # skipped would not have been filtered out, and if they hadn't been applied
3415 3414 # already, they'd have been in the graftstate.
3416 3415 if not (cont or opts.get('force')):
3417 3416 # check for ancestors of dest branch
3418 3417 crev = repo['.'].rev()
3419 3418 ancestors = repo.changelog.ancestors([crev], inclusive=True)
3420 3419 # XXX make this lazy in the future
3421 3420 # don't mutate while iterating, create a copy
3422 3421 for rev in list(revs):
3423 3422 if rev in ancestors:
3424 3423 ui.warn(_('skipping ancestor revision %d:%s\n') %
3425 3424 (rev, repo[rev]))
3426 3425 # XXX remove on list is slow
3427 3426 revs.remove(rev)
3428 3427 if not revs:
3429 3428 return -1
3430 3429
3431 3430 # analyze revs for earlier grafts
3432 3431 ids = {}
3433 3432 for ctx in repo.set("%ld", revs):
3434 3433 ids[ctx.hex()] = ctx.rev()
3435 3434 n = ctx.extra().get('source')
3436 3435 if n:
3437 3436 ids[n] = ctx.rev()
3438 3437
3439 3438 # check ancestors for earlier grafts
3440 3439 ui.debug('scanning for duplicate grafts\n')
3441 3440
3442 3441 for rev in repo.changelog.findmissingrevs(revs, [crev]):
3443 3442 ctx = repo[rev]
3444 3443 n = ctx.extra().get('source')
3445 3444 if n in ids:
3446 3445 try:
3447 3446 r = repo[n].rev()
3448 3447 except error.RepoLookupError:
3449 3448 r = None
3450 3449 if r in revs:
3451 3450 ui.warn(_('skipping revision %d:%s '
3452 3451 '(already grafted to %d:%s)\n')
3453 3452 % (r, repo[r], rev, ctx))
3454 3453 revs.remove(r)
3455 3454 elif ids[n] in revs:
3456 3455 if r is None:
3457 3456 ui.warn(_('skipping already grafted revision %d:%s '
3458 3457 '(%d:%s also has unknown origin %s)\n')
3459 3458 % (ids[n], repo[ids[n]], rev, ctx, n[:12]))
3460 3459 else:
3461 3460 ui.warn(_('skipping already grafted revision %d:%s '
3462 3461 '(%d:%s also has origin %d:%s)\n')
3463 3462 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12]))
3464 3463 revs.remove(ids[n])
3465 3464 elif ctx.hex() in ids:
3466 3465 r = ids[ctx.hex()]
3467 3466 ui.warn(_('skipping already grafted revision %d:%s '
3468 3467 '(was grafted from %d:%s)\n') %
3469 3468 (r, repo[r], rev, ctx))
3470 3469 revs.remove(r)
3471 3470 if not revs:
3472 3471 return -1
3473 3472
3474 3473 for pos, ctx in enumerate(repo.set("%ld", revs)):
3475 3474 desc = '%d:%s "%s"' % (ctx.rev(), ctx,
3476 3475 ctx.description().split('\n', 1)[0])
3477 3476 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
3478 3477 if names:
3479 3478 desc += ' (%s)' % ' '.join(names)
3480 3479 ui.status(_('grafting %s\n') % desc)
3481 3480 if opts.get('dry_run'):
3482 3481 continue
3483 3482
3484 3483 source = ctx.extra().get('source')
3485 3484 extra = {}
3486 3485 if source:
3487 3486 extra['source'] = source
3488 3487 extra['intermediate-source'] = ctx.hex()
3489 3488 else:
3490 3489 extra['source'] = ctx.hex()
3491 3490 user = ctx.user()
3492 3491 if opts.get('user'):
3493 3492 user = opts['user']
3494 3493 date = ctx.date()
3495 3494 if opts.get('date'):
3496 3495 date = opts['date']
3497 3496 message = ctx.description()
3498 3497 if opts.get('log'):
3499 3498 message += '\n(grafted from %s)' % ctx.hex()
3500 3499
3501 3500 # we don't merge the first commit when continuing
3502 3501 if not cont:
3503 3502 # perform the graft merge with p1(rev) as 'ancestor'
3504 3503 try:
3505 3504 # ui.forcemerge is an internal variable, do not document
3506 3505 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
3507 3506 'graft')
3508 3507 stats = mergemod.graft(repo, ctx, ctx.p1(),
3509 3508 ['local', 'graft'])
3510 3509 finally:
3511 3510 repo.ui.setconfig('ui', 'forcemerge', '', 'graft')
3512 3511 # report any conflicts
3513 3512 if stats and stats[3] > 0:
3514 3513 # write out state for --continue
3515 3514 nodelines = [repo[rev].hex() + "\n" for rev in revs[pos:]]
3516 3515 repo.vfs.write('graftstate', ''.join(nodelines))
3517 3516 extra = ''
3518 3517 if opts.get('user'):
3519 3518 extra += ' --user %s' % util.shellquote(opts['user'])
3520 3519 if opts.get('date'):
3521 3520 extra += ' --date %s' % util.shellquote(opts['date'])
3522 3521 if opts.get('log'):
3523 3522 extra += ' --log'
3524 3523 hint=_("use 'hg resolve' and 'hg graft --continue%s'") % extra
3525 3524 raise error.Abort(
3526 3525 _("unresolved conflicts, can't continue"),
3527 3526 hint=hint)
3528 3527 else:
3529 3528 cont = False
3530 3529
3531 3530 # commit
3532 3531 node = repo.commit(text=message, user=user,
3533 3532 date=date, extra=extra, editor=editor)
3534 3533 if node is None:
3535 3534 ui.warn(
3536 3535 _('note: graft of %d:%s created no changes to commit\n') %
3537 3536 (ctx.rev(), ctx))
3538 3537
3539 3538 # remove state when we complete successfully
3540 3539 if not opts.get('dry_run'):
3541 3540 util.unlinkpath(repo.join('graftstate'), ignoremissing=True)
3542 3541
3543 3542 return 0
3544 3543
3545 3544 @command('grep',
3546 3545 [('0', 'print0', None, _('end fields with NUL')),
3547 3546 ('', 'all', None, _('print all revisions that match')),
3548 3547 ('a', 'text', None, _('treat all files as text')),
3549 3548 ('f', 'follow', None,
3550 3549 _('follow changeset history,'
3551 3550 ' or file history across copies and renames')),
3552 3551 ('i', 'ignore-case', None, _('ignore case when matching')),
3553 3552 ('l', 'files-with-matches', None,
3554 3553 _('print only filenames and revisions that match')),
3555 3554 ('n', 'line-number', None, _('print matching line numbers')),
3556 3555 ('r', 'rev', [],
3557 3556 _('only search files changed within revision range'), _('REV')),
3558 3557 ('u', 'user', None, _('list the author (long with -v)')),
3559 3558 ('d', 'date', None, _('list the date (short with -q)')),
3560 3559 ] + formatteropts + walkopts,
3561 3560 _('[OPTION]... PATTERN [FILE]...'),
3562 3561 inferrepo=True)
3563 3562 def grep(ui, repo, pattern, *pats, **opts):
3564 3563 """search revision history for a pattern in specified files
3565 3564
3566 3565 Search revision history for a regular expression in the specified
3567 3566 files or the entire project.
3568 3567
3569 3568 By default, grep prints the most recent revision number for each
3570 3569 file in which it finds a match. To get it to print every revision
3571 3570 that contains a change in match status ("-" for a match that becomes
3572 3571 a non-match, or "+" for a non-match that becomes a match), use the
3573 3572 --all flag.
3574 3573
3575 3574 PATTERN can be any Python (roughly Perl-compatible) regular
3576 3575 expression.
3577 3576
3578 3577 If no FILEs are specified (and -f/--follow isn't set), all files in
3579 3578 the repository are searched, including those that don't exist in the
3580 3579 current branch or have been deleted in a prior changeset.
3581 3580
3582 3581 Returns 0 if a match is found, 1 otherwise.
3583 3582 """
3584 3583 reflags = re.M
3585 3584 if opts.get('ignore_case'):
3586 3585 reflags |= re.I
3587 3586 try:
3588 3587 regexp = util.re.compile(pattern, reflags)
3589 3588 except re.error as inst:
3590 3589 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
3591 3590 return 1
3592 3591 sep, eol = ':', '\n'
3593 3592 if opts.get('print0'):
3594 3593 sep = eol = '\0'
3595 3594
3596 3595 getfile = util.lrucachefunc(repo.file)
3597 3596
3598 3597 def matchlines(body):
3599 3598 begin = 0
3600 3599 linenum = 0
3601 3600 while begin < len(body):
3602 3601 match = regexp.search(body, begin)
3603 3602 if not match:
3604 3603 break
3605 3604 mstart, mend = match.span()
3606 3605 linenum += body.count('\n', begin, mstart) + 1
3607 3606 lstart = body.rfind('\n', begin, mstart) + 1 or begin
3608 3607 begin = body.find('\n', mend) + 1 or len(body) + 1
3609 3608 lend = begin - 1
3610 3609 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
3611 3610
3612 3611 class linestate(object):
3613 3612 def __init__(self, line, linenum, colstart, colend):
3614 3613 self.line = line
3615 3614 self.linenum = linenum
3616 3615 self.colstart = colstart
3617 3616 self.colend = colend
3618 3617
3619 3618 def __hash__(self):
3620 3619 return hash((self.linenum, self.line))
3621 3620
3622 3621 def __eq__(self, other):
3623 3622 return self.line == other.line
3624 3623
3625 3624 def findpos(self):
3626 3625 """Iterate all (start, end) indices of matches"""
3627 3626 yield self.colstart, self.colend
3628 3627 p = self.colend
3629 3628 while p < len(self.line):
3630 3629 m = regexp.search(self.line, p)
3631 3630 if not m:
3632 3631 break
3633 3632 yield m.span()
3634 3633 p = m.end()
3635 3634
3636 3635 matches = {}
3637 3636 copies = {}
3638 3637 def grepbody(fn, rev, body):
3639 3638 matches[rev].setdefault(fn, [])
3640 3639 m = matches[rev][fn]
3641 3640 for lnum, cstart, cend, line in matchlines(body):
3642 3641 s = linestate(line, lnum, cstart, cend)
3643 3642 m.append(s)
3644 3643
3645 3644 def difflinestates(a, b):
3646 3645 sm = difflib.SequenceMatcher(None, a, b)
3647 3646 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
3648 3647 if tag == 'insert':
3649 3648 for i in xrange(blo, bhi):
3650 3649 yield ('+', b[i])
3651 3650 elif tag == 'delete':
3652 3651 for i in xrange(alo, ahi):
3653 3652 yield ('-', a[i])
3654 3653 elif tag == 'replace':
3655 3654 for i in xrange(alo, ahi):
3656 3655 yield ('-', a[i])
3657 3656 for i in xrange(blo, bhi):
3658 3657 yield ('+', b[i])
3659 3658
3660 3659 def display(fm, fn, ctx, pstates, states):
3661 3660 rev = ctx.rev()
3662 3661 if fm.isplain():
3663 3662 formatuser = ui.shortuser
3664 3663 else:
3665 3664 formatuser = str
3666 3665 if ui.quiet:
3667 3666 datefmt = '%Y-%m-%d'
3668 3667 else:
3669 3668 datefmt = '%a %b %d %H:%M:%S %Y %1%2'
3670 3669 found = False
3671 3670 @util.cachefunc
3672 3671 def binary():
3673 3672 flog = getfile(fn)
3674 3673 return util.binary(flog.read(ctx.filenode(fn)))
3675 3674
3676 3675 fieldnamemap = {'filename': 'file', 'linenumber': 'line_number'}
3677 3676 if opts.get('all'):
3678 3677 iter = difflinestates(pstates, states)
3679 3678 else:
3680 3679 iter = [('', l) for l in states]
3681 3680 for change, l in iter:
3682 3681 fm.startitem()
3683 3682 fm.data(node=fm.hexfunc(ctx.node()))
3684 3683 cols = [
3685 3684 ('filename', fn, True),
3686 3685 ('rev', rev, True),
3687 3686 ('linenumber', l.linenum, opts.get('line_number')),
3688 3687 ]
3689 3688 if opts.get('all'):
3690 3689 cols.append(('change', change, True))
3691 3690 cols.extend([
3692 3691 ('user', formatuser(ctx.user()), opts.get('user')),
3693 3692 ('date', fm.formatdate(ctx.date(), datefmt), opts.get('date')),
3694 3693 ])
3695 3694 lastcol = next(name for name, data, cond in reversed(cols) if cond)
3696 3695 for name, data, cond in cols:
3697 3696 field = fieldnamemap.get(name, name)
3698 3697 fm.condwrite(cond, field, '%s', data, label='grep.%s' % name)
3699 3698 if cond and name != lastcol:
3700 3699 fm.plain(sep, label='grep.sep')
3701 3700 if not opts.get('files_with_matches'):
3702 3701 fm.plain(sep, label='grep.sep')
3703 3702 if not opts.get('text') and binary():
3704 3703 fm.plain(_(" Binary file matches"))
3705 3704 else:
3706 3705 displaymatches(fm.nested('texts'), l)
3707 3706 fm.plain(eol)
3708 3707 found = True
3709 3708 if opts.get('files_with_matches'):
3710 3709 break
3711 3710 return found
3712 3711
3713 3712 def displaymatches(fm, l):
3714 3713 p = 0
3715 3714 for s, e in l.findpos():
3716 3715 if p < s:
3717 3716 fm.startitem()
3718 3717 fm.write('text', '%s', l.line[p:s])
3719 3718 fm.data(matched=False)
3720 3719 fm.startitem()
3721 3720 fm.write('text', '%s', l.line[s:e], label='grep.match')
3722 3721 fm.data(matched=True)
3723 3722 p = e
3724 3723 if p < len(l.line):
3725 3724 fm.startitem()
3726 3725 fm.write('text', '%s', l.line[p:])
3727 3726 fm.data(matched=False)
3728 3727 fm.end()
3729 3728
3730 3729 skip = {}
3731 3730 revfiles = {}
3732 3731 matchfn = scmutil.match(repo[None], pats, opts)
3733 3732 found = False
3734 3733 follow = opts.get('follow')
3735 3734
3736 3735 def prep(ctx, fns):
3737 3736 rev = ctx.rev()
3738 3737 pctx = ctx.p1()
3739 3738 parent = pctx.rev()
3740 3739 matches.setdefault(rev, {})
3741 3740 matches.setdefault(parent, {})
3742 3741 files = revfiles.setdefault(rev, [])
3743 3742 for fn in fns:
3744 3743 flog = getfile(fn)
3745 3744 try:
3746 3745 fnode = ctx.filenode(fn)
3747 3746 except error.LookupError:
3748 3747 continue
3749 3748
3750 3749 copied = flog.renamed(fnode)
3751 3750 copy = follow and copied and copied[0]
3752 3751 if copy:
3753 3752 copies.setdefault(rev, {})[fn] = copy
3754 3753 if fn in skip:
3755 3754 if copy:
3756 3755 skip[copy] = True
3757 3756 continue
3758 3757 files.append(fn)
3759 3758
3760 3759 if fn not in matches[rev]:
3761 3760 grepbody(fn, rev, flog.read(fnode))
3762 3761
3763 3762 pfn = copy or fn
3764 3763 if pfn not in matches[parent]:
3765 3764 try:
3766 3765 fnode = pctx.filenode(pfn)
3767 3766 grepbody(pfn, parent, flog.read(fnode))
3768 3767 except error.LookupError:
3769 3768 pass
3770 3769
3771 3770 fm = ui.formatter('grep', opts)
3772 3771 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
3773 3772 rev = ctx.rev()
3774 3773 parent = ctx.p1().rev()
3775 3774 for fn in sorted(revfiles.get(rev, [])):
3776 3775 states = matches[rev][fn]
3777 3776 copy = copies.get(rev, {}).get(fn)
3778 3777 if fn in skip:
3779 3778 if copy:
3780 3779 skip[copy] = True
3781 3780 continue
3782 3781 pstates = matches.get(parent, {}).get(copy or fn, [])
3783 3782 if pstates or states:
3784 3783 r = display(fm, fn, ctx, pstates, states)
3785 3784 found = found or r
3786 3785 if r and not opts.get('all'):
3787 3786 skip[fn] = True
3788 3787 if copy:
3789 3788 skip[copy] = True
3790 3789 del matches[rev]
3791 3790 del revfiles[rev]
3792 3791 fm.end()
3793 3792
3794 3793 return not found
3795 3794
3796 3795 @command('heads',
3797 3796 [('r', 'rev', '',
3798 3797 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
3799 3798 ('t', 'topo', False, _('show topological heads only')),
3800 3799 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
3801 3800 ('c', 'closed', False, _('show normal and closed branch heads')),
3802 3801 ] + templateopts,
3803 3802 _('[-ct] [-r STARTREV] [REV]...'))
3804 3803 def heads(ui, repo, *branchrevs, **opts):
3805 3804 """show branch heads
3806 3805
3807 3806 With no arguments, show all open branch heads in the repository.
3808 3807 Branch heads are changesets that have no descendants on the
3809 3808 same branch. They are where development generally takes place and
3810 3809 are the usual targets for update and merge operations.
3811 3810
3812 3811 If one or more REVs are given, only open branch heads on the
3813 3812 branches associated with the specified changesets are shown. This
3814 3813 means that you can use :hg:`heads .` to see the heads on the
3815 3814 currently checked-out branch.
3816 3815
3817 3816 If -c/--closed is specified, also show branch heads marked closed
3818 3817 (see :hg:`commit --close-branch`).
3819 3818
3820 3819 If STARTREV is specified, only those heads that are descendants of
3821 3820 STARTREV will be displayed.
3822 3821
3823 3822 If -t/--topo is specified, named branch mechanics will be ignored and only
3824 3823 topological heads (changesets with no children) will be shown.
3825 3824
3826 3825 Returns 0 if matching heads are found, 1 if not.
3827 3826 """
3828 3827
3829 3828 start = None
3830 3829 if 'rev' in opts:
3831 3830 start = scmutil.revsingle(repo, opts['rev'], None).node()
3832 3831
3833 3832 if opts.get('topo'):
3834 3833 heads = [repo[h] for h in repo.heads(start)]
3835 3834 else:
3836 3835 heads = []
3837 3836 for branch in repo.branchmap():
3838 3837 heads += repo.branchheads(branch, start, opts.get('closed'))
3839 3838 heads = [repo[h] for h in heads]
3840 3839
3841 3840 if branchrevs:
3842 3841 branches = set(repo[br].branch() for br in branchrevs)
3843 3842 heads = [h for h in heads if h.branch() in branches]
3844 3843
3845 3844 if opts.get('active') and branchrevs:
3846 3845 dagheads = repo.heads(start)
3847 3846 heads = [h for h in heads if h.node() in dagheads]
3848 3847
3849 3848 if branchrevs:
3850 3849 haveheads = set(h.branch() for h in heads)
3851 3850 if branches - haveheads:
3852 3851 headless = ', '.join(b for b in branches - haveheads)
3853 3852 msg = _('no open branch heads found on branches %s')
3854 3853 if opts.get('rev'):
3855 3854 msg += _(' (started at %s)') % opts['rev']
3856 3855 ui.warn((msg + '\n') % headless)
3857 3856
3858 3857 if not heads:
3859 3858 return 1
3860 3859
3861 3860 heads = sorted(heads, key=lambda x: -x.rev())
3862 3861 displayer = cmdutil.show_changeset(ui, repo, opts)
3863 3862 for ctx in heads:
3864 3863 displayer.show(ctx)
3865 3864 displayer.close()
3866 3865
3867 3866 @command('help',
3868 3867 [('e', 'extension', None, _('show only help for extensions')),
3869 3868 ('c', 'command', None, _('show only help for commands')),
3870 3869 ('k', 'keyword', None, _('show topics matching keyword')),
3871 3870 ('s', 'system', [], _('show help for specific platform(s)')),
3872 3871 ],
3873 3872 _('[-ecks] [TOPIC]'),
3874 3873 norepo=True)
3875 3874 def help_(ui, name=None, **opts):
3876 3875 """show help for a given topic or a help overview
3877 3876
3878 3877 With no arguments, print a list of commands with short help messages.
3879 3878
3880 3879 Given a topic, extension, or command name, print help for that
3881 3880 topic.
3882 3881
3883 3882 Returns 0 if successful.
3884 3883 """
3885 3884
3886 3885 textwidth = ui.configint('ui', 'textwidth', 78)
3887 3886 termwidth = ui.termwidth() - 2
3888 3887 if textwidth <= 0 or termwidth < textwidth:
3889 3888 textwidth = termwidth
3890 3889
3891 3890 keep = opts.get('system') or []
3892 3891 if len(keep) == 0:
3893 3892 if pycompat.sysplatform.startswith('win'):
3894 3893 keep.append('windows')
3895 3894 elif pycompat.sysplatform == 'OpenVMS':
3896 3895 keep.append('vms')
3897 3896 elif pycompat.sysplatform == 'plan9':
3898 3897 keep.append('plan9')
3899 3898 else:
3900 3899 keep.append('unix')
3901 3900 keep.append(pycompat.sysplatform.lower())
3902 3901 if ui.verbose:
3903 3902 keep.append('verbose')
3904 3903
3905 3904 section = None
3906 3905 subtopic = None
3907 3906 if name and '.' in name:
3908 3907 name, remaining = name.split('.', 1)
3909 3908 remaining = encoding.lower(remaining)
3910 3909 if '.' in remaining:
3911 3910 subtopic, section = remaining.split('.', 1)
3912 3911 else:
3913 3912 if name in help.subtopics:
3914 3913 subtopic = remaining
3915 3914 else:
3916 3915 section = remaining
3917 3916
3918 3917 text = help.help_(ui, name, subtopic=subtopic, **opts)
3919 3918
3920 3919 formatted, pruned = minirst.format(text, textwidth, keep=keep,
3921 3920 section=section)
3922 3921
3923 3922 # We could have been given a weird ".foo" section without a name
3924 3923 # to look for, or we could have simply failed to found "foo.bar"
3925 3924 # because bar isn't a section of foo
3926 3925 if section and not (formatted and name):
3927 3926 raise error.Abort(_("help section not found"))
3928 3927
3929 3928 if 'verbose' in pruned:
3930 3929 keep.append('omitted')
3931 3930 else:
3932 3931 keep.append('notomitted')
3933 3932 formatted, pruned = minirst.format(text, textwidth, keep=keep,
3934 3933 section=section)
3935 3934 ui.write(formatted)
3936 3935
3937 3936
3938 3937 @command('identify|id',
3939 3938 [('r', 'rev', '',
3940 3939 _('identify the specified revision'), _('REV')),
3941 3940 ('n', 'num', None, _('show local revision number')),
3942 3941 ('i', 'id', None, _('show global revision id')),
3943 3942 ('b', 'branch', None, _('show branch')),
3944 3943 ('t', 'tags', None, _('show tags')),
3945 3944 ('B', 'bookmarks', None, _('show bookmarks')),
3946 3945 ] + remoteopts,
3947 3946 _('[-nibtB] [-r REV] [SOURCE]'),
3948 3947 optionalrepo=True)
3949 3948 def identify(ui, repo, source=None, rev=None,
3950 3949 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
3951 3950 """identify the working directory or specified revision
3952 3951
3953 3952 Print a summary identifying the repository state at REV using one or
3954 3953 two parent hash identifiers, followed by a "+" if the working
3955 3954 directory has uncommitted changes, the branch name (if not default),
3956 3955 a list of tags, and a list of bookmarks.
3957 3956
3958 3957 When REV is not given, print a summary of the current state of the
3959 3958 repository.
3960 3959
3961 3960 Specifying a path to a repository root or Mercurial bundle will
3962 3961 cause lookup to operate on that repository/bundle.
3963 3962
3964 3963 .. container:: verbose
3965 3964
3966 3965 Examples:
3967 3966
3968 3967 - generate a build identifier for the working directory::
3969 3968
3970 3969 hg id --id > build-id.dat
3971 3970
3972 3971 - find the revision corresponding to a tag::
3973 3972
3974 3973 hg id -n -r 1.3
3975 3974
3976 3975 - check the most recent revision of a remote repository::
3977 3976
3978 3977 hg id -r tip https://www.mercurial-scm.org/repo/hg/
3979 3978
3980 3979 See :hg:`log` for generating more information about specific revisions,
3981 3980 including full hash identifiers.
3982 3981
3983 3982 Returns 0 if successful.
3984 3983 """
3985 3984
3986 3985 if not repo and not source:
3987 3986 raise error.Abort(_("there is no Mercurial repository here "
3988 3987 "(.hg not found)"))
3989 3988
3990 3989 if ui.debugflag:
3991 3990 hexfunc = hex
3992 3991 else:
3993 3992 hexfunc = short
3994 3993 default = not (num or id or branch or tags or bookmarks)
3995 3994 output = []
3996 3995 revs = []
3997 3996
3998 3997 if source:
3999 3998 source, branches = hg.parseurl(ui.expandpath(source))
4000 3999 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
4001 4000 repo = peer.local()
4002 4001 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
4003 4002
4004 4003 if not repo:
4005 4004 if num or branch or tags:
4006 4005 raise error.Abort(
4007 4006 _("can't query remote revision number, branch, or tags"))
4008 4007 if not rev and revs:
4009 4008 rev = revs[0]
4010 4009 if not rev:
4011 4010 rev = "tip"
4012 4011
4013 4012 remoterev = peer.lookup(rev)
4014 4013 if default or id:
4015 4014 output = [hexfunc(remoterev)]
4016 4015
4017 4016 def getbms():
4018 4017 bms = []
4019 4018
4020 4019 if 'bookmarks' in peer.listkeys('namespaces'):
4021 4020 hexremoterev = hex(remoterev)
4022 4021 bms = [bm for bm, bmr in peer.listkeys('bookmarks').iteritems()
4023 4022 if bmr == hexremoterev]
4024 4023
4025 4024 return sorted(bms)
4026 4025
4027 4026 if bookmarks:
4028 4027 output.extend(getbms())
4029 4028 elif default and not ui.quiet:
4030 4029 # multiple bookmarks for a single parent separated by '/'
4031 4030 bm = '/'.join(getbms())
4032 4031 if bm:
4033 4032 output.append(bm)
4034 4033 else:
4035 4034 ctx = scmutil.revsingle(repo, rev, None)
4036 4035
4037 4036 if ctx.rev() is None:
4038 4037 ctx = repo[None]
4039 4038 parents = ctx.parents()
4040 4039 taglist = []
4041 4040 for p in parents:
4042 4041 taglist.extend(p.tags())
4043 4042
4044 4043 changed = ""
4045 4044 if default or id or num:
4046 4045 if (any(repo.status())
4047 4046 or any(ctx.sub(s).dirty() for s in ctx.substate)):
4048 4047 changed = '+'
4049 4048 if default or id:
4050 4049 output = ["%s%s" %
4051 4050 ('+'.join([hexfunc(p.node()) for p in parents]), changed)]
4052 4051 if num:
4053 4052 output.append("%s%s" %
4054 4053 ('+'.join([str(p.rev()) for p in parents]), changed))
4055 4054 else:
4056 4055 if default or id:
4057 4056 output = [hexfunc(ctx.node())]
4058 4057 if num:
4059 4058 output.append(str(ctx.rev()))
4060 4059 taglist = ctx.tags()
4061 4060
4062 4061 if default and not ui.quiet:
4063 4062 b = ctx.branch()
4064 4063 if b != 'default':
4065 4064 output.append("(%s)" % b)
4066 4065
4067 4066 # multiple tags for a single parent separated by '/'
4068 4067 t = '/'.join(taglist)
4069 4068 if t:
4070 4069 output.append(t)
4071 4070
4072 4071 # multiple bookmarks for a single parent separated by '/'
4073 4072 bm = '/'.join(ctx.bookmarks())
4074 4073 if bm:
4075 4074 output.append(bm)
4076 4075 else:
4077 4076 if branch:
4078 4077 output.append(ctx.branch())
4079 4078
4080 4079 if tags:
4081 4080 output.extend(taglist)
4082 4081
4083 4082 if bookmarks:
4084 4083 output.extend(ctx.bookmarks())
4085 4084
4086 4085 ui.write("%s\n" % ' '.join(output))
4087 4086
4088 4087 @command('import|patch',
4089 4088 [('p', 'strip', 1,
4090 4089 _('directory strip option for patch. This has the same '
4091 4090 'meaning as the corresponding patch option'), _('NUM')),
4092 4091 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
4093 4092 ('e', 'edit', False, _('invoke editor on commit messages')),
4094 4093 ('f', 'force', None,
4095 4094 _('skip check for outstanding uncommitted changes (DEPRECATED)')),
4096 4095 ('', 'no-commit', None,
4097 4096 _("don't commit, just update the working directory")),
4098 4097 ('', 'bypass', None,
4099 4098 _("apply patch without touching the working directory")),
4100 4099 ('', 'partial', None,
4101 4100 _('commit even if some hunks fail')),
4102 4101 ('', 'exact', None,
4103 4102 _('abort if patch would apply lossily')),
4104 4103 ('', 'prefix', '',
4105 4104 _('apply patch to subdirectory'), _('DIR')),
4106 4105 ('', 'import-branch', None,
4107 4106 _('use any branch information in patch (implied by --exact)'))] +
4108 4107 commitopts + commitopts2 + similarityopts,
4109 4108 _('[OPTION]... PATCH...'))
4110 4109 def import_(ui, repo, patch1=None, *patches, **opts):
4111 4110 """import an ordered set of patches
4112 4111
4113 4112 Import a list of patches and commit them individually (unless
4114 4113 --no-commit is specified).
4115 4114
4116 4115 To read a patch from standard input, use "-" as the patch name. If
4117 4116 a URL is specified, the patch will be downloaded from there.
4118 4117
4119 4118 Import first applies changes to the working directory (unless
4120 4119 --bypass is specified), import will abort if there are outstanding
4121 4120 changes.
4122 4121
4123 4122 Use --bypass to apply and commit patches directly to the
4124 4123 repository, without affecting the working directory. Without
4125 4124 --exact, patches will be applied on top of the working directory
4126 4125 parent revision.
4127 4126
4128 4127 You can import a patch straight from a mail message. Even patches
4129 4128 as attachments work (to use the body part, it must have type
4130 4129 text/plain or text/x-patch). From and Subject headers of email
4131 4130 message are used as default committer and commit message. All
4132 4131 text/plain body parts before first diff are added to the commit
4133 4132 message.
4134 4133
4135 4134 If the imported patch was generated by :hg:`export`, user and
4136 4135 description from patch override values from message headers and
4137 4136 body. Values given on command line with -m/--message and -u/--user
4138 4137 override these.
4139 4138
4140 4139 If --exact is specified, import will set the working directory to
4141 4140 the parent of each patch before applying it, and will abort if the
4142 4141 resulting changeset has a different ID than the one recorded in
4143 4142 the patch. This will guard against various ways that portable
4144 4143 patch formats and mail systems might fail to transfer Mercurial
4145 4144 data or metadata. See :hg:`bundle` for lossless transmission.
4146 4145
4147 4146 Use --partial to ensure a changeset will be created from the patch
4148 4147 even if some hunks fail to apply. Hunks that fail to apply will be
4149 4148 written to a <target-file>.rej file. Conflicts can then be resolved
4150 4149 by hand before :hg:`commit --amend` is run to update the created
4151 4150 changeset. This flag exists to let people import patches that
4152 4151 partially apply without losing the associated metadata (author,
4153 4152 date, description, ...).
4154 4153
4155 4154 .. note::
4156 4155
4157 4156 When no hunks apply cleanly, :hg:`import --partial` will create
4158 4157 an empty changeset, importing only the patch metadata.
4159 4158
4160 4159 With -s/--similarity, hg will attempt to discover renames and
4161 4160 copies in the patch in the same way as :hg:`addremove`.
4162 4161
4163 4162 It is possible to use external patch programs to perform the patch
4164 4163 by setting the ``ui.patch`` configuration option. For the default
4165 4164 internal tool, the fuzz can also be configured via ``patch.fuzz``.
4166 4165 See :hg:`help config` for more information about configuration
4167 4166 files and how to use these options.
4168 4167
4169 4168 See :hg:`help dates` for a list of formats valid for -d/--date.
4170 4169
4171 4170 .. container:: verbose
4172 4171
4173 4172 Examples:
4174 4173
4175 4174 - import a traditional patch from a website and detect renames::
4176 4175
4177 4176 hg import -s 80 http://example.com/bugfix.patch
4178 4177
4179 4178 - import a changeset from an hgweb server::
4180 4179
4181 4180 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
4182 4181
4183 4182 - import all the patches in an Unix-style mbox::
4184 4183
4185 4184 hg import incoming-patches.mbox
4186 4185
4187 4186 - attempt to exactly restore an exported changeset (not always
4188 4187 possible)::
4189 4188
4190 4189 hg import --exact proposed-fix.patch
4191 4190
4192 4191 - use an external tool to apply a patch which is too fuzzy for
4193 4192 the default internal tool.
4194 4193
4195 4194 hg import --config ui.patch="patch --merge" fuzzy.patch
4196 4195
4197 4196 - change the default fuzzing from 2 to a less strict 7
4198 4197
4199 4198 hg import --config ui.fuzz=7 fuzz.patch
4200 4199
4201 4200 Returns 0 on success, 1 on partial success (see --partial).
4202 4201 """
4203 4202
4204 4203 if not patch1:
4205 4204 raise error.Abort(_('need at least one patch to import'))
4206 4205
4207 4206 patches = (patch1,) + patches
4208 4207
4209 4208 date = opts.get('date')
4210 4209 if date:
4211 4210 opts['date'] = util.parsedate(date)
4212 4211
4213 4212 exact = opts.get('exact')
4214 4213 update = not opts.get('bypass')
4215 4214 if not update and opts.get('no_commit'):
4216 4215 raise error.Abort(_('cannot use --no-commit with --bypass'))
4217 4216 try:
4218 4217 sim = float(opts.get('similarity') or 0)
4219 4218 except ValueError:
4220 4219 raise error.Abort(_('similarity must be a number'))
4221 4220 if sim < 0 or sim > 100:
4222 4221 raise error.Abort(_('similarity must be between 0 and 100'))
4223 4222 if sim and not update:
4224 4223 raise error.Abort(_('cannot use --similarity with --bypass'))
4225 4224 if exact:
4226 4225 if opts.get('edit'):
4227 4226 raise error.Abort(_('cannot use --exact with --edit'))
4228 4227 if opts.get('prefix'):
4229 4228 raise error.Abort(_('cannot use --exact with --prefix'))
4230 4229
4231 4230 base = opts["base"]
4232 4231 wlock = dsguard = lock = tr = None
4233 4232 msgs = []
4234 4233 ret = 0
4235 4234
4236 4235
4237 4236 try:
4238 4237 wlock = repo.wlock()
4239 4238
4240 4239 if update:
4241 4240 cmdutil.checkunfinished(repo)
4242 4241 if (exact or not opts.get('force')):
4243 4242 cmdutil.bailifchanged(repo)
4244 4243
4245 4244 if not opts.get('no_commit'):
4246 4245 lock = repo.lock()
4247 4246 tr = repo.transaction('import')
4248 4247 else:
4249 4248 dsguard = dirstateguard.dirstateguard(repo, 'import')
4250 4249 parents = repo[None].parents()
4251 4250 for patchurl in patches:
4252 4251 if patchurl == '-':
4253 4252 ui.status(_('applying patch from stdin\n'))
4254 4253 patchfile = ui.fin
4255 4254 patchurl = 'stdin' # for error message
4256 4255 else:
4257 4256 patchurl = os.path.join(base, patchurl)
4258 4257 ui.status(_('applying %s\n') % patchurl)
4259 4258 patchfile = hg.openpath(ui, patchurl)
4260 4259
4261 4260 haspatch = False
4262 4261 for hunk in patch.split(patchfile):
4263 4262 (msg, node, rej) = cmdutil.tryimportone(ui, repo, hunk,
4264 4263 parents, opts,
4265 4264 msgs, hg.clean)
4266 4265 if msg:
4267 4266 haspatch = True
4268 4267 ui.note(msg + '\n')
4269 4268 if update or exact:
4270 4269 parents = repo[None].parents()
4271 4270 else:
4272 4271 parents = [repo[node]]
4273 4272 if rej:
4274 4273 ui.write_err(_("patch applied partially\n"))
4275 4274 ui.write_err(_("(fix the .rej files and run "
4276 4275 "`hg commit --amend`)\n"))
4277 4276 ret = 1
4278 4277 break
4279 4278
4280 4279 if not haspatch:
4281 4280 raise error.Abort(_('%s: no diffs found') % patchurl)
4282 4281
4283 4282 if tr:
4284 4283 tr.close()
4285 4284 if msgs:
4286 4285 repo.savecommitmessage('\n* * *\n'.join(msgs))
4287 4286 if dsguard:
4288 4287 dsguard.close()
4289 4288 return ret
4290 4289 finally:
4291 4290 if tr:
4292 4291 tr.release()
4293 4292 release(lock, dsguard, wlock)
4294 4293
4295 4294 @command('incoming|in',
4296 4295 [('f', 'force', None,
4297 4296 _('run even if remote repository is unrelated')),
4298 4297 ('n', 'newest-first', None, _('show newest record first')),
4299 4298 ('', 'bundle', '',
4300 4299 _('file to store the bundles into'), _('FILE')),
4301 4300 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
4302 4301 ('B', 'bookmarks', False, _("compare bookmarks")),
4303 4302 ('b', 'branch', [],
4304 4303 _('a specific branch you would like to pull'), _('BRANCH')),
4305 4304 ] + logopts + remoteopts + subrepoopts,
4306 4305 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
4307 4306 def incoming(ui, repo, source="default", **opts):
4308 4307 """show new changesets found in source
4309 4308
4310 4309 Show new changesets found in the specified path/URL or the default
4311 4310 pull location. These are the changesets that would have been pulled
4312 4311 if a pull at the time you issued this command.
4313 4312
4314 4313 See pull for valid source format details.
4315 4314
4316 4315 .. container:: verbose
4317 4316
4318 4317 With -B/--bookmarks, the result of bookmark comparison between
4319 4318 local and remote repositories is displayed. With -v/--verbose,
4320 4319 status is also displayed for each bookmark like below::
4321 4320
4322 4321 BM1 01234567890a added
4323 4322 BM2 1234567890ab advanced
4324 4323 BM3 234567890abc diverged
4325 4324 BM4 34567890abcd changed
4326 4325
4327 4326 The action taken locally when pulling depends on the
4328 4327 status of each bookmark:
4329 4328
4330 4329 :``added``: pull will create it
4331 4330 :``advanced``: pull will update it
4332 4331 :``diverged``: pull will create a divergent bookmark
4333 4332 :``changed``: result depends on remote changesets
4334 4333
4335 4334 From the point of view of pulling behavior, bookmark
4336 4335 existing only in the remote repository are treated as ``added``,
4337 4336 even if it is in fact locally deleted.
4338 4337
4339 4338 .. container:: verbose
4340 4339
4341 4340 For remote repository, using --bundle avoids downloading the
4342 4341 changesets twice if the incoming is followed by a pull.
4343 4342
4344 4343 Examples:
4345 4344
4346 4345 - show incoming changes with patches and full description::
4347 4346
4348 4347 hg incoming -vp
4349 4348
4350 4349 - show incoming changes excluding merges, store a bundle::
4351 4350
4352 4351 hg in -vpM --bundle incoming.hg
4353 4352 hg pull incoming.hg
4354 4353
4355 4354 - briefly list changes inside a bundle::
4356 4355
4357 4356 hg in changes.hg -T "{desc|firstline}\\n"
4358 4357
4359 4358 Returns 0 if there are incoming changes, 1 otherwise.
4360 4359 """
4361 4360 if opts.get('graph'):
4362 4361 cmdutil.checkunsupportedgraphflags([], opts)
4363 4362 def display(other, chlist, displayer):
4364 4363 revdag = cmdutil.graphrevs(other, chlist, opts)
4365 4364 cmdutil.displaygraph(ui, repo, revdag, displayer,
4366 4365 graphmod.asciiedges)
4367 4366
4368 4367 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
4369 4368 return 0
4370 4369
4371 4370 if opts.get('bundle') and opts.get('subrepos'):
4372 4371 raise error.Abort(_('cannot combine --bundle and --subrepos'))
4373 4372
4374 4373 if opts.get('bookmarks'):
4375 4374 source, branches = hg.parseurl(ui.expandpath(source),
4376 4375 opts.get('branch'))
4377 4376 other = hg.peer(repo, opts, source)
4378 4377 if 'bookmarks' not in other.listkeys('namespaces'):
4379 4378 ui.warn(_("remote doesn't support bookmarks\n"))
4380 4379 return 0
4381 4380 ui.status(_('comparing with %s\n') % util.hidepassword(source))
4382 4381 return bookmarks.incoming(ui, repo, other)
4383 4382
4384 4383 repo._subtoppath = ui.expandpath(source)
4385 4384 try:
4386 4385 return hg.incoming(ui, repo, source, opts)
4387 4386 finally:
4388 4387 del repo._subtoppath
4389 4388
4390 4389
4391 4390 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'),
4392 4391 norepo=True)
4393 4392 def init(ui, dest=".", **opts):
4394 4393 """create a new repository in the given directory
4395 4394
4396 4395 Initialize a new repository in the given directory. If the given
4397 4396 directory does not exist, it will be created.
4398 4397
4399 4398 If no directory is given, the current directory is used.
4400 4399
4401 4400 It is possible to specify an ``ssh://`` URL as the destination.
4402 4401 See :hg:`help urls` for more information.
4403 4402
4404 4403 Returns 0 on success.
4405 4404 """
4406 4405 hg.peer(ui, opts, ui.expandpath(dest), create=True)
4407 4406
4408 4407 @command('locate',
4409 4408 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
4410 4409 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
4411 4410 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
4412 4411 ] + walkopts,
4413 4412 _('[OPTION]... [PATTERN]...'))
4414 4413 def locate(ui, repo, *pats, **opts):
4415 4414 """locate files matching specific patterns (DEPRECATED)
4416 4415
4417 4416 Print files under Mercurial control in the working directory whose
4418 4417 names match the given patterns.
4419 4418
4420 4419 By default, this command searches all directories in the working
4421 4420 directory. To search just the current directory and its
4422 4421 subdirectories, use "--include .".
4423 4422
4424 4423 If no patterns are given to match, this command prints the names
4425 4424 of all files under Mercurial control in the working directory.
4426 4425
4427 4426 If you want to feed the output of this command into the "xargs"
4428 4427 command, use the -0 option to both this command and "xargs". This
4429 4428 will avoid the problem of "xargs" treating single filenames that
4430 4429 contain whitespace as multiple filenames.
4431 4430
4432 4431 See :hg:`help files` for a more versatile command.
4433 4432
4434 4433 Returns 0 if a match is found, 1 otherwise.
4435 4434 """
4436 4435 if opts.get('print0'):
4437 4436 end = '\0'
4438 4437 else:
4439 4438 end = '\n'
4440 4439 rev = scmutil.revsingle(repo, opts.get('rev'), None).node()
4441 4440
4442 4441 ret = 1
4443 4442 ctx = repo[rev]
4444 4443 m = scmutil.match(ctx, pats, opts, default='relglob',
4445 4444 badfn=lambda x, y: False)
4446 4445
4447 4446 for abs in ctx.matches(m):
4448 4447 if opts.get('fullpath'):
4449 4448 ui.write(repo.wjoin(abs), end)
4450 4449 else:
4451 4450 ui.write(((pats and m.rel(abs)) or abs), end)
4452 4451 ret = 0
4453 4452
4454 4453 return ret
4455 4454
4456 4455 @command('^log|history',
4457 4456 [('f', 'follow', None,
4458 4457 _('follow changeset history, or file history across copies and renames')),
4459 4458 ('', 'follow-first', None,
4460 4459 _('only follow the first parent of merge changesets (DEPRECATED)')),
4461 4460 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
4462 4461 ('C', 'copies', None, _('show copied files')),
4463 4462 ('k', 'keyword', [],
4464 4463 _('do case-insensitive search for a given text'), _('TEXT')),
4465 4464 ('r', 'rev', [], _('show the specified revision or revset'), _('REV')),
4466 4465 ('', 'removed', None, _('include revisions where files were removed')),
4467 4466 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
4468 4467 ('u', 'user', [], _('revisions committed by user'), _('USER')),
4469 4468 ('', 'only-branch', [],
4470 4469 _('show only changesets within the given named branch (DEPRECATED)'),
4471 4470 _('BRANCH')),
4472 4471 ('b', 'branch', [],
4473 4472 _('show changesets within the given named branch'), _('BRANCH')),
4474 4473 ('P', 'prune', [],
4475 4474 _('do not display revision or any of its ancestors'), _('REV')),
4476 4475 ] + logopts + walkopts,
4477 4476 _('[OPTION]... [FILE]'),
4478 4477 inferrepo=True)
4479 4478 def log(ui, repo, *pats, **opts):
4480 4479 """show revision history of entire repository or files
4481 4480
4482 4481 Print the revision history of the specified files or the entire
4483 4482 project.
4484 4483
4485 4484 If no revision range is specified, the default is ``tip:0`` unless
4486 4485 --follow is set, in which case the working directory parent is
4487 4486 used as the starting revision.
4488 4487
4489 4488 File history is shown without following rename or copy history of
4490 4489 files. Use -f/--follow with a filename to follow history across
4491 4490 renames and copies. --follow without a filename will only show
4492 4491 ancestors or descendants of the starting revision.
4493 4492
4494 4493 By default this command prints revision number and changeset id,
4495 4494 tags, non-trivial parents, user, date and time, and a summary for
4496 4495 each commit. When the -v/--verbose switch is used, the list of
4497 4496 changed files and full commit message are shown.
4498 4497
4499 4498 With --graph the revisions are shown as an ASCII art DAG with the most
4500 4499 recent changeset at the top.
4501 4500 'o' is a changeset, '@' is a working directory parent, 'x' is obsolete,
4502 4501 and '+' represents a fork where the changeset from the lines below is a
4503 4502 parent of the 'o' merge on the same line.
4504 4503
4505 4504 .. note::
4506 4505
4507 4506 :hg:`log --patch` may generate unexpected diff output for merge
4508 4507 changesets, as it will only compare the merge changeset against
4509 4508 its first parent. Also, only files different from BOTH parents
4510 4509 will appear in files:.
4511 4510
4512 4511 .. note::
4513 4512
4514 4513 For performance reasons, :hg:`log FILE` may omit duplicate changes
4515 4514 made on branches and will not show removals or mode changes. To
4516 4515 see all such changes, use the --removed switch.
4517 4516
4518 4517 .. container:: verbose
4519 4518
4520 4519 Some examples:
4521 4520
4522 4521 - changesets with full descriptions and file lists::
4523 4522
4524 4523 hg log -v
4525 4524
4526 4525 - changesets ancestral to the working directory::
4527 4526
4528 4527 hg log -f
4529 4528
4530 4529 - last 10 commits on the current branch::
4531 4530
4532 4531 hg log -l 10 -b .
4533 4532
4534 4533 - changesets showing all modifications of a file, including removals::
4535 4534
4536 4535 hg log --removed file.c
4537 4536
4538 4537 - all changesets that touch a directory, with diffs, excluding merges::
4539 4538
4540 4539 hg log -Mp lib/
4541 4540
4542 4541 - all revision numbers that match a keyword::
4543 4542
4544 4543 hg log -k bug --template "{rev}\\n"
4545 4544
4546 4545 - the full hash identifier of the working directory parent::
4547 4546
4548 4547 hg log -r . --template "{node}\\n"
4549 4548
4550 4549 - list available log templates::
4551 4550
4552 4551 hg log -T list
4553 4552
4554 4553 - check if a given changeset is included in a tagged release::
4555 4554
4556 4555 hg log -r "a21ccf and ancestor(1.9)"
4557 4556
4558 4557 - find all changesets by some user in a date range::
4559 4558
4560 4559 hg log -k alice -d "may 2008 to jul 2008"
4561 4560
4562 4561 - summary of all changesets after the last tag::
4563 4562
4564 4563 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
4565 4564
4566 4565 See :hg:`help dates` for a list of formats valid for -d/--date.
4567 4566
4568 4567 See :hg:`help revisions` and :hg:`help revsets` for more about
4569 4568 specifying and ordering revisions.
4570 4569
4571 4570 See :hg:`help templates` for more about pre-packaged styles and
4572 4571 specifying custom templates.
4573 4572
4574 4573 Returns 0 on success.
4575 4574
4576 4575 """
4577 4576 if opts.get('follow') and opts.get('rev'):
4578 4577 opts['rev'] = [revset.formatspec('reverse(::%lr)', opts.get('rev'))]
4579 4578 del opts['follow']
4580 4579
4581 4580 if opts.get('graph'):
4582 4581 return cmdutil.graphlog(ui, repo, *pats, **opts)
4583 4582
4584 4583 revs, expr, filematcher = cmdutil.getlogrevs(repo, pats, opts)
4585 4584 limit = cmdutil.loglimit(opts)
4586 4585 count = 0
4587 4586
4588 4587 getrenamed = None
4589 4588 if opts.get('copies'):
4590 4589 endrev = None
4591 4590 if opts.get('rev'):
4592 4591 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
4593 4592 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
4594 4593
4595 4594 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
4596 4595 for rev in revs:
4597 4596 if count == limit:
4598 4597 break
4599 4598 ctx = repo[rev]
4600 4599 copies = None
4601 4600 if getrenamed is not None and rev:
4602 4601 copies = []
4603 4602 for fn in ctx.files():
4604 4603 rename = getrenamed(fn, rev)
4605 4604 if rename:
4606 4605 copies.append((fn, rename[0]))
4607 4606 if filematcher:
4608 4607 revmatchfn = filematcher(ctx.rev())
4609 4608 else:
4610 4609 revmatchfn = None
4611 4610 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
4612 4611 if displayer.flush(ctx):
4613 4612 count += 1
4614 4613
4615 4614 displayer.close()
4616 4615
4617 4616 @command('manifest',
4618 4617 [('r', 'rev', '', _('revision to display'), _('REV')),
4619 4618 ('', 'all', False, _("list files from all revisions"))]
4620 4619 + formatteropts,
4621 4620 _('[-r REV]'))
4622 4621 def manifest(ui, repo, node=None, rev=None, **opts):
4623 4622 """output the current or given revision of the project manifest
4624 4623
4625 4624 Print a list of version controlled files for the given revision.
4626 4625 If no revision is given, the first parent of the working directory
4627 4626 is used, or the null revision if no revision is checked out.
4628 4627
4629 4628 With -v, print file permissions, symlink and executable bits.
4630 4629 With --debug, print file revision hashes.
4631 4630
4632 4631 If option --all is specified, the list of all files from all revisions
4633 4632 is printed. This includes deleted and renamed files.
4634 4633
4635 4634 Returns 0 on success.
4636 4635 """
4637 4636
4638 4637 fm = ui.formatter('manifest', opts)
4639 4638
4640 4639 if opts.get('all'):
4641 4640 if rev or node:
4642 4641 raise error.Abort(_("can't specify a revision with --all"))
4643 4642
4644 4643 res = []
4645 4644 prefix = "data/"
4646 4645 suffix = ".i"
4647 4646 plen = len(prefix)
4648 4647 slen = len(suffix)
4649 4648 with repo.lock():
4650 4649 for fn, b, size in repo.store.datafiles():
4651 4650 if size != 0 and fn[-slen:] == suffix and fn[:plen] == prefix:
4652 4651 res.append(fn[plen:-slen])
4653 4652 for f in res:
4654 4653 fm.startitem()
4655 4654 fm.write("path", '%s\n', f)
4656 4655 fm.end()
4657 4656 return
4658 4657
4659 4658 if rev and node:
4660 4659 raise error.Abort(_("please specify just one revision"))
4661 4660
4662 4661 if not node:
4663 4662 node = rev
4664 4663
4665 4664 char = {'l': '@', 'x': '*', '': ''}
4666 4665 mode = {'l': '644', 'x': '755', '': '644'}
4667 4666 ctx = scmutil.revsingle(repo, node)
4668 4667 mf = ctx.manifest()
4669 4668 for f in ctx:
4670 4669 fm.startitem()
4671 4670 fl = ctx[f].flags()
4672 4671 fm.condwrite(ui.debugflag, 'hash', '%s ', hex(mf[f]))
4673 4672 fm.condwrite(ui.verbose, 'mode type', '%s %1s ', mode[fl], char[fl])
4674 4673 fm.write('path', '%s\n', f)
4675 4674 fm.end()
4676 4675
4677 4676 @command('^merge',
4678 4677 [('f', 'force', None,
4679 4678 _('force a merge including outstanding changes (DEPRECATED)')),
4680 4679 ('r', 'rev', '', _('revision to merge'), _('REV')),
4681 4680 ('P', 'preview', None,
4682 4681 _('review revisions to merge (no merge is performed)'))
4683 4682 ] + mergetoolopts,
4684 4683 _('[-P] [[-r] REV]'))
4685 4684 def merge(ui, repo, node=None, **opts):
4686 4685 """merge another revision into working directory
4687 4686
4688 4687 The current working directory is updated with all changes made in
4689 4688 the requested revision since the last common predecessor revision.
4690 4689
4691 4690 Files that changed between either parent are marked as changed for
4692 4691 the next commit and a commit must be performed before any further
4693 4692 updates to the repository are allowed. The next commit will have
4694 4693 two parents.
4695 4694
4696 4695 ``--tool`` can be used to specify the merge tool used for file
4697 4696 merges. It overrides the HGMERGE environment variable and your
4698 4697 configuration files. See :hg:`help merge-tools` for options.
4699 4698
4700 4699 If no revision is specified, the working directory's parent is a
4701 4700 head revision, and the current branch contains exactly one other
4702 4701 head, the other head is merged with by default. Otherwise, an
4703 4702 explicit revision with which to merge with must be provided.
4704 4703
4705 4704 See :hg:`help resolve` for information on handling file conflicts.
4706 4705
4707 4706 To undo an uncommitted merge, use :hg:`update --clean .` which
4708 4707 will check out a clean copy of the original merge parent, losing
4709 4708 all changes.
4710 4709
4711 4710 Returns 0 on success, 1 if there are unresolved files.
4712 4711 """
4713 4712
4714 4713 if opts.get('rev') and node:
4715 4714 raise error.Abort(_("please specify just one revision"))
4716 4715 if not node:
4717 4716 node = opts.get('rev')
4718 4717
4719 4718 if node:
4720 4719 node = scmutil.revsingle(repo, node).node()
4721 4720
4722 4721 if not node:
4723 4722 node = repo[destutil.destmerge(repo)].node()
4724 4723
4725 4724 if opts.get('preview'):
4726 4725 # find nodes that are ancestors of p2 but not of p1
4727 4726 p1 = repo.lookup('.')
4728 4727 p2 = repo.lookup(node)
4729 4728 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4730 4729
4731 4730 displayer = cmdutil.show_changeset(ui, repo, opts)
4732 4731 for node in nodes:
4733 4732 displayer.show(repo[node])
4734 4733 displayer.close()
4735 4734 return 0
4736 4735
4737 4736 try:
4738 4737 # ui.forcemerge is an internal variable, do not document
4739 4738 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''), 'merge')
4740 4739 force = opts.get('force')
4741 4740 labels = ['working copy', 'merge rev']
4742 4741 return hg.merge(repo, node, force=force, mergeforce=force,
4743 4742 labels=labels)
4744 4743 finally:
4745 4744 ui.setconfig('ui', 'forcemerge', '', 'merge')
4746 4745
4747 4746 @command('outgoing|out',
4748 4747 [('f', 'force', None, _('run even when the destination is unrelated')),
4749 4748 ('r', 'rev', [],
4750 4749 _('a changeset intended to be included in the destination'), _('REV')),
4751 4750 ('n', 'newest-first', None, _('show newest record first')),
4752 4751 ('B', 'bookmarks', False, _('compare bookmarks')),
4753 4752 ('b', 'branch', [], _('a specific branch you would like to push'),
4754 4753 _('BRANCH')),
4755 4754 ] + logopts + remoteopts + subrepoopts,
4756 4755 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
4757 4756 def outgoing(ui, repo, dest=None, **opts):
4758 4757 """show changesets not found in the destination
4759 4758
4760 4759 Show changesets not found in the specified destination repository
4761 4760 or the default push location. These are the changesets that would
4762 4761 be pushed if a push was requested.
4763 4762
4764 4763 See pull for details of valid destination formats.
4765 4764
4766 4765 .. container:: verbose
4767 4766
4768 4767 With -B/--bookmarks, the result of bookmark comparison between
4769 4768 local and remote repositories is displayed. With -v/--verbose,
4770 4769 status is also displayed for each bookmark like below::
4771 4770
4772 4771 BM1 01234567890a added
4773 4772 BM2 deleted
4774 4773 BM3 234567890abc advanced
4775 4774 BM4 34567890abcd diverged
4776 4775 BM5 4567890abcde changed
4777 4776
4778 4777 The action taken when pushing depends on the
4779 4778 status of each bookmark:
4780 4779
4781 4780 :``added``: push with ``-B`` will create it
4782 4781 :``deleted``: push with ``-B`` will delete it
4783 4782 :``advanced``: push will update it
4784 4783 :``diverged``: push with ``-B`` will update it
4785 4784 :``changed``: push with ``-B`` will update it
4786 4785
4787 4786 From the point of view of pushing behavior, bookmarks
4788 4787 existing only in the remote repository are treated as
4789 4788 ``deleted``, even if it is in fact added remotely.
4790 4789
4791 4790 Returns 0 if there are outgoing changes, 1 otherwise.
4792 4791 """
4793 4792 if opts.get('graph'):
4794 4793 cmdutil.checkunsupportedgraphflags([], opts)
4795 4794 o, other = hg._outgoing(ui, repo, dest, opts)
4796 4795 if not o:
4797 4796 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4798 4797 return
4799 4798
4800 4799 revdag = cmdutil.graphrevs(repo, o, opts)
4801 4800 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
4802 4801 cmdutil.displaygraph(ui, repo, revdag, displayer, graphmod.asciiedges)
4803 4802 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4804 4803 return 0
4805 4804
4806 4805 if opts.get('bookmarks'):
4807 4806 dest = ui.expandpath(dest or 'default-push', dest or 'default')
4808 4807 dest, branches = hg.parseurl(dest, opts.get('branch'))
4809 4808 other = hg.peer(repo, opts, dest)
4810 4809 if 'bookmarks' not in other.listkeys('namespaces'):
4811 4810 ui.warn(_("remote doesn't support bookmarks\n"))
4812 4811 return 0
4813 4812 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
4814 4813 return bookmarks.outgoing(ui, repo, other)
4815 4814
4816 4815 repo._subtoppath = ui.expandpath(dest or 'default-push', dest or 'default')
4817 4816 try:
4818 4817 return hg.outgoing(ui, repo, dest, opts)
4819 4818 finally:
4820 4819 del repo._subtoppath
4821 4820
4822 4821 @command('parents',
4823 4822 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
4824 4823 ] + templateopts,
4825 4824 _('[-r REV] [FILE]'),
4826 4825 inferrepo=True)
4827 4826 def parents(ui, repo, file_=None, **opts):
4828 4827 """show the parents of the working directory or revision (DEPRECATED)
4829 4828
4830 4829 Print the working directory's parent revisions. If a revision is
4831 4830 given via -r/--rev, the parent of that revision will be printed.
4832 4831 If a file argument is given, the revision in which the file was
4833 4832 last changed (before the working directory revision or the
4834 4833 argument to --rev if given) is printed.
4835 4834
4836 4835 This command is equivalent to::
4837 4836
4838 4837 hg log -r "p1()+p2()" or
4839 4838 hg log -r "p1(REV)+p2(REV)" or
4840 4839 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
4841 4840 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
4842 4841
4843 4842 See :hg:`summary` and :hg:`help revsets` for related information.
4844 4843
4845 4844 Returns 0 on success.
4846 4845 """
4847 4846
4848 4847 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
4849 4848
4850 4849 if file_:
4851 4850 m = scmutil.match(ctx, (file_,), opts)
4852 4851 if m.anypats() or len(m.files()) != 1:
4853 4852 raise error.Abort(_('can only specify an explicit filename'))
4854 4853 file_ = m.files()[0]
4855 4854 filenodes = []
4856 4855 for cp in ctx.parents():
4857 4856 if not cp:
4858 4857 continue
4859 4858 try:
4860 4859 filenodes.append(cp.filenode(file_))
4861 4860 except error.LookupError:
4862 4861 pass
4863 4862 if not filenodes:
4864 4863 raise error.Abort(_("'%s' not found in manifest!") % file_)
4865 4864 p = []
4866 4865 for fn in filenodes:
4867 4866 fctx = repo.filectx(file_, fileid=fn)
4868 4867 p.append(fctx.node())
4869 4868 else:
4870 4869 p = [cp.node() for cp in ctx.parents()]
4871 4870
4872 4871 displayer = cmdutil.show_changeset(ui, repo, opts)
4873 4872 for n in p:
4874 4873 if n != nullid:
4875 4874 displayer.show(repo[n])
4876 4875 displayer.close()
4877 4876
4878 4877 @command('paths', formatteropts, _('[NAME]'), optionalrepo=True)
4879 4878 def paths(ui, repo, search=None, **opts):
4880 4879 """show aliases for remote repositories
4881 4880
4882 4881 Show definition of symbolic path name NAME. If no name is given,
4883 4882 show definition of all available names.
4884 4883
4885 4884 Option -q/--quiet suppresses all output when searching for NAME
4886 4885 and shows only the path names when listing all definitions.
4887 4886
4888 4887 Path names are defined in the [paths] section of your
4889 4888 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
4890 4889 repository, ``.hg/hgrc`` is used, too.
4891 4890
4892 4891 The path names ``default`` and ``default-push`` have a special
4893 4892 meaning. When performing a push or pull operation, they are used
4894 4893 as fallbacks if no location is specified on the command-line.
4895 4894 When ``default-push`` is set, it will be used for push and
4896 4895 ``default`` will be used for pull; otherwise ``default`` is used
4897 4896 as the fallback for both. When cloning a repository, the clone
4898 4897 source is written as ``default`` in ``.hg/hgrc``.
4899 4898
4900 4899 .. note::
4901 4900
4902 4901 ``default`` and ``default-push`` apply to all inbound (e.g.
4903 4902 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
4904 4903 and :hg:`bundle`) operations.
4905 4904
4906 4905 See :hg:`help urls` for more information.
4907 4906
4908 4907 Returns 0 on success.
4909 4908 """
4910 4909 if search:
4911 4910 pathitems = [(name, path) for name, path in ui.paths.iteritems()
4912 4911 if name == search]
4913 4912 else:
4914 4913 pathitems = sorted(ui.paths.iteritems())
4915 4914
4916 4915 fm = ui.formatter('paths', opts)
4917 4916 if fm.isplain():
4918 4917 hidepassword = util.hidepassword
4919 4918 else:
4920 4919 hidepassword = str
4921 4920 if ui.quiet:
4922 4921 namefmt = '%s\n'
4923 4922 else:
4924 4923 namefmt = '%s = '
4925 4924 showsubopts = not search and not ui.quiet
4926 4925
4927 4926 for name, path in pathitems:
4928 4927 fm.startitem()
4929 4928 fm.condwrite(not search, 'name', namefmt, name)
4930 4929 fm.condwrite(not ui.quiet, 'url', '%s\n', hidepassword(path.rawloc))
4931 4930 for subopt, value in sorted(path.suboptions.items()):
4932 4931 assert subopt not in ('name', 'url')
4933 4932 if showsubopts:
4934 4933 fm.plain('%s:%s = ' % (name, subopt))
4935 4934 fm.condwrite(showsubopts, subopt, '%s\n', value)
4936 4935
4937 4936 fm.end()
4938 4937
4939 4938 if search and not pathitems:
4940 4939 if not ui.quiet:
4941 4940 ui.warn(_("not found!\n"))
4942 4941 return 1
4943 4942 else:
4944 4943 return 0
4945 4944
4946 4945 @command('phase',
4947 4946 [('p', 'public', False, _('set changeset phase to public')),
4948 4947 ('d', 'draft', False, _('set changeset phase to draft')),
4949 4948 ('s', 'secret', False, _('set changeset phase to secret')),
4950 4949 ('f', 'force', False, _('allow to move boundary backward')),
4951 4950 ('r', 'rev', [], _('target revision'), _('REV')),
4952 4951 ],
4953 4952 _('[-p|-d|-s] [-f] [-r] [REV...]'))
4954 4953 def phase(ui, repo, *revs, **opts):
4955 4954 """set or show the current phase name
4956 4955
4957 4956 With no argument, show the phase name of the current revision(s).
4958 4957
4959 4958 With one of -p/--public, -d/--draft or -s/--secret, change the
4960 4959 phase value of the specified revisions.
4961 4960
4962 4961 Unless -f/--force is specified, :hg:`phase` won't move changeset from a
4963 4962 lower phase to an higher phase. Phases are ordered as follows::
4964 4963
4965 4964 public < draft < secret
4966 4965
4967 4966 Returns 0 on success, 1 if some phases could not be changed.
4968 4967
4969 4968 (For more information about the phases concept, see :hg:`help phases`.)
4970 4969 """
4971 4970 # search for a unique phase argument
4972 4971 targetphase = None
4973 4972 for idx, name in enumerate(phases.phasenames):
4974 4973 if opts[name]:
4975 4974 if targetphase is not None:
4976 4975 raise error.Abort(_('only one phase can be specified'))
4977 4976 targetphase = idx
4978 4977
4979 4978 # look for specified revision
4980 4979 revs = list(revs)
4981 4980 revs.extend(opts['rev'])
4982 4981 if not revs:
4983 4982 # display both parents as the second parent phase can influence
4984 4983 # the phase of a merge commit
4985 4984 revs = [c.rev() for c in repo[None].parents()]
4986 4985
4987 4986 revs = scmutil.revrange(repo, revs)
4988 4987
4989 4988 lock = None
4990 4989 ret = 0
4991 4990 if targetphase is None:
4992 4991 # display
4993 4992 for r in revs:
4994 4993 ctx = repo[r]
4995 4994 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
4996 4995 else:
4997 4996 tr = None
4998 4997 lock = repo.lock()
4999 4998 try:
5000 4999 tr = repo.transaction("phase")
5001 5000 # set phase
5002 5001 if not revs:
5003 5002 raise error.Abort(_('empty revision set'))
5004 5003 nodes = [repo[r].node() for r in revs]
5005 5004 # moving revision from public to draft may hide them
5006 5005 # We have to check result on an unfiltered repository
5007 5006 unfi = repo.unfiltered()
5008 5007 getphase = unfi._phasecache.phase
5009 5008 olddata = [getphase(unfi, r) for r in unfi]
5010 5009 phases.advanceboundary(repo, tr, targetphase, nodes)
5011 5010 if opts['force']:
5012 5011 phases.retractboundary(repo, tr, targetphase, nodes)
5013 5012 tr.close()
5014 5013 finally:
5015 5014 if tr is not None:
5016 5015 tr.release()
5017 5016 lock.release()
5018 5017 getphase = unfi._phasecache.phase
5019 5018 newdata = [getphase(unfi, r) for r in unfi]
5020 5019 changes = sum(newdata[r] != olddata[r] for r in unfi)
5021 5020 cl = unfi.changelog
5022 5021 rejected = [n for n in nodes
5023 5022 if newdata[cl.rev(n)] < targetphase]
5024 5023 if rejected:
5025 5024 ui.warn(_('cannot move %i changesets to a higher '
5026 5025 'phase, use --force\n') % len(rejected))
5027 5026 ret = 1
5028 5027 if changes:
5029 5028 msg = _('phase changed for %i changesets\n') % changes
5030 5029 if ret:
5031 5030 ui.status(msg)
5032 5031 else:
5033 5032 ui.note(msg)
5034 5033 else:
5035 5034 ui.warn(_('no phases changed\n'))
5036 5035 return ret
5037 5036
5038 5037 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
5039 5038 """Run after a changegroup has been added via pull/unbundle
5040 5039
5041 5040 This takes arguments below:
5042 5041
5043 5042 :modheads: change of heads by pull/unbundle
5044 5043 :optupdate: updating working directory is needed or not
5045 5044 :checkout: update destination revision (or None to default destination)
5046 5045 :brev: a name, which might be a bookmark to be activated after updating
5047 5046 """
5048 5047 if modheads == 0:
5049 5048 return
5050 5049 if optupdate:
5051 5050 try:
5052 5051 return hg.updatetotally(ui, repo, checkout, brev)
5053 5052 except error.UpdateAbort as inst:
5054 5053 msg = _("not updating: %s") % str(inst)
5055 5054 hint = inst.hint
5056 5055 raise error.UpdateAbort(msg, hint=hint)
5057 5056 if modheads > 1:
5058 5057 currentbranchheads = len(repo.branchheads())
5059 5058 if currentbranchheads == modheads:
5060 5059 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
5061 5060 elif currentbranchheads > 1:
5062 5061 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to "
5063 5062 "merge)\n"))
5064 5063 else:
5065 5064 ui.status(_("(run 'hg heads' to see heads)\n"))
5066 5065 else:
5067 5066 ui.status(_("(run 'hg update' to get a working copy)\n"))
5068 5067
5069 5068 @command('^pull',
5070 5069 [('u', 'update', None,
5071 5070 _('update to new branch head if changesets were pulled')),
5072 5071 ('f', 'force', None, _('run even when remote repository is unrelated')),
5073 5072 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
5074 5073 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
5075 5074 ('b', 'branch', [], _('a specific branch you would like to pull'),
5076 5075 _('BRANCH')),
5077 5076 ] + remoteopts,
5078 5077 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
5079 5078 def pull(ui, repo, source="default", **opts):
5080 5079 """pull changes from the specified source
5081 5080
5082 5081 Pull changes from a remote repository to a local one.
5083 5082
5084 5083 This finds all changes from the repository at the specified path
5085 5084 or URL and adds them to a local repository (the current one unless
5086 5085 -R is specified). By default, this does not update the copy of the
5087 5086 project in the working directory.
5088 5087
5089 5088 Use :hg:`incoming` if you want to see what would have been added
5090 5089 by a pull at the time you issued this command. If you then decide
5091 5090 to add those changes to the repository, you should use :hg:`pull
5092 5091 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
5093 5092
5094 5093 If SOURCE is omitted, the 'default' path will be used.
5095 5094 See :hg:`help urls` for more information.
5096 5095
5097 5096 Specifying bookmark as ``.`` is equivalent to specifying the active
5098 5097 bookmark's name.
5099 5098
5100 5099 Returns 0 on success, 1 if an update had unresolved files.
5101 5100 """
5102 5101 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
5103 5102 ui.status(_('pulling from %s\n') % util.hidepassword(source))
5104 5103 other = hg.peer(repo, opts, source)
5105 5104 try:
5106 5105 revs, checkout = hg.addbranchrevs(repo, other, branches,
5107 5106 opts.get('rev'))
5108 5107
5109 5108
5110 5109 pullopargs = {}
5111 5110 if opts.get('bookmark'):
5112 5111 if not revs:
5113 5112 revs = []
5114 5113 # The list of bookmark used here is not the one used to actually
5115 5114 # update the bookmark name. This can result in the revision pulled
5116 5115 # not ending up with the name of the bookmark because of a race
5117 5116 # condition on the server. (See issue 4689 for details)
5118 5117 remotebookmarks = other.listkeys('bookmarks')
5119 5118 pullopargs['remotebookmarks'] = remotebookmarks
5120 5119 for b in opts['bookmark']:
5121 5120 b = repo._bookmarks.expandname(b)
5122 5121 if b not in remotebookmarks:
5123 5122 raise error.Abort(_('remote bookmark %s not found!') % b)
5124 5123 revs.append(remotebookmarks[b])
5125 5124
5126 5125 if revs:
5127 5126 try:
5128 5127 # When 'rev' is a bookmark name, we cannot guarantee that it
5129 5128 # will be updated with that name because of a race condition
5130 5129 # server side. (See issue 4689 for details)
5131 5130 oldrevs = revs
5132 5131 revs = [] # actually, nodes
5133 5132 for r in oldrevs:
5134 5133 node = other.lookup(r)
5135 5134 revs.append(node)
5136 5135 if r == checkout:
5137 5136 checkout = node
5138 5137 except error.CapabilityError:
5139 5138 err = _("other repository doesn't support revision lookup, "
5140 5139 "so a rev cannot be specified.")
5141 5140 raise error.Abort(err)
5142 5141
5143 5142 pullopargs.update(opts.get('opargs', {}))
5144 5143 modheads = exchange.pull(repo, other, heads=revs,
5145 5144 force=opts.get('force'),
5146 5145 bookmarks=opts.get('bookmark', ()),
5147 5146 opargs=pullopargs).cgresult
5148 5147
5149 5148 # brev is a name, which might be a bookmark to be activated at
5150 5149 # the end of the update. In other words, it is an explicit
5151 5150 # destination of the update
5152 5151 brev = None
5153 5152
5154 5153 if checkout:
5155 5154 checkout = str(repo.changelog.rev(checkout))
5156 5155
5157 5156 # order below depends on implementation of
5158 5157 # hg.addbranchrevs(). opts['bookmark'] is ignored,
5159 5158 # because 'checkout' is determined without it.
5160 5159 if opts.get('rev'):
5161 5160 brev = opts['rev'][0]
5162 5161 elif opts.get('branch'):
5163 5162 brev = opts['branch'][0]
5164 5163 else:
5165 5164 brev = branches[0]
5166 5165 repo._subtoppath = source
5167 5166 try:
5168 5167 ret = postincoming(ui, repo, modheads, opts.get('update'),
5169 5168 checkout, brev)
5170 5169
5171 5170 finally:
5172 5171 del repo._subtoppath
5173 5172
5174 5173 finally:
5175 5174 other.close()
5176 5175 return ret
5177 5176
5178 5177 @command('^push',
5179 5178 [('f', 'force', None, _('force push')),
5180 5179 ('r', 'rev', [],
5181 5180 _('a changeset intended to be included in the destination'),
5182 5181 _('REV')),
5183 5182 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
5184 5183 ('b', 'branch', [],
5185 5184 _('a specific branch you would like to push'), _('BRANCH')),
5186 5185 ('', 'new-branch', False, _('allow pushing a new branch')),
5187 5186 ] + remoteopts,
5188 5187 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
5189 5188 def push(ui, repo, dest=None, **opts):
5190 5189 """push changes to the specified destination
5191 5190
5192 5191 Push changesets from the local repository to the specified
5193 5192 destination.
5194 5193
5195 5194 This operation is symmetrical to pull: it is identical to a pull
5196 5195 in the destination repository from the current one.
5197 5196
5198 5197 By default, push will not allow creation of new heads at the
5199 5198 destination, since multiple heads would make it unclear which head
5200 5199 to use. In this situation, it is recommended to pull and merge
5201 5200 before pushing.
5202 5201
5203 5202 Use --new-branch if you want to allow push to create a new named
5204 5203 branch that is not present at the destination. This allows you to
5205 5204 only create a new branch without forcing other changes.
5206 5205
5207 5206 .. note::
5208 5207
5209 5208 Extra care should be taken with the -f/--force option,
5210 5209 which will push all new heads on all branches, an action which will
5211 5210 almost always cause confusion for collaborators.
5212 5211
5213 5212 If -r/--rev is used, the specified revision and all its ancestors
5214 5213 will be pushed to the remote repository.
5215 5214
5216 5215 If -B/--bookmark is used, the specified bookmarked revision, its
5217 5216 ancestors, and the bookmark will be pushed to the remote
5218 5217 repository. Specifying ``.`` is equivalent to specifying the active
5219 5218 bookmark's name.
5220 5219
5221 5220 Please see :hg:`help urls` for important details about ``ssh://``
5222 5221 URLs. If DESTINATION is omitted, a default path will be used.
5223 5222
5224 5223 Returns 0 if push was successful, 1 if nothing to push.
5225 5224 """
5226 5225
5227 5226 if opts.get('bookmark'):
5228 5227 ui.setconfig('bookmarks', 'pushing', opts['bookmark'], 'push')
5229 5228 for b in opts['bookmark']:
5230 5229 # translate -B options to -r so changesets get pushed
5231 5230 b = repo._bookmarks.expandname(b)
5232 5231 if b in repo._bookmarks:
5233 5232 opts.setdefault('rev', []).append(b)
5234 5233 else:
5235 5234 # if we try to push a deleted bookmark, translate it to null
5236 5235 # this lets simultaneous -r, -b options continue working
5237 5236 opts.setdefault('rev', []).append("null")
5238 5237
5239 5238 path = ui.paths.getpath(dest, default=('default-push', 'default'))
5240 5239 if not path:
5241 5240 raise error.Abort(_('default repository not configured!'),
5242 5241 hint=_("see 'hg help config.paths'"))
5243 5242 dest = path.pushloc or path.loc
5244 5243 branches = (path.branch, opts.get('branch') or [])
5245 5244 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
5246 5245 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
5247 5246 other = hg.peer(repo, opts, dest)
5248 5247
5249 5248 if revs:
5250 5249 revs = [repo.lookup(r) for r in scmutil.revrange(repo, revs)]
5251 5250 if not revs:
5252 5251 raise error.Abort(_("specified revisions evaluate to an empty set"),
5253 5252 hint=_("use different revision arguments"))
5254 5253 elif path.pushrev:
5255 5254 # It doesn't make any sense to specify ancestor revisions. So limit
5256 5255 # to DAG heads to make discovery simpler.
5257 5256 expr = revset.formatspec('heads(%r)', path.pushrev)
5258 5257 revs = scmutil.revrange(repo, [expr])
5259 5258 revs = [repo[rev].node() for rev in revs]
5260 5259 if not revs:
5261 5260 raise error.Abort(_('default push revset for path evaluates to an '
5262 5261 'empty set'))
5263 5262
5264 5263 repo._subtoppath = dest
5265 5264 try:
5266 5265 # push subrepos depth-first for coherent ordering
5267 5266 c = repo['']
5268 5267 subs = c.substate # only repos that are committed
5269 5268 for s in sorted(subs):
5270 5269 result = c.sub(s).push(opts)
5271 5270 if result == 0:
5272 5271 return not result
5273 5272 finally:
5274 5273 del repo._subtoppath
5275 5274 pushop = exchange.push(repo, other, opts.get('force'), revs=revs,
5276 5275 newbranch=opts.get('new_branch'),
5277 5276 bookmarks=opts.get('bookmark', ()),
5278 5277 opargs=opts.get('opargs'))
5279 5278
5280 5279 result = not pushop.cgresult
5281 5280
5282 5281 if pushop.bkresult is not None:
5283 5282 if pushop.bkresult == 2:
5284 5283 result = 2
5285 5284 elif not result and pushop.bkresult:
5286 5285 result = 2
5287 5286
5288 5287 return result
5289 5288
5290 5289 @command('recover', [])
5291 5290 def recover(ui, repo):
5292 5291 """roll back an interrupted transaction
5293 5292
5294 5293 Recover from an interrupted commit or pull.
5295 5294
5296 5295 This command tries to fix the repository status after an
5297 5296 interrupted operation. It should only be necessary when Mercurial
5298 5297 suggests it.
5299 5298
5300 5299 Returns 0 if successful, 1 if nothing to recover or verify fails.
5301 5300 """
5302 5301 if repo.recover():
5303 5302 return hg.verify(repo)
5304 5303 return 1
5305 5304
5306 5305 @command('^remove|rm',
5307 5306 [('A', 'after', None, _('record delete for missing files')),
5308 5307 ('f', 'force', None,
5309 5308 _('forget added files, delete modified files')),
5310 5309 ] + subrepoopts + walkopts,
5311 5310 _('[OPTION]... FILE...'),
5312 5311 inferrepo=True)
5313 5312 def remove(ui, repo, *pats, **opts):
5314 5313 """remove the specified files on the next commit
5315 5314
5316 5315 Schedule the indicated files for removal from the current branch.
5317 5316
5318 5317 This command schedules the files to be removed at the next commit.
5319 5318 To undo a remove before that, see :hg:`revert`. To undo added
5320 5319 files, see :hg:`forget`.
5321 5320
5322 5321 .. container:: verbose
5323 5322
5324 5323 -A/--after can be used to remove only files that have already
5325 5324 been deleted, -f/--force can be used to force deletion, and -Af
5326 5325 can be used to remove files from the next revision without
5327 5326 deleting them from the working directory.
5328 5327
5329 5328 The following table details the behavior of remove for different
5330 5329 file states (columns) and option combinations (rows). The file
5331 5330 states are Added [A], Clean [C], Modified [M] and Missing [!]
5332 5331 (as reported by :hg:`status`). The actions are Warn, Remove
5333 5332 (from branch) and Delete (from disk):
5334 5333
5335 5334 ========= == == == ==
5336 5335 opt/state A C M !
5337 5336 ========= == == == ==
5338 5337 none W RD W R
5339 5338 -f R RD RD R
5340 5339 -A W W W R
5341 5340 -Af R R R R
5342 5341 ========= == == == ==
5343 5342
5344 5343 .. note::
5345 5344
5346 5345 :hg:`remove` never deletes files in Added [A] state from the
5347 5346 working directory, not even if ``--force`` is specified.
5348 5347
5349 5348 Returns 0 on success, 1 if any warnings encountered.
5350 5349 """
5351 5350
5352 5351 after, force = opts.get('after'), opts.get('force')
5353 5352 if not pats and not after:
5354 5353 raise error.Abort(_('no files specified'))
5355 5354
5356 5355 m = scmutil.match(repo[None], pats, opts)
5357 5356 subrepos = opts.get('subrepos')
5358 5357 return cmdutil.remove(ui, repo, m, "", after, force, subrepos)
5359 5358
5360 5359 @command('rename|move|mv',
5361 5360 [('A', 'after', None, _('record a rename that has already occurred')),
5362 5361 ('f', 'force', None, _('forcibly copy over an existing managed file')),
5363 5362 ] + walkopts + dryrunopts,
5364 5363 _('[OPTION]... SOURCE... DEST'))
5365 5364 def rename(ui, repo, *pats, **opts):
5366 5365 """rename files; equivalent of copy + remove
5367 5366
5368 5367 Mark dest as copies of sources; mark sources for deletion. If dest
5369 5368 is a directory, copies are put in that directory. If dest is a
5370 5369 file, there can only be one source.
5371 5370
5372 5371 By default, this command copies the contents of files as they
5373 5372 exist in the working directory. If invoked with -A/--after, the
5374 5373 operation is recorded, but no copying is performed.
5375 5374
5376 5375 This command takes effect at the next commit. To undo a rename
5377 5376 before that, see :hg:`revert`.
5378 5377
5379 5378 Returns 0 on success, 1 if errors are encountered.
5380 5379 """
5381 5380 with repo.wlock(False):
5382 5381 return cmdutil.copy(ui, repo, pats, opts, rename=True)
5383 5382
5384 5383 @command('resolve',
5385 5384 [('a', 'all', None, _('select all unresolved files')),
5386 5385 ('l', 'list', None, _('list state of files needing merge')),
5387 5386 ('m', 'mark', None, _('mark files as resolved')),
5388 5387 ('u', 'unmark', None, _('mark files as unresolved')),
5389 5388 ('n', 'no-status', None, _('hide status prefix'))]
5390 5389 + mergetoolopts + walkopts + formatteropts,
5391 5390 _('[OPTION]... [FILE]...'),
5392 5391 inferrepo=True)
5393 5392 def resolve(ui, repo, *pats, **opts):
5394 5393 """redo merges or set/view the merge status of files
5395 5394
5396 5395 Merges with unresolved conflicts are often the result of
5397 5396 non-interactive merging using the ``internal:merge`` configuration
5398 5397 setting, or a command-line merge tool like ``diff3``. The resolve
5399 5398 command is used to manage the files involved in a merge, after
5400 5399 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
5401 5400 working directory must have two parents). See :hg:`help
5402 5401 merge-tools` for information on configuring merge tools.
5403 5402
5404 5403 The resolve command can be used in the following ways:
5405 5404
5406 5405 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
5407 5406 files, discarding any previous merge attempts. Re-merging is not
5408 5407 performed for files already marked as resolved. Use ``--all/-a``
5409 5408 to select all unresolved files. ``--tool`` can be used to specify
5410 5409 the merge tool used for the given files. It overrides the HGMERGE
5411 5410 environment variable and your configuration files. Previous file
5412 5411 contents are saved with a ``.orig`` suffix.
5413 5412
5414 5413 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
5415 5414 (e.g. after having manually fixed-up the files). The default is
5416 5415 to mark all unresolved files.
5417 5416
5418 5417 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
5419 5418 default is to mark all resolved files.
5420 5419
5421 5420 - :hg:`resolve -l`: list files which had or still have conflicts.
5422 5421 In the printed list, ``U`` = unresolved and ``R`` = resolved.
5423 5422
5424 5423 .. note::
5425 5424
5426 5425 Mercurial will not let you commit files with unresolved merge
5427 5426 conflicts. You must use :hg:`resolve -m ...` before you can
5428 5427 commit after a conflicting merge.
5429 5428
5430 5429 Returns 0 on success, 1 if any files fail a resolve attempt.
5431 5430 """
5432 5431
5433 5432 flaglist = 'all mark unmark list no_status'.split()
5434 5433 all, mark, unmark, show, nostatus = \
5435 5434 [opts.get(o) for o in flaglist]
5436 5435
5437 5436 if (show and (mark or unmark)) or (mark and unmark):
5438 5437 raise error.Abort(_("too many options specified"))
5439 5438 if pats and all:
5440 5439 raise error.Abort(_("can't specify --all and patterns"))
5441 5440 if not (all or pats or show or mark or unmark):
5442 5441 raise error.Abort(_('no files or directories specified'),
5443 5442 hint=('use --all to re-merge all unresolved files'))
5444 5443
5445 5444 if show:
5446 5445 fm = ui.formatter('resolve', opts)
5447 5446 ms = mergemod.mergestate.read(repo)
5448 5447 m = scmutil.match(repo[None], pats, opts)
5449 5448 for f in ms:
5450 5449 if not m(f):
5451 5450 continue
5452 5451 l = 'resolve.' + {'u': 'unresolved', 'r': 'resolved',
5453 5452 'd': 'driverresolved'}[ms[f]]
5454 5453 fm.startitem()
5455 5454 fm.condwrite(not nostatus, 'status', '%s ', ms[f].upper(), label=l)
5456 5455 fm.write('path', '%s\n', f, label=l)
5457 5456 fm.end()
5458 5457 return 0
5459 5458
5460 5459 with repo.wlock():
5461 5460 ms = mergemod.mergestate.read(repo)
5462 5461
5463 5462 if not (ms.active() or repo.dirstate.p2() != nullid):
5464 5463 raise error.Abort(
5465 5464 _('resolve command not applicable when not merging'))
5466 5465
5467 5466 wctx = repo[None]
5468 5467
5469 5468 if ms.mergedriver and ms.mdstate() == 'u':
5470 5469 proceed = mergemod.driverpreprocess(repo, ms, wctx)
5471 5470 ms.commit()
5472 5471 # allow mark and unmark to go through
5473 5472 if not mark and not unmark and not proceed:
5474 5473 return 1
5475 5474
5476 5475 m = scmutil.match(wctx, pats, opts)
5477 5476 ret = 0
5478 5477 didwork = False
5479 5478 runconclude = False
5480 5479
5481 5480 tocomplete = []
5482 5481 for f in ms:
5483 5482 if not m(f):
5484 5483 continue
5485 5484
5486 5485 didwork = True
5487 5486
5488 5487 # don't let driver-resolved files be marked, and run the conclude
5489 5488 # step if asked to resolve
5490 5489 if ms[f] == "d":
5491 5490 exact = m.exact(f)
5492 5491 if mark:
5493 5492 if exact:
5494 5493 ui.warn(_('not marking %s as it is driver-resolved\n')
5495 5494 % f)
5496 5495 elif unmark:
5497 5496 if exact:
5498 5497 ui.warn(_('not unmarking %s as it is driver-resolved\n')
5499 5498 % f)
5500 5499 else:
5501 5500 runconclude = True
5502 5501 continue
5503 5502
5504 5503 if mark:
5505 5504 ms.mark(f, "r")
5506 5505 elif unmark:
5507 5506 ms.mark(f, "u")
5508 5507 else:
5509 5508 # backup pre-resolve (merge uses .orig for its own purposes)
5510 5509 a = repo.wjoin(f)
5511 5510 try:
5512 5511 util.copyfile(a, a + ".resolve")
5513 5512 except (IOError, OSError) as inst:
5514 5513 if inst.errno != errno.ENOENT:
5515 5514 raise
5516 5515
5517 5516 try:
5518 5517 # preresolve file
5519 5518 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
5520 5519 'resolve')
5521 5520 complete, r = ms.preresolve(f, wctx)
5522 5521 if not complete:
5523 5522 tocomplete.append(f)
5524 5523 elif r:
5525 5524 ret = 1
5526 5525 finally:
5527 5526 ui.setconfig('ui', 'forcemerge', '', 'resolve')
5528 5527 ms.commit()
5529 5528
5530 5529 # replace filemerge's .orig file with our resolve file, but only
5531 5530 # for merges that are complete
5532 5531 if complete:
5533 5532 try:
5534 5533 util.rename(a + ".resolve",
5535 5534 scmutil.origpath(ui, repo, a))
5536 5535 except OSError as inst:
5537 5536 if inst.errno != errno.ENOENT:
5538 5537 raise
5539 5538
5540 5539 for f in tocomplete:
5541 5540 try:
5542 5541 # resolve file
5543 5542 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
5544 5543 'resolve')
5545 5544 r = ms.resolve(f, wctx)
5546 5545 if r:
5547 5546 ret = 1
5548 5547 finally:
5549 5548 ui.setconfig('ui', 'forcemerge', '', 'resolve')
5550 5549 ms.commit()
5551 5550
5552 5551 # replace filemerge's .orig file with our resolve file
5553 5552 a = repo.wjoin(f)
5554 5553 try:
5555 5554 util.rename(a + ".resolve", scmutil.origpath(ui, repo, a))
5556 5555 except OSError as inst:
5557 5556 if inst.errno != errno.ENOENT:
5558 5557 raise
5559 5558
5560 5559 ms.commit()
5561 5560 ms.recordactions()
5562 5561
5563 5562 if not didwork and pats:
5564 5563 hint = None
5565 5564 if not any([p for p in pats if p.find(':') >= 0]):
5566 5565 pats = ['path:%s' % p for p in pats]
5567 5566 m = scmutil.match(wctx, pats, opts)
5568 5567 for f in ms:
5569 5568 if not m(f):
5570 5569 continue
5571 5570 flags = ''.join(['-%s ' % o[0] for o in flaglist
5572 5571 if opts.get(o)])
5573 5572 hint = _("(try: hg resolve %s%s)\n") % (
5574 5573 flags,
5575 5574 ' '.join(pats))
5576 5575 break
5577 5576 ui.warn(_("arguments do not match paths that need resolving\n"))
5578 5577 if hint:
5579 5578 ui.warn(hint)
5580 5579 elif ms.mergedriver and ms.mdstate() != 's':
5581 5580 # run conclude step when either a driver-resolved file is requested
5582 5581 # or there are no driver-resolved files
5583 5582 # we can't use 'ret' to determine whether any files are unresolved
5584 5583 # because we might not have tried to resolve some
5585 5584 if ((runconclude or not list(ms.driverresolved()))
5586 5585 and not list(ms.unresolved())):
5587 5586 proceed = mergemod.driverconclude(repo, ms, wctx)
5588 5587 ms.commit()
5589 5588 if not proceed:
5590 5589 return 1
5591 5590
5592 5591 # Nudge users into finishing an unfinished operation
5593 5592 unresolvedf = list(ms.unresolved())
5594 5593 driverresolvedf = list(ms.driverresolved())
5595 5594 if not unresolvedf and not driverresolvedf:
5596 5595 ui.status(_('(no more unresolved files)\n'))
5597 5596 cmdutil.checkafterresolved(repo)
5598 5597 elif not unresolvedf:
5599 5598 ui.status(_('(no more unresolved files -- '
5600 5599 'run "hg resolve --all" to conclude)\n'))
5601 5600
5602 5601 return ret
5603 5602
5604 5603 @command('revert',
5605 5604 [('a', 'all', None, _('revert all changes when no arguments given')),
5606 5605 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5607 5606 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
5608 5607 ('C', 'no-backup', None, _('do not save backup copies of files')),
5609 5608 ('i', 'interactive', None,
5610 5609 _('interactively select the changes (EXPERIMENTAL)')),
5611 5610 ] + walkopts + dryrunopts,
5612 5611 _('[OPTION]... [-r REV] [NAME]...'))
5613 5612 def revert(ui, repo, *pats, **opts):
5614 5613 """restore files to their checkout state
5615 5614
5616 5615 .. note::
5617 5616
5618 5617 To check out earlier revisions, you should use :hg:`update REV`.
5619 5618 To cancel an uncommitted merge (and lose your changes),
5620 5619 use :hg:`update --clean .`.
5621 5620
5622 5621 With no revision specified, revert the specified files or directories
5623 5622 to the contents they had in the parent of the working directory.
5624 5623 This restores the contents of files to an unmodified
5625 5624 state and unschedules adds, removes, copies, and renames. If the
5626 5625 working directory has two parents, you must explicitly specify a
5627 5626 revision.
5628 5627
5629 5628 Using the -r/--rev or -d/--date options, revert the given files or
5630 5629 directories to their states as of a specific revision. Because
5631 5630 revert does not change the working directory parents, this will
5632 5631 cause these files to appear modified. This can be helpful to "back
5633 5632 out" some or all of an earlier change. See :hg:`backout` for a
5634 5633 related method.
5635 5634
5636 5635 Modified files are saved with a .orig suffix before reverting.
5637 5636 To disable these backups, use --no-backup. It is possible to store
5638 5637 the backup files in a custom directory relative to the root of the
5639 5638 repository by setting the ``ui.origbackuppath`` configuration
5640 5639 option.
5641 5640
5642 5641 See :hg:`help dates` for a list of formats valid for -d/--date.
5643 5642
5644 5643 See :hg:`help backout` for a way to reverse the effect of an
5645 5644 earlier changeset.
5646 5645
5647 5646 Returns 0 on success.
5648 5647 """
5649 5648
5650 5649 if opts.get("date"):
5651 5650 if opts.get("rev"):
5652 5651 raise error.Abort(_("you can't specify a revision and a date"))
5653 5652 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
5654 5653
5655 5654 parent, p2 = repo.dirstate.parents()
5656 5655 if not opts.get('rev') and p2 != nullid:
5657 5656 # revert after merge is a trap for new users (issue2915)
5658 5657 raise error.Abort(_('uncommitted merge with no revision specified'),
5659 5658 hint=_("use 'hg update' or see 'hg help revert'"))
5660 5659
5661 5660 ctx = scmutil.revsingle(repo, opts.get('rev'))
5662 5661
5663 5662 if (not (pats or opts.get('include') or opts.get('exclude') or
5664 5663 opts.get('all') or opts.get('interactive'))):
5665 5664 msg = _("no files or directories specified")
5666 5665 if p2 != nullid:
5667 5666 hint = _("uncommitted merge, use --all to discard all changes,"
5668 5667 " or 'hg update -C .' to abort the merge")
5669 5668 raise error.Abort(msg, hint=hint)
5670 5669 dirty = any(repo.status())
5671 5670 node = ctx.node()
5672 5671 if node != parent:
5673 5672 if dirty:
5674 5673 hint = _("uncommitted changes, use --all to discard all"
5675 5674 " changes, or 'hg update %s' to update") % ctx.rev()
5676 5675 else:
5677 5676 hint = _("use --all to revert all files,"
5678 5677 " or 'hg update %s' to update") % ctx.rev()
5679 5678 elif dirty:
5680 5679 hint = _("uncommitted changes, use --all to discard all changes")
5681 5680 else:
5682 5681 hint = _("use --all to revert all files")
5683 5682 raise error.Abort(msg, hint=hint)
5684 5683
5685 5684 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats, **opts)
5686 5685
5687 5686 @command('rollback', dryrunopts +
5688 5687 [('f', 'force', False, _('ignore safety measures'))])
5689 5688 def rollback(ui, repo, **opts):
5690 5689 """roll back the last transaction (DANGEROUS) (DEPRECATED)
5691 5690
5692 5691 Please use :hg:`commit --amend` instead of rollback to correct
5693 5692 mistakes in the last commit.
5694 5693
5695 5694 This command should be used with care. There is only one level of
5696 5695 rollback, and there is no way to undo a rollback. It will also
5697 5696 restore the dirstate at the time of the last transaction, losing
5698 5697 any dirstate changes since that time. This command does not alter
5699 5698 the working directory.
5700 5699
5701 5700 Transactions are used to encapsulate the effects of all commands
5702 5701 that create new changesets or propagate existing changesets into a
5703 5702 repository.
5704 5703
5705 5704 .. container:: verbose
5706 5705
5707 5706 For example, the following commands are transactional, and their
5708 5707 effects can be rolled back:
5709 5708
5710 5709 - commit
5711 5710 - import
5712 5711 - pull
5713 5712 - push (with this repository as the destination)
5714 5713 - unbundle
5715 5714
5716 5715 To avoid permanent data loss, rollback will refuse to rollback a
5717 5716 commit transaction if it isn't checked out. Use --force to
5718 5717 override this protection.
5719 5718
5720 5719 The rollback command can be entirely disabled by setting the
5721 5720 ``ui.rollback`` configuration setting to false. If you're here
5722 5721 because you want to use rollback and it's disabled, you can
5723 5722 re-enable the command by setting ``ui.rollback`` to true.
5724 5723
5725 5724 This command is not intended for use on public repositories. Once
5726 5725 changes are visible for pull by other users, rolling a transaction
5727 5726 back locally is ineffective (someone else may already have pulled
5728 5727 the changes). Furthermore, a race is possible with readers of the
5729 5728 repository; for example an in-progress pull from the repository
5730 5729 may fail if a rollback is performed.
5731 5730
5732 5731 Returns 0 on success, 1 if no rollback data is available.
5733 5732 """
5734 5733 if not ui.configbool('ui', 'rollback', True):
5735 5734 raise error.Abort(_('rollback is disabled because it is unsafe'),
5736 5735 hint=('see `hg help -v rollback` for information'))
5737 5736 return repo.rollback(dryrun=opts.get('dry_run'),
5738 5737 force=opts.get('force'))
5739 5738
5740 5739 @command('root', [])
5741 5740 def root(ui, repo):
5742 5741 """print the root (top) of the current working directory
5743 5742
5744 5743 Print the root directory of the current repository.
5745 5744
5746 5745 Returns 0 on success.
5747 5746 """
5748 5747 ui.write(repo.root + "\n")
5749 5748
5750 5749 @command('^serve',
5751 5750 [('A', 'accesslog', '', _('name of access log file to write to'),
5752 5751 _('FILE')),
5753 5752 ('d', 'daemon', None, _('run server in background')),
5754 5753 ('', 'daemon-postexec', [], _('used internally by daemon mode')),
5755 5754 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
5756 5755 # use string type, then we can check if something was passed
5757 5756 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
5758 5757 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
5759 5758 _('ADDR')),
5760 5759 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
5761 5760 _('PREFIX')),
5762 5761 ('n', 'name', '',
5763 5762 _('name to show in web pages (default: working directory)'), _('NAME')),
5764 5763 ('', 'web-conf', '',
5765 5764 _("name of the hgweb config file (see 'hg help hgweb')"), _('FILE')),
5766 5765 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
5767 5766 _('FILE')),
5768 5767 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
5769 5768 ('', 'stdio', None, _('for remote clients')),
5770 5769 ('', 'cmdserver', '', _('for remote clients'), _('MODE')),
5771 5770 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
5772 5771 ('', 'style', '', _('template style to use'), _('STYLE')),
5773 5772 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
5774 5773 ('', 'certificate', '', _('SSL certificate file'), _('FILE'))],
5775 5774 _('[OPTION]...'),
5776 5775 optionalrepo=True)
5777 5776 def serve(ui, repo, **opts):
5778 5777 """start stand-alone webserver
5779 5778
5780 5779 Start a local HTTP repository browser and pull server. You can use
5781 5780 this for ad-hoc sharing and browsing of repositories. It is
5782 5781 recommended to use a real web server to serve a repository for
5783 5782 longer periods of time.
5784 5783
5785 5784 Please note that the server does not implement access control.
5786 5785 This means that, by default, anybody can read from the server and
5787 5786 nobody can write to it by default. Set the ``web.allow_push``
5788 5787 option to ``*`` to allow everybody to push to the server. You
5789 5788 should use a real web server if you need to authenticate users.
5790 5789
5791 5790 By default, the server logs accesses to stdout and errors to
5792 5791 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
5793 5792 files.
5794 5793
5795 5794 To have the server choose a free port number to listen on, specify
5796 5795 a port number of 0; in this case, the server will print the port
5797 5796 number it uses.
5798 5797
5799 5798 Returns 0 on success.
5800 5799 """
5801 5800
5802 5801 if opts["stdio"] and opts["cmdserver"]:
5803 5802 raise error.Abort(_("cannot use --stdio with --cmdserver"))
5804 5803
5805 5804 if opts["stdio"]:
5806 5805 if repo is None:
5807 5806 raise error.RepoError(_("there is no Mercurial repository here"
5808 5807 " (.hg not found)"))
5809 5808 s = sshserver.sshserver(ui, repo)
5810 5809 s.serve_forever()
5811 5810
5812 5811 service = server.createservice(ui, repo, opts)
5813 5812 return server.runservice(opts, initfn=service.init, runfn=service.run)
5814 5813
5815 5814 @command('^status|st',
5816 5815 [('A', 'all', None, _('show status of all files')),
5817 5816 ('m', 'modified', None, _('show only modified files')),
5818 5817 ('a', 'added', None, _('show only added files')),
5819 5818 ('r', 'removed', None, _('show only removed files')),
5820 5819 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
5821 5820 ('c', 'clean', None, _('show only files without changes')),
5822 5821 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
5823 5822 ('i', 'ignored', None, _('show only ignored files')),
5824 5823 ('n', 'no-status', None, _('hide status prefix')),
5825 5824 ('C', 'copies', None, _('show source of copied files')),
5826 5825 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
5827 5826 ('', 'rev', [], _('show difference from revision'), _('REV')),
5828 5827 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
5829 5828 ] + walkopts + subrepoopts + formatteropts,
5830 5829 _('[OPTION]... [FILE]...'),
5831 5830 inferrepo=True)
5832 5831 def status(ui, repo, *pats, **opts):
5833 5832 """show changed files in the working directory
5834 5833
5835 5834 Show status of files in the repository. If names are given, only
5836 5835 files that match are shown. Files that are clean or ignored or
5837 5836 the source of a copy/move operation, are not listed unless
5838 5837 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
5839 5838 Unless options described with "show only ..." are given, the
5840 5839 options -mardu are used.
5841 5840
5842 5841 Option -q/--quiet hides untracked (unknown and ignored) files
5843 5842 unless explicitly requested with -u/--unknown or -i/--ignored.
5844 5843
5845 5844 .. note::
5846 5845
5847 5846 :hg:`status` may appear to disagree with diff if permissions have
5848 5847 changed or a merge has occurred. The standard diff format does
5849 5848 not report permission changes and diff only reports changes
5850 5849 relative to one merge parent.
5851 5850
5852 5851 If one revision is given, it is used as the base revision.
5853 5852 If two revisions are given, the differences between them are
5854 5853 shown. The --change option can also be used as a shortcut to list
5855 5854 the changed files of a revision from its first parent.
5856 5855
5857 5856 The codes used to show the status of files are::
5858 5857
5859 5858 M = modified
5860 5859 A = added
5861 5860 R = removed
5862 5861 C = clean
5863 5862 ! = missing (deleted by non-hg command, but still tracked)
5864 5863 ? = not tracked
5865 5864 I = ignored
5866 5865 = origin of the previous file (with --copies)
5867 5866
5868 5867 .. container:: verbose
5869 5868
5870 5869 Examples:
5871 5870
5872 5871 - show changes in the working directory relative to a
5873 5872 changeset::
5874 5873
5875 5874 hg status --rev 9353
5876 5875
5877 5876 - show changes in the working directory relative to the
5878 5877 current directory (see :hg:`help patterns` for more information)::
5879 5878
5880 5879 hg status re:
5881 5880
5882 5881 - show all changes including copies in an existing changeset::
5883 5882
5884 5883 hg status --copies --change 9353
5885 5884
5886 5885 - get a NUL separated list of added files, suitable for xargs::
5887 5886
5888 5887 hg status -an0
5889 5888
5890 5889 Returns 0 on success.
5891 5890 """
5892 5891
5893 5892 revs = opts.get('rev')
5894 5893 change = opts.get('change')
5895 5894
5896 5895 if revs and change:
5897 5896 msg = _('cannot specify --rev and --change at the same time')
5898 5897 raise error.Abort(msg)
5899 5898 elif change:
5900 5899 node2 = scmutil.revsingle(repo, change, None).node()
5901 5900 node1 = repo[node2].p1().node()
5902 5901 else:
5903 5902 node1, node2 = scmutil.revpair(repo, revs)
5904 5903
5905 5904 if pats:
5906 5905 cwd = repo.getcwd()
5907 5906 else:
5908 5907 cwd = ''
5909 5908
5910 5909 if opts.get('print0'):
5911 5910 end = '\0'
5912 5911 else:
5913 5912 end = '\n'
5914 5913 copy = {}
5915 5914 states = 'modified added removed deleted unknown ignored clean'.split()
5916 5915 show = [k for k in states if opts.get(k)]
5917 5916 if opts.get('all'):
5918 5917 show += ui.quiet and (states[:4] + ['clean']) or states
5919 5918 if not show:
5920 5919 if ui.quiet:
5921 5920 show = states[:4]
5922 5921 else:
5923 5922 show = states[:5]
5924 5923
5925 5924 m = scmutil.match(repo[node2], pats, opts)
5926 5925 stat = repo.status(node1, node2, m,
5927 5926 'ignored' in show, 'clean' in show, 'unknown' in show,
5928 5927 opts.get('subrepos'))
5929 5928 changestates = zip(states, 'MAR!?IC', stat)
5930 5929
5931 5930 if (opts.get('all') or opts.get('copies')
5932 5931 or ui.configbool('ui', 'statuscopies')) and not opts.get('no_status'):
5933 5932 copy = copies.pathcopies(repo[node1], repo[node2], m)
5934 5933
5935 5934 fm = ui.formatter('status', opts)
5936 5935 fmt = '%s' + end
5937 5936 showchar = not opts.get('no_status')
5938 5937
5939 5938 for state, char, files in changestates:
5940 5939 if state in show:
5941 5940 label = 'status.' + state
5942 5941 for f in files:
5943 5942 fm.startitem()
5944 5943 fm.condwrite(showchar, 'status', '%s ', char, label=label)
5945 5944 fm.write('path', fmt, repo.pathto(f, cwd), label=label)
5946 5945 if f in copy:
5947 5946 fm.write("copy", ' %s' + end, repo.pathto(copy[f], cwd),
5948 5947 label='status.copied')
5949 5948 fm.end()
5950 5949
5951 5950 @command('^summary|sum',
5952 5951 [('', 'remote', None, _('check for push and pull'))], '[--remote]')
5953 5952 def summary(ui, repo, **opts):
5954 5953 """summarize working directory state
5955 5954
5956 5955 This generates a brief summary of the working directory state,
5957 5956 including parents, branch, commit status, phase and available updates.
5958 5957
5959 5958 With the --remote option, this will check the default paths for
5960 5959 incoming and outgoing changes. This can be time-consuming.
5961 5960
5962 5961 Returns 0 on success.
5963 5962 """
5964 5963
5965 5964 ctx = repo[None]
5966 5965 parents = ctx.parents()
5967 5966 pnode = parents[0].node()
5968 5967 marks = []
5969 5968
5970 5969 ms = None
5971 5970 try:
5972 5971 ms = mergemod.mergestate.read(repo)
5973 5972 except error.UnsupportedMergeRecords as e:
5974 5973 s = ' '.join(e.recordtypes)
5975 5974 ui.warn(
5976 5975 _('warning: merge state has unsupported record types: %s\n') % s)
5977 5976 unresolved = 0
5978 5977 else:
5979 5978 unresolved = [f for f in ms if ms[f] == 'u']
5980 5979
5981 5980 for p in parents:
5982 5981 # label with log.changeset (instead of log.parent) since this
5983 5982 # shows a working directory parent *changeset*:
5984 5983 # i18n: column positioning for "hg summary"
5985 5984 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
5986 5985 label='log.changeset changeset.%s' % p.phasestr())
5987 5986 ui.write(' '.join(p.tags()), label='log.tag')
5988 5987 if p.bookmarks():
5989 5988 marks.extend(p.bookmarks())
5990 5989 if p.rev() == -1:
5991 5990 if not len(repo):
5992 5991 ui.write(_(' (empty repository)'))
5993 5992 else:
5994 5993 ui.write(_(' (no revision checked out)'))
5995 5994 ui.write('\n')
5996 5995 if p.description():
5997 5996 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
5998 5997 label='log.summary')
5999 5998
6000 5999 branch = ctx.branch()
6001 6000 bheads = repo.branchheads(branch)
6002 6001 # i18n: column positioning for "hg summary"
6003 6002 m = _('branch: %s\n') % branch
6004 6003 if branch != 'default':
6005 6004 ui.write(m, label='log.branch')
6006 6005 else:
6007 6006 ui.status(m, label='log.branch')
6008 6007
6009 6008 if marks:
6010 6009 active = repo._activebookmark
6011 6010 # i18n: column positioning for "hg summary"
6012 6011 ui.write(_('bookmarks:'), label='log.bookmark')
6013 6012 if active is not None:
6014 6013 if active in marks:
6015 6014 ui.write(' *' + active, label=activebookmarklabel)
6016 6015 marks.remove(active)
6017 6016 else:
6018 6017 ui.write(' [%s]' % active, label=activebookmarklabel)
6019 6018 for m in marks:
6020 6019 ui.write(' ' + m, label='log.bookmark')
6021 6020 ui.write('\n', label='log.bookmark')
6022 6021
6023 6022 status = repo.status(unknown=True)
6024 6023
6025 6024 c = repo.dirstate.copies()
6026 6025 copied, renamed = [], []
6027 6026 for d, s in c.iteritems():
6028 6027 if s in status.removed:
6029 6028 status.removed.remove(s)
6030 6029 renamed.append(d)
6031 6030 else:
6032 6031 copied.append(d)
6033 6032 if d in status.added:
6034 6033 status.added.remove(d)
6035 6034
6036 6035 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
6037 6036
6038 6037 labels = [(ui.label(_('%d modified'), 'status.modified'), status.modified),
6039 6038 (ui.label(_('%d added'), 'status.added'), status.added),
6040 6039 (ui.label(_('%d removed'), 'status.removed'), status.removed),
6041 6040 (ui.label(_('%d renamed'), 'status.copied'), renamed),
6042 6041 (ui.label(_('%d copied'), 'status.copied'), copied),
6043 6042 (ui.label(_('%d deleted'), 'status.deleted'), status.deleted),
6044 6043 (ui.label(_('%d unknown'), 'status.unknown'), status.unknown),
6045 6044 (ui.label(_('%d unresolved'), 'resolve.unresolved'), unresolved),
6046 6045 (ui.label(_('%d subrepos'), 'status.modified'), subs)]
6047 6046 t = []
6048 6047 for l, s in labels:
6049 6048 if s:
6050 6049 t.append(l % len(s))
6051 6050
6052 6051 t = ', '.join(t)
6053 6052 cleanworkdir = False
6054 6053
6055 6054 if repo.vfs.exists('graftstate'):
6056 6055 t += _(' (graft in progress)')
6057 6056 if repo.vfs.exists('updatestate'):
6058 6057 t += _(' (interrupted update)')
6059 6058 elif len(parents) > 1:
6060 6059 t += _(' (merge)')
6061 6060 elif branch != parents[0].branch():
6062 6061 t += _(' (new branch)')
6063 6062 elif (parents[0].closesbranch() and
6064 6063 pnode in repo.branchheads(branch, closed=True)):
6065 6064 t += _(' (head closed)')
6066 6065 elif not (status.modified or status.added or status.removed or renamed or
6067 6066 copied or subs):
6068 6067 t += _(' (clean)')
6069 6068 cleanworkdir = True
6070 6069 elif pnode not in bheads:
6071 6070 t += _(' (new branch head)')
6072 6071
6073 6072 if parents:
6074 6073 pendingphase = max(p.phase() for p in parents)
6075 6074 else:
6076 6075 pendingphase = phases.public
6077 6076
6078 6077 if pendingphase > phases.newcommitphase(ui):
6079 6078 t += ' (%s)' % phases.phasenames[pendingphase]
6080 6079
6081 6080 if cleanworkdir:
6082 6081 # i18n: column positioning for "hg summary"
6083 6082 ui.status(_('commit: %s\n') % t.strip())
6084 6083 else:
6085 6084 # i18n: column positioning for "hg summary"
6086 6085 ui.write(_('commit: %s\n') % t.strip())
6087 6086
6088 6087 # all ancestors of branch heads - all ancestors of parent = new csets
6089 6088 new = len(repo.changelog.findmissing([pctx.node() for pctx in parents],
6090 6089 bheads))
6091 6090
6092 6091 if new == 0:
6093 6092 # i18n: column positioning for "hg summary"
6094 6093 ui.status(_('update: (current)\n'))
6095 6094 elif pnode not in bheads:
6096 6095 # i18n: column positioning for "hg summary"
6097 6096 ui.write(_('update: %d new changesets (update)\n') % new)
6098 6097 else:
6099 6098 # i18n: column positioning for "hg summary"
6100 6099 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
6101 6100 (new, len(bheads)))
6102 6101
6103 6102 t = []
6104 6103 draft = len(repo.revs('draft()'))
6105 6104 if draft:
6106 6105 t.append(_('%d draft') % draft)
6107 6106 secret = len(repo.revs('secret()'))
6108 6107 if secret:
6109 6108 t.append(_('%d secret') % secret)
6110 6109
6111 6110 if draft or secret:
6112 6111 ui.status(_('phases: %s\n') % ', '.join(t))
6113 6112
6114 6113 if obsolete.isenabled(repo, obsolete.createmarkersopt):
6115 6114 for trouble in ("unstable", "divergent", "bumped"):
6116 6115 numtrouble = len(repo.revs(trouble + "()"))
6117 6116 # We write all the possibilities to ease translation
6118 6117 troublemsg = {
6119 6118 "unstable": _("unstable: %d changesets"),
6120 6119 "divergent": _("divergent: %d changesets"),
6121 6120 "bumped": _("bumped: %d changesets"),
6122 6121 }
6123 6122 if numtrouble > 0:
6124 6123 ui.status(troublemsg[trouble] % numtrouble + "\n")
6125 6124
6126 6125 cmdutil.summaryhooks(ui, repo)
6127 6126
6128 6127 if opts.get('remote'):
6129 6128 needsincoming, needsoutgoing = True, True
6130 6129 else:
6131 6130 needsincoming, needsoutgoing = False, False
6132 6131 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
6133 6132 if i:
6134 6133 needsincoming = True
6135 6134 if o:
6136 6135 needsoutgoing = True
6137 6136 if not needsincoming and not needsoutgoing:
6138 6137 return
6139 6138
6140 6139 def getincoming():
6141 6140 source, branches = hg.parseurl(ui.expandpath('default'))
6142 6141 sbranch = branches[0]
6143 6142 try:
6144 6143 other = hg.peer(repo, {}, source)
6145 6144 except error.RepoError:
6146 6145 if opts.get('remote'):
6147 6146 raise
6148 6147 return source, sbranch, None, None, None
6149 6148 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
6150 6149 if revs:
6151 6150 revs = [other.lookup(rev) for rev in revs]
6152 6151 ui.debug('comparing with %s\n' % util.hidepassword(source))
6153 6152 repo.ui.pushbuffer()
6154 6153 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
6155 6154 repo.ui.popbuffer()
6156 6155 return source, sbranch, other, commoninc, commoninc[1]
6157 6156
6158 6157 if needsincoming:
6159 6158 source, sbranch, sother, commoninc, incoming = getincoming()
6160 6159 else:
6161 6160 source = sbranch = sother = commoninc = incoming = None
6162 6161
6163 6162 def getoutgoing():
6164 6163 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
6165 6164 dbranch = branches[0]
6166 6165 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
6167 6166 if source != dest:
6168 6167 try:
6169 6168 dother = hg.peer(repo, {}, dest)
6170 6169 except error.RepoError:
6171 6170 if opts.get('remote'):
6172 6171 raise
6173 6172 return dest, dbranch, None, None
6174 6173 ui.debug('comparing with %s\n' % util.hidepassword(dest))
6175 6174 elif sother is None:
6176 6175 # there is no explicit destination peer, but source one is invalid
6177 6176 return dest, dbranch, None, None
6178 6177 else:
6179 6178 dother = sother
6180 6179 if (source != dest or (sbranch is not None and sbranch != dbranch)):
6181 6180 common = None
6182 6181 else:
6183 6182 common = commoninc
6184 6183 if revs:
6185 6184 revs = [repo.lookup(rev) for rev in revs]
6186 6185 repo.ui.pushbuffer()
6187 6186 outgoing = discovery.findcommonoutgoing(repo, dother, onlyheads=revs,
6188 6187 commoninc=common)
6189 6188 repo.ui.popbuffer()
6190 6189 return dest, dbranch, dother, outgoing
6191 6190
6192 6191 if needsoutgoing:
6193 6192 dest, dbranch, dother, outgoing = getoutgoing()
6194 6193 else:
6195 6194 dest = dbranch = dother = outgoing = None
6196 6195
6197 6196 if opts.get('remote'):
6198 6197 t = []
6199 6198 if incoming:
6200 6199 t.append(_('1 or more incoming'))
6201 6200 o = outgoing.missing
6202 6201 if o:
6203 6202 t.append(_('%d outgoing') % len(o))
6204 6203 other = dother or sother
6205 6204 if 'bookmarks' in other.listkeys('namespaces'):
6206 6205 counts = bookmarks.summary(repo, other)
6207 6206 if counts[0] > 0:
6208 6207 t.append(_('%d incoming bookmarks') % counts[0])
6209 6208 if counts[1] > 0:
6210 6209 t.append(_('%d outgoing bookmarks') % counts[1])
6211 6210
6212 6211 if t:
6213 6212 # i18n: column positioning for "hg summary"
6214 6213 ui.write(_('remote: %s\n') % (', '.join(t)))
6215 6214 else:
6216 6215 # i18n: column positioning for "hg summary"
6217 6216 ui.status(_('remote: (synced)\n'))
6218 6217
6219 6218 cmdutil.summaryremotehooks(ui, repo, opts,
6220 6219 ((source, sbranch, sother, commoninc),
6221 6220 (dest, dbranch, dother, outgoing)))
6222 6221
6223 6222 @command('tag',
6224 6223 [('f', 'force', None, _('force tag')),
6225 6224 ('l', 'local', None, _('make the tag local')),
6226 6225 ('r', 'rev', '', _('revision to tag'), _('REV')),
6227 6226 ('', 'remove', None, _('remove a tag')),
6228 6227 # -l/--local is already there, commitopts cannot be used
6229 6228 ('e', 'edit', None, _('invoke editor on commit messages')),
6230 6229 ('m', 'message', '', _('use text as commit message'), _('TEXT')),
6231 6230 ] + commitopts2,
6232 6231 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
6233 6232 def tag(ui, repo, name1, *names, **opts):
6234 6233 """add one or more tags for the current or given revision
6235 6234
6236 6235 Name a particular revision using <name>.
6237 6236
6238 6237 Tags are used to name particular revisions of the repository and are
6239 6238 very useful to compare different revisions, to go back to significant
6240 6239 earlier versions or to mark branch points as releases, etc. Changing
6241 6240 an existing tag is normally disallowed; use -f/--force to override.
6242 6241
6243 6242 If no revision is given, the parent of the working directory is
6244 6243 used.
6245 6244
6246 6245 To facilitate version control, distribution, and merging of tags,
6247 6246 they are stored as a file named ".hgtags" which is managed similarly
6248 6247 to other project files and can be hand-edited if necessary. This
6249 6248 also means that tagging creates a new commit. The file
6250 6249 ".hg/localtags" is used for local tags (not shared among
6251 6250 repositories).
6252 6251
6253 6252 Tag commits are usually made at the head of a branch. If the parent
6254 6253 of the working directory is not a branch head, :hg:`tag` aborts; use
6255 6254 -f/--force to force the tag commit to be based on a non-head
6256 6255 changeset.
6257 6256
6258 6257 See :hg:`help dates` for a list of formats valid for -d/--date.
6259 6258
6260 6259 Since tag names have priority over branch names during revision
6261 6260 lookup, using an existing branch name as a tag name is discouraged.
6262 6261
6263 6262 Returns 0 on success.
6264 6263 """
6265 6264 wlock = lock = None
6266 6265 try:
6267 6266 wlock = repo.wlock()
6268 6267 lock = repo.lock()
6269 6268 rev_ = "."
6270 6269 names = [t.strip() for t in (name1,) + names]
6271 6270 if len(names) != len(set(names)):
6272 6271 raise error.Abort(_('tag names must be unique'))
6273 6272 for n in names:
6274 6273 scmutil.checknewlabel(repo, n, 'tag')
6275 6274 if not n:
6276 6275 raise error.Abort(_('tag names cannot consist entirely of '
6277 6276 'whitespace'))
6278 6277 if opts.get('rev') and opts.get('remove'):
6279 6278 raise error.Abort(_("--rev and --remove are incompatible"))
6280 6279 if opts.get('rev'):
6281 6280 rev_ = opts['rev']
6282 6281 message = opts.get('message')
6283 6282 if opts.get('remove'):
6284 6283 if opts.get('local'):
6285 6284 expectedtype = 'local'
6286 6285 else:
6287 6286 expectedtype = 'global'
6288 6287
6289 6288 for n in names:
6290 6289 if not repo.tagtype(n):
6291 6290 raise error.Abort(_("tag '%s' does not exist") % n)
6292 6291 if repo.tagtype(n) != expectedtype:
6293 6292 if expectedtype == 'global':
6294 6293 raise error.Abort(_("tag '%s' is not a global tag") % n)
6295 6294 else:
6296 6295 raise error.Abort(_("tag '%s' is not a local tag") % n)
6297 6296 rev_ = 'null'
6298 6297 if not message:
6299 6298 # we don't translate commit messages
6300 6299 message = 'Removed tag %s' % ', '.join(names)
6301 6300 elif not opts.get('force'):
6302 6301 for n in names:
6303 6302 if n in repo.tags():
6304 6303 raise error.Abort(_("tag '%s' already exists "
6305 6304 "(use -f to force)") % n)
6306 6305 if not opts.get('local'):
6307 6306 p1, p2 = repo.dirstate.parents()
6308 6307 if p2 != nullid:
6309 6308 raise error.Abort(_('uncommitted merge'))
6310 6309 bheads = repo.branchheads()
6311 6310 if not opts.get('force') and bheads and p1 not in bheads:
6312 6311 raise error.Abort(_('working directory is not at a branch head '
6313 6312 '(use -f to force)'))
6314 6313 r = scmutil.revsingle(repo, rev_).node()
6315 6314
6316 6315 if not message:
6317 6316 # we don't translate commit messages
6318 6317 message = ('Added tag %s for changeset %s' %
6319 6318 (', '.join(names), short(r)))
6320 6319
6321 6320 date = opts.get('date')
6322 6321 if date:
6323 6322 date = util.parsedate(date)
6324 6323
6325 6324 if opts.get('remove'):
6326 6325 editform = 'tag.remove'
6327 6326 else:
6328 6327 editform = 'tag.add'
6329 6328 editor = cmdutil.getcommiteditor(editform=editform, **opts)
6330 6329
6331 6330 # don't allow tagging the null rev
6332 6331 if (not opts.get('remove') and
6333 6332 scmutil.revsingle(repo, rev_).rev() == nullrev):
6334 6333 raise error.Abort(_("cannot tag null revision"))
6335 6334
6336 6335 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date,
6337 6336 editor=editor)
6338 6337 finally:
6339 6338 release(lock, wlock)
6340 6339
6341 6340 @command('tags', formatteropts, '')
6342 6341 def tags(ui, repo, **opts):
6343 6342 """list repository tags
6344 6343
6345 6344 This lists both regular and local tags. When the -v/--verbose
6346 6345 switch is used, a third column "local" is printed for local tags.
6347 6346 When the -q/--quiet switch is used, only the tag name is printed.
6348 6347
6349 6348 Returns 0 on success.
6350 6349 """
6351 6350
6352 6351 fm = ui.formatter('tags', opts)
6353 6352 hexfunc = fm.hexfunc
6354 6353 tagtype = ""
6355 6354
6356 6355 for t, n in reversed(repo.tagslist()):
6357 6356 hn = hexfunc(n)
6358 6357 label = 'tags.normal'
6359 6358 tagtype = ''
6360 6359 if repo.tagtype(t) == 'local':
6361 6360 label = 'tags.local'
6362 6361 tagtype = 'local'
6363 6362
6364 6363 fm.startitem()
6365 6364 fm.write('tag', '%s', t, label=label)
6366 6365 fmt = " " * (30 - encoding.colwidth(t)) + ' %5d:%s'
6367 6366 fm.condwrite(not ui.quiet, 'rev node', fmt,
6368 6367 repo.changelog.rev(n), hn, label=label)
6369 6368 fm.condwrite(ui.verbose and tagtype, 'type', ' %s',
6370 6369 tagtype, label=label)
6371 6370 fm.plain('\n')
6372 6371 fm.end()
6373 6372
6374 6373 @command('tip',
6375 6374 [('p', 'patch', None, _('show patch')),
6376 6375 ('g', 'git', None, _('use git extended diff format')),
6377 6376 ] + templateopts,
6378 6377 _('[-p] [-g]'))
6379 6378 def tip(ui, repo, **opts):
6380 6379 """show the tip revision (DEPRECATED)
6381 6380
6382 6381 The tip revision (usually just called the tip) is the changeset
6383 6382 most recently added to the repository (and therefore the most
6384 6383 recently changed head).
6385 6384
6386 6385 If you have just made a commit, that commit will be the tip. If
6387 6386 you have just pulled changes from another repository, the tip of
6388 6387 that repository becomes the current tip. The "tip" tag is special
6389 6388 and cannot be renamed or assigned to a different changeset.
6390 6389
6391 6390 This command is deprecated, please use :hg:`heads` instead.
6392 6391
6393 6392 Returns 0 on success.
6394 6393 """
6395 6394 displayer = cmdutil.show_changeset(ui, repo, opts)
6396 6395 displayer.show(repo['tip'])
6397 6396 displayer.close()
6398 6397
6399 6398 @command('unbundle',
6400 6399 [('u', 'update', None,
6401 6400 _('update to new branch head if changesets were unbundled'))],
6402 6401 _('[-u] FILE...'))
6403 6402 def unbundle(ui, repo, fname1, *fnames, **opts):
6404 6403 """apply one or more changegroup files
6405 6404
6406 6405 Apply one or more compressed changegroup files generated by the
6407 6406 bundle command.
6408 6407
6409 6408 Returns 0 on success, 1 if an update has unresolved files.
6410 6409 """
6411 6410 fnames = (fname1,) + fnames
6412 6411
6413 6412 with repo.lock():
6414 6413 for fname in fnames:
6415 6414 f = hg.openpath(ui, fname)
6416 6415 gen = exchange.readbundle(ui, f, fname)
6417 6416 if isinstance(gen, bundle2.unbundle20):
6418 6417 tr = repo.transaction('unbundle')
6419 6418 try:
6420 6419 op = bundle2.applybundle(repo, gen, tr, source='unbundle',
6421 6420 url='bundle:' + fname)
6422 6421 tr.close()
6423 6422 except error.BundleUnknownFeatureError as exc:
6424 6423 raise error.Abort(_('%s: unknown bundle feature, %s')
6425 6424 % (fname, exc),
6426 6425 hint=_("see https://mercurial-scm.org/"
6427 6426 "wiki/BundleFeature for more "
6428 6427 "information"))
6429 6428 finally:
6430 6429 if tr:
6431 6430 tr.release()
6432 6431 changes = [r.get('return', 0)
6433 6432 for r in op.records['changegroup']]
6434 6433 modheads = changegroup.combineresults(changes)
6435 6434 elif isinstance(gen, streamclone.streamcloneapplier):
6436 6435 raise error.Abort(
6437 6436 _('packed bundles cannot be applied with '
6438 6437 '"hg unbundle"'),
6439 6438 hint=_('use "hg debugapplystreamclonebundle"'))
6440 6439 else:
6441 6440 modheads = gen.apply(repo, 'unbundle', 'bundle:' + fname)
6442 6441
6443 6442 return postincoming(ui, repo, modheads, opts.get('update'), None, None)
6444 6443
6445 6444 @command('^update|up|checkout|co',
6446 6445 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
6447 6446 ('c', 'check', None, _('require clean working directory')),
6448 6447 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
6449 6448 ('r', 'rev', '', _('revision'), _('REV'))
6450 6449 ] + mergetoolopts,
6451 6450 _('[-c] [-C] [-d DATE] [[-r] REV]'))
6452 6451 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False,
6453 6452 tool=None):
6454 6453 """update working directory (or switch revisions)
6455 6454
6456 6455 Update the repository's working directory to the specified
6457 6456 changeset. If no changeset is specified, update to the tip of the
6458 6457 current named branch and move the active bookmark (see :hg:`help
6459 6458 bookmarks`).
6460 6459
6461 6460 Update sets the working directory's parent revision to the specified
6462 6461 changeset (see :hg:`help parents`).
6463 6462
6464 6463 If the changeset is not a descendant or ancestor of the working
6465 6464 directory's parent, the update is aborted. With the -c/--check
6466 6465 option, the working directory is checked for uncommitted changes; if
6467 6466 none are found, the working directory is updated to the specified
6468 6467 changeset.
6469 6468
6470 6469 .. container:: verbose
6471 6470
6472 6471 The following rules apply when the working directory contains
6473 6472 uncommitted changes:
6474 6473
6475 6474 1. If neither -c/--check nor -C/--clean is specified, and if
6476 6475 the requested changeset is an ancestor or descendant of
6477 6476 the working directory's parent, the uncommitted changes
6478 6477 are merged into the requested changeset and the merged
6479 6478 result is left uncommitted. If the requested changeset is
6480 6479 not an ancestor or descendant (that is, it is on another
6481 6480 branch), the update is aborted and the uncommitted changes
6482 6481 are preserved.
6483 6482
6484 6483 2. With the -c/--check option, the update is aborted and the
6485 6484 uncommitted changes are preserved.
6486 6485
6487 6486 3. With the -C/--clean option, uncommitted changes are discarded and
6488 6487 the working directory is updated to the requested changeset.
6489 6488
6490 6489 To cancel an uncommitted merge (and lose your changes), use
6491 6490 :hg:`update --clean .`.
6492 6491
6493 6492 Use null as the changeset to remove the working directory (like
6494 6493 :hg:`clone -U`).
6495 6494
6496 6495 If you want to revert just one file to an older revision, use
6497 6496 :hg:`revert [-r REV] NAME`.
6498 6497
6499 6498 See :hg:`help dates` for a list of formats valid for -d/--date.
6500 6499
6501 6500 Returns 0 on success, 1 if there are unresolved files.
6502 6501 """
6503 6502 if rev and node:
6504 6503 raise error.Abort(_("please specify just one revision"))
6505 6504
6506 6505 if rev is None or rev == '':
6507 6506 rev = node
6508 6507
6509 6508 if date and rev is not None:
6510 6509 raise error.Abort(_("you can't specify a revision and a date"))
6511 6510
6512 6511 if check and clean:
6513 6512 raise error.Abort(_("cannot specify both -c/--check and -C/--clean"))
6514 6513
6515 6514 with repo.wlock():
6516 6515 cmdutil.clearunfinished(repo)
6517 6516
6518 6517 if date:
6519 6518 rev = cmdutil.finddate(ui, repo, date)
6520 6519
6521 6520 # if we defined a bookmark, we have to remember the original name
6522 6521 brev = rev
6523 6522 rev = scmutil.revsingle(repo, rev, rev).rev()
6524 6523
6525 6524 if check:
6526 6525 cmdutil.bailifchanged(repo, merge=False)
6527 6526
6528 6527 repo.ui.setconfig('ui', 'forcemerge', tool, 'update')
6529 6528
6530 6529 return hg.updatetotally(ui, repo, rev, brev, clean=clean, check=check)
6531 6530
6532 6531 @command('verify', [])
6533 6532 def verify(ui, repo):
6534 6533 """verify the integrity of the repository
6535 6534
6536 6535 Verify the integrity of the current repository.
6537 6536
6538 6537 This will perform an extensive check of the repository's
6539 6538 integrity, validating the hashes and checksums of each entry in
6540 6539 the changelog, manifest, and tracked files, as well as the
6541 6540 integrity of their crosslinks and indices.
6542 6541
6543 6542 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
6544 6543 for more information about recovery from corruption of the
6545 6544 repository.
6546 6545
6547 6546 Returns 0 on success, 1 if errors are encountered.
6548 6547 """
6549 6548 return hg.verify(repo)
6550 6549
6551 6550 @command('version', [] + formatteropts, norepo=True)
6552 6551 def version_(ui, **opts):
6553 6552 """output version and copyright information"""
6554 6553 fm = ui.formatter("version", opts)
6555 6554 fm.startitem()
6556 6555 fm.write("ver", _("Mercurial Distributed SCM (version %s)\n"),
6557 6556 util.version())
6558 6557 license = _(
6559 6558 "(see https://mercurial-scm.org for more information)\n"
6560 6559 "\nCopyright (C) 2005-2016 Matt Mackall and others\n"
6561 6560 "This is free software; see the source for copying conditions. "
6562 6561 "There is NO\nwarranty; "
6563 6562 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
6564 6563 )
6565 6564 if not ui.quiet:
6566 6565 fm.plain(license)
6567 6566
6568 6567 if ui.verbose:
6569 6568 fm.plain(_("\nEnabled extensions:\n\n"))
6570 6569 # format names and versions into columns
6571 6570 names = []
6572 6571 vers = []
6573 6572 isinternals = []
6574 6573 for name, module in extensions.extensions():
6575 6574 names.append(name)
6576 6575 vers.append(extensions.moduleversion(module) or None)
6577 6576 isinternals.append(extensions.ismoduleinternal(module))
6578 6577 fn = fm.nested("extensions")
6579 6578 if names:
6580 6579 namefmt = " %%-%ds " % max(len(n) for n in names)
6581 6580 places = [_("external"), _("internal")]
6582 6581 for n, v, p in zip(names, vers, isinternals):
6583 6582 fn.startitem()
6584 6583 fn.condwrite(ui.verbose, "name", namefmt, n)
6585 6584 if ui.verbose:
6586 6585 fn.plain("%s " % places[p])
6587 6586 fn.data(bundled=p)
6588 6587 fn.condwrite(ui.verbose and v, "ver", "%s", v)
6589 6588 if ui.verbose:
6590 6589 fn.plain("\n")
6591 6590 fn.end()
6592 6591 fm.end()
6593 6592
6594 6593 def loadcmdtable(ui, name, cmdtable):
6595 6594 """Load command functions from specified cmdtable
6596 6595 """
6597 6596 overrides = [cmd for cmd in cmdtable if cmd in table]
6598 6597 if overrides:
6599 6598 ui.warn(_("extension '%s' overrides commands: %s\n")
6600 6599 % (name, " ".join(overrides)))
6601 6600 table.update(cmdtable)
@@ -1,889 +1,888 b''
1 1 # dispatch.py - command dispatching for mercurial
2 2 #
3 3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from __future__ import absolute_import, print_function
9 9
10 10 import atexit
11 11 import difflib
12 12 import errno
13 13 import getopt
14 14 import os
15 15 import pdb
16 16 import re
17 import shlex
18 17 import signal
19 18 import sys
20 19 import time
21 20 import traceback
22 21
23 22
24 23 from .i18n import _
25 24
26 25 from . import (
27 26 cmdutil,
28 27 color,
29 28 commands,
30 29 debugcommands,
31 30 demandimport,
32 31 encoding,
33 32 error,
34 33 extensions,
35 34 fancyopts,
36 35 fileset,
37 36 hg,
38 37 hook,
39 38 profiling,
40 39 pycompat,
41 40 revset,
42 41 scmutil,
43 42 templatefilters,
44 43 templatekw,
45 44 templater,
46 45 ui as uimod,
47 46 util,
48 47 )
49 48
50 49 class request(object):
51 50 def __init__(self, args, ui=None, repo=None, fin=None, fout=None,
52 51 ferr=None):
53 52 self.args = args
54 53 self.ui = ui
55 54 self.repo = repo
56 55
57 56 # input/output/error streams
58 57 self.fin = fin
59 58 self.fout = fout
60 59 self.ferr = ferr
61 60
62 61 def run():
63 62 "run the command in sys.argv"
64 63 sys.exit((dispatch(request(pycompat.sysargv[1:])) or 0) & 255)
65 64
66 65 def _getsimilar(symbols, value):
67 66 sim = lambda x: difflib.SequenceMatcher(None, value, x).ratio()
68 67 # The cutoff for similarity here is pretty arbitrary. It should
69 68 # probably be investigated and tweaked.
70 69 return [s for s in symbols if sim(s) > 0.6]
71 70
72 71 def _reportsimilar(write, similar):
73 72 if len(similar) == 1:
74 73 write(_("(did you mean %s?)\n") % similar[0])
75 74 elif similar:
76 75 ss = ", ".join(sorted(similar))
77 76 write(_("(did you mean one of %s?)\n") % ss)
78 77
79 78 def _formatparse(write, inst):
80 79 similar = []
81 80 if isinstance(inst, error.UnknownIdentifier):
82 81 # make sure to check fileset first, as revset can invoke fileset
83 82 similar = _getsimilar(inst.symbols, inst.function)
84 83 if len(inst.args) > 1:
85 84 write(_("hg: parse error at %s: %s\n") %
86 85 (inst.args[1], inst.args[0]))
87 86 if (inst.args[0][0] == ' '):
88 87 write(_("unexpected leading whitespace\n"))
89 88 else:
90 89 write(_("hg: parse error: %s\n") % inst.args[0])
91 90 _reportsimilar(write, similar)
92 91 if inst.hint:
93 92 write(_("(%s)\n") % inst.hint)
94 93
95 94 def dispatch(req):
96 95 "run the command specified in req.args"
97 96 if req.ferr:
98 97 ferr = req.ferr
99 98 elif req.ui:
100 99 ferr = req.ui.ferr
101 100 else:
102 101 ferr = util.stderr
103 102
104 103 try:
105 104 if not req.ui:
106 105 req.ui = uimod.ui.load()
107 106 if '--traceback' in req.args:
108 107 req.ui.setconfig('ui', 'traceback', 'on', '--traceback')
109 108
110 109 # set ui streams from the request
111 110 if req.fin:
112 111 req.ui.fin = req.fin
113 112 if req.fout:
114 113 req.ui.fout = req.fout
115 114 if req.ferr:
116 115 req.ui.ferr = req.ferr
117 116 except error.Abort as inst:
118 117 ferr.write(_("abort: %s\n") % inst)
119 118 if inst.hint:
120 119 ferr.write(_("(%s)\n") % inst.hint)
121 120 return -1
122 121 except error.ParseError as inst:
123 122 _formatparse(ferr.write, inst)
124 123 return -1
125 124
126 125 msg = ' '.join(' ' in a and repr(a) or a for a in req.args)
127 126 starttime = time.time()
128 127 ret = None
129 128 try:
130 129 ret = _runcatch(req)
131 130 except KeyboardInterrupt:
132 131 try:
133 132 req.ui.warn(_("interrupted!\n"))
134 133 except IOError as inst:
135 134 if inst.errno != errno.EPIPE:
136 135 raise
137 136 ret = -1
138 137 finally:
139 138 duration = time.time() - starttime
140 139 req.ui.flush()
141 140 req.ui.log("commandfinish", "%s exited %s after %0.2f seconds\n",
142 141 msg, ret or 0, duration)
143 142 return ret
144 143
145 144 def _runcatch(req):
146 145 def catchterm(*args):
147 146 raise error.SignalInterrupt
148 147
149 148 ui = req.ui
150 149 try:
151 150 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
152 151 num = getattr(signal, name, None)
153 152 if num:
154 153 signal.signal(num, catchterm)
155 154 except ValueError:
156 155 pass # happens if called in a thread
157 156
158 157 def _runcatchfunc():
159 158 try:
160 159 debugger = 'pdb'
161 160 debugtrace = {
162 161 'pdb' : pdb.set_trace
163 162 }
164 163 debugmortem = {
165 164 'pdb' : pdb.post_mortem
166 165 }
167 166
168 167 # read --config before doing anything else
169 168 # (e.g. to change trust settings for reading .hg/hgrc)
170 169 cfgs = _parseconfig(req.ui, _earlygetopt(['--config'], req.args))
171 170
172 171 if req.repo:
173 172 # copy configs that were passed on the cmdline (--config) to
174 173 # the repo ui
175 174 for sec, name, val in cfgs:
176 175 req.repo.ui.setconfig(sec, name, val, source='--config')
177 176
178 177 # developer config: ui.debugger
179 178 debugger = ui.config("ui", "debugger")
180 179 debugmod = pdb
181 180 if not debugger or ui.plain():
182 181 # if we are in HGPLAIN mode, then disable custom debugging
183 182 debugger = 'pdb'
184 183 elif '--debugger' in req.args:
185 184 # This import can be slow for fancy debuggers, so only
186 185 # do it when absolutely necessary, i.e. when actual
187 186 # debugging has been requested
188 187 with demandimport.deactivated():
189 188 try:
190 189 debugmod = __import__(debugger)
191 190 except ImportError:
192 191 pass # Leave debugmod = pdb
193 192
194 193 debugtrace[debugger] = debugmod.set_trace
195 194 debugmortem[debugger] = debugmod.post_mortem
196 195
197 196 # enter the debugger before command execution
198 197 if '--debugger' in req.args:
199 198 ui.warn(_("entering debugger - "
200 199 "type c to continue starting hg or h for help\n"))
201 200
202 201 if (debugger != 'pdb' and
203 202 debugtrace[debugger] == debugtrace['pdb']):
204 203 ui.warn(_("%s debugger specified "
205 204 "but its module was not found\n") % debugger)
206 205 with demandimport.deactivated():
207 206 debugtrace[debugger]()
208 207 try:
209 208 return _dispatch(req)
210 209 finally:
211 210 ui.flush()
212 211 except: # re-raises
213 212 # enter the debugger when we hit an exception
214 213 if '--debugger' in req.args:
215 214 traceback.print_exc()
216 215 debugmortem[debugger](sys.exc_info()[2])
217 216 ui.traceback()
218 217 raise
219 218
220 219 return callcatch(ui, _runcatchfunc)
221 220
222 221 def callcatch(ui, func):
223 222 """like scmutil.callcatch but handles more high-level exceptions about
224 223 config parsing and commands. besides, use handlecommandexception to handle
225 224 uncaught exceptions.
226 225 """
227 226 try:
228 227 return scmutil.callcatch(ui, func)
229 228 except error.AmbiguousCommand as inst:
230 229 ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
231 230 (inst.args[0], " ".join(inst.args[1])))
232 231 except error.CommandError as inst:
233 232 if inst.args[0]:
234 233 ui.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
235 234 commands.help_(ui, inst.args[0], full=False, command=True)
236 235 else:
237 236 ui.warn(_("hg: %s\n") % inst.args[1])
238 237 commands.help_(ui, 'shortlist')
239 238 except error.ParseError as inst:
240 239 _formatparse(ui.warn, inst)
241 240 return -1
242 241 except error.UnknownCommand as inst:
243 242 ui.warn(_("hg: unknown command '%s'\n") % inst.args[0])
244 243 try:
245 244 # check if the command is in a disabled extension
246 245 # (but don't check for extensions themselves)
247 246 commands.help_(ui, inst.args[0], unknowncmd=True)
248 247 except (error.UnknownCommand, error.Abort):
249 248 suggested = False
250 249 if len(inst.args) == 2:
251 250 sim = _getsimilar(inst.args[1], inst.args[0])
252 251 if sim:
253 252 _reportsimilar(ui.warn, sim)
254 253 suggested = True
255 254 if not suggested:
256 255 commands.help_(ui, 'shortlist')
257 256 except IOError:
258 257 raise
259 258 except KeyboardInterrupt:
260 259 raise
261 260 except: # probably re-raises
262 261 if not handlecommandexception(ui):
263 262 raise
264 263
265 264 return -1
266 265
267 266 def aliasargs(fn, givenargs):
268 267 args = getattr(fn, 'args', [])
269 268 if args:
270 269 cmd = ' '.join(map(util.shellquote, args))
271 270
272 271 nums = []
273 272 def replacer(m):
274 273 num = int(m.group(1)) - 1
275 274 nums.append(num)
276 275 if num < len(givenargs):
277 276 return givenargs[num]
278 277 raise error.Abort(_('too few arguments for command alias'))
279 278 cmd = re.sub(r'\$(\d+|\$)', replacer, cmd)
280 279 givenargs = [x for i, x in enumerate(givenargs)
281 280 if i not in nums]
282 args = shlex.split(cmd)
281 args = pycompat.shlexsplit(cmd)
283 282 return args + givenargs
284 283
285 284 def aliasinterpolate(name, args, cmd):
286 285 '''interpolate args into cmd for shell aliases
287 286
288 287 This also handles $0, $@ and "$@".
289 288 '''
290 289 # util.interpolate can't deal with "$@" (with quotes) because it's only
291 290 # built to match prefix + patterns.
292 291 replacemap = dict(('$%d' % (i + 1), arg) for i, arg in enumerate(args))
293 292 replacemap['$0'] = name
294 293 replacemap['$$'] = '$'
295 294 replacemap['$@'] = ' '.join(args)
296 295 # Typical Unix shells interpolate "$@" (with quotes) as all the positional
297 296 # parameters, separated out into words. Emulate the same behavior here by
298 297 # quoting the arguments individually. POSIX shells will then typically
299 298 # tokenize each argument into exactly one word.
300 299 replacemap['"$@"'] = ' '.join(util.shellquote(arg) for arg in args)
301 300 # escape '\$' for regex
302 301 regex = '|'.join(replacemap.keys()).replace('$', r'\$')
303 302 r = re.compile(regex)
304 303 return r.sub(lambda x: replacemap[x.group()], cmd)
305 304
306 305 class cmdalias(object):
307 306 def __init__(self, name, definition, cmdtable, source):
308 307 self.name = self.cmd = name
309 308 self.cmdname = ''
310 309 self.definition = definition
311 310 self.fn = None
312 311 self.givenargs = []
313 312 self.opts = []
314 313 self.help = ''
315 314 self.badalias = None
316 315 self.unknowncmd = False
317 316 self.source = source
318 317
319 318 try:
320 319 aliases, entry = cmdutil.findcmd(self.name, cmdtable)
321 320 for alias, e in cmdtable.iteritems():
322 321 if e is entry:
323 322 self.cmd = alias
324 323 break
325 324 self.shadows = True
326 325 except error.UnknownCommand:
327 326 self.shadows = False
328 327
329 328 if not self.definition:
330 329 self.badalias = _("no definition for alias '%s'") % self.name
331 330 return
332 331
333 332 if self.definition.startswith('!'):
334 333 self.shell = True
335 334 def fn(ui, *args):
336 335 env = {'HG_ARGS': ' '.join((self.name,) + args)}
337 336 def _checkvar(m):
338 337 if m.groups()[0] == '$':
339 338 return m.group()
340 339 elif int(m.groups()[0]) <= len(args):
341 340 return m.group()
342 341 else:
343 342 ui.debug("No argument found for substitution "
344 343 "of %i variable in alias '%s' definition."
345 344 % (int(m.groups()[0]), self.name))
346 345 return ''
347 346 cmd = re.sub(r'\$(\d+|\$)', _checkvar, self.definition[1:])
348 347 cmd = aliasinterpolate(self.name, args, cmd)
349 348 return ui.system(cmd, environ=env)
350 349 self.fn = fn
351 350 return
352 351
353 352 try:
354 args = shlex.split(self.definition)
353 args = pycompat.shlexsplit(self.definition)
355 354 except ValueError as inst:
356 355 self.badalias = (_("error in definition for alias '%s': %s")
357 356 % (self.name, inst))
358 357 return
359 358 self.cmdname = cmd = args.pop(0)
360 359 self.givenargs = args
361 360
362 361 for invalidarg in ("--cwd", "-R", "--repository", "--repo", "--config"):
363 362 if _earlygetopt([invalidarg], args):
364 363 self.badalias = (_("error in definition for alias '%s': %s may "
365 364 "only be given on the command line")
366 365 % (self.name, invalidarg))
367 366 return
368 367
369 368 try:
370 369 tableentry = cmdutil.findcmd(cmd, cmdtable, False)[1]
371 370 if len(tableentry) > 2:
372 371 self.fn, self.opts, self.help = tableentry
373 372 else:
374 373 self.fn, self.opts = tableentry
375 374
376 375 if self.help.startswith("hg " + cmd):
377 376 # drop prefix in old-style help lines so hg shows the alias
378 377 self.help = self.help[4 + len(cmd):]
379 378 self.__doc__ = self.fn.__doc__
380 379
381 380 except error.UnknownCommand:
382 381 self.badalias = (_("alias '%s' resolves to unknown command '%s'")
383 382 % (self.name, cmd))
384 383 self.unknowncmd = True
385 384 except error.AmbiguousCommand:
386 385 self.badalias = (_("alias '%s' resolves to ambiguous command '%s'")
387 386 % (self.name, cmd))
388 387
389 388 @property
390 389 def args(self):
391 390 args = map(util.expandpath, self.givenargs)
392 391 return aliasargs(self.fn, args)
393 392
394 393 def __getattr__(self, name):
395 394 adefaults = {'norepo': True, 'optionalrepo': False, 'inferrepo': False}
396 395 if name not in adefaults:
397 396 raise AttributeError(name)
398 397 if self.badalias or util.safehasattr(self, 'shell'):
399 398 return adefaults[name]
400 399 return getattr(self.fn, name)
401 400
402 401 def __call__(self, ui, *args, **opts):
403 402 if self.badalias:
404 403 hint = None
405 404 if self.unknowncmd:
406 405 try:
407 406 # check if the command is in a disabled extension
408 407 cmd, ext = extensions.disabledcmd(ui, self.cmdname)[:2]
409 408 hint = _("'%s' is provided by '%s' extension") % (cmd, ext)
410 409 except error.UnknownCommand:
411 410 pass
412 411 raise error.Abort(self.badalias, hint=hint)
413 412 if self.shadows:
414 413 ui.debug("alias '%s' shadows command '%s'\n" %
415 414 (self.name, self.cmdname))
416 415
417 416 ui.log('commandalias', "alias '%s' expands to '%s'\n",
418 417 self.name, self.definition)
419 418 if util.safehasattr(self, 'shell'):
420 419 return self.fn(ui, *args, **opts)
421 420 else:
422 421 try:
423 422 return util.checksignature(self.fn)(ui, *args, **opts)
424 423 except error.SignatureError:
425 424 args = ' '.join([self.cmdname] + self.args)
426 425 ui.debug("alias '%s' expands to '%s'\n" % (self.name, args))
427 426 raise
428 427
429 428 def addaliases(ui, cmdtable):
430 429 # aliases are processed after extensions have been loaded, so they
431 430 # may use extension commands. Aliases can also use other alias definitions,
432 431 # but only if they have been defined prior to the current definition.
433 432 for alias, definition in ui.configitems('alias'):
434 433 source = ui.configsource('alias', alias)
435 434 aliasdef = cmdalias(alias, definition, cmdtable, source)
436 435
437 436 try:
438 437 olddef = cmdtable[aliasdef.cmd][0]
439 438 if olddef.definition == aliasdef.definition:
440 439 continue
441 440 except (KeyError, AttributeError):
442 441 # definition might not exist or it might not be a cmdalias
443 442 pass
444 443
445 444 cmdtable[aliasdef.name] = (aliasdef, aliasdef.opts, aliasdef.help)
446 445
447 446 def _parse(ui, args):
448 447 options = {}
449 448 cmdoptions = {}
450 449
451 450 try:
452 451 args = fancyopts.fancyopts(args, commands.globalopts, options)
453 452 except getopt.GetoptError as inst:
454 453 raise error.CommandError(None, inst)
455 454
456 455 if args:
457 456 cmd, args = args[0], args[1:]
458 457 aliases, entry = cmdutil.findcmd(cmd, commands.table,
459 458 ui.configbool("ui", "strict"))
460 459 cmd = aliases[0]
461 460 args = aliasargs(entry[0], args)
462 461 defaults = ui.config("defaults", cmd)
463 462 if defaults:
464 args = map(util.expandpath, shlex.split(defaults)) + args
463 args = map(util.expandpath, pycompat.shlexsplit(defaults)) + args
465 464 c = list(entry[1])
466 465 else:
467 466 cmd = None
468 467 c = []
469 468
470 469 # combine global options into local
471 470 for o in commands.globalopts:
472 471 c.append((o[0], o[1], options[o[1]], o[3]))
473 472
474 473 try:
475 474 args = fancyopts.fancyopts(args, c, cmdoptions, gnu=True)
476 475 except getopt.GetoptError as inst:
477 476 raise error.CommandError(cmd, inst)
478 477
479 478 # separate global options back out
480 479 for o in commands.globalopts:
481 480 n = o[1]
482 481 options[n] = cmdoptions[n]
483 482 del cmdoptions[n]
484 483
485 484 return (cmd, cmd and entry[0] or None, args, options, cmdoptions)
486 485
487 486 def _parseconfig(ui, config):
488 487 """parse the --config options from the command line"""
489 488 configs = []
490 489
491 490 for cfg in config:
492 491 try:
493 492 name, value = [cfgelem.strip()
494 493 for cfgelem in cfg.split('=', 1)]
495 494 section, name = name.split('.', 1)
496 495 if not section or not name:
497 496 raise IndexError
498 497 ui.setconfig(section, name, value, '--config')
499 498 configs.append((section, name, value))
500 499 except (IndexError, ValueError):
501 500 raise error.Abort(_('malformed --config option: %r '
502 501 '(use --config section.name=value)') % cfg)
503 502
504 503 return configs
505 504
506 505 def _earlygetopt(aliases, args):
507 506 """Return list of values for an option (or aliases).
508 507
509 508 The values are listed in the order they appear in args.
510 509 The options and values are removed from args.
511 510
512 511 >>> args = ['x', '--cwd', 'foo', 'y']
513 512 >>> _earlygetopt(['--cwd'], args), args
514 513 (['foo'], ['x', 'y'])
515 514
516 515 >>> args = ['x', '--cwd=bar', 'y']
517 516 >>> _earlygetopt(['--cwd'], args), args
518 517 (['bar'], ['x', 'y'])
519 518
520 519 >>> args = ['x', '-R', 'foo', 'y']
521 520 >>> _earlygetopt(['-R'], args), args
522 521 (['foo'], ['x', 'y'])
523 522
524 523 >>> args = ['x', '-Rbar', 'y']
525 524 >>> _earlygetopt(['-R'], args), args
526 525 (['bar'], ['x', 'y'])
527 526 """
528 527 try:
529 528 argcount = args.index("--")
530 529 except ValueError:
531 530 argcount = len(args)
532 531 shortopts = [opt for opt in aliases if len(opt) == 2]
533 532 values = []
534 533 pos = 0
535 534 while pos < argcount:
536 535 fullarg = arg = args[pos]
537 536 equals = arg.find('=')
538 537 if equals > -1:
539 538 arg = arg[:equals]
540 539 if arg in aliases:
541 540 del args[pos]
542 541 if equals > -1:
543 542 values.append(fullarg[equals + 1:])
544 543 argcount -= 1
545 544 else:
546 545 if pos + 1 >= argcount:
547 546 # ignore and let getopt report an error if there is no value
548 547 break
549 548 values.append(args.pop(pos))
550 549 argcount -= 2
551 550 elif arg[:2] in shortopts:
552 551 # short option can have no following space, e.g. hg log -Rfoo
553 552 values.append(args.pop(pos)[2:])
554 553 argcount -= 1
555 554 else:
556 555 pos += 1
557 556 return values
558 557
559 558 def runcommand(lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions):
560 559 # run pre-hook, and abort if it fails
561 560 hook.hook(lui, repo, "pre-%s" % cmd, True, args=" ".join(fullargs),
562 561 pats=cmdpats, opts=cmdoptions)
563 562 try:
564 563 ret = _runcommand(ui, options, cmd, d)
565 564 # run post-hook, passing command result
566 565 hook.hook(lui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
567 566 result=ret, pats=cmdpats, opts=cmdoptions)
568 567 except Exception:
569 568 # run failure hook and re-raise
570 569 hook.hook(lui, repo, "fail-%s" % cmd, False, args=" ".join(fullargs),
571 570 pats=cmdpats, opts=cmdoptions)
572 571 raise
573 572 return ret
574 573
575 574 def _getlocal(ui, rpath, wd=None):
576 575 """Return (path, local ui object) for the given target path.
577 576
578 577 Takes paths in [cwd]/.hg/hgrc into account."
579 578 """
580 579 if wd is None:
581 580 try:
582 581 wd = pycompat.getcwd()
583 582 except OSError as e:
584 583 raise error.Abort(_("error getting current working directory: %s") %
585 584 e.strerror)
586 585 path = cmdutil.findrepo(wd) or ""
587 586 if not path:
588 587 lui = ui
589 588 else:
590 589 lui = ui.copy()
591 590 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
592 591
593 592 if rpath and rpath[-1]:
594 593 path = lui.expandpath(rpath[-1])
595 594 lui = ui.copy()
596 595 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
597 596
598 597 return path, lui
599 598
600 599 def _checkshellalias(lui, ui, args):
601 600 """Return the function to run the shell alias, if it is required"""
602 601 options = {}
603 602
604 603 try:
605 604 args = fancyopts.fancyopts(args, commands.globalopts, options)
606 605 except getopt.GetoptError:
607 606 return
608 607
609 608 if not args:
610 609 return
611 610
612 611 cmdtable = commands.table
613 612
614 613 cmd = args[0]
615 614 try:
616 615 strict = ui.configbool("ui", "strict")
617 616 aliases, entry = cmdutil.findcmd(cmd, cmdtable, strict)
618 617 except (error.AmbiguousCommand, error.UnknownCommand):
619 618 return
620 619
621 620 cmd = aliases[0]
622 621 fn = entry[0]
623 622
624 623 if cmd and util.safehasattr(fn, 'shell'):
625 624 d = lambda: fn(ui, *args[1:])
626 625 return lambda: runcommand(lui, None, cmd, args[:1], ui, options, d,
627 626 [], {})
628 627
629 628 _loaded = set()
630 629
631 630 # list of (objname, loadermod, loadername) tuple:
632 631 # - objname is the name of an object in extension module, from which
633 632 # extra information is loaded
634 633 # - loadermod is the module where loader is placed
635 634 # - loadername is the name of the function, which takes (ui, extensionname,
636 635 # extraobj) arguments
637 636 extraloaders = [
638 637 ('cmdtable', commands, 'loadcmdtable'),
639 638 ('colortable', color, 'loadcolortable'),
640 639 ('filesetpredicate', fileset, 'loadpredicate'),
641 640 ('revsetpredicate', revset, 'loadpredicate'),
642 641 ('templatefilter', templatefilters, 'loadfilter'),
643 642 ('templatefunc', templater, 'loadfunction'),
644 643 ('templatekeyword', templatekw, 'loadkeyword'),
645 644 ]
646 645
647 646 def _dispatch(req):
648 647 args = req.args
649 648 ui = req.ui
650 649
651 650 # check for cwd
652 651 cwd = _earlygetopt(['--cwd'], args)
653 652 if cwd:
654 653 os.chdir(cwd[-1])
655 654
656 655 rpath = _earlygetopt(["-R", "--repository", "--repo"], args)
657 656 path, lui = _getlocal(ui, rpath)
658 657
659 658 # Configure extensions in phases: uisetup, extsetup, cmdtable, and
660 659 # reposetup. Programs like TortoiseHg will call _dispatch several
661 660 # times so we keep track of configured extensions in _loaded.
662 661 extensions.loadall(lui)
663 662 exts = [ext for ext in extensions.extensions() if ext[0] not in _loaded]
664 663 # Propagate any changes to lui.__class__ by extensions
665 664 ui.__class__ = lui.__class__
666 665
667 666 # (uisetup and extsetup are handled in extensions.loadall)
668 667
669 668 for name, module in exts:
670 669 for objname, loadermod, loadername in extraloaders:
671 670 extraobj = getattr(module, objname, None)
672 671 if extraobj is not None:
673 672 getattr(loadermod, loadername)(ui, name, extraobj)
674 673 _loaded.add(name)
675 674
676 675 # (reposetup is handled in hg.repository)
677 676
678 677 # Side-effect of accessing is debugcommands module is guaranteed to be
679 678 # imported and commands.table is populated.
680 679 debugcommands.command
681 680
682 681 addaliases(lui, commands.table)
683 682
684 683 # All aliases and commands are completely defined, now.
685 684 # Check abbreviation/ambiguity of shell alias.
686 685 shellaliasfn = _checkshellalias(lui, ui, args)
687 686 if shellaliasfn:
688 687 with profiling.maybeprofile(lui):
689 688 return shellaliasfn()
690 689
691 690 # check for fallback encoding
692 691 fallback = lui.config('ui', 'fallbackencoding')
693 692 if fallback:
694 693 encoding.fallbackencoding = fallback
695 694
696 695 fullargs = args
697 696 cmd, func, args, options, cmdoptions = _parse(lui, args)
698 697
699 698 if options["config"]:
700 699 raise error.Abort(_("option --config may not be abbreviated!"))
701 700 if options["cwd"]:
702 701 raise error.Abort(_("option --cwd may not be abbreviated!"))
703 702 if options["repository"]:
704 703 raise error.Abort(_(
705 704 "option -R has to be separated from other options (e.g. not -qR) "
706 705 "and --repository may only be abbreviated as --repo!"))
707 706
708 707 if options["encoding"]:
709 708 encoding.encoding = options["encoding"]
710 709 if options["encodingmode"]:
711 710 encoding.encodingmode = options["encodingmode"]
712 711 if options["time"]:
713 712 def get_times():
714 713 t = os.times()
715 714 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
716 715 t = (t[0], t[1], t[2], t[3], time.clock())
717 716 return t
718 717 s = get_times()
719 718 def print_time():
720 719 t = get_times()
721 720 ui.warn(_("time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
722 721 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
723 722 atexit.register(print_time)
724 723
725 724 uis = set([ui, lui])
726 725
727 726 if req.repo:
728 727 uis.add(req.repo.ui)
729 728
730 729 if options['verbose'] or options['debug'] or options['quiet']:
731 730 for opt in ('verbose', 'debug', 'quiet'):
732 731 val = str(bool(options[opt]))
733 732 for ui_ in uis:
734 733 ui_.setconfig('ui', opt, val, '--' + opt)
735 734
736 735 if options['profile']:
737 736 for ui_ in uis:
738 737 ui_.setconfig('profiling', 'enabled', 'true', '--profile')
739 738
740 739 if options['traceback']:
741 740 for ui_ in uis:
742 741 ui_.setconfig('ui', 'traceback', 'on', '--traceback')
743 742
744 743 if options['noninteractive']:
745 744 for ui_ in uis:
746 745 ui_.setconfig('ui', 'interactive', 'off', '-y')
747 746
748 747 if cmdoptions.get('insecure', False):
749 748 for ui_ in uis:
750 749 ui_.insecureconnections = True
751 750
752 751 if options['version']:
753 752 return commands.version_(ui)
754 753 if options['help']:
755 754 return commands.help_(ui, cmd, command=cmd is not None)
756 755 elif not cmd:
757 756 return commands.help_(ui, 'shortlist')
758 757
759 758 with profiling.maybeprofile(lui):
760 759 repo = None
761 760 cmdpats = args[:]
762 761 if not func.norepo:
763 762 # use the repo from the request only if we don't have -R
764 763 if not rpath and not cwd:
765 764 repo = req.repo
766 765
767 766 if repo:
768 767 # set the descriptors of the repo ui to those of ui
769 768 repo.ui.fin = ui.fin
770 769 repo.ui.fout = ui.fout
771 770 repo.ui.ferr = ui.ferr
772 771 else:
773 772 try:
774 773 repo = hg.repository(ui, path=path)
775 774 if not repo.local():
776 775 raise error.Abort(_("repository '%s' is not local")
777 776 % path)
778 777 repo.ui.setconfig("bundle", "mainreporoot", repo.root,
779 778 'repo')
780 779 except error.RequirementError:
781 780 raise
782 781 except error.RepoError:
783 782 if rpath and rpath[-1]: # invalid -R path
784 783 raise
785 784 if not func.optionalrepo:
786 785 if func.inferrepo and args and not path:
787 786 # try to infer -R from command args
788 787 repos = map(cmdutil.findrepo, args)
789 788 guess = repos[0]
790 789 if guess and repos.count(guess) == len(repos):
791 790 req.args = ['--repository', guess] + fullargs
792 791 return _dispatch(req)
793 792 if not path:
794 793 raise error.RepoError(_("no repository found in"
795 794 " '%s' (.hg not found)")
796 795 % pycompat.getcwd())
797 796 raise
798 797 if repo:
799 798 ui = repo.ui
800 799 if options['hidden']:
801 800 repo = repo.unfiltered()
802 801 args.insert(0, repo)
803 802 elif rpath:
804 803 ui.warn(_("warning: --repository ignored\n"))
805 804
806 805 msg = ' '.join(' ' in a and repr(a) or a for a in fullargs)
807 806 ui.log("command", '%s\n', msg)
808 807 strcmdopt = pycompat.strkwargs(cmdoptions)
809 808 d = lambda: util.checksignature(func)(ui, *args, **strcmdopt)
810 809 try:
811 810 return runcommand(lui, repo, cmd, fullargs, ui, options, d,
812 811 cmdpats, cmdoptions)
813 812 finally:
814 813 if repo and repo != req.repo:
815 814 repo.close()
816 815
817 816 def _runcommand(ui, options, cmd, cmdfunc):
818 817 """Run a command function, possibly with profiling enabled."""
819 818 try:
820 819 return cmdfunc()
821 820 except error.SignatureError:
822 821 raise error.CommandError(cmd, _('invalid arguments'))
823 822
824 823 def _exceptionwarning(ui):
825 824 """Produce a warning message for the current active exception"""
826 825
827 826 # For compatibility checking, we discard the portion of the hg
828 827 # version after the + on the assumption that if a "normal
829 828 # user" is running a build with a + in it the packager
830 829 # probably built from fairly close to a tag and anyone with a
831 830 # 'make local' copy of hg (where the version number can be out
832 831 # of date) will be clueful enough to notice the implausible
833 832 # version number and try updating.
834 833 ct = util.versiontuple(n=2)
835 834 worst = None, ct, ''
836 835 if ui.config('ui', 'supportcontact', None) is None:
837 836 for name, mod in extensions.extensions():
838 837 testedwith = getattr(mod, 'testedwith', '')
839 838 report = getattr(mod, 'buglink', _('the extension author.'))
840 839 if not testedwith.strip():
841 840 # We found an untested extension. It's likely the culprit.
842 841 worst = name, 'unknown', report
843 842 break
844 843
845 844 # Never blame on extensions bundled with Mercurial.
846 845 if extensions.ismoduleinternal(mod):
847 846 continue
848 847
849 848 tested = [util.versiontuple(t, 2) for t in testedwith.split()]
850 849 if ct in tested:
851 850 continue
852 851
853 852 lower = [t for t in tested if t < ct]
854 853 nearest = max(lower or tested)
855 854 if worst[0] is None or nearest < worst[1]:
856 855 worst = name, nearest, report
857 856 if worst[0] is not None:
858 857 name, testedwith, report = worst
859 858 if not isinstance(testedwith, str):
860 859 testedwith = '.'.join([str(c) for c in testedwith])
861 860 warning = (_('** Unknown exception encountered with '
862 861 'possibly-broken third-party extension %s\n'
863 862 '** which supports versions %s of Mercurial.\n'
864 863 '** Please disable %s and try your action again.\n'
865 864 '** If that fixes the bug please report it to %s\n')
866 865 % (name, testedwith, name, report))
867 866 else:
868 867 bugtracker = ui.config('ui', 'supportcontact', None)
869 868 if bugtracker is None:
870 869 bugtracker = _("https://mercurial-scm.org/wiki/BugTracker")
871 870 warning = (_("** unknown exception encountered, "
872 871 "please report by visiting\n** ") + bugtracker + '\n')
873 872 warning += ((_("** Python %s\n") % sys.version.replace('\n', '')) +
874 873 (_("** Mercurial Distributed SCM (version %s)\n") %
875 874 util.version()) +
876 875 (_("** Extensions loaded: %s\n") %
877 876 ", ".join([x[0] for x in extensions.extensions()])))
878 877 return warning
879 878
880 879 def handlecommandexception(ui):
881 880 """Produce a warning message for broken commands
882 881
883 882 Called when handling an exception; the exception is reraised if
884 883 this function returns False, ignored otherwise.
885 884 """
886 885 warning = _exceptionwarning(ui)
887 886 ui.log("commandexception", "%s\n%s\n", warning, traceback.format_exc())
888 887 ui.warn(warning)
889 888 return False # re-raise the exception
@@ -1,283 +1,293 b''
1 1 # pycompat.py - portability shim for python 3
2 2 #
3 3 # This software may be used and distributed according to the terms of the
4 4 # GNU General Public License version 2 or any later version.
5 5
6 6 """Mercurial portability shim for python 3.
7 7
8 8 This contains aliases to hide python version-specific details from the core.
9 9 """
10 10
11 11 from __future__ import absolute_import
12 12
13 13 import getopt
14 14 import os
15 import shlex
15 16 import sys
16 17
17 18 ispy3 = (sys.version_info[0] >= 3)
18 19
19 20 if not ispy3:
20 21 import cPickle as pickle
21 22 import cStringIO as io
22 23 import httplib
23 24 import Queue as _queue
24 25 import SocketServer as socketserver
25 26 import urlparse
26 27 urlunquote = urlparse.unquote
27 28 import xmlrpclib
28 29 else:
29 30 import http.client as httplib
30 31 import io
31 32 import pickle
32 33 import queue as _queue
33 34 import socketserver
34 35 import urllib.parse as urlparse
35 36 urlunquote = urlparse.unquote_to_bytes
36 37 import xmlrpc.client as xmlrpclib
37 38
38 39 if ispy3:
39 40 import builtins
40 41 import functools
41 42 fsencode = os.fsencode
42 43 fsdecode = os.fsdecode
43 44 # A bytes version of os.name.
44 45 osname = os.name.encode('ascii')
45 46 ospathsep = os.pathsep.encode('ascii')
46 47 ossep = os.sep.encode('ascii')
47 48 osaltsep = os.altsep
48 49 osgetenv = os.getenvb
49 50 if osaltsep:
50 51 osaltsep = osaltsep.encode('ascii')
51 52 # os.getcwd() on Python 3 returns string, but it has os.getcwdb() which
52 53 # returns bytes.
53 54 getcwd = os.getcwdb
54 55 sysplatform = sys.platform.encode('ascii')
55 56 sysexecutable = sys.executable
56 57 if sysexecutable:
57 58 sysexecutable = os.fsencode(sysexecutable)
58 59
59 60 # TODO: .buffer might not exist if std streams were replaced; we'll need
60 61 # a silly wrapper to make a bytes stream backed by a unicode one.
61 62 stdin = sys.stdin.buffer
62 63 stdout = sys.stdout.buffer
63 64 stderr = sys.stderr.buffer
64 65
65 66 # Since Python 3 converts argv to wchar_t type by Py_DecodeLocale() on Unix,
66 67 # we can use os.fsencode() to get back bytes argv.
67 68 #
68 69 # https://hg.python.org/cpython/file/v3.5.1/Programs/python.c#l55
69 70 #
70 71 # TODO: On Windows, the native argv is wchar_t, so we'll need a different
71 72 # workaround to simulate the Python 2 (i.e. ANSI Win32 API) behavior.
72 73 sysargv = list(map(os.fsencode, sys.argv))
73 74
74 75 def sysstr(s):
75 76 """Return a keyword str to be passed to Python functions such as
76 77 getattr() and str.encode()
77 78
78 79 This never raises UnicodeDecodeError. Non-ascii characters are
79 80 considered invalid and mapped to arbitrary but unique code points
80 81 such that 'sysstr(a) != sysstr(b)' for all 'a != b'.
81 82 """
82 83 if isinstance(s, builtins.str):
83 84 return s
84 85 return s.decode(u'latin-1')
85 86
86 87 def _wrapattrfunc(f):
87 88 @functools.wraps(f)
88 89 def w(object, name, *args):
89 90 return f(object, sysstr(name), *args)
90 91 return w
91 92
92 93 # these wrappers are automagically imported by hgloader
93 94 delattr = _wrapattrfunc(builtins.delattr)
94 95 getattr = _wrapattrfunc(builtins.getattr)
95 96 hasattr = _wrapattrfunc(builtins.hasattr)
96 97 setattr = _wrapattrfunc(builtins.setattr)
97 98 xrange = builtins.range
98 99
99 100 # getopt.getopt() on Python 3 deals with unicodes internally so we cannot
100 101 # pass bytes there. Passing unicodes will result in unicodes as return
101 102 # values which we need to convert again to bytes.
102 103 def getoptb(args, shortlist, namelist):
103 104 args = [a.decode('latin-1') for a in args]
104 105 shortlist = shortlist.decode('latin-1')
105 106 namelist = [a.decode('latin-1') for a in namelist]
106 107 opts, args = getopt.getopt(args, shortlist, namelist)
107 108 opts = [(a[0].encode('latin-1'), a[1].encode('latin-1'))
108 109 for a in opts]
109 110 args = [a.encode('latin-1') for a in args]
110 111 return opts, args
111 112
112 113 # keys of keyword arguments in Python need to be strings which are unicodes
113 114 # Python 3. This function takes keyword arguments, convert the keys to str.
114 115 def strkwargs(dic):
115 116 dic = dict((k.decode('latin-1'), v) for k, v in dic.iteritems())
116 117 return dic
117 118
118 119 # keys of keyword arguments need to be unicode while passing into
119 120 # a function. This function helps us to convert those keys back to bytes
120 121 # again as we need to deal with bytes.
121 122 def byteskwargs(dic):
122 123 dic = dict((k.encode('latin-1'), v) for k, v in dic.iteritems())
123 124 return dic
124 125
126 # shlex.split() accepts unicodes on Python 3. This function takes bytes
127 # argument, convert it into unicodes, pass into shlex.split(), convert the
128 # returned value to bytes and return that.
129 # TODO: handle shlex.shlex().
130 def shlexsplit(s):
131 ret = shlex.split(s.decode('latin-1'))
132 return [a.encode('latin-1') for a in ret]
133
125 134 else:
126 135 def sysstr(s):
127 136 return s
128 137
129 138 # Partial backport from os.py in Python 3, which only accepts bytes.
130 139 # In Python 2, our paths should only ever be bytes, a unicode path
131 140 # indicates a bug.
132 141 def fsencode(filename):
133 142 if isinstance(filename, str):
134 143 return filename
135 144 else:
136 145 raise TypeError(
137 146 "expect str, not %s" % type(filename).__name__)
138 147
139 148 # In Python 2, fsdecode() has a very chance to receive bytes. So it's
140 149 # better not to touch Python 2 part as it's already working fine.
141 150 def fsdecode(filename):
142 151 return filename
143 152
144 153 def getoptb(args, shortlist, namelist):
145 154 return getopt.getopt(args, shortlist, namelist)
146 155
147 156 def strkwargs(dic):
148 157 return dic
149 158
150 159 def byteskwargs(dic):
151 160 return dic
152 161
153 162 osname = os.name
154 163 ospathsep = os.pathsep
155 164 ossep = os.sep
156 165 osaltsep = os.altsep
157 166 stdin = sys.stdin
158 167 stdout = sys.stdout
159 168 stderr = sys.stderr
160 169 sysargv = sys.argv
161 170 sysplatform = sys.platform
162 171 getcwd = os.getcwd
163 172 osgetenv = os.getenv
164 173 sysexecutable = sys.executable
174 shlexsplit = shlex.split
165 175
166 176 stringio = io.StringIO
167 177 empty = _queue.Empty
168 178 queue = _queue.Queue
169 179
170 180 class _pycompatstub(object):
171 181 def __init__(self):
172 182 self._aliases = {}
173 183
174 184 def _registeraliases(self, origin, items):
175 185 """Add items that will be populated at the first access"""
176 186 items = map(sysstr, items)
177 187 self._aliases.update(
178 188 (item.replace(sysstr('_'), sysstr('')).lower(), (origin, item))
179 189 for item in items)
180 190
181 191 def __getattr__(self, name):
182 192 try:
183 193 origin, item = self._aliases[name]
184 194 except KeyError:
185 195 raise AttributeError(name)
186 196 self.__dict__[name] = obj = getattr(origin, item)
187 197 return obj
188 198
189 199 httpserver = _pycompatstub()
190 200 urlreq = _pycompatstub()
191 201 urlerr = _pycompatstub()
192 202 if not ispy3:
193 203 import BaseHTTPServer
194 204 import CGIHTTPServer
195 205 import SimpleHTTPServer
196 206 import urllib2
197 207 import urllib
198 208 urlreq._registeraliases(urllib, (
199 209 "addclosehook",
200 210 "addinfourl",
201 211 "ftpwrapper",
202 212 "pathname2url",
203 213 "quote",
204 214 "splitattr",
205 215 "splitpasswd",
206 216 "splitport",
207 217 "splituser",
208 218 "unquote",
209 219 "url2pathname",
210 220 "urlencode",
211 221 ))
212 222 urlreq._registeraliases(urllib2, (
213 223 "AbstractHTTPHandler",
214 224 "BaseHandler",
215 225 "build_opener",
216 226 "FileHandler",
217 227 "FTPHandler",
218 228 "HTTPBasicAuthHandler",
219 229 "HTTPDigestAuthHandler",
220 230 "HTTPHandler",
221 231 "HTTPPasswordMgrWithDefaultRealm",
222 232 "HTTPSHandler",
223 233 "install_opener",
224 234 "ProxyHandler",
225 235 "Request",
226 236 "urlopen",
227 237 ))
228 238 urlerr._registeraliases(urllib2, (
229 239 "HTTPError",
230 240 "URLError",
231 241 ))
232 242 httpserver._registeraliases(BaseHTTPServer, (
233 243 "HTTPServer",
234 244 "BaseHTTPRequestHandler",
235 245 ))
236 246 httpserver._registeraliases(SimpleHTTPServer, (
237 247 "SimpleHTTPRequestHandler",
238 248 ))
239 249 httpserver._registeraliases(CGIHTTPServer, (
240 250 "CGIHTTPRequestHandler",
241 251 ))
242 252
243 253 else:
244 254 import urllib.request
245 255 urlreq._registeraliases(urllib.request, (
246 256 "AbstractHTTPHandler",
247 257 "addclosehook",
248 258 "addinfourl",
249 259 "BaseHandler",
250 260 "build_opener",
251 261 "FileHandler",
252 262 "FTPHandler",
253 263 "ftpwrapper",
254 264 "HTTPHandler",
255 265 "HTTPSHandler",
256 266 "install_opener",
257 267 "pathname2url",
258 268 "HTTPBasicAuthHandler",
259 269 "HTTPDigestAuthHandler",
260 270 "HTTPPasswordMgrWithDefaultRealm",
261 271 "ProxyHandler",
262 272 "quote",
263 273 "Request",
264 274 "splitattr",
265 275 "splitpasswd",
266 276 "splitport",
267 277 "splituser",
268 278 "unquote",
269 279 "url2pathname",
270 280 "urlopen",
271 281 ))
272 282 import urllib.error
273 283 urlerr._registeraliases(urllib.error, (
274 284 "HTTPError",
275 285 "URLError",
276 286 ))
277 287 import http.server
278 288 httpserver._registeraliases(http.server, (
279 289 "HTTPServer",
280 290 "BaseHTTPRequestHandler",
281 291 "SimpleHTTPRequestHandler",
282 292 "CGIHTTPRequestHandler",
283 293 ))
General Comments 0
You need to be logged in to leave comments. Login now