##// 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 # extdiff.py - external diff program support for mercurial
1 # extdiff.py - external diff program support for mercurial
2 #
2 #
3 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
3 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 '''command to allow external programs to compare revisions
8 '''command to allow external programs to compare revisions
9
9
10 The extdiff Mercurial extension allows you to use external programs
10 The extdiff Mercurial extension allows you to use external programs
11 to compare revisions, or revision with working directory. The external
11 to compare revisions, or revision with working directory. The external
12 diff programs are called with a configurable set of options and two
12 diff programs are called with a configurable set of options and two
13 non-option arguments: paths to directories containing snapshots of
13 non-option arguments: paths to directories containing snapshots of
14 files to compare.
14 files to compare.
15
15
16 The extdiff extension also allows you to configure new diff commands, so
16 The extdiff extension also allows you to configure new diff commands, so
17 you do not need to type :hg:`extdiff -p kdiff3` always. ::
17 you do not need to type :hg:`extdiff -p kdiff3` always. ::
18
18
19 [extdiff]
19 [extdiff]
20 # add new command that runs GNU diff(1) in 'context diff' mode
20 # add new command that runs GNU diff(1) in 'context diff' mode
21 cdiff = gdiff -Nprc5
21 cdiff = gdiff -Nprc5
22 ## or the old way:
22 ## or the old way:
23 #cmd.cdiff = gdiff
23 #cmd.cdiff = gdiff
24 #opts.cdiff = -Nprc5
24 #opts.cdiff = -Nprc5
25
25
26 # add new command called meld, runs meld (no need to name twice). If
26 # add new command called meld, runs meld (no need to name twice). If
27 # the meld executable is not available, the meld tool in [merge-tools]
27 # the meld executable is not available, the meld tool in [merge-tools]
28 # will be used, if available
28 # will be used, if available
29 meld =
29 meld =
30
30
31 # add new command called vimdiff, runs gvimdiff with DirDiff plugin
31 # add new command called vimdiff, runs gvimdiff with DirDiff plugin
32 # (see http://www.vim.org/scripts/script.php?script_id=102) Non
32 # (see http://www.vim.org/scripts/script.php?script_id=102) Non
33 # English user, be sure to put "let g:DirDiffDynamicDiffText = 1" in
33 # English user, be sure to put "let g:DirDiffDynamicDiffText = 1" in
34 # your .vimrc
34 # your .vimrc
35 vimdiff = gvim -f "+next" \\
35 vimdiff = gvim -f "+next" \\
36 "+execute 'DirDiff' fnameescape(argv(0)) fnameescape(argv(1))"
36 "+execute 'DirDiff' fnameescape(argv(0)) fnameescape(argv(1))"
37
37
38 Tool arguments can include variables that are expanded at runtime::
38 Tool arguments can include variables that are expanded at runtime::
39
39
40 $parent1, $plabel1 - filename, descriptive label of first parent
40 $parent1, $plabel1 - filename, descriptive label of first parent
41 $child, $clabel - filename, descriptive label of child revision
41 $child, $clabel - filename, descriptive label of child revision
42 $parent2, $plabel2 - filename, descriptive label of second parent
42 $parent2, $plabel2 - filename, descriptive label of second parent
43 $root - repository root
43 $root - repository root
44 $parent is an alias for $parent1.
44 $parent is an alias for $parent1.
45
45
46 The extdiff extension will look in your [diff-tools] and [merge-tools]
46 The extdiff extension will look in your [diff-tools] and [merge-tools]
47 sections for diff tool arguments, when none are specified in [extdiff].
47 sections for diff tool arguments, when none are specified in [extdiff].
48
48
49 ::
49 ::
50
50
51 [extdiff]
51 [extdiff]
52 kdiff3 =
52 kdiff3 =
53
53
54 [diff-tools]
54 [diff-tools]
55 kdiff3.diffargs=--L1 '$plabel1' --L2 '$clabel' $parent $child
55 kdiff3.diffargs=--L1 '$plabel1' --L2 '$clabel' $parent $child
56
56
57 You can use -I/-X and list of file or directory names like normal
57 You can use -I/-X and list of file or directory names like normal
58 :hg:`diff` command. The extdiff extension makes snapshots of only
58 :hg:`diff` command. The extdiff extension makes snapshots of only
59 needed files, so running the external diff program will actually be
59 needed files, so running the external diff program will actually be
60 pretty fast (at least faster than having to compare the entire tree).
60 pretty fast (at least faster than having to compare the entire tree).
61 '''
61 '''
62
62
63 from __future__ import absolute_import
63 from __future__ import absolute_import
64
64
65 import os
65 import os
66 import re
66 import re
67 import shlex
68 import shutil
67 import shutil
69 import tempfile
68 import tempfile
70 from mercurial.i18n import _
69 from mercurial.i18n import _
71 from mercurial.node import (
70 from mercurial.node import (
72 nullid,
71 nullid,
73 short,
72 short,
74 )
73 )
75 from mercurial import (
74 from mercurial import (
76 archival,
75 archival,
77 cmdutil,
76 cmdutil,
78 commands,
77 commands,
79 error,
78 error,
80 filemerge,
79 filemerge,
80 pycompat,
81 scmutil,
81 scmutil,
82 util,
82 util,
83 )
83 )
84
84
85 cmdtable = {}
85 cmdtable = {}
86 command = cmdutil.command(cmdtable)
86 command = cmdutil.command(cmdtable)
87 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
87 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
88 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
88 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
89 # be specifying the version(s) of Mercurial they are tested with, or
89 # be specifying the version(s) of Mercurial they are tested with, or
90 # leave the attribute unspecified.
90 # leave the attribute unspecified.
91 testedwith = 'ships-with-hg-core'
91 testedwith = 'ships-with-hg-core'
92
92
93 def snapshot(ui, repo, files, node, tmproot, listsubrepos):
93 def snapshot(ui, repo, files, node, tmproot, listsubrepos):
94 '''snapshot files as of some revision
94 '''snapshot files as of some revision
95 if not using snapshot, -I/-X does not work and recursive diff
95 if not using snapshot, -I/-X does not work and recursive diff
96 in tools like kdiff3 and meld displays too many files.'''
96 in tools like kdiff3 and meld displays too many files.'''
97 dirname = os.path.basename(repo.root)
97 dirname = os.path.basename(repo.root)
98 if dirname == "":
98 if dirname == "":
99 dirname = "root"
99 dirname = "root"
100 if node is not None:
100 if node is not None:
101 dirname = '%s.%s' % (dirname, short(node))
101 dirname = '%s.%s' % (dirname, short(node))
102 base = os.path.join(tmproot, dirname)
102 base = os.path.join(tmproot, dirname)
103 os.mkdir(base)
103 os.mkdir(base)
104 fns_and_mtime = []
104 fns_and_mtime = []
105
105
106 if node is not None:
106 if node is not None:
107 ui.note(_('making snapshot of %d files from rev %s\n') %
107 ui.note(_('making snapshot of %d files from rev %s\n') %
108 (len(files), short(node)))
108 (len(files), short(node)))
109 else:
109 else:
110 ui.note(_('making snapshot of %d files from working directory\n') %
110 ui.note(_('making snapshot of %d files from working directory\n') %
111 (len(files)))
111 (len(files)))
112
112
113 if files:
113 if files:
114 repo.ui.setconfig("ui", "archivemeta", False)
114 repo.ui.setconfig("ui", "archivemeta", False)
115
115
116 archival.archive(repo, base, node, 'files',
116 archival.archive(repo, base, node, 'files',
117 matchfn=scmutil.matchfiles(repo, files),
117 matchfn=scmutil.matchfiles(repo, files),
118 subrepos=listsubrepos)
118 subrepos=listsubrepos)
119
119
120 for fn in sorted(files):
120 for fn in sorted(files):
121 wfn = util.pconvert(fn)
121 wfn = util.pconvert(fn)
122 ui.note(' %s\n' % wfn)
122 ui.note(' %s\n' % wfn)
123
123
124 if node is None:
124 if node is None:
125 dest = os.path.join(base, wfn)
125 dest = os.path.join(base, wfn)
126
126
127 fns_and_mtime.append((dest, repo.wjoin(fn),
127 fns_and_mtime.append((dest, repo.wjoin(fn),
128 os.lstat(dest).st_mtime))
128 os.lstat(dest).st_mtime))
129 return dirname, fns_and_mtime
129 return dirname, fns_and_mtime
130
130
131 def dodiff(ui, repo, cmdline, pats, opts):
131 def dodiff(ui, repo, cmdline, pats, opts):
132 '''Do the actual diff:
132 '''Do the actual diff:
133
133
134 - copy to a temp structure if diffing 2 internal revisions
134 - copy to a temp structure if diffing 2 internal revisions
135 - copy to a temp structure if diffing working revision with
135 - copy to a temp structure if diffing working revision with
136 another one and more than 1 file is changed
136 another one and more than 1 file is changed
137 - just invoke the diff for a single file in the working dir
137 - just invoke the diff for a single file in the working dir
138 '''
138 '''
139
139
140 revs = opts.get('rev')
140 revs = opts.get('rev')
141 change = opts.get('change')
141 change = opts.get('change')
142 do3way = '$parent2' in cmdline
142 do3way = '$parent2' in cmdline
143
143
144 if revs and change:
144 if revs and change:
145 msg = _('cannot specify --rev and --change at the same time')
145 msg = _('cannot specify --rev and --change at the same time')
146 raise error.Abort(msg)
146 raise error.Abort(msg)
147 elif change:
147 elif change:
148 node2 = scmutil.revsingle(repo, change, None).node()
148 node2 = scmutil.revsingle(repo, change, None).node()
149 node1a, node1b = repo.changelog.parents(node2)
149 node1a, node1b = repo.changelog.parents(node2)
150 else:
150 else:
151 node1a, node2 = scmutil.revpair(repo, revs)
151 node1a, node2 = scmutil.revpair(repo, revs)
152 if not revs:
152 if not revs:
153 node1b = repo.dirstate.p2()
153 node1b = repo.dirstate.p2()
154 else:
154 else:
155 node1b = nullid
155 node1b = nullid
156
156
157 # Disable 3-way merge if there is only one parent
157 # Disable 3-way merge if there is only one parent
158 if do3way:
158 if do3way:
159 if node1b == nullid:
159 if node1b == nullid:
160 do3way = False
160 do3way = False
161
161
162 subrepos=opts.get('subrepos')
162 subrepos=opts.get('subrepos')
163
163
164 matcher = scmutil.match(repo[node2], pats, opts)
164 matcher = scmutil.match(repo[node2], pats, opts)
165
165
166 if opts.get('patch'):
166 if opts.get('patch'):
167 if subrepos:
167 if subrepos:
168 raise error.Abort(_('--patch cannot be used with --subrepos'))
168 raise error.Abort(_('--patch cannot be used with --subrepos'))
169 if node2 is None:
169 if node2 is None:
170 raise error.Abort(_('--patch requires two revisions'))
170 raise error.Abort(_('--patch requires two revisions'))
171 else:
171 else:
172 mod_a, add_a, rem_a = map(set, repo.status(node1a, node2, matcher,
172 mod_a, add_a, rem_a = map(set, repo.status(node1a, node2, matcher,
173 listsubrepos=subrepos)[:3])
173 listsubrepos=subrepos)[:3])
174 if do3way:
174 if do3way:
175 mod_b, add_b, rem_b = map(set,
175 mod_b, add_b, rem_b = map(set,
176 repo.status(node1b, node2, matcher,
176 repo.status(node1b, node2, matcher,
177 listsubrepos=subrepos)[:3])
177 listsubrepos=subrepos)[:3])
178 else:
178 else:
179 mod_b, add_b, rem_b = set(), set(), set()
179 mod_b, add_b, rem_b = set(), set(), set()
180 modadd = mod_a | add_a | mod_b | add_b
180 modadd = mod_a | add_a | mod_b | add_b
181 common = modadd | rem_a | rem_b
181 common = modadd | rem_a | rem_b
182 if not common:
182 if not common:
183 return 0
183 return 0
184
184
185 tmproot = tempfile.mkdtemp(prefix='extdiff.')
185 tmproot = tempfile.mkdtemp(prefix='extdiff.')
186 try:
186 try:
187 if not opts.get('patch'):
187 if not opts.get('patch'):
188 # Always make a copy of node1a (and node1b, if applicable)
188 # Always make a copy of node1a (and node1b, if applicable)
189 dir1a_files = mod_a | rem_a | ((mod_b | add_b) - add_a)
189 dir1a_files = mod_a | rem_a | ((mod_b | add_b) - add_a)
190 dir1a = snapshot(ui, repo, dir1a_files, node1a, tmproot,
190 dir1a = snapshot(ui, repo, dir1a_files, node1a, tmproot,
191 subrepos)[0]
191 subrepos)[0]
192 rev1a = '@%d' % repo[node1a].rev()
192 rev1a = '@%d' % repo[node1a].rev()
193 if do3way:
193 if do3way:
194 dir1b_files = mod_b | rem_b | ((mod_a | add_a) - add_b)
194 dir1b_files = mod_b | rem_b | ((mod_a | add_a) - add_b)
195 dir1b = snapshot(ui, repo, dir1b_files, node1b, tmproot,
195 dir1b = snapshot(ui, repo, dir1b_files, node1b, tmproot,
196 subrepos)[0]
196 subrepos)[0]
197 rev1b = '@%d' % repo[node1b].rev()
197 rev1b = '@%d' % repo[node1b].rev()
198 else:
198 else:
199 dir1b = None
199 dir1b = None
200 rev1b = ''
200 rev1b = ''
201
201
202 fns_and_mtime = []
202 fns_and_mtime = []
203
203
204 # If node2 in not the wc or there is >1 change, copy it
204 # If node2 in not the wc or there is >1 change, copy it
205 dir2root = ''
205 dir2root = ''
206 rev2 = ''
206 rev2 = ''
207 if node2:
207 if node2:
208 dir2 = snapshot(ui, repo, modadd, node2, tmproot, subrepos)[0]
208 dir2 = snapshot(ui, repo, modadd, node2, tmproot, subrepos)[0]
209 rev2 = '@%d' % repo[node2].rev()
209 rev2 = '@%d' % repo[node2].rev()
210 elif len(common) > 1:
210 elif len(common) > 1:
211 #we only actually need to get the files to copy back to
211 #we only actually need to get the files to copy back to
212 #the working dir in this case (because the other cases
212 #the working dir in this case (because the other cases
213 #are: diffing 2 revisions or single file -- in which case
213 #are: diffing 2 revisions or single file -- in which case
214 #the file is already directly passed to the diff tool).
214 #the file is already directly passed to the diff tool).
215 dir2, fns_and_mtime = snapshot(ui, repo, modadd, None, tmproot,
215 dir2, fns_and_mtime = snapshot(ui, repo, modadd, None, tmproot,
216 subrepos)
216 subrepos)
217 else:
217 else:
218 # This lets the diff tool open the changed file directly
218 # This lets the diff tool open the changed file directly
219 dir2 = ''
219 dir2 = ''
220 dir2root = repo.root
220 dir2root = repo.root
221
221
222 label1a = rev1a
222 label1a = rev1a
223 label1b = rev1b
223 label1b = rev1b
224 label2 = rev2
224 label2 = rev2
225
225
226 # If only one change, diff the files instead of the directories
226 # If only one change, diff the files instead of the directories
227 # Handle bogus modifies correctly by checking if the files exist
227 # Handle bogus modifies correctly by checking if the files exist
228 if len(common) == 1:
228 if len(common) == 1:
229 common_file = util.localpath(common.pop())
229 common_file = util.localpath(common.pop())
230 dir1a = os.path.join(tmproot, dir1a, common_file)
230 dir1a = os.path.join(tmproot, dir1a, common_file)
231 label1a = common_file + rev1a
231 label1a = common_file + rev1a
232 if not os.path.isfile(dir1a):
232 if not os.path.isfile(dir1a):
233 dir1a = os.devnull
233 dir1a = os.devnull
234 if do3way:
234 if do3way:
235 dir1b = os.path.join(tmproot, dir1b, common_file)
235 dir1b = os.path.join(tmproot, dir1b, common_file)
236 label1b = common_file + rev1b
236 label1b = common_file + rev1b
237 if not os.path.isfile(dir1b):
237 if not os.path.isfile(dir1b):
238 dir1b = os.devnull
238 dir1b = os.devnull
239 dir2 = os.path.join(dir2root, dir2, common_file)
239 dir2 = os.path.join(dir2root, dir2, common_file)
240 label2 = common_file + rev2
240 label2 = common_file + rev2
241 else:
241 else:
242 template = 'hg-%h.patch'
242 template = 'hg-%h.patch'
243 cmdutil.export(repo, [repo[node1a].rev(), repo[node2].rev()],
243 cmdutil.export(repo, [repo[node1a].rev(), repo[node2].rev()],
244 template=repo.vfs.reljoin(tmproot, template),
244 template=repo.vfs.reljoin(tmproot, template),
245 match=matcher)
245 match=matcher)
246 label1a = cmdutil.makefilename(repo, template, node1a)
246 label1a = cmdutil.makefilename(repo, template, node1a)
247 label2 = cmdutil.makefilename(repo, template, node2)
247 label2 = cmdutil.makefilename(repo, template, node2)
248 dir1a = repo.vfs.reljoin(tmproot, label1a)
248 dir1a = repo.vfs.reljoin(tmproot, label1a)
249 dir2 = repo.vfs.reljoin(tmproot, label2)
249 dir2 = repo.vfs.reljoin(tmproot, label2)
250 dir1b = None
250 dir1b = None
251 label1b = None
251 label1b = None
252 fns_and_mtime = []
252 fns_and_mtime = []
253
253
254 # Function to quote file/dir names in the argument string.
254 # Function to quote file/dir names in the argument string.
255 # When not operating in 3-way mode, an empty string is
255 # When not operating in 3-way mode, an empty string is
256 # returned for parent2
256 # returned for parent2
257 replace = {'parent': dir1a, 'parent1': dir1a, 'parent2': dir1b,
257 replace = {'parent': dir1a, 'parent1': dir1a, 'parent2': dir1b,
258 'plabel1': label1a, 'plabel2': label1b,
258 'plabel1': label1a, 'plabel2': label1b,
259 'clabel': label2, 'child': dir2,
259 'clabel': label2, 'child': dir2,
260 'root': repo.root}
260 'root': repo.root}
261 def quote(match):
261 def quote(match):
262 pre = match.group(2)
262 pre = match.group(2)
263 key = match.group(3)
263 key = match.group(3)
264 if not do3way and key == 'parent2':
264 if not do3way and key == 'parent2':
265 return pre
265 return pre
266 return pre + util.shellquote(replace[key])
266 return pre + util.shellquote(replace[key])
267
267
268 # Match parent2 first, so 'parent1?' will match both parent1 and parent
268 # Match parent2 first, so 'parent1?' will match both parent1 and parent
269 regex = (r'''(['"]?)([^\s'"$]*)'''
269 regex = (r'''(['"]?)([^\s'"$]*)'''
270 r'\$(parent2|parent1?|child|plabel1|plabel2|clabel|root)\1')
270 r'\$(parent2|parent1?|child|plabel1|plabel2|clabel|root)\1')
271 if not do3way and not re.search(regex, cmdline):
271 if not do3way and not re.search(regex, cmdline):
272 cmdline += ' $parent1 $child'
272 cmdline += ' $parent1 $child'
273 cmdline = re.sub(regex, quote, cmdline)
273 cmdline = re.sub(regex, quote, cmdline)
274
274
275 ui.debug('running %r in %s\n' % (cmdline, tmproot))
275 ui.debug('running %r in %s\n' % (cmdline, tmproot))
276 ui.system(cmdline, cwd=tmproot)
276 ui.system(cmdline, cwd=tmproot)
277
277
278 for copy_fn, working_fn, mtime in fns_and_mtime:
278 for copy_fn, working_fn, mtime in fns_and_mtime:
279 if os.lstat(copy_fn).st_mtime != mtime:
279 if os.lstat(copy_fn).st_mtime != mtime:
280 ui.debug('file changed while diffing. '
280 ui.debug('file changed while diffing. '
281 'Overwriting: %s (src: %s)\n' % (working_fn, copy_fn))
281 'Overwriting: %s (src: %s)\n' % (working_fn, copy_fn))
282 util.copyfile(copy_fn, working_fn)
282 util.copyfile(copy_fn, working_fn)
283
283
284 return 1
284 return 1
285 finally:
285 finally:
286 ui.note(_('cleaning up temp directory\n'))
286 ui.note(_('cleaning up temp directory\n'))
287 shutil.rmtree(tmproot)
287 shutil.rmtree(tmproot)
288
288
289 extdiffopts = [
289 extdiffopts = [
290 ('o', 'option', [],
290 ('o', 'option', [],
291 _('pass option to comparison program'), _('OPT')),
291 _('pass option to comparison program'), _('OPT')),
292 ('r', 'rev', [], _('revision'), _('REV')),
292 ('r', 'rev', [], _('revision'), _('REV')),
293 ('c', 'change', '', _('change made by revision'), _('REV')),
293 ('c', 'change', '', _('change made by revision'), _('REV')),
294 ('', 'patch', None, _('compare patches for two revisions'))
294 ('', 'patch', None, _('compare patches for two revisions'))
295 ] + commands.walkopts + commands.subrepoopts
295 ] + commands.walkopts + commands.subrepoopts
296
296
297 @command('extdiff',
297 @command('extdiff',
298 [('p', 'program', '', _('comparison program to run'), _('CMD')),
298 [('p', 'program', '', _('comparison program to run'), _('CMD')),
299 ] + extdiffopts,
299 ] + extdiffopts,
300 _('hg extdiff [OPT]... [FILE]...'),
300 _('hg extdiff [OPT]... [FILE]...'),
301 inferrepo=True)
301 inferrepo=True)
302 def extdiff(ui, repo, *pats, **opts):
302 def extdiff(ui, repo, *pats, **opts):
303 '''use external program to diff repository (or selected files)
303 '''use external program to diff repository (or selected files)
304
304
305 Show differences between revisions for the specified files, using
305 Show differences between revisions for the specified files, using
306 an external program. The default program used is diff, with
306 an external program. The default program used is diff, with
307 default options "-Npru".
307 default options "-Npru".
308
308
309 To select a different program, use the -p/--program option. The
309 To select a different program, use the -p/--program option. The
310 program will be passed the names of two directories to compare. To
310 program will be passed the names of two directories to compare. To
311 pass additional options to the program, use -o/--option. These
311 pass additional options to the program, use -o/--option. These
312 will be passed before the names of the directories to compare.
312 will be passed before the names of the directories to compare.
313
313
314 When two revision arguments are given, then changes are shown
314 When two revision arguments are given, then changes are shown
315 between those revisions. If only one revision is specified then
315 between those revisions. If only one revision is specified then
316 that revision is compared to the working directory, and, when no
316 that revision is compared to the working directory, and, when no
317 revisions are specified, the working directory files are compared
317 revisions are specified, the working directory files are compared
318 to its parent.'''
318 to its parent.'''
319 program = opts.get('program')
319 program = opts.get('program')
320 option = opts.get('option')
320 option = opts.get('option')
321 if not program:
321 if not program:
322 program = 'diff'
322 program = 'diff'
323 option = option or ['-Npru']
323 option = option or ['-Npru']
324 cmdline = ' '.join(map(util.shellquote, [program] + option))
324 cmdline = ' '.join(map(util.shellquote, [program] + option))
325 return dodiff(ui, repo, cmdline, pats, opts)
325 return dodiff(ui, repo, cmdline, pats, opts)
326
326
327 class savedcmd(object):
327 class savedcmd(object):
328 """use external program to diff repository (or selected files)
328 """use external program to diff repository (or selected files)
329
329
330 Show differences between revisions for the specified files, using
330 Show differences between revisions for the specified files, using
331 the following program::
331 the following program::
332
332
333 %(path)s
333 %(path)s
334
334
335 When two revision arguments are given, then changes are shown
335 When two revision arguments are given, then changes are shown
336 between those revisions. If only one revision is specified then
336 between those revisions. If only one revision is specified then
337 that revision is compared to the working directory, and, when no
337 that revision is compared to the working directory, and, when no
338 revisions are specified, the working directory files are compared
338 revisions are specified, the working directory files are compared
339 to its parent.
339 to its parent.
340 """
340 """
341
341
342 def __init__(self, path, cmdline):
342 def __init__(self, path, cmdline):
343 # We can't pass non-ASCII through docstrings (and path is
343 # We can't pass non-ASCII through docstrings (and path is
344 # in an unknown encoding anyway)
344 # in an unknown encoding anyway)
345 docpath = path.encode("string-escape")
345 docpath = path.encode("string-escape")
346 self.__doc__ = self.__doc__ % {'path': util.uirepr(docpath)}
346 self.__doc__ = self.__doc__ % {'path': util.uirepr(docpath)}
347 self._cmdline = cmdline
347 self._cmdline = cmdline
348
348
349 def __call__(self, ui, repo, *pats, **opts):
349 def __call__(self, ui, repo, *pats, **opts):
350 options = ' '.join(map(util.shellquote, opts['option']))
350 options = ' '.join(map(util.shellquote, opts['option']))
351 if options:
351 if options:
352 options = ' ' + options
352 options = ' ' + options
353 return dodiff(ui, repo, self._cmdline + options, pats, opts)
353 return dodiff(ui, repo, self._cmdline + options, pats, opts)
354
354
355 def uisetup(ui):
355 def uisetup(ui):
356 for cmd, path in ui.configitems('extdiff'):
356 for cmd, path in ui.configitems('extdiff'):
357 path = util.expandpath(path)
357 path = util.expandpath(path)
358 if cmd.startswith('cmd.'):
358 if cmd.startswith('cmd.'):
359 cmd = cmd[4:]
359 cmd = cmd[4:]
360 if not path:
360 if not path:
361 path = util.findexe(cmd)
361 path = util.findexe(cmd)
362 if path is None:
362 if path is None:
363 path = filemerge.findexternaltool(ui, cmd) or cmd
363 path = filemerge.findexternaltool(ui, cmd) or cmd
364 diffopts = ui.config('extdiff', 'opts.' + cmd, '')
364 diffopts = ui.config('extdiff', 'opts.' + cmd, '')
365 cmdline = util.shellquote(path)
365 cmdline = util.shellquote(path)
366 if diffopts:
366 if diffopts:
367 cmdline += ' ' + diffopts
367 cmdline += ' ' + diffopts
368 elif cmd.startswith('opts.'):
368 elif cmd.startswith('opts.'):
369 continue
369 continue
370 else:
370 else:
371 if path:
371 if path:
372 # case "cmd = path opts"
372 # case "cmd = path opts"
373 cmdline = path
373 cmdline = path
374 diffopts = len(shlex.split(cmdline)) > 1
374 diffopts = len(pycompat.shlexsplit(cmdline)) > 1
375 else:
375 else:
376 # case "cmd ="
376 # case "cmd ="
377 path = util.findexe(cmd)
377 path = util.findexe(cmd)
378 if path is None:
378 if path is None:
379 path = filemerge.findexternaltool(ui, cmd) or cmd
379 path = filemerge.findexternaltool(ui, cmd) or cmd
380 cmdline = util.shellquote(path)
380 cmdline = util.shellquote(path)
381 diffopts = False
381 diffopts = False
382 # look for diff arguments in [diff-tools] then [merge-tools]
382 # look for diff arguments in [diff-tools] then [merge-tools]
383 if not diffopts:
383 if not diffopts:
384 args = ui.config('diff-tools', cmd+'.diffargs') or \
384 args = ui.config('diff-tools', cmd+'.diffargs') or \
385 ui.config('merge-tools', cmd+'.diffargs')
385 ui.config('merge-tools', cmd+'.diffargs')
386 if args:
386 if args:
387 cmdline += ' ' + args
387 cmdline += ' ' + args
388 command(cmd, extdiffopts[:], _('hg %s [OPTION]... [FILE]...') % cmd,
388 command(cmd, extdiffopts[:], _('hg %s [OPTION]... [FILE]...') % cmd,
389 inferrepo=True)(savedcmd(path, cmdline))
389 inferrepo=True)(savedcmd(path, cmdline))
390
390
391 # tell hggettext to extract docstrings from these functions:
391 # tell hggettext to extract docstrings from these functions:
392 i18nfunctions = [savedcmd]
392 i18nfunctions = [savedcmd]
@@ -1,6601 +1,6600 b''
1 # commands.py - command processing for mercurial
1 # commands.py - command processing for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import difflib
10 import difflib
11 import errno
11 import errno
12 import os
12 import os
13 import re
13 import re
14 import shlex
15 import socket
14 import socket
16 import string
15 import string
17 import sys
16 import sys
18 import tempfile
17 import tempfile
19 import time
18 import time
20
19
21 from .i18n import _
20 from .i18n import _
22 from .node import (
21 from .node import (
23 bin,
22 bin,
24 hex,
23 hex,
25 nullhex,
24 nullhex,
26 nullid,
25 nullid,
27 nullrev,
26 nullrev,
28 short,
27 short,
29 )
28 )
30 from . import (
29 from . import (
31 archival,
30 archival,
32 bookmarks,
31 bookmarks,
33 bundle2,
32 bundle2,
34 changegroup,
33 changegroup,
35 cmdutil,
34 cmdutil,
36 copies,
35 copies,
37 destutil,
36 destutil,
38 dirstateguard,
37 dirstateguard,
39 discovery,
38 discovery,
40 encoding,
39 encoding,
41 error,
40 error,
42 exchange,
41 exchange,
43 extensions,
42 extensions,
44 formatter,
43 formatter,
45 graphmod,
44 graphmod,
46 hbisect,
45 hbisect,
47 help,
46 help,
48 hg,
47 hg,
49 lock as lockmod,
48 lock as lockmod,
50 merge as mergemod,
49 merge as mergemod,
51 minirst,
50 minirst,
52 obsolete,
51 obsolete,
53 patch,
52 patch,
54 phases,
53 phases,
55 policy,
54 policy,
56 pvec,
55 pvec,
57 pycompat,
56 pycompat,
58 repair,
57 repair,
59 revlog,
58 revlog,
60 revset,
59 revset,
61 scmutil,
60 scmutil,
62 server,
61 server,
63 sshserver,
62 sshserver,
64 sslutil,
63 sslutil,
65 streamclone,
64 streamclone,
66 templatekw,
65 templatekw,
67 templater,
66 templater,
68 ui as uimod,
67 ui as uimod,
69 util,
68 util,
70 )
69 )
71
70
72 release = lockmod.release
71 release = lockmod.release
73
72
74 table = {}
73 table = {}
75
74
76 command = cmdutil.command(table)
75 command = cmdutil.command(table)
77
76
78 # label constants
77 # label constants
79 # until 3.5, bookmarks.current was the advertised name, not
78 # until 3.5, bookmarks.current was the advertised name, not
80 # bookmarks.active, so we must use both to avoid breaking old
79 # bookmarks.active, so we must use both to avoid breaking old
81 # custom styles
80 # custom styles
82 activebookmarklabel = 'bookmarks.active bookmarks.current'
81 activebookmarklabel = 'bookmarks.active bookmarks.current'
83
82
84 # common command options
83 # common command options
85
84
86 globalopts = [
85 globalopts = [
87 ('R', 'repository', '',
86 ('R', 'repository', '',
88 _('repository root directory or name of overlay bundle file'),
87 _('repository root directory or name of overlay bundle file'),
89 _('REPO')),
88 _('REPO')),
90 ('', 'cwd', '',
89 ('', 'cwd', '',
91 _('change working directory'), _('DIR')),
90 _('change working directory'), _('DIR')),
92 ('y', 'noninteractive', None,
91 ('y', 'noninteractive', None,
93 _('do not prompt, automatically pick the first choice for all prompts')),
92 _('do not prompt, automatically pick the first choice for all prompts')),
94 ('q', 'quiet', None, _('suppress output')),
93 ('q', 'quiet', None, _('suppress output')),
95 ('v', 'verbose', None, _('enable additional output')),
94 ('v', 'verbose', None, _('enable additional output')),
96 ('', 'config', [],
95 ('', 'config', [],
97 _('set/override config option (use \'section.name=value\')'),
96 _('set/override config option (use \'section.name=value\')'),
98 _('CONFIG')),
97 _('CONFIG')),
99 ('', 'debug', None, _('enable debugging output')),
98 ('', 'debug', None, _('enable debugging output')),
100 ('', 'debugger', None, _('start debugger')),
99 ('', 'debugger', None, _('start debugger')),
101 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
100 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
102 _('ENCODE')),
101 _('ENCODE')),
103 ('', 'encodingmode', encoding.encodingmode,
102 ('', 'encodingmode', encoding.encodingmode,
104 _('set the charset encoding mode'), _('MODE')),
103 _('set the charset encoding mode'), _('MODE')),
105 ('', 'traceback', None, _('always print a traceback on exception')),
104 ('', 'traceback', None, _('always print a traceback on exception')),
106 ('', 'time', None, _('time how long the command takes')),
105 ('', 'time', None, _('time how long the command takes')),
107 ('', 'profile', None, _('print command execution profile')),
106 ('', 'profile', None, _('print command execution profile')),
108 ('', 'version', None, _('output version information and exit')),
107 ('', 'version', None, _('output version information and exit')),
109 ('h', 'help', None, _('display help and exit')),
108 ('h', 'help', None, _('display help and exit')),
110 ('', 'hidden', False, _('consider hidden changesets')),
109 ('', 'hidden', False, _('consider hidden changesets')),
111 ]
110 ]
112
111
113 dryrunopts = [('n', 'dry-run', None,
112 dryrunopts = [('n', 'dry-run', None,
114 _('do not perform actions, just print output'))]
113 _('do not perform actions, just print output'))]
115
114
116 remoteopts = [
115 remoteopts = [
117 ('e', 'ssh', '',
116 ('e', 'ssh', '',
118 _('specify ssh command to use'), _('CMD')),
117 _('specify ssh command to use'), _('CMD')),
119 ('', 'remotecmd', '',
118 ('', 'remotecmd', '',
120 _('specify hg command to run on the remote side'), _('CMD')),
119 _('specify hg command to run on the remote side'), _('CMD')),
121 ('', 'insecure', None,
120 ('', 'insecure', None,
122 _('do not verify server certificate (ignoring web.cacerts config)')),
121 _('do not verify server certificate (ignoring web.cacerts config)')),
123 ]
122 ]
124
123
125 walkopts = [
124 walkopts = [
126 ('I', 'include', [],
125 ('I', 'include', [],
127 _('include names matching the given patterns'), _('PATTERN')),
126 _('include names matching the given patterns'), _('PATTERN')),
128 ('X', 'exclude', [],
127 ('X', 'exclude', [],
129 _('exclude names matching the given patterns'), _('PATTERN')),
128 _('exclude names matching the given patterns'), _('PATTERN')),
130 ]
129 ]
131
130
132 commitopts = [
131 commitopts = [
133 ('m', 'message', '',
132 ('m', 'message', '',
134 _('use text as commit message'), _('TEXT')),
133 _('use text as commit message'), _('TEXT')),
135 ('l', 'logfile', '',
134 ('l', 'logfile', '',
136 _('read commit message from file'), _('FILE')),
135 _('read commit message from file'), _('FILE')),
137 ]
136 ]
138
137
139 commitopts2 = [
138 commitopts2 = [
140 ('d', 'date', '',
139 ('d', 'date', '',
141 _('record the specified date as commit date'), _('DATE')),
140 _('record the specified date as commit date'), _('DATE')),
142 ('u', 'user', '',
141 ('u', 'user', '',
143 _('record the specified user as committer'), _('USER')),
142 _('record the specified user as committer'), _('USER')),
144 ]
143 ]
145
144
146 # hidden for now
145 # hidden for now
147 formatteropts = [
146 formatteropts = [
148 ('T', 'template', '',
147 ('T', 'template', '',
149 _('display with template (EXPERIMENTAL)'), _('TEMPLATE')),
148 _('display with template (EXPERIMENTAL)'), _('TEMPLATE')),
150 ]
149 ]
151
150
152 templateopts = [
151 templateopts = [
153 ('', 'style', '',
152 ('', 'style', '',
154 _('display using template map file (DEPRECATED)'), _('STYLE')),
153 _('display using template map file (DEPRECATED)'), _('STYLE')),
155 ('T', 'template', '',
154 ('T', 'template', '',
156 _('display with template'), _('TEMPLATE')),
155 _('display with template'), _('TEMPLATE')),
157 ]
156 ]
158
157
159 logopts = [
158 logopts = [
160 ('p', 'patch', None, _('show patch')),
159 ('p', 'patch', None, _('show patch')),
161 ('g', 'git', None, _('use git extended diff format')),
160 ('g', 'git', None, _('use git extended diff format')),
162 ('l', 'limit', '',
161 ('l', 'limit', '',
163 _('limit number of changes displayed'), _('NUM')),
162 _('limit number of changes displayed'), _('NUM')),
164 ('M', 'no-merges', None, _('do not show merges')),
163 ('M', 'no-merges', None, _('do not show merges')),
165 ('', 'stat', None, _('output diffstat-style summary of changes')),
164 ('', 'stat', None, _('output diffstat-style summary of changes')),
166 ('G', 'graph', None, _("show the revision DAG")),
165 ('G', 'graph', None, _("show the revision DAG")),
167 ] + templateopts
166 ] + templateopts
168
167
169 diffopts = [
168 diffopts = [
170 ('a', 'text', None, _('treat all files as text')),
169 ('a', 'text', None, _('treat all files as text')),
171 ('g', 'git', None, _('use git extended diff format')),
170 ('g', 'git', None, _('use git extended diff format')),
172 ('', 'nodates', None, _('omit dates from diff headers'))
171 ('', 'nodates', None, _('omit dates from diff headers'))
173 ]
172 ]
174
173
175 diffwsopts = [
174 diffwsopts = [
176 ('w', 'ignore-all-space', None,
175 ('w', 'ignore-all-space', None,
177 _('ignore white space when comparing lines')),
176 _('ignore white space when comparing lines')),
178 ('b', 'ignore-space-change', None,
177 ('b', 'ignore-space-change', None,
179 _('ignore changes in the amount of white space')),
178 _('ignore changes in the amount of white space')),
180 ('B', 'ignore-blank-lines', None,
179 ('B', 'ignore-blank-lines', None,
181 _('ignore changes whose lines are all blank')),
180 _('ignore changes whose lines are all blank')),
182 ]
181 ]
183
182
184 diffopts2 = [
183 diffopts2 = [
185 ('', 'noprefix', None, _('omit a/ and b/ prefixes from filenames')),
184 ('', 'noprefix', None, _('omit a/ and b/ prefixes from filenames')),
186 ('p', 'show-function', None, _('show which function each change is in')),
185 ('p', 'show-function', None, _('show which function each change is in')),
187 ('', 'reverse', None, _('produce a diff that undoes the changes')),
186 ('', 'reverse', None, _('produce a diff that undoes the changes')),
188 ] + diffwsopts + [
187 ] + diffwsopts + [
189 ('U', 'unified', '',
188 ('U', 'unified', '',
190 _('number of lines of context to show'), _('NUM')),
189 _('number of lines of context to show'), _('NUM')),
191 ('', 'stat', None, _('output diffstat-style summary of changes')),
190 ('', 'stat', None, _('output diffstat-style summary of changes')),
192 ('', 'root', '', _('produce diffs relative to subdirectory'), _('DIR')),
191 ('', 'root', '', _('produce diffs relative to subdirectory'), _('DIR')),
193 ]
192 ]
194
193
195 mergetoolopts = [
194 mergetoolopts = [
196 ('t', 'tool', '', _('specify merge tool')),
195 ('t', 'tool', '', _('specify merge tool')),
197 ]
196 ]
198
197
199 similarityopts = [
198 similarityopts = [
200 ('s', 'similarity', '',
199 ('s', 'similarity', '',
201 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
200 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
202 ]
201 ]
203
202
204 subrepoopts = [
203 subrepoopts = [
205 ('S', 'subrepos', None,
204 ('S', 'subrepos', None,
206 _('recurse into subrepositories'))
205 _('recurse into subrepositories'))
207 ]
206 ]
208
207
209 debugrevlogopts = [
208 debugrevlogopts = [
210 ('c', 'changelog', False, _('open changelog')),
209 ('c', 'changelog', False, _('open changelog')),
211 ('m', 'manifest', False, _('open manifest')),
210 ('m', 'manifest', False, _('open manifest')),
212 ('', 'dir', '', _('open directory manifest')),
211 ('', 'dir', '', _('open directory manifest')),
213 ]
212 ]
214
213
215 # Commands start here, listed alphabetically
214 # Commands start here, listed alphabetically
216
215
217 @command('^add',
216 @command('^add',
218 walkopts + subrepoopts + dryrunopts,
217 walkopts + subrepoopts + dryrunopts,
219 _('[OPTION]... [FILE]...'),
218 _('[OPTION]... [FILE]...'),
220 inferrepo=True)
219 inferrepo=True)
221 def add(ui, repo, *pats, **opts):
220 def add(ui, repo, *pats, **opts):
222 """add the specified files on the next commit
221 """add the specified files on the next commit
223
222
224 Schedule files to be version controlled and added to the
223 Schedule files to be version controlled and added to the
225 repository.
224 repository.
226
225
227 The files will be added to the repository at the next commit. To
226 The files will be added to the repository at the next commit. To
228 undo an add before that, see :hg:`forget`.
227 undo an add before that, see :hg:`forget`.
229
228
230 If no names are given, add all files to the repository (except
229 If no names are given, add all files to the repository (except
231 files matching ``.hgignore``).
230 files matching ``.hgignore``).
232
231
233 .. container:: verbose
232 .. container:: verbose
234
233
235 Examples:
234 Examples:
236
235
237 - New (unknown) files are added
236 - New (unknown) files are added
238 automatically by :hg:`add`::
237 automatically by :hg:`add`::
239
238
240 $ ls
239 $ ls
241 foo.c
240 foo.c
242 $ hg status
241 $ hg status
243 ? foo.c
242 ? foo.c
244 $ hg add
243 $ hg add
245 adding foo.c
244 adding foo.c
246 $ hg status
245 $ hg status
247 A foo.c
246 A foo.c
248
247
249 - Specific files to be added can be specified::
248 - Specific files to be added can be specified::
250
249
251 $ ls
250 $ ls
252 bar.c foo.c
251 bar.c foo.c
253 $ hg status
252 $ hg status
254 ? bar.c
253 ? bar.c
255 ? foo.c
254 ? foo.c
256 $ hg add bar.c
255 $ hg add bar.c
257 $ hg status
256 $ hg status
258 A bar.c
257 A bar.c
259 ? foo.c
258 ? foo.c
260
259
261 Returns 0 if all files are successfully added.
260 Returns 0 if all files are successfully added.
262 """
261 """
263
262
264 m = scmutil.match(repo[None], pats, opts)
263 m = scmutil.match(repo[None], pats, opts)
265 rejected = cmdutil.add(ui, repo, m, "", False, **opts)
264 rejected = cmdutil.add(ui, repo, m, "", False, **opts)
266 return rejected and 1 or 0
265 return rejected and 1 or 0
267
266
268 @command('addremove',
267 @command('addremove',
269 similarityopts + subrepoopts + walkopts + dryrunopts,
268 similarityopts + subrepoopts + walkopts + dryrunopts,
270 _('[OPTION]... [FILE]...'),
269 _('[OPTION]... [FILE]...'),
271 inferrepo=True)
270 inferrepo=True)
272 def addremove(ui, repo, *pats, **opts):
271 def addremove(ui, repo, *pats, **opts):
273 """add all new files, delete all missing files
272 """add all new files, delete all missing files
274
273
275 Add all new files and remove all missing files from the
274 Add all new files and remove all missing files from the
276 repository.
275 repository.
277
276
278 Unless names are given, new files are ignored if they match any of
277 Unless names are given, new files are ignored if they match any of
279 the patterns in ``.hgignore``. As with add, these changes take
278 the patterns in ``.hgignore``. As with add, these changes take
280 effect at the next commit.
279 effect at the next commit.
281
280
282 Use the -s/--similarity option to detect renamed files. This
281 Use the -s/--similarity option to detect renamed files. This
283 option takes a percentage between 0 (disabled) and 100 (files must
282 option takes a percentage between 0 (disabled) and 100 (files must
284 be identical) as its parameter. With a parameter greater than 0,
283 be identical) as its parameter. With a parameter greater than 0,
285 this compares every removed file with every added file and records
284 this compares every removed file with every added file and records
286 those similar enough as renames. Detecting renamed files this way
285 those similar enough as renames. Detecting renamed files this way
287 can be expensive. After using this option, :hg:`status -C` can be
286 can be expensive. After using this option, :hg:`status -C` can be
288 used to check which files were identified as moved or renamed. If
287 used to check which files were identified as moved or renamed. If
289 not specified, -s/--similarity defaults to 100 and only renames of
288 not specified, -s/--similarity defaults to 100 and only renames of
290 identical files are detected.
289 identical files are detected.
291
290
292 .. container:: verbose
291 .. container:: verbose
293
292
294 Examples:
293 Examples:
295
294
296 - A number of files (bar.c and foo.c) are new,
295 - A number of files (bar.c and foo.c) are new,
297 while foobar.c has been removed (without using :hg:`remove`)
296 while foobar.c has been removed (without using :hg:`remove`)
298 from the repository::
297 from the repository::
299
298
300 $ ls
299 $ ls
301 bar.c foo.c
300 bar.c foo.c
302 $ hg status
301 $ hg status
303 ! foobar.c
302 ! foobar.c
304 ? bar.c
303 ? bar.c
305 ? foo.c
304 ? foo.c
306 $ hg addremove
305 $ hg addremove
307 adding bar.c
306 adding bar.c
308 adding foo.c
307 adding foo.c
309 removing foobar.c
308 removing foobar.c
310 $ hg status
309 $ hg status
311 A bar.c
310 A bar.c
312 A foo.c
311 A foo.c
313 R foobar.c
312 R foobar.c
314
313
315 - A file foobar.c was moved to foo.c without using :hg:`rename`.
314 - A file foobar.c was moved to foo.c without using :hg:`rename`.
316 Afterwards, it was edited slightly::
315 Afterwards, it was edited slightly::
317
316
318 $ ls
317 $ ls
319 foo.c
318 foo.c
320 $ hg status
319 $ hg status
321 ! foobar.c
320 ! foobar.c
322 ? foo.c
321 ? foo.c
323 $ hg addremove --similarity 90
322 $ hg addremove --similarity 90
324 removing foobar.c
323 removing foobar.c
325 adding foo.c
324 adding foo.c
326 recording removal of foobar.c as rename to foo.c (94% similar)
325 recording removal of foobar.c as rename to foo.c (94% similar)
327 $ hg status -C
326 $ hg status -C
328 A foo.c
327 A foo.c
329 foobar.c
328 foobar.c
330 R foobar.c
329 R foobar.c
331
330
332 Returns 0 if all files are successfully added.
331 Returns 0 if all files are successfully added.
333 """
332 """
334 try:
333 try:
335 sim = float(opts.get('similarity') or 100)
334 sim = float(opts.get('similarity') or 100)
336 except ValueError:
335 except ValueError:
337 raise error.Abort(_('similarity must be a number'))
336 raise error.Abort(_('similarity must be a number'))
338 if sim < 0 or sim > 100:
337 if sim < 0 or sim > 100:
339 raise error.Abort(_('similarity must be between 0 and 100'))
338 raise error.Abort(_('similarity must be between 0 and 100'))
340 matcher = scmutil.match(repo[None], pats, opts)
339 matcher = scmutil.match(repo[None], pats, opts)
341 return scmutil.addremove(repo, matcher, "", opts, similarity=sim / 100.0)
340 return scmutil.addremove(repo, matcher, "", opts, similarity=sim / 100.0)
342
341
343 @command('^annotate|blame',
342 @command('^annotate|blame',
344 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
343 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
345 ('', 'follow', None,
344 ('', 'follow', None,
346 _('follow copies/renames and list the filename (DEPRECATED)')),
345 _('follow copies/renames and list the filename (DEPRECATED)')),
347 ('', 'no-follow', None, _("don't follow copies and renames")),
346 ('', 'no-follow', None, _("don't follow copies and renames")),
348 ('a', 'text', None, _('treat all files as text')),
347 ('a', 'text', None, _('treat all files as text')),
349 ('u', 'user', None, _('list the author (long with -v)')),
348 ('u', 'user', None, _('list the author (long with -v)')),
350 ('f', 'file', None, _('list the filename')),
349 ('f', 'file', None, _('list the filename')),
351 ('d', 'date', None, _('list the date (short with -q)')),
350 ('d', 'date', None, _('list the date (short with -q)')),
352 ('n', 'number', None, _('list the revision number (default)')),
351 ('n', 'number', None, _('list the revision number (default)')),
353 ('c', 'changeset', None, _('list the changeset')),
352 ('c', 'changeset', None, _('list the changeset')),
354 ('l', 'line-number', None, _('show line number at the first appearance'))
353 ('l', 'line-number', None, _('show line number at the first appearance'))
355 ] + diffwsopts + walkopts + formatteropts,
354 ] + diffwsopts + walkopts + formatteropts,
356 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
355 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
357 inferrepo=True)
356 inferrepo=True)
358 def annotate(ui, repo, *pats, **opts):
357 def annotate(ui, repo, *pats, **opts):
359 """show changeset information by line for each file
358 """show changeset information by line for each file
360
359
361 List changes in files, showing the revision id responsible for
360 List changes in files, showing the revision id responsible for
362 each line.
361 each line.
363
362
364 This command is useful for discovering when a change was made and
363 This command is useful for discovering when a change was made and
365 by whom.
364 by whom.
366
365
367 If you include --file, --user, or --date, the revision number is
366 If you include --file, --user, or --date, the revision number is
368 suppressed unless you also include --number.
367 suppressed unless you also include --number.
369
368
370 Without the -a/--text option, annotate will avoid processing files
369 Without the -a/--text option, annotate will avoid processing files
371 it detects as binary. With -a, annotate will annotate the file
370 it detects as binary. With -a, annotate will annotate the file
372 anyway, although the results will probably be neither useful
371 anyway, although the results will probably be neither useful
373 nor desirable.
372 nor desirable.
374
373
375 Returns 0 on success.
374 Returns 0 on success.
376 """
375 """
377 if not pats:
376 if not pats:
378 raise error.Abort(_('at least one filename or pattern is required'))
377 raise error.Abort(_('at least one filename or pattern is required'))
379
378
380 if opts.get('follow'):
379 if opts.get('follow'):
381 # --follow is deprecated and now just an alias for -f/--file
380 # --follow is deprecated and now just an alias for -f/--file
382 # to mimic the behavior of Mercurial before version 1.5
381 # to mimic the behavior of Mercurial before version 1.5
383 opts['file'] = True
382 opts['file'] = True
384
383
385 ctx = scmutil.revsingle(repo, opts.get('rev'))
384 ctx = scmutil.revsingle(repo, opts.get('rev'))
386
385
387 fm = ui.formatter('annotate', opts)
386 fm = ui.formatter('annotate', opts)
388 if ui.quiet:
387 if ui.quiet:
389 datefunc = util.shortdate
388 datefunc = util.shortdate
390 else:
389 else:
391 datefunc = util.datestr
390 datefunc = util.datestr
392 if ctx.rev() is None:
391 if ctx.rev() is None:
393 def hexfn(node):
392 def hexfn(node):
394 if node is None:
393 if node is None:
395 return None
394 return None
396 else:
395 else:
397 return fm.hexfunc(node)
396 return fm.hexfunc(node)
398 if opts.get('changeset'):
397 if opts.get('changeset'):
399 # omit "+" suffix which is appended to node hex
398 # omit "+" suffix which is appended to node hex
400 def formatrev(rev):
399 def formatrev(rev):
401 if rev is None:
400 if rev is None:
402 return '%d' % ctx.p1().rev()
401 return '%d' % ctx.p1().rev()
403 else:
402 else:
404 return '%d' % rev
403 return '%d' % rev
405 else:
404 else:
406 def formatrev(rev):
405 def formatrev(rev):
407 if rev is None:
406 if rev is None:
408 return '%d+' % ctx.p1().rev()
407 return '%d+' % ctx.p1().rev()
409 else:
408 else:
410 return '%d ' % rev
409 return '%d ' % rev
411 def formathex(hex):
410 def formathex(hex):
412 if hex is None:
411 if hex is None:
413 return '%s+' % fm.hexfunc(ctx.p1().node())
412 return '%s+' % fm.hexfunc(ctx.p1().node())
414 else:
413 else:
415 return '%s ' % hex
414 return '%s ' % hex
416 else:
415 else:
417 hexfn = fm.hexfunc
416 hexfn = fm.hexfunc
418 formatrev = formathex = str
417 formatrev = formathex = str
419
418
420 opmap = [('user', ' ', lambda x: x[0].user(), ui.shortuser),
419 opmap = [('user', ' ', lambda x: x[0].user(), ui.shortuser),
421 ('number', ' ', lambda x: x[0].rev(), formatrev),
420 ('number', ' ', lambda x: x[0].rev(), formatrev),
422 ('changeset', ' ', lambda x: hexfn(x[0].node()), formathex),
421 ('changeset', ' ', lambda x: hexfn(x[0].node()), formathex),
423 ('date', ' ', lambda x: x[0].date(), util.cachefunc(datefunc)),
422 ('date', ' ', lambda x: x[0].date(), util.cachefunc(datefunc)),
424 ('file', ' ', lambda x: x[0].path(), str),
423 ('file', ' ', lambda x: x[0].path(), str),
425 ('line_number', ':', lambda x: x[1], str),
424 ('line_number', ':', lambda x: x[1], str),
426 ]
425 ]
427 fieldnamemap = {'number': 'rev', 'changeset': 'node'}
426 fieldnamemap = {'number': 'rev', 'changeset': 'node'}
428
427
429 if (not opts.get('user') and not opts.get('changeset')
428 if (not opts.get('user') and not opts.get('changeset')
430 and not opts.get('date') and not opts.get('file')):
429 and not opts.get('date') and not opts.get('file')):
431 opts['number'] = True
430 opts['number'] = True
432
431
433 linenumber = opts.get('line_number') is not None
432 linenumber = opts.get('line_number') is not None
434 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
433 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
435 raise error.Abort(_('at least one of -n/-c is required for -l'))
434 raise error.Abort(_('at least one of -n/-c is required for -l'))
436
435
437 if fm.isplain():
436 if fm.isplain():
438 def makefunc(get, fmt):
437 def makefunc(get, fmt):
439 return lambda x: fmt(get(x))
438 return lambda x: fmt(get(x))
440 else:
439 else:
441 def makefunc(get, fmt):
440 def makefunc(get, fmt):
442 return get
441 return get
443 funcmap = [(makefunc(get, fmt), sep) for op, sep, get, fmt in opmap
442 funcmap = [(makefunc(get, fmt), sep) for op, sep, get, fmt in opmap
444 if opts.get(op)]
443 if opts.get(op)]
445 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
444 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
446 fields = ' '.join(fieldnamemap.get(op, op) for op, sep, get, fmt in opmap
445 fields = ' '.join(fieldnamemap.get(op, op) for op, sep, get, fmt in opmap
447 if opts.get(op))
446 if opts.get(op))
448
447
449 def bad(x, y):
448 def bad(x, y):
450 raise error.Abort("%s: %s" % (x, y))
449 raise error.Abort("%s: %s" % (x, y))
451
450
452 m = scmutil.match(ctx, pats, opts, badfn=bad)
451 m = scmutil.match(ctx, pats, opts, badfn=bad)
453
452
454 follow = not opts.get('no_follow')
453 follow = not opts.get('no_follow')
455 diffopts = patch.difffeatureopts(ui, opts, section='annotate',
454 diffopts = patch.difffeatureopts(ui, opts, section='annotate',
456 whitespace=True)
455 whitespace=True)
457 for abs in ctx.walk(m):
456 for abs in ctx.walk(m):
458 fctx = ctx[abs]
457 fctx = ctx[abs]
459 if not opts.get('text') and util.binary(fctx.data()):
458 if not opts.get('text') and util.binary(fctx.data()):
460 fm.plain(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
459 fm.plain(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
461 continue
460 continue
462
461
463 lines = fctx.annotate(follow=follow, linenumber=linenumber,
462 lines = fctx.annotate(follow=follow, linenumber=linenumber,
464 diffopts=diffopts)
463 diffopts=diffopts)
465 if not lines:
464 if not lines:
466 continue
465 continue
467 formats = []
466 formats = []
468 pieces = []
467 pieces = []
469
468
470 for f, sep in funcmap:
469 for f, sep in funcmap:
471 l = [f(n) for n, dummy in lines]
470 l = [f(n) for n, dummy in lines]
472 if fm.isplain():
471 if fm.isplain():
473 sizes = [encoding.colwidth(x) for x in l]
472 sizes = [encoding.colwidth(x) for x in l]
474 ml = max(sizes)
473 ml = max(sizes)
475 formats.append([sep + ' ' * (ml - w) + '%s' for w in sizes])
474 formats.append([sep + ' ' * (ml - w) + '%s' for w in sizes])
476 else:
475 else:
477 formats.append(['%s' for x in l])
476 formats.append(['%s' for x in l])
478 pieces.append(l)
477 pieces.append(l)
479
478
480 for f, p, l in zip(zip(*formats), zip(*pieces), lines):
479 for f, p, l in zip(zip(*formats), zip(*pieces), lines):
481 fm.startitem()
480 fm.startitem()
482 fm.write(fields, "".join(f), *p)
481 fm.write(fields, "".join(f), *p)
483 fm.write('line', ": %s", l[1])
482 fm.write('line', ": %s", l[1])
484
483
485 if not lines[-1][1].endswith('\n'):
484 if not lines[-1][1].endswith('\n'):
486 fm.plain('\n')
485 fm.plain('\n')
487
486
488 fm.end()
487 fm.end()
489
488
490 @command('archive',
489 @command('archive',
491 [('', 'no-decode', None, _('do not pass files through decoders')),
490 [('', 'no-decode', None, _('do not pass files through decoders')),
492 ('p', 'prefix', '', _('directory prefix for files in archive'),
491 ('p', 'prefix', '', _('directory prefix for files in archive'),
493 _('PREFIX')),
492 _('PREFIX')),
494 ('r', 'rev', '', _('revision to distribute'), _('REV')),
493 ('r', 'rev', '', _('revision to distribute'), _('REV')),
495 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
494 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
496 ] + subrepoopts + walkopts,
495 ] + subrepoopts + walkopts,
497 _('[OPTION]... DEST'))
496 _('[OPTION]... DEST'))
498 def archive(ui, repo, dest, **opts):
497 def archive(ui, repo, dest, **opts):
499 '''create an unversioned archive of a repository revision
498 '''create an unversioned archive of a repository revision
500
499
501 By default, the revision used is the parent of the working
500 By default, the revision used is the parent of the working
502 directory; use -r/--rev to specify a different revision.
501 directory; use -r/--rev to specify a different revision.
503
502
504 The archive type is automatically detected based on file
503 The archive type is automatically detected based on file
505 extension (to override, use -t/--type).
504 extension (to override, use -t/--type).
506
505
507 .. container:: verbose
506 .. container:: verbose
508
507
509 Examples:
508 Examples:
510
509
511 - create a zip file containing the 1.0 release::
510 - create a zip file containing the 1.0 release::
512
511
513 hg archive -r 1.0 project-1.0.zip
512 hg archive -r 1.0 project-1.0.zip
514
513
515 - create a tarball excluding .hg files::
514 - create a tarball excluding .hg files::
516
515
517 hg archive project.tar.gz -X ".hg*"
516 hg archive project.tar.gz -X ".hg*"
518
517
519 Valid types are:
518 Valid types are:
520
519
521 :``files``: a directory full of files (default)
520 :``files``: a directory full of files (default)
522 :``tar``: tar archive, uncompressed
521 :``tar``: tar archive, uncompressed
523 :``tbz2``: tar archive, compressed using bzip2
522 :``tbz2``: tar archive, compressed using bzip2
524 :``tgz``: tar archive, compressed using gzip
523 :``tgz``: tar archive, compressed using gzip
525 :``uzip``: zip archive, uncompressed
524 :``uzip``: zip archive, uncompressed
526 :``zip``: zip archive, compressed using deflate
525 :``zip``: zip archive, compressed using deflate
527
526
528 The exact name of the destination archive or directory is given
527 The exact name of the destination archive or directory is given
529 using a format string; see :hg:`help export` for details.
528 using a format string; see :hg:`help export` for details.
530
529
531 Each member added to an archive file has a directory prefix
530 Each member added to an archive file has a directory prefix
532 prepended. Use -p/--prefix to specify a format string for the
531 prepended. Use -p/--prefix to specify a format string for the
533 prefix. The default is the basename of the archive, with suffixes
532 prefix. The default is the basename of the archive, with suffixes
534 removed.
533 removed.
535
534
536 Returns 0 on success.
535 Returns 0 on success.
537 '''
536 '''
538
537
539 ctx = scmutil.revsingle(repo, opts.get('rev'))
538 ctx = scmutil.revsingle(repo, opts.get('rev'))
540 if not ctx:
539 if not ctx:
541 raise error.Abort(_('no working directory: please specify a revision'))
540 raise error.Abort(_('no working directory: please specify a revision'))
542 node = ctx.node()
541 node = ctx.node()
543 dest = cmdutil.makefilename(repo, dest, node)
542 dest = cmdutil.makefilename(repo, dest, node)
544 if os.path.realpath(dest) == repo.root:
543 if os.path.realpath(dest) == repo.root:
545 raise error.Abort(_('repository root cannot be destination'))
544 raise error.Abort(_('repository root cannot be destination'))
546
545
547 kind = opts.get('type') or archival.guesskind(dest) or 'files'
546 kind = opts.get('type') or archival.guesskind(dest) or 'files'
548 prefix = opts.get('prefix')
547 prefix = opts.get('prefix')
549
548
550 if dest == '-':
549 if dest == '-':
551 if kind == 'files':
550 if kind == 'files':
552 raise error.Abort(_('cannot archive plain files to stdout'))
551 raise error.Abort(_('cannot archive plain files to stdout'))
553 dest = cmdutil.makefileobj(repo, dest)
552 dest = cmdutil.makefileobj(repo, dest)
554 if not prefix:
553 if not prefix:
555 prefix = os.path.basename(repo.root) + '-%h'
554 prefix = os.path.basename(repo.root) + '-%h'
556
555
557 prefix = cmdutil.makefilename(repo, prefix, node)
556 prefix = cmdutil.makefilename(repo, prefix, node)
558 matchfn = scmutil.match(ctx, [], opts)
557 matchfn = scmutil.match(ctx, [], opts)
559 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
558 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
560 matchfn, prefix, subrepos=opts.get('subrepos'))
559 matchfn, prefix, subrepos=opts.get('subrepos'))
561
560
562 @command('backout',
561 @command('backout',
563 [('', 'merge', None, _('merge with old dirstate parent after backout')),
562 [('', 'merge', None, _('merge with old dirstate parent after backout')),
564 ('', 'commit', None,
563 ('', 'commit', None,
565 _('commit if no conflicts were encountered (DEPRECATED)')),
564 _('commit if no conflicts were encountered (DEPRECATED)')),
566 ('', 'no-commit', None, _('do not commit')),
565 ('', 'no-commit', None, _('do not commit')),
567 ('', 'parent', '',
566 ('', 'parent', '',
568 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
567 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
569 ('r', 'rev', '', _('revision to backout'), _('REV')),
568 ('r', 'rev', '', _('revision to backout'), _('REV')),
570 ('e', 'edit', False, _('invoke editor on commit messages')),
569 ('e', 'edit', False, _('invoke editor on commit messages')),
571 ] + mergetoolopts + walkopts + commitopts + commitopts2,
570 ] + mergetoolopts + walkopts + commitopts + commitopts2,
572 _('[OPTION]... [-r] REV'))
571 _('[OPTION]... [-r] REV'))
573 def backout(ui, repo, node=None, rev=None, **opts):
572 def backout(ui, repo, node=None, rev=None, **opts):
574 '''reverse effect of earlier changeset
573 '''reverse effect of earlier changeset
575
574
576 Prepare a new changeset with the effect of REV undone in the
575 Prepare a new changeset with the effect of REV undone in the
577 current working directory. If no conflicts were encountered,
576 current working directory. If no conflicts were encountered,
578 it will be committed immediately.
577 it will be committed immediately.
579
578
580 If REV is the parent of the working directory, then this new changeset
579 If REV is the parent of the working directory, then this new changeset
581 is committed automatically (unless --no-commit is specified).
580 is committed automatically (unless --no-commit is specified).
582
581
583 .. note::
582 .. note::
584
583
585 :hg:`backout` cannot be used to fix either an unwanted or
584 :hg:`backout` cannot be used to fix either an unwanted or
586 incorrect merge.
585 incorrect merge.
587
586
588 .. container:: verbose
587 .. container:: verbose
589
588
590 Examples:
589 Examples:
591
590
592 - Reverse the effect of the parent of the working directory.
591 - Reverse the effect of the parent of the working directory.
593 This backout will be committed immediately::
592 This backout will be committed immediately::
594
593
595 hg backout -r .
594 hg backout -r .
596
595
597 - Reverse the effect of previous bad revision 23::
596 - Reverse the effect of previous bad revision 23::
598
597
599 hg backout -r 23
598 hg backout -r 23
600
599
601 - Reverse the effect of previous bad revision 23 and
600 - Reverse the effect of previous bad revision 23 and
602 leave changes uncommitted::
601 leave changes uncommitted::
603
602
604 hg backout -r 23 --no-commit
603 hg backout -r 23 --no-commit
605 hg commit -m "Backout revision 23"
604 hg commit -m "Backout revision 23"
606
605
607 By default, the pending changeset will have one parent,
606 By default, the pending changeset will have one parent,
608 maintaining a linear history. With --merge, the pending
607 maintaining a linear history. With --merge, the pending
609 changeset will instead have two parents: the old parent of the
608 changeset will instead have two parents: the old parent of the
610 working directory and a new child of REV that simply undoes REV.
609 working directory and a new child of REV that simply undoes REV.
611
610
612 Before version 1.7, the behavior without --merge was equivalent
611 Before version 1.7, the behavior without --merge was equivalent
613 to specifying --merge followed by :hg:`update --clean .` to
612 to specifying --merge followed by :hg:`update --clean .` to
614 cancel the merge and leave the child of REV as a head to be
613 cancel the merge and leave the child of REV as a head to be
615 merged separately.
614 merged separately.
616
615
617 See :hg:`help dates` for a list of formats valid for -d/--date.
616 See :hg:`help dates` for a list of formats valid for -d/--date.
618
617
619 See :hg:`help revert` for a way to restore files to the state
618 See :hg:`help revert` for a way to restore files to the state
620 of another revision.
619 of another revision.
621
620
622 Returns 0 on success, 1 if nothing to backout or there are unresolved
621 Returns 0 on success, 1 if nothing to backout or there are unresolved
623 files.
622 files.
624 '''
623 '''
625 wlock = lock = None
624 wlock = lock = None
626 try:
625 try:
627 wlock = repo.wlock()
626 wlock = repo.wlock()
628 lock = repo.lock()
627 lock = repo.lock()
629 return _dobackout(ui, repo, node, rev, **opts)
628 return _dobackout(ui, repo, node, rev, **opts)
630 finally:
629 finally:
631 release(lock, wlock)
630 release(lock, wlock)
632
631
633 def _dobackout(ui, repo, node=None, rev=None, **opts):
632 def _dobackout(ui, repo, node=None, rev=None, **opts):
634 if opts.get('commit') and opts.get('no_commit'):
633 if opts.get('commit') and opts.get('no_commit'):
635 raise error.Abort(_("cannot use --commit with --no-commit"))
634 raise error.Abort(_("cannot use --commit with --no-commit"))
636 if opts.get('merge') and opts.get('no_commit'):
635 if opts.get('merge') and opts.get('no_commit'):
637 raise error.Abort(_("cannot use --merge with --no-commit"))
636 raise error.Abort(_("cannot use --merge with --no-commit"))
638
637
639 if rev and node:
638 if rev and node:
640 raise error.Abort(_("please specify just one revision"))
639 raise error.Abort(_("please specify just one revision"))
641
640
642 if not rev:
641 if not rev:
643 rev = node
642 rev = node
644
643
645 if not rev:
644 if not rev:
646 raise error.Abort(_("please specify a revision to backout"))
645 raise error.Abort(_("please specify a revision to backout"))
647
646
648 date = opts.get('date')
647 date = opts.get('date')
649 if date:
648 if date:
650 opts['date'] = util.parsedate(date)
649 opts['date'] = util.parsedate(date)
651
650
652 cmdutil.checkunfinished(repo)
651 cmdutil.checkunfinished(repo)
653 cmdutil.bailifchanged(repo)
652 cmdutil.bailifchanged(repo)
654 node = scmutil.revsingle(repo, rev).node()
653 node = scmutil.revsingle(repo, rev).node()
655
654
656 op1, op2 = repo.dirstate.parents()
655 op1, op2 = repo.dirstate.parents()
657 if not repo.changelog.isancestor(node, op1):
656 if not repo.changelog.isancestor(node, op1):
658 raise error.Abort(_('cannot backout change that is not an ancestor'))
657 raise error.Abort(_('cannot backout change that is not an ancestor'))
659
658
660 p1, p2 = repo.changelog.parents(node)
659 p1, p2 = repo.changelog.parents(node)
661 if p1 == nullid:
660 if p1 == nullid:
662 raise error.Abort(_('cannot backout a change with no parents'))
661 raise error.Abort(_('cannot backout a change with no parents'))
663 if p2 != nullid:
662 if p2 != nullid:
664 if not opts.get('parent'):
663 if not opts.get('parent'):
665 raise error.Abort(_('cannot backout a merge changeset'))
664 raise error.Abort(_('cannot backout a merge changeset'))
666 p = repo.lookup(opts['parent'])
665 p = repo.lookup(opts['parent'])
667 if p not in (p1, p2):
666 if p not in (p1, p2):
668 raise error.Abort(_('%s is not a parent of %s') %
667 raise error.Abort(_('%s is not a parent of %s') %
669 (short(p), short(node)))
668 (short(p), short(node)))
670 parent = p
669 parent = p
671 else:
670 else:
672 if opts.get('parent'):
671 if opts.get('parent'):
673 raise error.Abort(_('cannot use --parent on non-merge changeset'))
672 raise error.Abort(_('cannot use --parent on non-merge changeset'))
674 parent = p1
673 parent = p1
675
674
676 # the backout should appear on the same branch
675 # the backout should appear on the same branch
677 branch = repo.dirstate.branch()
676 branch = repo.dirstate.branch()
678 bheads = repo.branchheads(branch)
677 bheads = repo.branchheads(branch)
679 rctx = scmutil.revsingle(repo, hex(parent))
678 rctx = scmutil.revsingle(repo, hex(parent))
680 if not opts.get('merge') and op1 != node:
679 if not opts.get('merge') and op1 != node:
681 dsguard = dirstateguard.dirstateguard(repo, 'backout')
680 dsguard = dirstateguard.dirstateguard(repo, 'backout')
682 try:
681 try:
683 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
682 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
684 'backout')
683 'backout')
685 stats = mergemod.update(repo, parent, True, True, node, False)
684 stats = mergemod.update(repo, parent, True, True, node, False)
686 repo.setparents(op1, op2)
685 repo.setparents(op1, op2)
687 dsguard.close()
686 dsguard.close()
688 hg._showstats(repo, stats)
687 hg._showstats(repo, stats)
689 if stats[3]:
688 if stats[3]:
690 repo.ui.status(_("use 'hg resolve' to retry unresolved "
689 repo.ui.status(_("use 'hg resolve' to retry unresolved "
691 "file merges\n"))
690 "file merges\n"))
692 return 1
691 return 1
693 finally:
692 finally:
694 ui.setconfig('ui', 'forcemerge', '', '')
693 ui.setconfig('ui', 'forcemerge', '', '')
695 lockmod.release(dsguard)
694 lockmod.release(dsguard)
696 else:
695 else:
697 hg.clean(repo, node, show_stats=False)
696 hg.clean(repo, node, show_stats=False)
698 repo.dirstate.setbranch(branch)
697 repo.dirstate.setbranch(branch)
699 cmdutil.revert(ui, repo, rctx, repo.dirstate.parents())
698 cmdutil.revert(ui, repo, rctx, repo.dirstate.parents())
700
699
701 if opts.get('no_commit'):
700 if opts.get('no_commit'):
702 msg = _("changeset %s backed out, "
701 msg = _("changeset %s backed out, "
703 "don't forget to commit.\n")
702 "don't forget to commit.\n")
704 ui.status(msg % short(node))
703 ui.status(msg % short(node))
705 return 0
704 return 0
706
705
707 def commitfunc(ui, repo, message, match, opts):
706 def commitfunc(ui, repo, message, match, opts):
708 editform = 'backout'
707 editform = 'backout'
709 e = cmdutil.getcommiteditor(editform=editform, **opts)
708 e = cmdutil.getcommiteditor(editform=editform, **opts)
710 if not message:
709 if not message:
711 # we don't translate commit messages
710 # we don't translate commit messages
712 message = "Backed out changeset %s" % short(node)
711 message = "Backed out changeset %s" % short(node)
713 e = cmdutil.getcommiteditor(edit=True, editform=editform)
712 e = cmdutil.getcommiteditor(edit=True, editform=editform)
714 return repo.commit(message, opts.get('user'), opts.get('date'),
713 return repo.commit(message, opts.get('user'), opts.get('date'),
715 match, editor=e)
714 match, editor=e)
716 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
715 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
717 if not newnode:
716 if not newnode:
718 ui.status(_("nothing changed\n"))
717 ui.status(_("nothing changed\n"))
719 return 1
718 return 1
720 cmdutil.commitstatus(repo, newnode, branch, bheads)
719 cmdutil.commitstatus(repo, newnode, branch, bheads)
721
720
722 def nice(node):
721 def nice(node):
723 return '%d:%s' % (repo.changelog.rev(node), short(node))
722 return '%d:%s' % (repo.changelog.rev(node), short(node))
724 ui.status(_('changeset %s backs out changeset %s\n') %
723 ui.status(_('changeset %s backs out changeset %s\n') %
725 (nice(repo.changelog.tip()), nice(node)))
724 (nice(repo.changelog.tip()), nice(node)))
726 if opts.get('merge') and op1 != node:
725 if opts.get('merge') and op1 != node:
727 hg.clean(repo, op1, show_stats=False)
726 hg.clean(repo, op1, show_stats=False)
728 ui.status(_('merging with changeset %s\n')
727 ui.status(_('merging with changeset %s\n')
729 % nice(repo.changelog.tip()))
728 % nice(repo.changelog.tip()))
730 try:
729 try:
731 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
730 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
732 'backout')
731 'backout')
733 return hg.merge(repo, hex(repo.changelog.tip()))
732 return hg.merge(repo, hex(repo.changelog.tip()))
734 finally:
733 finally:
735 ui.setconfig('ui', 'forcemerge', '', '')
734 ui.setconfig('ui', 'forcemerge', '', '')
736 return 0
735 return 0
737
736
738 @command('bisect',
737 @command('bisect',
739 [('r', 'reset', False, _('reset bisect state')),
738 [('r', 'reset', False, _('reset bisect state')),
740 ('g', 'good', False, _('mark changeset good')),
739 ('g', 'good', False, _('mark changeset good')),
741 ('b', 'bad', False, _('mark changeset bad')),
740 ('b', 'bad', False, _('mark changeset bad')),
742 ('s', 'skip', False, _('skip testing changeset')),
741 ('s', 'skip', False, _('skip testing changeset')),
743 ('e', 'extend', False, _('extend the bisect range')),
742 ('e', 'extend', False, _('extend the bisect range')),
744 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
743 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
745 ('U', 'noupdate', False, _('do not update to target'))],
744 ('U', 'noupdate', False, _('do not update to target'))],
746 _("[-gbsr] [-U] [-c CMD] [REV]"))
745 _("[-gbsr] [-U] [-c CMD] [REV]"))
747 def bisect(ui, repo, rev=None, extra=None, command=None,
746 def bisect(ui, repo, rev=None, extra=None, command=None,
748 reset=None, good=None, bad=None, skip=None, extend=None,
747 reset=None, good=None, bad=None, skip=None, extend=None,
749 noupdate=None):
748 noupdate=None):
750 """subdivision search of changesets
749 """subdivision search of changesets
751
750
752 This command helps to find changesets which introduce problems. To
751 This command helps to find changesets which introduce problems. To
753 use, mark the earliest changeset you know exhibits the problem as
752 use, mark the earliest changeset you know exhibits the problem as
754 bad, then mark the latest changeset which is free from the problem
753 bad, then mark the latest changeset which is free from the problem
755 as good. Bisect will update your working directory to a revision
754 as good. Bisect will update your working directory to a revision
756 for testing (unless the -U/--noupdate option is specified). Once
755 for testing (unless the -U/--noupdate option is specified). Once
757 you have performed tests, mark the working directory as good or
756 you have performed tests, mark the working directory as good or
758 bad, and bisect will either update to another candidate changeset
757 bad, and bisect will either update to another candidate changeset
759 or announce that it has found the bad revision.
758 or announce that it has found the bad revision.
760
759
761 As a shortcut, you can also use the revision argument to mark a
760 As a shortcut, you can also use the revision argument to mark a
762 revision as good or bad without checking it out first.
761 revision as good or bad without checking it out first.
763
762
764 If you supply a command, it will be used for automatic bisection.
763 If you supply a command, it will be used for automatic bisection.
765 The environment variable HG_NODE will contain the ID of the
764 The environment variable HG_NODE will contain the ID of the
766 changeset being tested. The exit status of the command will be
765 changeset being tested. The exit status of the command will be
767 used to mark revisions as good or bad: status 0 means good, 125
766 used to mark revisions as good or bad: status 0 means good, 125
768 means to skip the revision, 127 (command not found) will abort the
767 means to skip the revision, 127 (command not found) will abort the
769 bisection, and any other non-zero exit status means the revision
768 bisection, and any other non-zero exit status means the revision
770 is bad.
769 is bad.
771
770
772 .. container:: verbose
771 .. container:: verbose
773
772
774 Some examples:
773 Some examples:
775
774
776 - start a bisection with known bad revision 34, and good revision 12::
775 - start a bisection with known bad revision 34, and good revision 12::
777
776
778 hg bisect --bad 34
777 hg bisect --bad 34
779 hg bisect --good 12
778 hg bisect --good 12
780
779
781 - advance the current bisection by marking current revision as good or
780 - advance the current bisection by marking current revision as good or
782 bad::
781 bad::
783
782
784 hg bisect --good
783 hg bisect --good
785 hg bisect --bad
784 hg bisect --bad
786
785
787 - mark the current revision, or a known revision, to be skipped (e.g. if
786 - mark the current revision, or a known revision, to be skipped (e.g. if
788 that revision is not usable because of another issue)::
787 that revision is not usable because of another issue)::
789
788
790 hg bisect --skip
789 hg bisect --skip
791 hg bisect --skip 23
790 hg bisect --skip 23
792
791
793 - skip all revisions that do not touch directories ``foo`` or ``bar``::
792 - skip all revisions that do not touch directories ``foo`` or ``bar``::
794
793
795 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
794 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
796
795
797 - forget the current bisection::
796 - forget the current bisection::
798
797
799 hg bisect --reset
798 hg bisect --reset
800
799
801 - use 'make && make tests' to automatically find the first broken
800 - use 'make && make tests' to automatically find the first broken
802 revision::
801 revision::
803
802
804 hg bisect --reset
803 hg bisect --reset
805 hg bisect --bad 34
804 hg bisect --bad 34
806 hg bisect --good 12
805 hg bisect --good 12
807 hg bisect --command "make && make tests"
806 hg bisect --command "make && make tests"
808
807
809 - see all changesets whose states are already known in the current
808 - see all changesets whose states are already known in the current
810 bisection::
809 bisection::
811
810
812 hg log -r "bisect(pruned)"
811 hg log -r "bisect(pruned)"
813
812
814 - see the changeset currently being bisected (especially useful
813 - see the changeset currently being bisected (especially useful
815 if running with -U/--noupdate)::
814 if running with -U/--noupdate)::
816
815
817 hg log -r "bisect(current)"
816 hg log -r "bisect(current)"
818
817
819 - see all changesets that took part in the current bisection::
818 - see all changesets that took part in the current bisection::
820
819
821 hg log -r "bisect(range)"
820 hg log -r "bisect(range)"
822
821
823 - you can even get a nice graph::
822 - you can even get a nice graph::
824
823
825 hg log --graph -r "bisect(range)"
824 hg log --graph -r "bisect(range)"
826
825
827 See :hg:`help revsets` for more about the `bisect()` keyword.
826 See :hg:`help revsets` for more about the `bisect()` keyword.
828
827
829 Returns 0 on success.
828 Returns 0 on success.
830 """
829 """
831 # backward compatibility
830 # backward compatibility
832 if rev in "good bad reset init".split():
831 if rev in "good bad reset init".split():
833 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
832 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
834 cmd, rev, extra = rev, extra, None
833 cmd, rev, extra = rev, extra, None
835 if cmd == "good":
834 if cmd == "good":
836 good = True
835 good = True
837 elif cmd == "bad":
836 elif cmd == "bad":
838 bad = True
837 bad = True
839 else:
838 else:
840 reset = True
839 reset = True
841 elif extra or good + bad + skip + reset + extend + bool(command) > 1:
840 elif extra or good + bad + skip + reset + extend + bool(command) > 1:
842 raise error.Abort(_('incompatible arguments'))
841 raise error.Abort(_('incompatible arguments'))
843
842
844 cmdutil.checkunfinished(repo)
843 cmdutil.checkunfinished(repo)
845
844
846 if reset:
845 if reset:
847 hbisect.resetstate(repo)
846 hbisect.resetstate(repo)
848 return
847 return
849
848
850 state = hbisect.load_state(repo)
849 state = hbisect.load_state(repo)
851
850
852 # update state
851 # update state
853 if good or bad or skip:
852 if good or bad or skip:
854 if rev:
853 if rev:
855 nodes = [repo.lookup(i) for i in scmutil.revrange(repo, [rev])]
854 nodes = [repo.lookup(i) for i in scmutil.revrange(repo, [rev])]
856 else:
855 else:
857 nodes = [repo.lookup('.')]
856 nodes = [repo.lookup('.')]
858 if good:
857 if good:
859 state['good'] += nodes
858 state['good'] += nodes
860 elif bad:
859 elif bad:
861 state['bad'] += nodes
860 state['bad'] += nodes
862 elif skip:
861 elif skip:
863 state['skip'] += nodes
862 state['skip'] += nodes
864 hbisect.save_state(repo, state)
863 hbisect.save_state(repo, state)
865 if not (state['good'] and state['bad']):
864 if not (state['good'] and state['bad']):
866 return
865 return
867
866
868 def mayupdate(repo, node, show_stats=True):
867 def mayupdate(repo, node, show_stats=True):
869 """common used update sequence"""
868 """common used update sequence"""
870 if noupdate:
869 if noupdate:
871 return
870 return
872 cmdutil.bailifchanged(repo)
871 cmdutil.bailifchanged(repo)
873 return hg.clean(repo, node, show_stats=show_stats)
872 return hg.clean(repo, node, show_stats=show_stats)
874
873
875 displayer = cmdutil.show_changeset(ui, repo, {})
874 displayer = cmdutil.show_changeset(ui, repo, {})
876
875
877 if command:
876 if command:
878 changesets = 1
877 changesets = 1
879 if noupdate:
878 if noupdate:
880 try:
879 try:
881 node = state['current'][0]
880 node = state['current'][0]
882 except LookupError:
881 except LookupError:
883 raise error.Abort(_('current bisect revision is unknown - '
882 raise error.Abort(_('current bisect revision is unknown - '
884 'start a new bisect to fix'))
883 'start a new bisect to fix'))
885 else:
884 else:
886 node, p2 = repo.dirstate.parents()
885 node, p2 = repo.dirstate.parents()
887 if p2 != nullid:
886 if p2 != nullid:
888 raise error.Abort(_('current bisect revision is a merge'))
887 raise error.Abort(_('current bisect revision is a merge'))
889 if rev:
888 if rev:
890 node = repo[scmutil.revsingle(repo, rev, node)].node()
889 node = repo[scmutil.revsingle(repo, rev, node)].node()
891 try:
890 try:
892 while changesets:
891 while changesets:
893 # update state
892 # update state
894 state['current'] = [node]
893 state['current'] = [node]
895 hbisect.save_state(repo, state)
894 hbisect.save_state(repo, state)
896 status = ui.system(command, environ={'HG_NODE': hex(node)})
895 status = ui.system(command, environ={'HG_NODE': hex(node)})
897 if status == 125:
896 if status == 125:
898 transition = "skip"
897 transition = "skip"
899 elif status == 0:
898 elif status == 0:
900 transition = "good"
899 transition = "good"
901 # status < 0 means process was killed
900 # status < 0 means process was killed
902 elif status == 127:
901 elif status == 127:
903 raise error.Abort(_("failed to execute %s") % command)
902 raise error.Abort(_("failed to execute %s") % command)
904 elif status < 0:
903 elif status < 0:
905 raise error.Abort(_("%s killed") % command)
904 raise error.Abort(_("%s killed") % command)
906 else:
905 else:
907 transition = "bad"
906 transition = "bad"
908 state[transition].append(node)
907 state[transition].append(node)
909 ctx = repo[node]
908 ctx = repo[node]
910 ui.status(_('changeset %d:%s: %s\n') % (ctx, ctx, transition))
909 ui.status(_('changeset %d:%s: %s\n') % (ctx, ctx, transition))
911 hbisect.checkstate(state)
910 hbisect.checkstate(state)
912 # bisect
911 # bisect
913 nodes, changesets, bgood = hbisect.bisect(repo.changelog, state)
912 nodes, changesets, bgood = hbisect.bisect(repo.changelog, state)
914 # update to next check
913 # update to next check
915 node = nodes[0]
914 node = nodes[0]
916 mayupdate(repo, node, show_stats=False)
915 mayupdate(repo, node, show_stats=False)
917 finally:
916 finally:
918 state['current'] = [node]
917 state['current'] = [node]
919 hbisect.save_state(repo, state)
918 hbisect.save_state(repo, state)
920 hbisect.printresult(ui, repo, state, displayer, nodes, bgood)
919 hbisect.printresult(ui, repo, state, displayer, nodes, bgood)
921 return
920 return
922
921
923 hbisect.checkstate(state)
922 hbisect.checkstate(state)
924
923
925 # actually bisect
924 # actually bisect
926 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
925 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
927 if extend:
926 if extend:
928 if not changesets:
927 if not changesets:
929 extendnode = hbisect.extendrange(repo, state, nodes, good)
928 extendnode = hbisect.extendrange(repo, state, nodes, good)
930 if extendnode is not None:
929 if extendnode is not None:
931 ui.write(_("Extending search to changeset %d:%s\n")
930 ui.write(_("Extending search to changeset %d:%s\n")
932 % (extendnode.rev(), extendnode))
931 % (extendnode.rev(), extendnode))
933 state['current'] = [extendnode.node()]
932 state['current'] = [extendnode.node()]
934 hbisect.save_state(repo, state)
933 hbisect.save_state(repo, state)
935 return mayupdate(repo, extendnode.node())
934 return mayupdate(repo, extendnode.node())
936 raise error.Abort(_("nothing to extend"))
935 raise error.Abort(_("nothing to extend"))
937
936
938 if changesets == 0:
937 if changesets == 0:
939 hbisect.printresult(ui, repo, state, displayer, nodes, good)
938 hbisect.printresult(ui, repo, state, displayer, nodes, good)
940 else:
939 else:
941 assert len(nodes) == 1 # only a single node can be tested next
940 assert len(nodes) == 1 # only a single node can be tested next
942 node = nodes[0]
941 node = nodes[0]
943 # compute the approximate number of remaining tests
942 # compute the approximate number of remaining tests
944 tests, size = 0, 2
943 tests, size = 0, 2
945 while size <= changesets:
944 while size <= changesets:
946 tests, size = tests + 1, size * 2
945 tests, size = tests + 1, size * 2
947 rev = repo.changelog.rev(node)
946 rev = repo.changelog.rev(node)
948 ui.write(_("Testing changeset %d:%s "
947 ui.write(_("Testing changeset %d:%s "
949 "(%d changesets remaining, ~%d tests)\n")
948 "(%d changesets remaining, ~%d tests)\n")
950 % (rev, short(node), changesets, tests))
949 % (rev, short(node), changesets, tests))
951 state['current'] = [node]
950 state['current'] = [node]
952 hbisect.save_state(repo, state)
951 hbisect.save_state(repo, state)
953 return mayupdate(repo, node)
952 return mayupdate(repo, node)
954
953
955 @command('bookmarks|bookmark',
954 @command('bookmarks|bookmark',
956 [('f', 'force', False, _('force')),
955 [('f', 'force', False, _('force')),
957 ('r', 'rev', '', _('revision for bookmark action'), _('REV')),
956 ('r', 'rev', '', _('revision for bookmark action'), _('REV')),
958 ('d', 'delete', False, _('delete a given bookmark')),
957 ('d', 'delete', False, _('delete a given bookmark')),
959 ('m', 'rename', '', _('rename a given bookmark'), _('OLD')),
958 ('m', 'rename', '', _('rename a given bookmark'), _('OLD')),
960 ('i', 'inactive', False, _('mark a bookmark inactive')),
959 ('i', 'inactive', False, _('mark a bookmark inactive')),
961 ] + formatteropts,
960 ] + formatteropts,
962 _('hg bookmarks [OPTIONS]... [NAME]...'))
961 _('hg bookmarks [OPTIONS]... [NAME]...'))
963 def bookmark(ui, repo, *names, **opts):
962 def bookmark(ui, repo, *names, **opts):
964 '''create a new bookmark or list existing bookmarks
963 '''create a new bookmark or list existing bookmarks
965
964
966 Bookmarks are labels on changesets to help track lines of development.
965 Bookmarks are labels on changesets to help track lines of development.
967 Bookmarks are unversioned and can be moved, renamed and deleted.
966 Bookmarks are unversioned and can be moved, renamed and deleted.
968 Deleting or moving a bookmark has no effect on the associated changesets.
967 Deleting or moving a bookmark has no effect on the associated changesets.
969
968
970 Creating or updating to a bookmark causes it to be marked as 'active'.
969 Creating or updating to a bookmark causes it to be marked as 'active'.
971 The active bookmark is indicated with a '*'.
970 The active bookmark is indicated with a '*'.
972 When a commit is made, the active bookmark will advance to the new commit.
971 When a commit is made, the active bookmark will advance to the new commit.
973 A plain :hg:`update` will also advance an active bookmark, if possible.
972 A plain :hg:`update` will also advance an active bookmark, if possible.
974 Updating away from a bookmark will cause it to be deactivated.
973 Updating away from a bookmark will cause it to be deactivated.
975
974
976 Bookmarks can be pushed and pulled between repositories (see
975 Bookmarks can be pushed and pulled between repositories (see
977 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
976 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
978 diverged, a new 'divergent bookmark' of the form 'name@path' will
977 diverged, a new 'divergent bookmark' of the form 'name@path' will
979 be created. Using :hg:`merge` will resolve the divergence.
978 be created. Using :hg:`merge` will resolve the divergence.
980
979
981 A bookmark named '@' has the special property that :hg:`clone` will
980 A bookmark named '@' has the special property that :hg:`clone` will
982 check it out by default if it exists.
981 check it out by default if it exists.
983
982
984 .. container:: verbose
983 .. container:: verbose
985
984
986 Examples:
985 Examples:
987
986
988 - create an active bookmark for a new line of development::
987 - create an active bookmark for a new line of development::
989
988
990 hg book new-feature
989 hg book new-feature
991
990
992 - create an inactive bookmark as a place marker::
991 - create an inactive bookmark as a place marker::
993
992
994 hg book -i reviewed
993 hg book -i reviewed
995
994
996 - create an inactive bookmark on another changeset::
995 - create an inactive bookmark on another changeset::
997
996
998 hg book -r .^ tested
997 hg book -r .^ tested
999
998
1000 - rename bookmark turkey to dinner::
999 - rename bookmark turkey to dinner::
1001
1000
1002 hg book -m turkey dinner
1001 hg book -m turkey dinner
1003
1002
1004 - move the '@' bookmark from another branch::
1003 - move the '@' bookmark from another branch::
1005
1004
1006 hg book -f @
1005 hg book -f @
1007 '''
1006 '''
1008 force = opts.get('force')
1007 force = opts.get('force')
1009 rev = opts.get('rev')
1008 rev = opts.get('rev')
1010 delete = opts.get('delete')
1009 delete = opts.get('delete')
1011 rename = opts.get('rename')
1010 rename = opts.get('rename')
1012 inactive = opts.get('inactive')
1011 inactive = opts.get('inactive')
1013
1012
1014 def checkformat(mark):
1013 def checkformat(mark):
1015 mark = mark.strip()
1014 mark = mark.strip()
1016 if not mark:
1015 if not mark:
1017 raise error.Abort(_("bookmark names cannot consist entirely of "
1016 raise error.Abort(_("bookmark names cannot consist entirely of "
1018 "whitespace"))
1017 "whitespace"))
1019 scmutil.checknewlabel(repo, mark, 'bookmark')
1018 scmutil.checknewlabel(repo, mark, 'bookmark')
1020 return mark
1019 return mark
1021
1020
1022 def checkconflict(repo, mark, cur, force=False, target=None):
1021 def checkconflict(repo, mark, cur, force=False, target=None):
1023 if mark in marks and not force:
1022 if mark in marks and not force:
1024 if target:
1023 if target:
1025 if marks[mark] == target and target == cur:
1024 if marks[mark] == target and target == cur:
1026 # re-activating a bookmark
1025 # re-activating a bookmark
1027 return
1026 return
1028 anc = repo.changelog.ancestors([repo[target].rev()])
1027 anc = repo.changelog.ancestors([repo[target].rev()])
1029 bmctx = repo[marks[mark]]
1028 bmctx = repo[marks[mark]]
1030 divs = [repo[b].node() for b in marks
1029 divs = [repo[b].node() for b in marks
1031 if b.split('@', 1)[0] == mark.split('@', 1)[0]]
1030 if b.split('@', 1)[0] == mark.split('@', 1)[0]]
1032
1031
1033 # allow resolving a single divergent bookmark even if moving
1032 # allow resolving a single divergent bookmark even if moving
1034 # the bookmark across branches when a revision is specified
1033 # the bookmark across branches when a revision is specified
1035 # that contains a divergent bookmark
1034 # that contains a divergent bookmark
1036 if bmctx.rev() not in anc and target in divs:
1035 if bmctx.rev() not in anc and target in divs:
1037 bookmarks.deletedivergent(repo, [target], mark)
1036 bookmarks.deletedivergent(repo, [target], mark)
1038 return
1037 return
1039
1038
1040 deletefrom = [b for b in divs
1039 deletefrom = [b for b in divs
1041 if repo[b].rev() in anc or b == target]
1040 if repo[b].rev() in anc or b == target]
1042 bookmarks.deletedivergent(repo, deletefrom, mark)
1041 bookmarks.deletedivergent(repo, deletefrom, mark)
1043 if bookmarks.validdest(repo, bmctx, repo[target]):
1042 if bookmarks.validdest(repo, bmctx, repo[target]):
1044 ui.status(_("moving bookmark '%s' forward from %s\n") %
1043 ui.status(_("moving bookmark '%s' forward from %s\n") %
1045 (mark, short(bmctx.node())))
1044 (mark, short(bmctx.node())))
1046 return
1045 return
1047 raise error.Abort(_("bookmark '%s' already exists "
1046 raise error.Abort(_("bookmark '%s' already exists "
1048 "(use -f to force)") % mark)
1047 "(use -f to force)") % mark)
1049 if ((mark in repo.branchmap() or mark == repo.dirstate.branch())
1048 if ((mark in repo.branchmap() or mark == repo.dirstate.branch())
1050 and not force):
1049 and not force):
1051 raise error.Abort(
1050 raise error.Abort(
1052 _("a bookmark cannot have the name of an existing branch"))
1051 _("a bookmark cannot have the name of an existing branch"))
1053
1052
1054 if delete and rename:
1053 if delete and rename:
1055 raise error.Abort(_("--delete and --rename are incompatible"))
1054 raise error.Abort(_("--delete and --rename are incompatible"))
1056 if delete and rev:
1055 if delete and rev:
1057 raise error.Abort(_("--rev is incompatible with --delete"))
1056 raise error.Abort(_("--rev is incompatible with --delete"))
1058 if rename and rev:
1057 if rename and rev:
1059 raise error.Abort(_("--rev is incompatible with --rename"))
1058 raise error.Abort(_("--rev is incompatible with --rename"))
1060 if not names and (delete or rev):
1059 if not names and (delete or rev):
1061 raise error.Abort(_("bookmark name required"))
1060 raise error.Abort(_("bookmark name required"))
1062
1061
1063 if delete or rename or names or inactive:
1062 if delete or rename or names or inactive:
1064 wlock = lock = tr = None
1063 wlock = lock = tr = None
1065 try:
1064 try:
1066 wlock = repo.wlock()
1065 wlock = repo.wlock()
1067 lock = repo.lock()
1066 lock = repo.lock()
1068 cur = repo.changectx('.').node()
1067 cur = repo.changectx('.').node()
1069 marks = repo._bookmarks
1068 marks = repo._bookmarks
1070 if delete:
1069 if delete:
1071 tr = repo.transaction('bookmark')
1070 tr = repo.transaction('bookmark')
1072 for mark in names:
1071 for mark in names:
1073 if mark not in marks:
1072 if mark not in marks:
1074 raise error.Abort(_("bookmark '%s' does not exist") %
1073 raise error.Abort(_("bookmark '%s' does not exist") %
1075 mark)
1074 mark)
1076 if mark == repo._activebookmark:
1075 if mark == repo._activebookmark:
1077 bookmarks.deactivate(repo)
1076 bookmarks.deactivate(repo)
1078 del marks[mark]
1077 del marks[mark]
1079
1078
1080 elif rename:
1079 elif rename:
1081 tr = repo.transaction('bookmark')
1080 tr = repo.transaction('bookmark')
1082 if not names:
1081 if not names:
1083 raise error.Abort(_("new bookmark name required"))
1082 raise error.Abort(_("new bookmark name required"))
1084 elif len(names) > 1:
1083 elif len(names) > 1:
1085 raise error.Abort(_("only one new bookmark name allowed"))
1084 raise error.Abort(_("only one new bookmark name allowed"))
1086 mark = checkformat(names[0])
1085 mark = checkformat(names[0])
1087 if rename not in marks:
1086 if rename not in marks:
1088 raise error.Abort(_("bookmark '%s' does not exist")
1087 raise error.Abort(_("bookmark '%s' does not exist")
1089 % rename)
1088 % rename)
1090 checkconflict(repo, mark, cur, force)
1089 checkconflict(repo, mark, cur, force)
1091 marks[mark] = marks[rename]
1090 marks[mark] = marks[rename]
1092 if repo._activebookmark == rename and not inactive:
1091 if repo._activebookmark == rename and not inactive:
1093 bookmarks.activate(repo, mark)
1092 bookmarks.activate(repo, mark)
1094 del marks[rename]
1093 del marks[rename]
1095 elif names:
1094 elif names:
1096 tr = repo.transaction('bookmark')
1095 tr = repo.transaction('bookmark')
1097 newact = None
1096 newact = None
1098 for mark in names:
1097 for mark in names:
1099 mark = checkformat(mark)
1098 mark = checkformat(mark)
1100 if newact is None:
1099 if newact is None:
1101 newact = mark
1100 newact = mark
1102 if inactive and mark == repo._activebookmark:
1101 if inactive and mark == repo._activebookmark:
1103 bookmarks.deactivate(repo)
1102 bookmarks.deactivate(repo)
1104 return
1103 return
1105 tgt = cur
1104 tgt = cur
1106 if rev:
1105 if rev:
1107 tgt = scmutil.revsingle(repo, rev).node()
1106 tgt = scmutil.revsingle(repo, rev).node()
1108 checkconflict(repo, mark, cur, force, tgt)
1107 checkconflict(repo, mark, cur, force, tgt)
1109 marks[mark] = tgt
1108 marks[mark] = tgt
1110 if not inactive and cur == marks[newact] and not rev:
1109 if not inactive and cur == marks[newact] and not rev:
1111 bookmarks.activate(repo, newact)
1110 bookmarks.activate(repo, newact)
1112 elif cur != tgt and newact == repo._activebookmark:
1111 elif cur != tgt and newact == repo._activebookmark:
1113 bookmarks.deactivate(repo)
1112 bookmarks.deactivate(repo)
1114 elif inactive:
1113 elif inactive:
1115 if len(marks) == 0:
1114 if len(marks) == 0:
1116 ui.status(_("no bookmarks set\n"))
1115 ui.status(_("no bookmarks set\n"))
1117 elif not repo._activebookmark:
1116 elif not repo._activebookmark:
1118 ui.status(_("no active bookmark\n"))
1117 ui.status(_("no active bookmark\n"))
1119 else:
1118 else:
1120 bookmarks.deactivate(repo)
1119 bookmarks.deactivate(repo)
1121 if tr is not None:
1120 if tr is not None:
1122 marks.recordchange(tr)
1121 marks.recordchange(tr)
1123 tr.close()
1122 tr.close()
1124 finally:
1123 finally:
1125 lockmod.release(tr, lock, wlock)
1124 lockmod.release(tr, lock, wlock)
1126 else: # show bookmarks
1125 else: # show bookmarks
1127 fm = ui.formatter('bookmarks', opts)
1126 fm = ui.formatter('bookmarks', opts)
1128 hexfn = fm.hexfunc
1127 hexfn = fm.hexfunc
1129 marks = repo._bookmarks
1128 marks = repo._bookmarks
1130 if len(marks) == 0 and fm.isplain():
1129 if len(marks) == 0 and fm.isplain():
1131 ui.status(_("no bookmarks set\n"))
1130 ui.status(_("no bookmarks set\n"))
1132 for bmark, n in sorted(marks.iteritems()):
1131 for bmark, n in sorted(marks.iteritems()):
1133 active = repo._activebookmark
1132 active = repo._activebookmark
1134 if bmark == active:
1133 if bmark == active:
1135 prefix, label = '*', activebookmarklabel
1134 prefix, label = '*', activebookmarklabel
1136 else:
1135 else:
1137 prefix, label = ' ', ''
1136 prefix, label = ' ', ''
1138
1137
1139 fm.startitem()
1138 fm.startitem()
1140 if not ui.quiet:
1139 if not ui.quiet:
1141 fm.plain(' %s ' % prefix, label=label)
1140 fm.plain(' %s ' % prefix, label=label)
1142 fm.write('bookmark', '%s', bmark, label=label)
1141 fm.write('bookmark', '%s', bmark, label=label)
1143 pad = " " * (25 - encoding.colwidth(bmark))
1142 pad = " " * (25 - encoding.colwidth(bmark))
1144 fm.condwrite(not ui.quiet, 'rev node', pad + ' %d:%s',
1143 fm.condwrite(not ui.quiet, 'rev node', pad + ' %d:%s',
1145 repo.changelog.rev(n), hexfn(n), label=label)
1144 repo.changelog.rev(n), hexfn(n), label=label)
1146 fm.data(active=(bmark == active))
1145 fm.data(active=(bmark == active))
1147 fm.plain('\n')
1146 fm.plain('\n')
1148 fm.end()
1147 fm.end()
1149
1148
1150 @command('branch',
1149 @command('branch',
1151 [('f', 'force', None,
1150 [('f', 'force', None,
1152 _('set branch name even if it shadows an existing branch')),
1151 _('set branch name even if it shadows an existing branch')),
1153 ('C', 'clean', None, _('reset branch name to parent branch name'))],
1152 ('C', 'clean', None, _('reset branch name to parent branch name'))],
1154 _('[-fC] [NAME]'))
1153 _('[-fC] [NAME]'))
1155 def branch(ui, repo, label=None, **opts):
1154 def branch(ui, repo, label=None, **opts):
1156 """set or show the current branch name
1155 """set or show the current branch name
1157
1156
1158 .. note::
1157 .. note::
1159
1158
1160 Branch names are permanent and global. Use :hg:`bookmark` to create a
1159 Branch names are permanent and global. Use :hg:`bookmark` to create a
1161 light-weight bookmark instead. See :hg:`help glossary` for more
1160 light-weight bookmark instead. See :hg:`help glossary` for more
1162 information about named branches and bookmarks.
1161 information about named branches and bookmarks.
1163
1162
1164 With no argument, show the current branch name. With one argument,
1163 With no argument, show the current branch name. With one argument,
1165 set the working directory branch name (the branch will not exist
1164 set the working directory branch name (the branch will not exist
1166 in the repository until the next commit). Standard practice
1165 in the repository until the next commit). Standard practice
1167 recommends that primary development take place on the 'default'
1166 recommends that primary development take place on the 'default'
1168 branch.
1167 branch.
1169
1168
1170 Unless -f/--force is specified, branch will not let you set a
1169 Unless -f/--force is specified, branch will not let you set a
1171 branch name that already exists.
1170 branch name that already exists.
1172
1171
1173 Use -C/--clean to reset the working directory branch to that of
1172 Use -C/--clean to reset the working directory branch to that of
1174 the parent of the working directory, negating a previous branch
1173 the parent of the working directory, negating a previous branch
1175 change.
1174 change.
1176
1175
1177 Use the command :hg:`update` to switch to an existing branch. Use
1176 Use the command :hg:`update` to switch to an existing branch. Use
1178 :hg:`commit --close-branch` to mark this branch head as closed.
1177 :hg:`commit --close-branch` to mark this branch head as closed.
1179 When all heads of a branch are closed, the branch will be
1178 When all heads of a branch are closed, the branch will be
1180 considered closed.
1179 considered closed.
1181
1180
1182 Returns 0 on success.
1181 Returns 0 on success.
1183 """
1182 """
1184 if label:
1183 if label:
1185 label = label.strip()
1184 label = label.strip()
1186
1185
1187 if not opts.get('clean') and not label:
1186 if not opts.get('clean') and not label:
1188 ui.write("%s\n" % repo.dirstate.branch())
1187 ui.write("%s\n" % repo.dirstate.branch())
1189 return
1188 return
1190
1189
1191 with repo.wlock():
1190 with repo.wlock():
1192 if opts.get('clean'):
1191 if opts.get('clean'):
1193 label = repo[None].p1().branch()
1192 label = repo[None].p1().branch()
1194 repo.dirstate.setbranch(label)
1193 repo.dirstate.setbranch(label)
1195 ui.status(_('reset working directory to branch %s\n') % label)
1194 ui.status(_('reset working directory to branch %s\n') % label)
1196 elif label:
1195 elif label:
1197 if not opts.get('force') and label in repo.branchmap():
1196 if not opts.get('force') and label in repo.branchmap():
1198 if label not in [p.branch() for p in repo[None].parents()]:
1197 if label not in [p.branch() for p in repo[None].parents()]:
1199 raise error.Abort(_('a branch of the same name already'
1198 raise error.Abort(_('a branch of the same name already'
1200 ' exists'),
1199 ' exists'),
1201 # i18n: "it" refers to an existing branch
1200 # i18n: "it" refers to an existing branch
1202 hint=_("use 'hg update' to switch to it"))
1201 hint=_("use 'hg update' to switch to it"))
1203 scmutil.checknewlabel(repo, label, 'branch')
1202 scmutil.checknewlabel(repo, label, 'branch')
1204 repo.dirstate.setbranch(label)
1203 repo.dirstate.setbranch(label)
1205 ui.status(_('marked working directory as branch %s\n') % label)
1204 ui.status(_('marked working directory as branch %s\n') % label)
1206
1205
1207 # find any open named branches aside from default
1206 # find any open named branches aside from default
1208 others = [n for n, h, t, c in repo.branchmap().iterbranches()
1207 others = [n for n, h, t, c in repo.branchmap().iterbranches()
1209 if n != "default" and not c]
1208 if n != "default" and not c]
1210 if not others:
1209 if not others:
1211 ui.status(_('(branches are permanent and global, '
1210 ui.status(_('(branches are permanent and global, '
1212 'did you want a bookmark?)\n'))
1211 'did you want a bookmark?)\n'))
1213
1212
1214 @command('branches',
1213 @command('branches',
1215 [('a', 'active', False,
1214 [('a', 'active', False,
1216 _('show only branches that have unmerged heads (DEPRECATED)')),
1215 _('show only branches that have unmerged heads (DEPRECATED)')),
1217 ('c', 'closed', False, _('show normal and closed branches')),
1216 ('c', 'closed', False, _('show normal and closed branches')),
1218 ] + formatteropts,
1217 ] + formatteropts,
1219 _('[-c]'))
1218 _('[-c]'))
1220 def branches(ui, repo, active=False, closed=False, **opts):
1219 def branches(ui, repo, active=False, closed=False, **opts):
1221 """list repository named branches
1220 """list repository named branches
1222
1221
1223 List the repository's named branches, indicating which ones are
1222 List the repository's named branches, indicating which ones are
1224 inactive. If -c/--closed is specified, also list branches which have
1223 inactive. If -c/--closed is specified, also list branches which have
1225 been marked closed (see :hg:`commit --close-branch`).
1224 been marked closed (see :hg:`commit --close-branch`).
1226
1225
1227 Use the command :hg:`update` to switch to an existing branch.
1226 Use the command :hg:`update` to switch to an existing branch.
1228
1227
1229 Returns 0.
1228 Returns 0.
1230 """
1229 """
1231
1230
1232 fm = ui.formatter('branches', opts)
1231 fm = ui.formatter('branches', opts)
1233 hexfunc = fm.hexfunc
1232 hexfunc = fm.hexfunc
1234
1233
1235 allheads = set(repo.heads())
1234 allheads = set(repo.heads())
1236 branches = []
1235 branches = []
1237 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1236 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1238 isactive = not isclosed and bool(set(heads) & allheads)
1237 isactive = not isclosed and bool(set(heads) & allheads)
1239 branches.append((tag, repo[tip], isactive, not isclosed))
1238 branches.append((tag, repo[tip], isactive, not isclosed))
1240 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]),
1239 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]),
1241 reverse=True)
1240 reverse=True)
1242
1241
1243 for tag, ctx, isactive, isopen in branches:
1242 for tag, ctx, isactive, isopen in branches:
1244 if active and not isactive:
1243 if active and not isactive:
1245 continue
1244 continue
1246 if isactive:
1245 if isactive:
1247 label = 'branches.active'
1246 label = 'branches.active'
1248 notice = ''
1247 notice = ''
1249 elif not isopen:
1248 elif not isopen:
1250 if not closed:
1249 if not closed:
1251 continue
1250 continue
1252 label = 'branches.closed'
1251 label = 'branches.closed'
1253 notice = _(' (closed)')
1252 notice = _(' (closed)')
1254 else:
1253 else:
1255 label = 'branches.inactive'
1254 label = 'branches.inactive'
1256 notice = _(' (inactive)')
1255 notice = _(' (inactive)')
1257 current = (tag == repo.dirstate.branch())
1256 current = (tag == repo.dirstate.branch())
1258 if current:
1257 if current:
1259 label = 'branches.current'
1258 label = 'branches.current'
1260
1259
1261 fm.startitem()
1260 fm.startitem()
1262 fm.write('branch', '%s', tag, label=label)
1261 fm.write('branch', '%s', tag, label=label)
1263 rev = ctx.rev()
1262 rev = ctx.rev()
1264 padsize = max(31 - len(str(rev)) - encoding.colwidth(tag), 0)
1263 padsize = max(31 - len(str(rev)) - encoding.colwidth(tag), 0)
1265 fmt = ' ' * padsize + ' %d:%s'
1264 fmt = ' ' * padsize + ' %d:%s'
1266 fm.condwrite(not ui.quiet, 'rev node', fmt, rev, hexfunc(ctx.node()),
1265 fm.condwrite(not ui.quiet, 'rev node', fmt, rev, hexfunc(ctx.node()),
1267 label='log.changeset changeset.%s' % ctx.phasestr())
1266 label='log.changeset changeset.%s' % ctx.phasestr())
1268 fm.data(active=isactive, closed=not isopen, current=current)
1267 fm.data(active=isactive, closed=not isopen, current=current)
1269 if not ui.quiet:
1268 if not ui.quiet:
1270 fm.plain(notice)
1269 fm.plain(notice)
1271 fm.plain('\n')
1270 fm.plain('\n')
1272 fm.end()
1271 fm.end()
1273
1272
1274 @command('bundle',
1273 @command('bundle',
1275 [('f', 'force', None, _('run even when the destination is unrelated')),
1274 [('f', 'force', None, _('run even when the destination is unrelated')),
1276 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
1275 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
1277 _('REV')),
1276 _('REV')),
1278 ('b', 'branch', [], _('a specific branch you would like to bundle'),
1277 ('b', 'branch', [], _('a specific branch you would like to bundle'),
1279 _('BRANCH')),
1278 _('BRANCH')),
1280 ('', 'base', [],
1279 ('', 'base', [],
1281 _('a base changeset assumed to be available at the destination'),
1280 _('a base changeset assumed to be available at the destination'),
1282 _('REV')),
1281 _('REV')),
1283 ('a', 'all', None, _('bundle all changesets in the repository')),
1282 ('a', 'all', None, _('bundle all changesets in the repository')),
1284 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
1283 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
1285 ] + remoteopts,
1284 ] + remoteopts,
1286 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]'))
1285 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]'))
1287 def bundle(ui, repo, fname, dest=None, **opts):
1286 def bundle(ui, repo, fname, dest=None, **opts):
1288 """create a changegroup file
1287 """create a changegroup file
1289
1288
1290 Generate a changegroup file collecting changesets to be added
1289 Generate a changegroup file collecting changesets to be added
1291 to a repository.
1290 to a repository.
1292
1291
1293 To create a bundle containing all changesets, use -a/--all
1292 To create a bundle containing all changesets, use -a/--all
1294 (or --base null). Otherwise, hg assumes the destination will have
1293 (or --base null). Otherwise, hg assumes the destination will have
1295 all the nodes you specify with --base parameters. Otherwise, hg
1294 all the nodes you specify with --base parameters. Otherwise, hg
1296 will assume the repository has all the nodes in destination, or
1295 will assume the repository has all the nodes in destination, or
1297 default-push/default if no destination is specified.
1296 default-push/default if no destination is specified.
1298
1297
1299 You can change bundle format with the -t/--type option. You can
1298 You can change bundle format with the -t/--type option. You can
1300 specify a compression, a bundle version or both using a dash
1299 specify a compression, a bundle version or both using a dash
1301 (comp-version). The available compression methods are: none, bzip2,
1300 (comp-version). The available compression methods are: none, bzip2,
1302 and gzip (by default, bundles are compressed using bzip2). The
1301 and gzip (by default, bundles are compressed using bzip2). The
1303 available formats are: v1, v2 (default to most suitable).
1302 available formats are: v1, v2 (default to most suitable).
1304
1303
1305 The bundle file can then be transferred using conventional means
1304 The bundle file can then be transferred using conventional means
1306 and applied to another repository with the unbundle or pull
1305 and applied to another repository with the unbundle or pull
1307 command. This is useful when direct push and pull are not
1306 command. This is useful when direct push and pull are not
1308 available or when exporting an entire repository is undesirable.
1307 available or when exporting an entire repository is undesirable.
1309
1308
1310 Applying bundles preserves all changeset contents including
1309 Applying bundles preserves all changeset contents including
1311 permissions, copy/rename information, and revision history.
1310 permissions, copy/rename information, and revision history.
1312
1311
1313 Returns 0 on success, 1 if no changes found.
1312 Returns 0 on success, 1 if no changes found.
1314 """
1313 """
1315 revs = None
1314 revs = None
1316 if 'rev' in opts:
1315 if 'rev' in opts:
1317 revstrings = opts['rev']
1316 revstrings = opts['rev']
1318 revs = scmutil.revrange(repo, revstrings)
1317 revs = scmutil.revrange(repo, revstrings)
1319 if revstrings and not revs:
1318 if revstrings and not revs:
1320 raise error.Abort(_('no commits to bundle'))
1319 raise error.Abort(_('no commits to bundle'))
1321
1320
1322 bundletype = opts.get('type', 'bzip2').lower()
1321 bundletype = opts.get('type', 'bzip2').lower()
1323 try:
1322 try:
1324 bcompression, cgversion, params = exchange.parsebundlespec(
1323 bcompression, cgversion, params = exchange.parsebundlespec(
1325 repo, bundletype, strict=False)
1324 repo, bundletype, strict=False)
1326 except error.UnsupportedBundleSpecification as e:
1325 except error.UnsupportedBundleSpecification as e:
1327 raise error.Abort(str(e),
1326 raise error.Abort(str(e),
1328 hint=_("see 'hg help bundle' for supported "
1327 hint=_("see 'hg help bundle' for supported "
1329 "values for --type"))
1328 "values for --type"))
1330
1329
1331 # Packed bundles are a pseudo bundle format for now.
1330 # Packed bundles are a pseudo bundle format for now.
1332 if cgversion == 's1':
1331 if cgversion == 's1':
1333 raise error.Abort(_('packed bundles cannot be produced by "hg bundle"'),
1332 raise error.Abort(_('packed bundles cannot be produced by "hg bundle"'),
1334 hint=_("use 'hg debugcreatestreamclonebundle'"))
1333 hint=_("use 'hg debugcreatestreamclonebundle'"))
1335
1334
1336 if opts.get('all'):
1335 if opts.get('all'):
1337 if dest:
1336 if dest:
1338 raise error.Abort(_("--all is incompatible with specifying "
1337 raise error.Abort(_("--all is incompatible with specifying "
1339 "a destination"))
1338 "a destination"))
1340 if opts.get('base'):
1339 if opts.get('base'):
1341 ui.warn(_("ignoring --base because --all was specified\n"))
1340 ui.warn(_("ignoring --base because --all was specified\n"))
1342 base = ['null']
1341 base = ['null']
1343 else:
1342 else:
1344 base = scmutil.revrange(repo, opts.get('base'))
1343 base = scmutil.revrange(repo, opts.get('base'))
1345 # TODO: get desired bundlecaps from command line.
1344 # TODO: get desired bundlecaps from command line.
1346 bundlecaps = None
1345 bundlecaps = None
1347 if cgversion not in changegroup.supportedoutgoingversions(repo):
1346 if cgversion not in changegroup.supportedoutgoingversions(repo):
1348 raise error.Abort(_("repository does not support bundle version %s") %
1347 raise error.Abort(_("repository does not support bundle version %s") %
1349 cgversion)
1348 cgversion)
1350
1349
1351 if base:
1350 if base:
1352 if dest:
1351 if dest:
1353 raise error.Abort(_("--base is incompatible with specifying "
1352 raise error.Abort(_("--base is incompatible with specifying "
1354 "a destination"))
1353 "a destination"))
1355 common = [repo.lookup(rev) for rev in base]
1354 common = [repo.lookup(rev) for rev in base]
1356 heads = revs and map(repo.lookup, revs) or None
1355 heads = revs and map(repo.lookup, revs) or None
1357 outgoing = discovery.outgoing(repo, common, heads)
1356 outgoing = discovery.outgoing(repo, common, heads)
1358 cg = changegroup.getchangegroup(repo, 'bundle', outgoing,
1357 cg = changegroup.getchangegroup(repo, 'bundle', outgoing,
1359 bundlecaps=bundlecaps,
1358 bundlecaps=bundlecaps,
1360 version=cgversion)
1359 version=cgversion)
1361 outgoing = None
1360 outgoing = None
1362 else:
1361 else:
1363 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1362 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1364 dest, branches = hg.parseurl(dest, opts.get('branch'))
1363 dest, branches = hg.parseurl(dest, opts.get('branch'))
1365 other = hg.peer(repo, opts, dest)
1364 other = hg.peer(repo, opts, dest)
1366 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1365 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1367 heads = revs and map(repo.lookup, revs) or revs
1366 heads = revs and map(repo.lookup, revs) or revs
1368 outgoing = discovery.findcommonoutgoing(repo, other,
1367 outgoing = discovery.findcommonoutgoing(repo, other,
1369 onlyheads=heads,
1368 onlyheads=heads,
1370 force=opts.get('force'),
1369 force=opts.get('force'),
1371 portable=True)
1370 portable=True)
1372 cg = changegroup.getlocalchangegroup(repo, 'bundle', outgoing,
1371 cg = changegroup.getlocalchangegroup(repo, 'bundle', outgoing,
1373 bundlecaps, version=cgversion)
1372 bundlecaps, version=cgversion)
1374 if not cg:
1373 if not cg:
1375 scmutil.nochangesfound(ui, repo, outgoing and outgoing.excluded)
1374 scmutil.nochangesfound(ui, repo, outgoing and outgoing.excluded)
1376 return 1
1375 return 1
1377
1376
1378 if cgversion == '01': #bundle1
1377 if cgversion == '01': #bundle1
1379 if bcompression is None:
1378 if bcompression is None:
1380 bcompression = 'UN'
1379 bcompression = 'UN'
1381 bversion = 'HG10' + bcompression
1380 bversion = 'HG10' + bcompression
1382 bcompression = None
1381 bcompression = None
1383 else:
1382 else:
1384 assert cgversion == '02'
1383 assert cgversion == '02'
1385 bversion = 'HG20'
1384 bversion = 'HG20'
1386
1385
1387 bundle2.writebundle(ui, cg, fname, bversion, compression=bcompression)
1386 bundle2.writebundle(ui, cg, fname, bversion, compression=bcompression)
1388
1387
1389 @command('cat',
1388 @command('cat',
1390 [('o', 'output', '',
1389 [('o', 'output', '',
1391 _('print output to file with formatted name'), _('FORMAT')),
1390 _('print output to file with formatted name'), _('FORMAT')),
1392 ('r', 'rev', '', _('print the given revision'), _('REV')),
1391 ('r', 'rev', '', _('print the given revision'), _('REV')),
1393 ('', 'decode', None, _('apply any matching decode filter')),
1392 ('', 'decode', None, _('apply any matching decode filter')),
1394 ] + walkopts,
1393 ] + walkopts,
1395 _('[OPTION]... FILE...'),
1394 _('[OPTION]... FILE...'),
1396 inferrepo=True)
1395 inferrepo=True)
1397 def cat(ui, repo, file1, *pats, **opts):
1396 def cat(ui, repo, file1, *pats, **opts):
1398 """output the current or given revision of files
1397 """output the current or given revision of files
1399
1398
1400 Print the specified files as they were at the given revision. If
1399 Print the specified files as they were at the given revision. If
1401 no revision is given, the parent of the working directory is used.
1400 no revision is given, the parent of the working directory is used.
1402
1401
1403 Output may be to a file, in which case the name of the file is
1402 Output may be to a file, in which case the name of the file is
1404 given using a format string. The formatting rules as follows:
1403 given using a format string. The formatting rules as follows:
1405
1404
1406 :``%%``: literal "%" character
1405 :``%%``: literal "%" character
1407 :``%s``: basename of file being printed
1406 :``%s``: basename of file being printed
1408 :``%d``: dirname of file being printed, or '.' if in repository root
1407 :``%d``: dirname of file being printed, or '.' if in repository root
1409 :``%p``: root-relative path name of file being printed
1408 :``%p``: root-relative path name of file being printed
1410 :``%H``: changeset hash (40 hexadecimal digits)
1409 :``%H``: changeset hash (40 hexadecimal digits)
1411 :``%R``: changeset revision number
1410 :``%R``: changeset revision number
1412 :``%h``: short-form changeset hash (12 hexadecimal digits)
1411 :``%h``: short-form changeset hash (12 hexadecimal digits)
1413 :``%r``: zero-padded changeset revision number
1412 :``%r``: zero-padded changeset revision number
1414 :``%b``: basename of the exporting repository
1413 :``%b``: basename of the exporting repository
1415
1414
1416 Returns 0 on success.
1415 Returns 0 on success.
1417 """
1416 """
1418 ctx = scmutil.revsingle(repo, opts.get('rev'))
1417 ctx = scmutil.revsingle(repo, opts.get('rev'))
1419 m = scmutil.match(ctx, (file1,) + pats, opts)
1418 m = scmutil.match(ctx, (file1,) + pats, opts)
1420
1419
1421 return cmdutil.cat(ui, repo, ctx, m, '', **opts)
1420 return cmdutil.cat(ui, repo, ctx, m, '', **opts)
1422
1421
1423 @command('^clone',
1422 @command('^clone',
1424 [('U', 'noupdate', None, _('the clone will include an empty working '
1423 [('U', 'noupdate', None, _('the clone will include an empty working '
1425 'directory (only a repository)')),
1424 'directory (only a repository)')),
1426 ('u', 'updaterev', '', _('revision, tag, or branch to check out'),
1425 ('u', 'updaterev', '', _('revision, tag, or branch to check out'),
1427 _('REV')),
1426 _('REV')),
1428 ('r', 'rev', [], _('include the specified changeset'), _('REV')),
1427 ('r', 'rev', [], _('include the specified changeset'), _('REV')),
1429 ('b', 'branch', [], _('clone only the specified branch'), _('BRANCH')),
1428 ('b', 'branch', [], _('clone only the specified branch'), _('BRANCH')),
1430 ('', 'pull', None, _('use pull protocol to copy metadata')),
1429 ('', 'pull', None, _('use pull protocol to copy metadata')),
1431 ('', 'uncompressed', None, _('use uncompressed transfer (fast over LAN)')),
1430 ('', 'uncompressed', None, _('use uncompressed transfer (fast over LAN)')),
1432 ] + remoteopts,
1431 ] + remoteopts,
1433 _('[OPTION]... SOURCE [DEST]'),
1432 _('[OPTION]... SOURCE [DEST]'),
1434 norepo=True)
1433 norepo=True)
1435 def clone(ui, source, dest=None, **opts):
1434 def clone(ui, source, dest=None, **opts):
1436 """make a copy of an existing repository
1435 """make a copy of an existing repository
1437
1436
1438 Create a copy of an existing repository in a new directory.
1437 Create a copy of an existing repository in a new directory.
1439
1438
1440 If no destination directory name is specified, it defaults to the
1439 If no destination directory name is specified, it defaults to the
1441 basename of the source.
1440 basename of the source.
1442
1441
1443 The location of the source is added to the new repository's
1442 The location of the source is added to the new repository's
1444 ``.hg/hgrc`` file, as the default to be used for future pulls.
1443 ``.hg/hgrc`` file, as the default to be used for future pulls.
1445
1444
1446 Only local paths and ``ssh://`` URLs are supported as
1445 Only local paths and ``ssh://`` URLs are supported as
1447 destinations. For ``ssh://`` destinations, no working directory or
1446 destinations. For ``ssh://`` destinations, no working directory or
1448 ``.hg/hgrc`` will be created on the remote side.
1447 ``.hg/hgrc`` will be created on the remote side.
1449
1448
1450 If the source repository has a bookmark called '@' set, that
1449 If the source repository has a bookmark called '@' set, that
1451 revision will be checked out in the new repository by default.
1450 revision will be checked out in the new repository by default.
1452
1451
1453 To check out a particular version, use -u/--update, or
1452 To check out a particular version, use -u/--update, or
1454 -U/--noupdate to create a clone with no working directory.
1453 -U/--noupdate to create a clone with no working directory.
1455
1454
1456 To pull only a subset of changesets, specify one or more revisions
1455 To pull only a subset of changesets, specify one or more revisions
1457 identifiers with -r/--rev or branches with -b/--branch. The
1456 identifiers with -r/--rev or branches with -b/--branch. The
1458 resulting clone will contain only the specified changesets and
1457 resulting clone will contain only the specified changesets and
1459 their ancestors. These options (or 'clone src#rev dest') imply
1458 their ancestors. These options (or 'clone src#rev dest') imply
1460 --pull, even for local source repositories.
1459 --pull, even for local source repositories.
1461
1460
1462 .. note::
1461 .. note::
1463
1462
1464 Specifying a tag will include the tagged changeset but not the
1463 Specifying a tag will include the tagged changeset but not the
1465 changeset containing the tag.
1464 changeset containing the tag.
1466
1465
1467 .. container:: verbose
1466 .. container:: verbose
1468
1467
1469 For efficiency, hardlinks are used for cloning whenever the
1468 For efficiency, hardlinks are used for cloning whenever the
1470 source and destination are on the same filesystem (note this
1469 source and destination are on the same filesystem (note this
1471 applies only to the repository data, not to the working
1470 applies only to the repository data, not to the working
1472 directory). Some filesystems, such as AFS, implement hardlinking
1471 directory). Some filesystems, such as AFS, implement hardlinking
1473 incorrectly, but do not report errors. In these cases, use the
1472 incorrectly, but do not report errors. In these cases, use the
1474 --pull option to avoid hardlinking.
1473 --pull option to avoid hardlinking.
1475
1474
1476 In some cases, you can clone repositories and the working
1475 In some cases, you can clone repositories and the working
1477 directory using full hardlinks with ::
1476 directory using full hardlinks with ::
1478
1477
1479 $ cp -al REPO REPOCLONE
1478 $ cp -al REPO REPOCLONE
1480
1479
1481 This is the fastest way to clone, but it is not always safe. The
1480 This is the fastest way to clone, but it is not always safe. The
1482 operation is not atomic (making sure REPO is not modified during
1481 operation is not atomic (making sure REPO is not modified during
1483 the operation is up to you) and you have to make sure your
1482 the operation is up to you) and you have to make sure your
1484 editor breaks hardlinks (Emacs and most Linux Kernel tools do
1483 editor breaks hardlinks (Emacs and most Linux Kernel tools do
1485 so). Also, this is not compatible with certain extensions that
1484 so). Also, this is not compatible with certain extensions that
1486 place their metadata under the .hg directory, such as mq.
1485 place their metadata under the .hg directory, such as mq.
1487
1486
1488 Mercurial will update the working directory to the first applicable
1487 Mercurial will update the working directory to the first applicable
1489 revision from this list:
1488 revision from this list:
1490
1489
1491 a) null if -U or the source repository has no changesets
1490 a) null if -U or the source repository has no changesets
1492 b) if -u . and the source repository is local, the first parent of
1491 b) if -u . and the source repository is local, the first parent of
1493 the source repository's working directory
1492 the source repository's working directory
1494 c) the changeset specified with -u (if a branch name, this means the
1493 c) the changeset specified with -u (if a branch name, this means the
1495 latest head of that branch)
1494 latest head of that branch)
1496 d) the changeset specified with -r
1495 d) the changeset specified with -r
1497 e) the tipmost head specified with -b
1496 e) the tipmost head specified with -b
1498 f) the tipmost head specified with the url#branch source syntax
1497 f) the tipmost head specified with the url#branch source syntax
1499 g) the revision marked with the '@' bookmark, if present
1498 g) the revision marked with the '@' bookmark, if present
1500 h) the tipmost head of the default branch
1499 h) the tipmost head of the default branch
1501 i) tip
1500 i) tip
1502
1501
1503 When cloning from servers that support it, Mercurial may fetch
1502 When cloning from servers that support it, Mercurial may fetch
1504 pre-generated data from a server-advertised URL. When this is done,
1503 pre-generated data from a server-advertised URL. When this is done,
1505 hooks operating on incoming changesets and changegroups may fire twice,
1504 hooks operating on incoming changesets and changegroups may fire twice,
1506 once for the bundle fetched from the URL and another for any additional
1505 once for the bundle fetched from the URL and another for any additional
1507 data not fetched from this URL. In addition, if an error occurs, the
1506 data not fetched from this URL. In addition, if an error occurs, the
1508 repository may be rolled back to a partial clone. This behavior may
1507 repository may be rolled back to a partial clone. This behavior may
1509 change in future releases. See :hg:`help -e clonebundles` for more.
1508 change in future releases. See :hg:`help -e clonebundles` for more.
1510
1509
1511 Examples:
1510 Examples:
1512
1511
1513 - clone a remote repository to a new directory named hg/::
1512 - clone a remote repository to a new directory named hg/::
1514
1513
1515 hg clone https://www.mercurial-scm.org/repo/hg/
1514 hg clone https://www.mercurial-scm.org/repo/hg/
1516
1515
1517 - create a lightweight local clone::
1516 - create a lightweight local clone::
1518
1517
1519 hg clone project/ project-feature/
1518 hg clone project/ project-feature/
1520
1519
1521 - clone from an absolute path on an ssh server (note double-slash)::
1520 - clone from an absolute path on an ssh server (note double-slash)::
1522
1521
1523 hg clone ssh://user@server//home/projects/alpha/
1522 hg clone ssh://user@server//home/projects/alpha/
1524
1523
1525 - do a high-speed clone over a LAN while checking out a
1524 - do a high-speed clone over a LAN while checking out a
1526 specified version::
1525 specified version::
1527
1526
1528 hg clone --uncompressed http://server/repo -u 1.5
1527 hg clone --uncompressed http://server/repo -u 1.5
1529
1528
1530 - create a repository without changesets after a particular revision::
1529 - create a repository without changesets after a particular revision::
1531
1530
1532 hg clone -r 04e544 experimental/ good/
1531 hg clone -r 04e544 experimental/ good/
1533
1532
1534 - clone (and track) a particular named branch::
1533 - clone (and track) a particular named branch::
1535
1534
1536 hg clone https://www.mercurial-scm.org/repo/hg/#stable
1535 hg clone https://www.mercurial-scm.org/repo/hg/#stable
1537
1536
1538 See :hg:`help urls` for details on specifying URLs.
1537 See :hg:`help urls` for details on specifying URLs.
1539
1538
1540 Returns 0 on success.
1539 Returns 0 on success.
1541 """
1540 """
1542 if opts.get('noupdate') and opts.get('updaterev'):
1541 if opts.get('noupdate') and opts.get('updaterev'):
1543 raise error.Abort(_("cannot specify both --noupdate and --updaterev"))
1542 raise error.Abort(_("cannot specify both --noupdate and --updaterev"))
1544
1543
1545 r = hg.clone(ui, opts, source, dest,
1544 r = hg.clone(ui, opts, source, dest,
1546 pull=opts.get('pull'),
1545 pull=opts.get('pull'),
1547 stream=opts.get('uncompressed'),
1546 stream=opts.get('uncompressed'),
1548 rev=opts.get('rev'),
1547 rev=opts.get('rev'),
1549 update=opts.get('updaterev') or not opts.get('noupdate'),
1548 update=opts.get('updaterev') or not opts.get('noupdate'),
1550 branch=opts.get('branch'),
1549 branch=opts.get('branch'),
1551 shareopts=opts.get('shareopts'))
1550 shareopts=opts.get('shareopts'))
1552
1551
1553 return r is None
1552 return r is None
1554
1553
1555 @command('^commit|ci',
1554 @command('^commit|ci',
1556 [('A', 'addremove', None,
1555 [('A', 'addremove', None,
1557 _('mark new/missing files as added/removed before committing')),
1556 _('mark new/missing files as added/removed before committing')),
1558 ('', 'close-branch', None,
1557 ('', 'close-branch', None,
1559 _('mark a branch head as closed')),
1558 _('mark a branch head as closed')),
1560 ('', 'amend', None, _('amend the parent of the working directory')),
1559 ('', 'amend', None, _('amend the parent of the working directory')),
1561 ('s', 'secret', None, _('use the secret phase for committing')),
1560 ('s', 'secret', None, _('use the secret phase for committing')),
1562 ('e', 'edit', None, _('invoke editor on commit messages')),
1561 ('e', 'edit', None, _('invoke editor on commit messages')),
1563 ('i', 'interactive', None, _('use interactive mode')),
1562 ('i', 'interactive', None, _('use interactive mode')),
1564 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1563 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1565 _('[OPTION]... [FILE]...'),
1564 _('[OPTION]... [FILE]...'),
1566 inferrepo=True)
1565 inferrepo=True)
1567 def commit(ui, repo, *pats, **opts):
1566 def commit(ui, repo, *pats, **opts):
1568 """commit the specified files or all outstanding changes
1567 """commit the specified files or all outstanding changes
1569
1568
1570 Commit changes to the given files into the repository. Unlike a
1569 Commit changes to the given files into the repository. Unlike a
1571 centralized SCM, this operation is a local operation. See
1570 centralized SCM, this operation is a local operation. See
1572 :hg:`push` for a way to actively distribute your changes.
1571 :hg:`push` for a way to actively distribute your changes.
1573
1572
1574 If a list of files is omitted, all changes reported by :hg:`status`
1573 If a list of files is omitted, all changes reported by :hg:`status`
1575 will be committed.
1574 will be committed.
1576
1575
1577 If you are committing the result of a merge, do not provide any
1576 If you are committing the result of a merge, do not provide any
1578 filenames or -I/-X filters.
1577 filenames or -I/-X filters.
1579
1578
1580 If no commit message is specified, Mercurial starts your
1579 If no commit message is specified, Mercurial starts your
1581 configured editor where you can enter a message. In case your
1580 configured editor where you can enter a message. In case your
1582 commit fails, you will find a backup of your message in
1581 commit fails, you will find a backup of your message in
1583 ``.hg/last-message.txt``.
1582 ``.hg/last-message.txt``.
1584
1583
1585 The --close-branch flag can be used to mark the current branch
1584 The --close-branch flag can be used to mark the current branch
1586 head closed. When all heads of a branch are closed, the branch
1585 head closed. When all heads of a branch are closed, the branch
1587 will be considered closed and no longer listed.
1586 will be considered closed and no longer listed.
1588
1587
1589 The --amend flag can be used to amend the parent of the
1588 The --amend flag can be used to amend the parent of the
1590 working directory with a new commit that contains the changes
1589 working directory with a new commit that contains the changes
1591 in the parent in addition to those currently reported by :hg:`status`,
1590 in the parent in addition to those currently reported by :hg:`status`,
1592 if there are any. The old commit is stored in a backup bundle in
1591 if there are any. The old commit is stored in a backup bundle in
1593 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1592 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1594 on how to restore it).
1593 on how to restore it).
1595
1594
1596 Message, user and date are taken from the amended commit unless
1595 Message, user and date are taken from the amended commit unless
1597 specified. When a message isn't specified on the command line,
1596 specified. When a message isn't specified on the command line,
1598 the editor will open with the message of the amended commit.
1597 the editor will open with the message of the amended commit.
1599
1598
1600 It is not possible to amend public changesets (see :hg:`help phases`)
1599 It is not possible to amend public changesets (see :hg:`help phases`)
1601 or changesets that have children.
1600 or changesets that have children.
1602
1601
1603 See :hg:`help dates` for a list of formats valid for -d/--date.
1602 See :hg:`help dates` for a list of formats valid for -d/--date.
1604
1603
1605 Returns 0 on success, 1 if nothing changed.
1604 Returns 0 on success, 1 if nothing changed.
1606
1605
1607 .. container:: verbose
1606 .. container:: verbose
1608
1607
1609 Examples:
1608 Examples:
1610
1609
1611 - commit all files ending in .py::
1610 - commit all files ending in .py::
1612
1611
1613 hg commit --include "set:**.py"
1612 hg commit --include "set:**.py"
1614
1613
1615 - commit all non-binary files::
1614 - commit all non-binary files::
1616
1615
1617 hg commit --exclude "set:binary()"
1616 hg commit --exclude "set:binary()"
1618
1617
1619 - amend the current commit and set the date to now::
1618 - amend the current commit and set the date to now::
1620
1619
1621 hg commit --amend --date now
1620 hg commit --amend --date now
1622 """
1621 """
1623 wlock = lock = None
1622 wlock = lock = None
1624 try:
1623 try:
1625 wlock = repo.wlock()
1624 wlock = repo.wlock()
1626 lock = repo.lock()
1625 lock = repo.lock()
1627 return _docommit(ui, repo, *pats, **opts)
1626 return _docommit(ui, repo, *pats, **opts)
1628 finally:
1627 finally:
1629 release(lock, wlock)
1628 release(lock, wlock)
1630
1629
1631 def _docommit(ui, repo, *pats, **opts):
1630 def _docommit(ui, repo, *pats, **opts):
1632 if opts.get('interactive'):
1631 if opts.get('interactive'):
1633 opts.pop('interactive')
1632 opts.pop('interactive')
1634 ret = cmdutil.dorecord(ui, repo, commit, None, False,
1633 ret = cmdutil.dorecord(ui, repo, commit, None, False,
1635 cmdutil.recordfilter, *pats, **opts)
1634 cmdutil.recordfilter, *pats, **opts)
1636 # ret can be 0 (no changes to record) or the value returned by
1635 # ret can be 0 (no changes to record) or the value returned by
1637 # commit(), 1 if nothing changed or None on success.
1636 # commit(), 1 if nothing changed or None on success.
1638 return 1 if ret == 0 else ret
1637 return 1 if ret == 0 else ret
1639
1638
1640 if opts.get('subrepos'):
1639 if opts.get('subrepos'):
1641 if opts.get('amend'):
1640 if opts.get('amend'):
1642 raise error.Abort(_('cannot amend with --subrepos'))
1641 raise error.Abort(_('cannot amend with --subrepos'))
1643 # Let --subrepos on the command line override config setting.
1642 # Let --subrepos on the command line override config setting.
1644 ui.setconfig('ui', 'commitsubrepos', True, 'commit')
1643 ui.setconfig('ui', 'commitsubrepos', True, 'commit')
1645
1644
1646 cmdutil.checkunfinished(repo, commit=True)
1645 cmdutil.checkunfinished(repo, commit=True)
1647
1646
1648 branch = repo[None].branch()
1647 branch = repo[None].branch()
1649 bheads = repo.branchheads(branch)
1648 bheads = repo.branchheads(branch)
1650
1649
1651 extra = {}
1650 extra = {}
1652 if opts.get('close_branch'):
1651 if opts.get('close_branch'):
1653 extra['close'] = 1
1652 extra['close'] = 1
1654
1653
1655 if not bheads:
1654 if not bheads:
1656 raise error.Abort(_('can only close branch heads'))
1655 raise error.Abort(_('can only close branch heads'))
1657 elif opts.get('amend'):
1656 elif opts.get('amend'):
1658 if repo[None].parents()[0].p1().branch() != branch and \
1657 if repo[None].parents()[0].p1().branch() != branch and \
1659 repo[None].parents()[0].p2().branch() != branch:
1658 repo[None].parents()[0].p2().branch() != branch:
1660 raise error.Abort(_('can only close branch heads'))
1659 raise error.Abort(_('can only close branch heads'))
1661
1660
1662 if opts.get('amend'):
1661 if opts.get('amend'):
1663 if ui.configbool('ui', 'commitsubrepos'):
1662 if ui.configbool('ui', 'commitsubrepos'):
1664 raise error.Abort(_('cannot amend with ui.commitsubrepos enabled'))
1663 raise error.Abort(_('cannot amend with ui.commitsubrepos enabled'))
1665
1664
1666 old = repo['.']
1665 old = repo['.']
1667 if not old.mutable():
1666 if not old.mutable():
1668 raise error.Abort(_('cannot amend public changesets'))
1667 raise error.Abort(_('cannot amend public changesets'))
1669 if len(repo[None].parents()) > 1:
1668 if len(repo[None].parents()) > 1:
1670 raise error.Abort(_('cannot amend while merging'))
1669 raise error.Abort(_('cannot amend while merging'))
1671 allowunstable = obsolete.isenabled(repo, obsolete.allowunstableopt)
1670 allowunstable = obsolete.isenabled(repo, obsolete.allowunstableopt)
1672 if not allowunstable and old.children():
1671 if not allowunstable and old.children():
1673 raise error.Abort(_('cannot amend changeset with children'))
1672 raise error.Abort(_('cannot amend changeset with children'))
1674
1673
1675 # Currently histedit gets confused if an amend happens while histedit
1674 # Currently histedit gets confused if an amend happens while histedit
1676 # is in progress. Since we have a checkunfinished command, we are
1675 # is in progress. Since we have a checkunfinished command, we are
1677 # temporarily honoring it.
1676 # temporarily honoring it.
1678 #
1677 #
1679 # Note: eventually this guard will be removed. Please do not expect
1678 # Note: eventually this guard will be removed. Please do not expect
1680 # this behavior to remain.
1679 # this behavior to remain.
1681 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
1680 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
1682 cmdutil.checkunfinished(repo)
1681 cmdutil.checkunfinished(repo)
1683
1682
1684 # commitfunc is used only for temporary amend commit by cmdutil.amend
1683 # commitfunc is used only for temporary amend commit by cmdutil.amend
1685 def commitfunc(ui, repo, message, match, opts):
1684 def commitfunc(ui, repo, message, match, opts):
1686 return repo.commit(message,
1685 return repo.commit(message,
1687 opts.get('user') or old.user(),
1686 opts.get('user') or old.user(),
1688 opts.get('date') or old.date(),
1687 opts.get('date') or old.date(),
1689 match,
1688 match,
1690 extra=extra)
1689 extra=extra)
1691
1690
1692 node = cmdutil.amend(ui, repo, commitfunc, old, extra, pats, opts)
1691 node = cmdutil.amend(ui, repo, commitfunc, old, extra, pats, opts)
1693 if node == old.node():
1692 if node == old.node():
1694 ui.status(_("nothing changed\n"))
1693 ui.status(_("nothing changed\n"))
1695 return 1
1694 return 1
1696 else:
1695 else:
1697 def commitfunc(ui, repo, message, match, opts):
1696 def commitfunc(ui, repo, message, match, opts):
1698 backup = ui.backupconfig('phases', 'new-commit')
1697 backup = ui.backupconfig('phases', 'new-commit')
1699 baseui = repo.baseui
1698 baseui = repo.baseui
1700 basebackup = baseui.backupconfig('phases', 'new-commit')
1699 basebackup = baseui.backupconfig('phases', 'new-commit')
1701 try:
1700 try:
1702 if opts.get('secret'):
1701 if opts.get('secret'):
1703 ui.setconfig('phases', 'new-commit', 'secret', 'commit')
1702 ui.setconfig('phases', 'new-commit', 'secret', 'commit')
1704 # Propagate to subrepos
1703 # Propagate to subrepos
1705 baseui.setconfig('phases', 'new-commit', 'secret', 'commit')
1704 baseui.setconfig('phases', 'new-commit', 'secret', 'commit')
1706
1705
1707 editform = cmdutil.mergeeditform(repo[None], 'commit.normal')
1706 editform = cmdutil.mergeeditform(repo[None], 'commit.normal')
1708 editor = cmdutil.getcommiteditor(editform=editform, **opts)
1707 editor = cmdutil.getcommiteditor(editform=editform, **opts)
1709 return repo.commit(message, opts.get('user'), opts.get('date'),
1708 return repo.commit(message, opts.get('user'), opts.get('date'),
1710 match,
1709 match,
1711 editor=editor,
1710 editor=editor,
1712 extra=extra)
1711 extra=extra)
1713 finally:
1712 finally:
1714 ui.restoreconfig(backup)
1713 ui.restoreconfig(backup)
1715 repo.baseui.restoreconfig(basebackup)
1714 repo.baseui.restoreconfig(basebackup)
1716
1715
1717
1716
1718 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1717 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1719
1718
1720 if not node:
1719 if not node:
1721 stat = cmdutil.postcommitstatus(repo, pats, opts)
1720 stat = cmdutil.postcommitstatus(repo, pats, opts)
1722 if stat[3]:
1721 if stat[3]:
1723 ui.status(_("nothing changed (%d missing files, see "
1722 ui.status(_("nothing changed (%d missing files, see "
1724 "'hg status')\n") % len(stat[3]))
1723 "'hg status')\n") % len(stat[3]))
1725 else:
1724 else:
1726 ui.status(_("nothing changed\n"))
1725 ui.status(_("nothing changed\n"))
1727 return 1
1726 return 1
1728
1727
1729 cmdutil.commitstatus(repo, node, branch, bheads, opts)
1728 cmdutil.commitstatus(repo, node, branch, bheads, opts)
1730
1729
1731 @command('config|showconfig|debugconfig',
1730 @command('config|showconfig|debugconfig',
1732 [('u', 'untrusted', None, _('show untrusted configuration options')),
1731 [('u', 'untrusted', None, _('show untrusted configuration options')),
1733 ('e', 'edit', None, _('edit user config')),
1732 ('e', 'edit', None, _('edit user config')),
1734 ('l', 'local', None, _('edit repository config')),
1733 ('l', 'local', None, _('edit repository config')),
1735 ('g', 'global', None, _('edit global config'))] + formatteropts,
1734 ('g', 'global', None, _('edit global config'))] + formatteropts,
1736 _('[-u] [NAME]...'),
1735 _('[-u] [NAME]...'),
1737 optionalrepo=True)
1736 optionalrepo=True)
1738 def config(ui, repo, *values, **opts):
1737 def config(ui, repo, *values, **opts):
1739 """show combined config settings from all hgrc files
1738 """show combined config settings from all hgrc files
1740
1739
1741 With no arguments, print names and values of all config items.
1740 With no arguments, print names and values of all config items.
1742
1741
1743 With one argument of the form section.name, print just the value
1742 With one argument of the form section.name, print just the value
1744 of that config item.
1743 of that config item.
1745
1744
1746 With multiple arguments, print names and values of all config
1745 With multiple arguments, print names and values of all config
1747 items with matching section names.
1746 items with matching section names.
1748
1747
1749 With --edit, start an editor on the user-level config file. With
1748 With --edit, start an editor on the user-level config file. With
1750 --global, edit the system-wide config file. With --local, edit the
1749 --global, edit the system-wide config file. With --local, edit the
1751 repository-level config file.
1750 repository-level config file.
1752
1751
1753 With --debug, the source (filename and line number) is printed
1752 With --debug, the source (filename and line number) is printed
1754 for each config item.
1753 for each config item.
1755
1754
1756 See :hg:`help config` for more information about config files.
1755 See :hg:`help config` for more information about config files.
1757
1756
1758 Returns 0 on success, 1 if NAME does not exist.
1757 Returns 0 on success, 1 if NAME does not exist.
1759
1758
1760 """
1759 """
1761
1760
1762 if opts.get('edit') or opts.get('local') or opts.get('global'):
1761 if opts.get('edit') or opts.get('local') or opts.get('global'):
1763 if opts.get('local') and opts.get('global'):
1762 if opts.get('local') and opts.get('global'):
1764 raise error.Abort(_("can't use --local and --global together"))
1763 raise error.Abort(_("can't use --local and --global together"))
1765
1764
1766 if opts.get('local'):
1765 if opts.get('local'):
1767 if not repo:
1766 if not repo:
1768 raise error.Abort(_("can't use --local outside a repository"))
1767 raise error.Abort(_("can't use --local outside a repository"))
1769 paths = [repo.join('hgrc')]
1768 paths = [repo.join('hgrc')]
1770 elif opts.get('global'):
1769 elif opts.get('global'):
1771 paths = scmutil.systemrcpath()
1770 paths = scmutil.systemrcpath()
1772 else:
1771 else:
1773 paths = scmutil.userrcpath()
1772 paths = scmutil.userrcpath()
1774
1773
1775 for f in paths:
1774 for f in paths:
1776 if os.path.exists(f):
1775 if os.path.exists(f):
1777 break
1776 break
1778 else:
1777 else:
1779 if opts.get('global'):
1778 if opts.get('global'):
1780 samplehgrc = uimod.samplehgrcs['global']
1779 samplehgrc = uimod.samplehgrcs['global']
1781 elif opts.get('local'):
1780 elif opts.get('local'):
1782 samplehgrc = uimod.samplehgrcs['local']
1781 samplehgrc = uimod.samplehgrcs['local']
1783 else:
1782 else:
1784 samplehgrc = uimod.samplehgrcs['user']
1783 samplehgrc = uimod.samplehgrcs['user']
1785
1784
1786 f = paths[0]
1785 f = paths[0]
1787 fp = open(f, "w")
1786 fp = open(f, "w")
1788 fp.write(samplehgrc)
1787 fp.write(samplehgrc)
1789 fp.close()
1788 fp.close()
1790
1789
1791 editor = ui.geteditor()
1790 editor = ui.geteditor()
1792 ui.system("%s \"%s\"" % (editor, f),
1791 ui.system("%s \"%s\"" % (editor, f),
1793 onerr=error.Abort, errprefix=_("edit failed"))
1792 onerr=error.Abort, errprefix=_("edit failed"))
1794 return
1793 return
1795
1794
1796 fm = ui.formatter('config', opts)
1795 fm = ui.formatter('config', opts)
1797 for f in scmutil.rcpath():
1796 for f in scmutil.rcpath():
1798 ui.debug('read config from: %s\n' % f)
1797 ui.debug('read config from: %s\n' % f)
1799 untrusted = bool(opts.get('untrusted'))
1798 untrusted = bool(opts.get('untrusted'))
1800 if values:
1799 if values:
1801 sections = [v for v in values if '.' not in v]
1800 sections = [v for v in values if '.' not in v]
1802 items = [v for v in values if '.' in v]
1801 items = [v for v in values if '.' in v]
1803 if len(items) > 1 or items and sections:
1802 if len(items) > 1 or items and sections:
1804 raise error.Abort(_('only one config item permitted'))
1803 raise error.Abort(_('only one config item permitted'))
1805 matched = False
1804 matched = False
1806 for section, name, value in ui.walkconfig(untrusted=untrusted):
1805 for section, name, value in ui.walkconfig(untrusted=untrusted):
1807 source = ui.configsource(section, name, untrusted)
1806 source = ui.configsource(section, name, untrusted)
1808 value = str(value)
1807 value = str(value)
1809 if fm.isplain():
1808 if fm.isplain():
1810 source = source or 'none'
1809 source = source or 'none'
1811 value = value.replace('\n', '\\n')
1810 value = value.replace('\n', '\\n')
1812 entryname = section + '.' + name
1811 entryname = section + '.' + name
1813 if values:
1812 if values:
1814 for v in values:
1813 for v in values:
1815 if v == section:
1814 if v == section:
1816 fm.startitem()
1815 fm.startitem()
1817 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1816 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1818 fm.write('name value', '%s=%s\n', entryname, value)
1817 fm.write('name value', '%s=%s\n', entryname, value)
1819 matched = True
1818 matched = True
1820 elif v == entryname:
1819 elif v == entryname:
1821 fm.startitem()
1820 fm.startitem()
1822 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1821 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1823 fm.write('value', '%s\n', value)
1822 fm.write('value', '%s\n', value)
1824 fm.data(name=entryname)
1823 fm.data(name=entryname)
1825 matched = True
1824 matched = True
1826 else:
1825 else:
1827 fm.startitem()
1826 fm.startitem()
1828 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1827 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1829 fm.write('name value', '%s=%s\n', entryname, value)
1828 fm.write('name value', '%s=%s\n', entryname, value)
1830 matched = True
1829 matched = True
1831 fm.end()
1830 fm.end()
1832 if matched:
1831 if matched:
1833 return 0
1832 return 0
1834 return 1
1833 return 1
1835
1834
1836 @command('copy|cp',
1835 @command('copy|cp',
1837 [('A', 'after', None, _('record a copy that has already occurred')),
1836 [('A', 'after', None, _('record a copy that has already occurred')),
1838 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1837 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1839 ] + walkopts + dryrunopts,
1838 ] + walkopts + dryrunopts,
1840 _('[OPTION]... [SOURCE]... DEST'))
1839 _('[OPTION]... [SOURCE]... DEST'))
1841 def copy(ui, repo, *pats, **opts):
1840 def copy(ui, repo, *pats, **opts):
1842 """mark files as copied for the next commit
1841 """mark files as copied for the next commit
1843
1842
1844 Mark dest as having copies of source files. If dest is a
1843 Mark dest as having copies of source files. If dest is a
1845 directory, copies are put in that directory. If dest is a file,
1844 directory, copies are put in that directory. If dest is a file,
1846 the source must be a single file.
1845 the source must be a single file.
1847
1846
1848 By default, this command copies the contents of files as they
1847 By default, this command copies the contents of files as they
1849 exist in the working directory. If invoked with -A/--after, the
1848 exist in the working directory. If invoked with -A/--after, the
1850 operation is recorded, but no copying is performed.
1849 operation is recorded, but no copying is performed.
1851
1850
1852 This command takes effect with the next commit. To undo a copy
1851 This command takes effect with the next commit. To undo a copy
1853 before that, see :hg:`revert`.
1852 before that, see :hg:`revert`.
1854
1853
1855 Returns 0 on success, 1 if errors are encountered.
1854 Returns 0 on success, 1 if errors are encountered.
1856 """
1855 """
1857 with repo.wlock(False):
1856 with repo.wlock(False):
1858 return cmdutil.copy(ui, repo, pats, opts)
1857 return cmdutil.copy(ui, repo, pats, opts)
1859
1858
1860 @command('debuginstall', [] + formatteropts, '', norepo=True)
1859 @command('debuginstall', [] + formatteropts, '', norepo=True)
1861 def debuginstall(ui, **opts):
1860 def debuginstall(ui, **opts):
1862 '''test Mercurial installation
1861 '''test Mercurial installation
1863
1862
1864 Returns 0 on success.
1863 Returns 0 on success.
1865 '''
1864 '''
1866
1865
1867 def writetemp(contents):
1866 def writetemp(contents):
1868 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
1867 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
1869 f = os.fdopen(fd, "wb")
1868 f = os.fdopen(fd, "wb")
1870 f.write(contents)
1869 f.write(contents)
1871 f.close()
1870 f.close()
1872 return name
1871 return name
1873
1872
1874 problems = 0
1873 problems = 0
1875
1874
1876 fm = ui.formatter('debuginstall', opts)
1875 fm = ui.formatter('debuginstall', opts)
1877 fm.startitem()
1876 fm.startitem()
1878
1877
1879 # encoding
1878 # encoding
1880 fm.write('encoding', _("checking encoding (%s)...\n"), encoding.encoding)
1879 fm.write('encoding', _("checking encoding (%s)...\n"), encoding.encoding)
1881 err = None
1880 err = None
1882 try:
1881 try:
1883 encoding.fromlocal("test")
1882 encoding.fromlocal("test")
1884 except error.Abort as inst:
1883 except error.Abort as inst:
1885 err = inst
1884 err = inst
1886 problems += 1
1885 problems += 1
1887 fm.condwrite(err, 'encodingerror', _(" %s\n"
1886 fm.condwrite(err, 'encodingerror', _(" %s\n"
1888 " (check that your locale is properly set)\n"), err)
1887 " (check that your locale is properly set)\n"), err)
1889
1888
1890 # Python
1889 # Python
1891 fm.write('pythonexe', _("checking Python executable (%s)\n"),
1890 fm.write('pythonexe', _("checking Python executable (%s)\n"),
1892 pycompat.sysexecutable)
1891 pycompat.sysexecutable)
1893 fm.write('pythonver', _("checking Python version (%s)\n"),
1892 fm.write('pythonver', _("checking Python version (%s)\n"),
1894 ("%d.%d.%d" % sys.version_info[:3]))
1893 ("%d.%d.%d" % sys.version_info[:3]))
1895 fm.write('pythonlib', _("checking Python lib (%s)...\n"),
1894 fm.write('pythonlib', _("checking Python lib (%s)...\n"),
1896 os.path.dirname(os.__file__))
1895 os.path.dirname(os.__file__))
1897
1896
1898 security = set(sslutil.supportedprotocols)
1897 security = set(sslutil.supportedprotocols)
1899 if sslutil.hassni:
1898 if sslutil.hassni:
1900 security.add('sni')
1899 security.add('sni')
1901
1900
1902 fm.write('pythonsecurity', _("checking Python security support (%s)\n"),
1901 fm.write('pythonsecurity', _("checking Python security support (%s)\n"),
1903 fm.formatlist(sorted(security), name='protocol',
1902 fm.formatlist(sorted(security), name='protocol',
1904 fmt='%s', sep=','))
1903 fmt='%s', sep=','))
1905
1904
1906 # These are warnings, not errors. So don't increment problem count. This
1905 # These are warnings, not errors. So don't increment problem count. This
1907 # may change in the future.
1906 # may change in the future.
1908 if 'tls1.2' not in security:
1907 if 'tls1.2' not in security:
1909 fm.plain(_(' TLS 1.2 not supported by Python install; '
1908 fm.plain(_(' TLS 1.2 not supported by Python install; '
1910 'network connections lack modern security\n'))
1909 'network connections lack modern security\n'))
1911 if 'sni' not in security:
1910 if 'sni' not in security:
1912 fm.plain(_(' SNI not supported by Python install; may have '
1911 fm.plain(_(' SNI not supported by Python install; may have '
1913 'connectivity issues with some servers\n'))
1912 'connectivity issues with some servers\n'))
1914
1913
1915 # TODO print CA cert info
1914 # TODO print CA cert info
1916
1915
1917 # hg version
1916 # hg version
1918 hgver = util.version()
1917 hgver = util.version()
1919 fm.write('hgver', _("checking Mercurial version (%s)\n"),
1918 fm.write('hgver', _("checking Mercurial version (%s)\n"),
1920 hgver.split('+')[0])
1919 hgver.split('+')[0])
1921 fm.write('hgverextra', _("checking Mercurial custom build (%s)\n"),
1920 fm.write('hgverextra', _("checking Mercurial custom build (%s)\n"),
1922 '+'.join(hgver.split('+')[1:]))
1921 '+'.join(hgver.split('+')[1:]))
1923
1922
1924 # compiled modules
1923 # compiled modules
1925 fm.write('hgmodulepolicy', _("checking module policy (%s)\n"),
1924 fm.write('hgmodulepolicy', _("checking module policy (%s)\n"),
1926 policy.policy)
1925 policy.policy)
1927 fm.write('hgmodules', _("checking installed modules (%s)...\n"),
1926 fm.write('hgmodules', _("checking installed modules (%s)...\n"),
1928 os.path.dirname(__file__))
1927 os.path.dirname(__file__))
1929
1928
1930 err = None
1929 err = None
1931 try:
1930 try:
1932 from . import (
1931 from . import (
1933 base85,
1932 base85,
1934 bdiff,
1933 bdiff,
1935 mpatch,
1934 mpatch,
1936 osutil,
1935 osutil,
1937 )
1936 )
1938 dir(bdiff), dir(mpatch), dir(base85), dir(osutil) # quiet pyflakes
1937 dir(bdiff), dir(mpatch), dir(base85), dir(osutil) # quiet pyflakes
1939 except Exception as inst:
1938 except Exception as inst:
1940 err = inst
1939 err = inst
1941 problems += 1
1940 problems += 1
1942 fm.condwrite(err, 'extensionserror', " %s\n", err)
1941 fm.condwrite(err, 'extensionserror', " %s\n", err)
1943
1942
1944 compengines = util.compengines._engines.values()
1943 compengines = util.compengines._engines.values()
1945 fm.write('compengines', _('checking registered compression engines (%s)\n'),
1944 fm.write('compengines', _('checking registered compression engines (%s)\n'),
1946 fm.formatlist(sorted(e.name() for e in compengines),
1945 fm.formatlist(sorted(e.name() for e in compengines),
1947 name='compengine', fmt='%s', sep=', '))
1946 name='compengine', fmt='%s', sep=', '))
1948 fm.write('compenginesavail', _('checking available compression engines '
1947 fm.write('compenginesavail', _('checking available compression engines '
1949 '(%s)\n'),
1948 '(%s)\n'),
1950 fm.formatlist(sorted(e.name() for e in compengines
1949 fm.formatlist(sorted(e.name() for e in compengines
1951 if e.available()),
1950 if e.available()),
1952 name='compengine', fmt='%s', sep=', '))
1951 name='compengine', fmt='%s', sep=', '))
1953
1952
1954 # templates
1953 # templates
1955 p = templater.templatepaths()
1954 p = templater.templatepaths()
1956 fm.write('templatedirs', 'checking templates (%s)...\n', ' '.join(p))
1955 fm.write('templatedirs', 'checking templates (%s)...\n', ' '.join(p))
1957 fm.condwrite(not p, '', _(" no template directories found\n"))
1956 fm.condwrite(not p, '', _(" no template directories found\n"))
1958 if p:
1957 if p:
1959 m = templater.templatepath("map-cmdline.default")
1958 m = templater.templatepath("map-cmdline.default")
1960 if m:
1959 if m:
1961 # template found, check if it is working
1960 # template found, check if it is working
1962 err = None
1961 err = None
1963 try:
1962 try:
1964 templater.templater.frommapfile(m)
1963 templater.templater.frommapfile(m)
1965 except Exception as inst:
1964 except Exception as inst:
1966 err = inst
1965 err = inst
1967 p = None
1966 p = None
1968 fm.condwrite(err, 'defaulttemplateerror', " %s\n", err)
1967 fm.condwrite(err, 'defaulttemplateerror', " %s\n", err)
1969 else:
1968 else:
1970 p = None
1969 p = None
1971 fm.condwrite(p, 'defaulttemplate',
1970 fm.condwrite(p, 'defaulttemplate',
1972 _("checking default template (%s)\n"), m)
1971 _("checking default template (%s)\n"), m)
1973 fm.condwrite(not m, 'defaulttemplatenotfound',
1972 fm.condwrite(not m, 'defaulttemplatenotfound',
1974 _(" template '%s' not found\n"), "default")
1973 _(" template '%s' not found\n"), "default")
1975 if not p:
1974 if not p:
1976 problems += 1
1975 problems += 1
1977 fm.condwrite(not p, '',
1976 fm.condwrite(not p, '',
1978 _(" (templates seem to have been installed incorrectly)\n"))
1977 _(" (templates seem to have been installed incorrectly)\n"))
1979
1978
1980 # editor
1979 # editor
1981 editor = ui.geteditor()
1980 editor = ui.geteditor()
1982 editor = util.expandpath(editor)
1981 editor = util.expandpath(editor)
1983 fm.write('editor', _("checking commit editor... (%s)\n"), editor)
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 fm.condwrite(not cmdpath and editor == 'vi', 'vinotfound',
1984 fm.condwrite(not cmdpath and editor == 'vi', 'vinotfound',
1986 _(" No commit editor set and can't find %s in PATH\n"
1985 _(" No commit editor set and can't find %s in PATH\n"
1987 " (specify a commit editor in your configuration"
1986 " (specify a commit editor in your configuration"
1988 " file)\n"), not cmdpath and editor == 'vi' and editor)
1987 " file)\n"), not cmdpath and editor == 'vi' and editor)
1989 fm.condwrite(not cmdpath and editor != 'vi', 'editornotfound',
1988 fm.condwrite(not cmdpath and editor != 'vi', 'editornotfound',
1990 _(" Can't find editor '%s' in PATH\n"
1989 _(" Can't find editor '%s' in PATH\n"
1991 " (specify a commit editor in your configuration"
1990 " (specify a commit editor in your configuration"
1992 " file)\n"), not cmdpath and editor)
1991 " file)\n"), not cmdpath and editor)
1993 if not cmdpath and editor != 'vi':
1992 if not cmdpath and editor != 'vi':
1994 problems += 1
1993 problems += 1
1995
1994
1996 # check username
1995 # check username
1997 username = None
1996 username = None
1998 err = None
1997 err = None
1999 try:
1998 try:
2000 username = ui.username()
1999 username = ui.username()
2001 except error.Abort as e:
2000 except error.Abort as e:
2002 err = e
2001 err = e
2003 problems += 1
2002 problems += 1
2004
2003
2005 fm.condwrite(username, 'username', _("checking username (%s)\n"), username)
2004 fm.condwrite(username, 'username', _("checking username (%s)\n"), username)
2006 fm.condwrite(err, 'usernameerror', _("checking username...\n %s\n"
2005 fm.condwrite(err, 'usernameerror', _("checking username...\n %s\n"
2007 " (specify a username in your configuration file)\n"), err)
2006 " (specify a username in your configuration file)\n"), err)
2008
2007
2009 fm.condwrite(not problems, '',
2008 fm.condwrite(not problems, '',
2010 _("no problems detected\n"))
2009 _("no problems detected\n"))
2011 if not problems:
2010 if not problems:
2012 fm.data(problems=problems)
2011 fm.data(problems=problems)
2013 fm.condwrite(problems, 'problems',
2012 fm.condwrite(problems, 'problems',
2014 _("%d problems detected,"
2013 _("%d problems detected,"
2015 " please check your install!\n"), problems)
2014 " please check your install!\n"), problems)
2016 fm.end()
2015 fm.end()
2017
2016
2018 return problems
2017 return problems
2019
2018
2020 @command('debugknown', [], _('REPO ID...'), norepo=True)
2019 @command('debugknown', [], _('REPO ID...'), norepo=True)
2021 def debugknown(ui, repopath, *ids, **opts):
2020 def debugknown(ui, repopath, *ids, **opts):
2022 """test whether node ids are known to a repo
2021 """test whether node ids are known to a repo
2023
2022
2024 Every ID must be a full-length hex node id string. Returns a list of 0s
2023 Every ID must be a full-length hex node id string. Returns a list of 0s
2025 and 1s indicating unknown/known.
2024 and 1s indicating unknown/known.
2026 """
2025 """
2027 repo = hg.peer(ui, opts, repopath)
2026 repo = hg.peer(ui, opts, repopath)
2028 if not repo.capable('known'):
2027 if not repo.capable('known'):
2029 raise error.Abort("known() not supported by target repository")
2028 raise error.Abort("known() not supported by target repository")
2030 flags = repo.known([bin(s) for s in ids])
2029 flags = repo.known([bin(s) for s in ids])
2031 ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
2030 ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
2032
2031
2033 @command('debuglabelcomplete', [], _('LABEL...'))
2032 @command('debuglabelcomplete', [], _('LABEL...'))
2034 def debuglabelcomplete(ui, repo, *args):
2033 def debuglabelcomplete(ui, repo, *args):
2035 '''backwards compatibility with old bash completion scripts (DEPRECATED)'''
2034 '''backwards compatibility with old bash completion scripts (DEPRECATED)'''
2036 debugnamecomplete(ui, repo, *args)
2035 debugnamecomplete(ui, repo, *args)
2037
2036
2038 @command('debugmergestate', [], '')
2037 @command('debugmergestate', [], '')
2039 def debugmergestate(ui, repo, *args):
2038 def debugmergestate(ui, repo, *args):
2040 """print merge state
2039 """print merge state
2041
2040
2042 Use --verbose to print out information about whether v1 or v2 merge state
2041 Use --verbose to print out information about whether v1 or v2 merge state
2043 was chosen."""
2042 was chosen."""
2044 def _hashornull(h):
2043 def _hashornull(h):
2045 if h == nullhex:
2044 if h == nullhex:
2046 return 'null'
2045 return 'null'
2047 else:
2046 else:
2048 return h
2047 return h
2049
2048
2050 def printrecords(version):
2049 def printrecords(version):
2051 ui.write(('* version %s records\n') % version)
2050 ui.write(('* version %s records\n') % version)
2052 if version == 1:
2051 if version == 1:
2053 records = v1records
2052 records = v1records
2054 else:
2053 else:
2055 records = v2records
2054 records = v2records
2056
2055
2057 for rtype, record in records:
2056 for rtype, record in records:
2058 # pretty print some record types
2057 # pretty print some record types
2059 if rtype == 'L':
2058 if rtype == 'L':
2060 ui.write(('local: %s\n') % record)
2059 ui.write(('local: %s\n') % record)
2061 elif rtype == 'O':
2060 elif rtype == 'O':
2062 ui.write(('other: %s\n') % record)
2061 ui.write(('other: %s\n') % record)
2063 elif rtype == 'm':
2062 elif rtype == 'm':
2064 driver, mdstate = record.split('\0', 1)
2063 driver, mdstate = record.split('\0', 1)
2065 ui.write(('merge driver: %s (state "%s")\n')
2064 ui.write(('merge driver: %s (state "%s")\n')
2066 % (driver, mdstate))
2065 % (driver, mdstate))
2067 elif rtype in 'FDC':
2066 elif rtype in 'FDC':
2068 r = record.split('\0')
2067 r = record.split('\0')
2069 f, state, hash, lfile, afile, anode, ofile = r[0:7]
2068 f, state, hash, lfile, afile, anode, ofile = r[0:7]
2070 if version == 1:
2069 if version == 1:
2071 onode = 'not stored in v1 format'
2070 onode = 'not stored in v1 format'
2072 flags = r[7]
2071 flags = r[7]
2073 else:
2072 else:
2074 onode, flags = r[7:9]
2073 onode, flags = r[7:9]
2075 ui.write(('file: %s (record type "%s", state "%s", hash %s)\n')
2074 ui.write(('file: %s (record type "%s", state "%s", hash %s)\n')
2076 % (f, rtype, state, _hashornull(hash)))
2075 % (f, rtype, state, _hashornull(hash)))
2077 ui.write((' local path: %s (flags "%s")\n') % (lfile, flags))
2076 ui.write((' local path: %s (flags "%s")\n') % (lfile, flags))
2078 ui.write((' ancestor path: %s (node %s)\n')
2077 ui.write((' ancestor path: %s (node %s)\n')
2079 % (afile, _hashornull(anode)))
2078 % (afile, _hashornull(anode)))
2080 ui.write((' other path: %s (node %s)\n')
2079 ui.write((' other path: %s (node %s)\n')
2081 % (ofile, _hashornull(onode)))
2080 % (ofile, _hashornull(onode)))
2082 elif rtype == 'f':
2081 elif rtype == 'f':
2083 filename, rawextras = record.split('\0', 1)
2082 filename, rawextras = record.split('\0', 1)
2084 extras = rawextras.split('\0')
2083 extras = rawextras.split('\0')
2085 i = 0
2084 i = 0
2086 extrastrings = []
2085 extrastrings = []
2087 while i < len(extras):
2086 while i < len(extras):
2088 extrastrings.append('%s = %s' % (extras[i], extras[i + 1]))
2087 extrastrings.append('%s = %s' % (extras[i], extras[i + 1]))
2089 i += 2
2088 i += 2
2090
2089
2091 ui.write(('file extras: %s (%s)\n')
2090 ui.write(('file extras: %s (%s)\n')
2092 % (filename, ', '.join(extrastrings)))
2091 % (filename, ', '.join(extrastrings)))
2093 elif rtype == 'l':
2092 elif rtype == 'l':
2094 labels = record.split('\0', 2)
2093 labels = record.split('\0', 2)
2095 labels = [l for l in labels if len(l) > 0]
2094 labels = [l for l in labels if len(l) > 0]
2096 ui.write(('labels:\n'))
2095 ui.write(('labels:\n'))
2097 ui.write((' local: %s\n' % labels[0]))
2096 ui.write((' local: %s\n' % labels[0]))
2098 ui.write((' other: %s\n' % labels[1]))
2097 ui.write((' other: %s\n' % labels[1]))
2099 if len(labels) > 2:
2098 if len(labels) > 2:
2100 ui.write((' base: %s\n' % labels[2]))
2099 ui.write((' base: %s\n' % labels[2]))
2101 else:
2100 else:
2102 ui.write(('unrecognized entry: %s\t%s\n')
2101 ui.write(('unrecognized entry: %s\t%s\n')
2103 % (rtype, record.replace('\0', '\t')))
2102 % (rtype, record.replace('\0', '\t')))
2104
2103
2105 # Avoid mergestate.read() since it may raise an exception for unsupported
2104 # Avoid mergestate.read() since it may raise an exception for unsupported
2106 # merge state records. We shouldn't be doing this, but this is OK since this
2105 # merge state records. We shouldn't be doing this, but this is OK since this
2107 # command is pretty low-level.
2106 # command is pretty low-level.
2108 ms = mergemod.mergestate(repo)
2107 ms = mergemod.mergestate(repo)
2109
2108
2110 # sort so that reasonable information is on top
2109 # sort so that reasonable information is on top
2111 v1records = ms._readrecordsv1()
2110 v1records = ms._readrecordsv1()
2112 v2records = ms._readrecordsv2()
2111 v2records = ms._readrecordsv2()
2113 order = 'LOml'
2112 order = 'LOml'
2114 def key(r):
2113 def key(r):
2115 idx = order.find(r[0])
2114 idx = order.find(r[0])
2116 if idx == -1:
2115 if idx == -1:
2117 return (1, r[1])
2116 return (1, r[1])
2118 else:
2117 else:
2119 return (0, idx)
2118 return (0, idx)
2120 v1records.sort(key=key)
2119 v1records.sort(key=key)
2121 v2records.sort(key=key)
2120 v2records.sort(key=key)
2122
2121
2123 if not v1records and not v2records:
2122 if not v1records and not v2records:
2124 ui.write(('no merge state found\n'))
2123 ui.write(('no merge state found\n'))
2125 elif not v2records:
2124 elif not v2records:
2126 ui.note(('no version 2 merge state\n'))
2125 ui.note(('no version 2 merge state\n'))
2127 printrecords(1)
2126 printrecords(1)
2128 elif ms._v1v2match(v1records, v2records):
2127 elif ms._v1v2match(v1records, v2records):
2129 ui.note(('v1 and v2 states match: using v2\n'))
2128 ui.note(('v1 and v2 states match: using v2\n'))
2130 printrecords(2)
2129 printrecords(2)
2131 else:
2130 else:
2132 ui.note(('v1 and v2 states mismatch: using v1\n'))
2131 ui.note(('v1 and v2 states mismatch: using v1\n'))
2133 printrecords(1)
2132 printrecords(1)
2134 if ui.verbose:
2133 if ui.verbose:
2135 printrecords(2)
2134 printrecords(2)
2136
2135
2137 @command('debugnamecomplete', [], _('NAME...'))
2136 @command('debugnamecomplete', [], _('NAME...'))
2138 def debugnamecomplete(ui, repo, *args):
2137 def debugnamecomplete(ui, repo, *args):
2139 '''complete "names" - tags, open branch names, bookmark names'''
2138 '''complete "names" - tags, open branch names, bookmark names'''
2140
2139
2141 names = set()
2140 names = set()
2142 # since we previously only listed open branches, we will handle that
2141 # since we previously only listed open branches, we will handle that
2143 # specially (after this for loop)
2142 # specially (after this for loop)
2144 for name, ns in repo.names.iteritems():
2143 for name, ns in repo.names.iteritems():
2145 if name != 'branches':
2144 if name != 'branches':
2146 names.update(ns.listnames(repo))
2145 names.update(ns.listnames(repo))
2147 names.update(tag for (tag, heads, tip, closed)
2146 names.update(tag for (tag, heads, tip, closed)
2148 in repo.branchmap().iterbranches() if not closed)
2147 in repo.branchmap().iterbranches() if not closed)
2149 completions = set()
2148 completions = set()
2150 if not args:
2149 if not args:
2151 args = ['']
2150 args = ['']
2152 for a in args:
2151 for a in args:
2153 completions.update(n for n in names if n.startswith(a))
2152 completions.update(n for n in names if n.startswith(a))
2154 ui.write('\n'.join(sorted(completions)))
2153 ui.write('\n'.join(sorted(completions)))
2155 ui.write('\n')
2154 ui.write('\n')
2156
2155
2157 @command('debuglocks',
2156 @command('debuglocks',
2158 [('L', 'force-lock', None, _('free the store lock (DANGEROUS)')),
2157 [('L', 'force-lock', None, _('free the store lock (DANGEROUS)')),
2159 ('W', 'force-wlock', None,
2158 ('W', 'force-wlock', None,
2160 _('free the working state lock (DANGEROUS)'))],
2159 _('free the working state lock (DANGEROUS)'))],
2161 _('[OPTION]...'))
2160 _('[OPTION]...'))
2162 def debuglocks(ui, repo, **opts):
2161 def debuglocks(ui, repo, **opts):
2163 """show or modify state of locks
2162 """show or modify state of locks
2164
2163
2165 By default, this command will show which locks are held. This
2164 By default, this command will show which locks are held. This
2166 includes the user and process holding the lock, the amount of time
2165 includes the user and process holding the lock, the amount of time
2167 the lock has been held, and the machine name where the process is
2166 the lock has been held, and the machine name where the process is
2168 running if it's not local.
2167 running if it's not local.
2169
2168
2170 Locks protect the integrity of Mercurial's data, so should be
2169 Locks protect the integrity of Mercurial's data, so should be
2171 treated with care. System crashes or other interruptions may cause
2170 treated with care. System crashes or other interruptions may cause
2172 locks to not be properly released, though Mercurial will usually
2171 locks to not be properly released, though Mercurial will usually
2173 detect and remove such stale locks automatically.
2172 detect and remove such stale locks automatically.
2174
2173
2175 However, detecting stale locks may not always be possible (for
2174 However, detecting stale locks may not always be possible (for
2176 instance, on a shared filesystem). Removing locks may also be
2175 instance, on a shared filesystem). Removing locks may also be
2177 blocked by filesystem permissions.
2176 blocked by filesystem permissions.
2178
2177
2179 Returns 0 if no locks are held.
2178 Returns 0 if no locks are held.
2180
2179
2181 """
2180 """
2182
2181
2183 if opts.get('force_lock'):
2182 if opts.get('force_lock'):
2184 repo.svfs.unlink('lock')
2183 repo.svfs.unlink('lock')
2185 if opts.get('force_wlock'):
2184 if opts.get('force_wlock'):
2186 repo.vfs.unlink('wlock')
2185 repo.vfs.unlink('wlock')
2187 if opts.get('force_lock') or opts.get('force_lock'):
2186 if opts.get('force_lock') or opts.get('force_lock'):
2188 return 0
2187 return 0
2189
2188
2190 now = time.time()
2189 now = time.time()
2191 held = 0
2190 held = 0
2192
2191
2193 def report(vfs, name, method):
2192 def report(vfs, name, method):
2194 # this causes stale locks to get reaped for more accurate reporting
2193 # this causes stale locks to get reaped for more accurate reporting
2195 try:
2194 try:
2196 l = method(False)
2195 l = method(False)
2197 except error.LockHeld:
2196 except error.LockHeld:
2198 l = None
2197 l = None
2199
2198
2200 if l:
2199 if l:
2201 l.release()
2200 l.release()
2202 else:
2201 else:
2203 try:
2202 try:
2204 stat = vfs.lstat(name)
2203 stat = vfs.lstat(name)
2205 age = now - stat.st_mtime
2204 age = now - stat.st_mtime
2206 user = util.username(stat.st_uid)
2205 user = util.username(stat.st_uid)
2207 locker = vfs.readlock(name)
2206 locker = vfs.readlock(name)
2208 if ":" in locker:
2207 if ":" in locker:
2209 host, pid = locker.split(':')
2208 host, pid = locker.split(':')
2210 if host == socket.gethostname():
2209 if host == socket.gethostname():
2211 locker = 'user %s, process %s' % (user, pid)
2210 locker = 'user %s, process %s' % (user, pid)
2212 else:
2211 else:
2213 locker = 'user %s, process %s, host %s' \
2212 locker = 'user %s, process %s, host %s' \
2214 % (user, pid, host)
2213 % (user, pid, host)
2215 ui.write(("%-6s %s (%ds)\n") % (name + ":", locker, age))
2214 ui.write(("%-6s %s (%ds)\n") % (name + ":", locker, age))
2216 return 1
2215 return 1
2217 except OSError as e:
2216 except OSError as e:
2218 if e.errno != errno.ENOENT:
2217 if e.errno != errno.ENOENT:
2219 raise
2218 raise
2220
2219
2221 ui.write(("%-6s free\n") % (name + ":"))
2220 ui.write(("%-6s free\n") % (name + ":"))
2222 return 0
2221 return 0
2223
2222
2224 held += report(repo.svfs, "lock", repo.lock)
2223 held += report(repo.svfs, "lock", repo.lock)
2225 held += report(repo.vfs, "wlock", repo.wlock)
2224 held += report(repo.vfs, "wlock", repo.wlock)
2226
2225
2227 return held
2226 return held
2228
2227
2229 @command('debugobsolete',
2228 @command('debugobsolete',
2230 [('', 'flags', 0, _('markers flag')),
2229 [('', 'flags', 0, _('markers flag')),
2231 ('', 'record-parents', False,
2230 ('', 'record-parents', False,
2232 _('record parent information for the precursor')),
2231 _('record parent information for the precursor')),
2233 ('r', 'rev', [], _('display markers relevant to REV')),
2232 ('r', 'rev', [], _('display markers relevant to REV')),
2234 ('', 'index', False, _('display index of the marker')),
2233 ('', 'index', False, _('display index of the marker')),
2235 ('', 'delete', [], _('delete markers specified by indices')),
2234 ('', 'delete', [], _('delete markers specified by indices')),
2236 ] + commitopts2 + formatteropts,
2235 ] + commitopts2 + formatteropts,
2237 _('[OBSOLETED [REPLACEMENT ...]]'))
2236 _('[OBSOLETED [REPLACEMENT ...]]'))
2238 def debugobsolete(ui, repo, precursor=None, *successors, **opts):
2237 def debugobsolete(ui, repo, precursor=None, *successors, **opts):
2239 """create arbitrary obsolete marker
2238 """create arbitrary obsolete marker
2240
2239
2241 With no arguments, displays the list of obsolescence markers."""
2240 With no arguments, displays the list of obsolescence markers."""
2242
2241
2243 def parsenodeid(s):
2242 def parsenodeid(s):
2244 try:
2243 try:
2245 # We do not use revsingle/revrange functions here to accept
2244 # We do not use revsingle/revrange functions here to accept
2246 # arbitrary node identifiers, possibly not present in the
2245 # arbitrary node identifiers, possibly not present in the
2247 # local repository.
2246 # local repository.
2248 n = bin(s)
2247 n = bin(s)
2249 if len(n) != len(nullid):
2248 if len(n) != len(nullid):
2250 raise TypeError()
2249 raise TypeError()
2251 return n
2250 return n
2252 except TypeError:
2251 except TypeError:
2253 raise error.Abort('changeset references must be full hexadecimal '
2252 raise error.Abort('changeset references must be full hexadecimal '
2254 'node identifiers')
2253 'node identifiers')
2255
2254
2256 if opts.get('delete'):
2255 if opts.get('delete'):
2257 indices = []
2256 indices = []
2258 for v in opts.get('delete'):
2257 for v in opts.get('delete'):
2259 try:
2258 try:
2260 indices.append(int(v))
2259 indices.append(int(v))
2261 except ValueError:
2260 except ValueError:
2262 raise error.Abort(_('invalid index value: %r') % v,
2261 raise error.Abort(_('invalid index value: %r') % v,
2263 hint=_('use integers for indices'))
2262 hint=_('use integers for indices'))
2264
2263
2265 if repo.currenttransaction():
2264 if repo.currenttransaction():
2266 raise error.Abort(_('cannot delete obsmarkers in the middle '
2265 raise error.Abort(_('cannot delete obsmarkers in the middle '
2267 'of transaction.'))
2266 'of transaction.'))
2268
2267
2269 with repo.lock():
2268 with repo.lock():
2270 n = repair.deleteobsmarkers(repo.obsstore, indices)
2269 n = repair.deleteobsmarkers(repo.obsstore, indices)
2271 ui.write(_('deleted %i obsolescence markers\n') % n)
2270 ui.write(_('deleted %i obsolescence markers\n') % n)
2272
2271
2273 return
2272 return
2274
2273
2275 if precursor is not None:
2274 if precursor is not None:
2276 if opts['rev']:
2275 if opts['rev']:
2277 raise error.Abort('cannot select revision when creating marker')
2276 raise error.Abort('cannot select revision when creating marker')
2278 metadata = {}
2277 metadata = {}
2279 metadata['user'] = opts['user'] or ui.username()
2278 metadata['user'] = opts['user'] or ui.username()
2280 succs = tuple(parsenodeid(succ) for succ in successors)
2279 succs = tuple(parsenodeid(succ) for succ in successors)
2281 l = repo.lock()
2280 l = repo.lock()
2282 try:
2281 try:
2283 tr = repo.transaction('debugobsolete')
2282 tr = repo.transaction('debugobsolete')
2284 try:
2283 try:
2285 date = opts.get('date')
2284 date = opts.get('date')
2286 if date:
2285 if date:
2287 date = util.parsedate(date)
2286 date = util.parsedate(date)
2288 else:
2287 else:
2289 date = None
2288 date = None
2290 prec = parsenodeid(precursor)
2289 prec = parsenodeid(precursor)
2291 parents = None
2290 parents = None
2292 if opts['record_parents']:
2291 if opts['record_parents']:
2293 if prec not in repo.unfiltered():
2292 if prec not in repo.unfiltered():
2294 raise error.Abort('cannot used --record-parents on '
2293 raise error.Abort('cannot used --record-parents on '
2295 'unknown changesets')
2294 'unknown changesets')
2296 parents = repo.unfiltered()[prec].parents()
2295 parents = repo.unfiltered()[prec].parents()
2297 parents = tuple(p.node() for p in parents)
2296 parents = tuple(p.node() for p in parents)
2298 repo.obsstore.create(tr, prec, succs, opts['flags'],
2297 repo.obsstore.create(tr, prec, succs, opts['flags'],
2299 parents=parents, date=date,
2298 parents=parents, date=date,
2300 metadata=metadata)
2299 metadata=metadata)
2301 tr.close()
2300 tr.close()
2302 except ValueError as exc:
2301 except ValueError as exc:
2303 raise error.Abort(_('bad obsmarker input: %s') % exc)
2302 raise error.Abort(_('bad obsmarker input: %s') % exc)
2304 finally:
2303 finally:
2305 tr.release()
2304 tr.release()
2306 finally:
2305 finally:
2307 l.release()
2306 l.release()
2308 else:
2307 else:
2309 if opts['rev']:
2308 if opts['rev']:
2310 revs = scmutil.revrange(repo, opts['rev'])
2309 revs = scmutil.revrange(repo, opts['rev'])
2311 nodes = [repo[r].node() for r in revs]
2310 nodes = [repo[r].node() for r in revs]
2312 markers = list(obsolete.getmarkers(repo, nodes=nodes))
2311 markers = list(obsolete.getmarkers(repo, nodes=nodes))
2313 markers.sort(key=lambda x: x._data)
2312 markers.sort(key=lambda x: x._data)
2314 else:
2313 else:
2315 markers = obsolete.getmarkers(repo)
2314 markers = obsolete.getmarkers(repo)
2316
2315
2317 markerstoiter = markers
2316 markerstoiter = markers
2318 isrelevant = lambda m: True
2317 isrelevant = lambda m: True
2319 if opts.get('rev') and opts.get('index'):
2318 if opts.get('rev') and opts.get('index'):
2320 markerstoiter = obsolete.getmarkers(repo)
2319 markerstoiter = obsolete.getmarkers(repo)
2321 markerset = set(markers)
2320 markerset = set(markers)
2322 isrelevant = lambda m: m in markerset
2321 isrelevant = lambda m: m in markerset
2323
2322
2324 fm = ui.formatter('debugobsolete', opts)
2323 fm = ui.formatter('debugobsolete', opts)
2325 for i, m in enumerate(markerstoiter):
2324 for i, m in enumerate(markerstoiter):
2326 if not isrelevant(m):
2325 if not isrelevant(m):
2327 # marker can be irrelevant when we're iterating over a set
2326 # marker can be irrelevant when we're iterating over a set
2328 # of markers (markerstoiter) which is bigger than the set
2327 # of markers (markerstoiter) which is bigger than the set
2329 # of markers we want to display (markers)
2328 # of markers we want to display (markers)
2330 # this can happen if both --index and --rev options are
2329 # this can happen if both --index and --rev options are
2331 # provided and thus we need to iterate over all of the markers
2330 # provided and thus we need to iterate over all of the markers
2332 # to get the correct indices, but only display the ones that
2331 # to get the correct indices, but only display the ones that
2333 # are relevant to --rev value
2332 # are relevant to --rev value
2334 continue
2333 continue
2335 fm.startitem()
2334 fm.startitem()
2336 ind = i if opts.get('index') else None
2335 ind = i if opts.get('index') else None
2337 cmdutil.showmarker(fm, m, index=ind)
2336 cmdutil.showmarker(fm, m, index=ind)
2338 fm.end()
2337 fm.end()
2339
2338
2340 @command('debugpathcomplete',
2339 @command('debugpathcomplete',
2341 [('f', 'full', None, _('complete an entire path')),
2340 [('f', 'full', None, _('complete an entire path')),
2342 ('n', 'normal', None, _('show only normal files')),
2341 ('n', 'normal', None, _('show only normal files')),
2343 ('a', 'added', None, _('show only added files')),
2342 ('a', 'added', None, _('show only added files')),
2344 ('r', 'removed', None, _('show only removed files'))],
2343 ('r', 'removed', None, _('show only removed files'))],
2345 _('FILESPEC...'))
2344 _('FILESPEC...'))
2346 def debugpathcomplete(ui, repo, *specs, **opts):
2345 def debugpathcomplete(ui, repo, *specs, **opts):
2347 '''complete part or all of a tracked path
2346 '''complete part or all of a tracked path
2348
2347
2349 This command supports shells that offer path name completion. It
2348 This command supports shells that offer path name completion. It
2350 currently completes only files already known to the dirstate.
2349 currently completes only files already known to the dirstate.
2351
2350
2352 Completion extends only to the next path segment unless
2351 Completion extends only to the next path segment unless
2353 --full is specified, in which case entire paths are used.'''
2352 --full is specified, in which case entire paths are used.'''
2354
2353
2355 def complete(path, acceptable):
2354 def complete(path, acceptable):
2356 dirstate = repo.dirstate
2355 dirstate = repo.dirstate
2357 spec = os.path.normpath(os.path.join(pycompat.getcwd(), path))
2356 spec = os.path.normpath(os.path.join(pycompat.getcwd(), path))
2358 rootdir = repo.root + pycompat.ossep
2357 rootdir = repo.root + pycompat.ossep
2359 if spec != repo.root and not spec.startswith(rootdir):
2358 if spec != repo.root and not spec.startswith(rootdir):
2360 return [], []
2359 return [], []
2361 if os.path.isdir(spec):
2360 if os.path.isdir(spec):
2362 spec += '/'
2361 spec += '/'
2363 spec = spec[len(rootdir):]
2362 spec = spec[len(rootdir):]
2364 fixpaths = pycompat.ossep != '/'
2363 fixpaths = pycompat.ossep != '/'
2365 if fixpaths:
2364 if fixpaths:
2366 spec = spec.replace(pycompat.ossep, '/')
2365 spec = spec.replace(pycompat.ossep, '/')
2367 speclen = len(spec)
2366 speclen = len(spec)
2368 fullpaths = opts['full']
2367 fullpaths = opts['full']
2369 files, dirs = set(), set()
2368 files, dirs = set(), set()
2370 adddir, addfile = dirs.add, files.add
2369 adddir, addfile = dirs.add, files.add
2371 for f, st in dirstate.iteritems():
2370 for f, st in dirstate.iteritems():
2372 if f.startswith(spec) and st[0] in acceptable:
2371 if f.startswith(spec) and st[0] in acceptable:
2373 if fixpaths:
2372 if fixpaths:
2374 f = f.replace('/', pycompat.ossep)
2373 f = f.replace('/', pycompat.ossep)
2375 if fullpaths:
2374 if fullpaths:
2376 addfile(f)
2375 addfile(f)
2377 continue
2376 continue
2378 s = f.find(pycompat.ossep, speclen)
2377 s = f.find(pycompat.ossep, speclen)
2379 if s >= 0:
2378 if s >= 0:
2380 adddir(f[:s])
2379 adddir(f[:s])
2381 else:
2380 else:
2382 addfile(f)
2381 addfile(f)
2383 return files, dirs
2382 return files, dirs
2384
2383
2385 acceptable = ''
2384 acceptable = ''
2386 if opts['normal']:
2385 if opts['normal']:
2387 acceptable += 'nm'
2386 acceptable += 'nm'
2388 if opts['added']:
2387 if opts['added']:
2389 acceptable += 'a'
2388 acceptable += 'a'
2390 if opts['removed']:
2389 if opts['removed']:
2391 acceptable += 'r'
2390 acceptable += 'r'
2392 cwd = repo.getcwd()
2391 cwd = repo.getcwd()
2393 if not specs:
2392 if not specs:
2394 specs = ['.']
2393 specs = ['.']
2395
2394
2396 files, dirs = set(), set()
2395 files, dirs = set(), set()
2397 for spec in specs:
2396 for spec in specs:
2398 f, d = complete(spec, acceptable or 'nmar')
2397 f, d = complete(spec, acceptable or 'nmar')
2399 files.update(f)
2398 files.update(f)
2400 dirs.update(d)
2399 dirs.update(d)
2401 files.update(dirs)
2400 files.update(dirs)
2402 ui.write('\n'.join(repo.pathto(p, cwd) for p in sorted(files)))
2401 ui.write('\n'.join(repo.pathto(p, cwd) for p in sorted(files)))
2403 ui.write('\n')
2402 ui.write('\n')
2404
2403
2405 @command('debugpushkey', [], _('REPO NAMESPACE [KEY OLD NEW]'), norepo=True)
2404 @command('debugpushkey', [], _('REPO NAMESPACE [KEY OLD NEW]'), norepo=True)
2406 def debugpushkey(ui, repopath, namespace, *keyinfo, **opts):
2405 def debugpushkey(ui, repopath, namespace, *keyinfo, **opts):
2407 '''access the pushkey key/value protocol
2406 '''access the pushkey key/value protocol
2408
2407
2409 With two args, list the keys in the given namespace.
2408 With two args, list the keys in the given namespace.
2410
2409
2411 With five args, set a key to new if it currently is set to old.
2410 With five args, set a key to new if it currently is set to old.
2412 Reports success or failure.
2411 Reports success or failure.
2413 '''
2412 '''
2414
2413
2415 target = hg.peer(ui, {}, repopath)
2414 target = hg.peer(ui, {}, repopath)
2416 if keyinfo:
2415 if keyinfo:
2417 key, old, new = keyinfo
2416 key, old, new = keyinfo
2418 r = target.pushkey(namespace, key, old, new)
2417 r = target.pushkey(namespace, key, old, new)
2419 ui.status(str(r) + '\n')
2418 ui.status(str(r) + '\n')
2420 return not r
2419 return not r
2421 else:
2420 else:
2422 for k, v in sorted(target.listkeys(namespace).iteritems()):
2421 for k, v in sorted(target.listkeys(namespace).iteritems()):
2423 ui.write("%s\t%s\n" % (k.encode('string-escape'),
2422 ui.write("%s\t%s\n" % (k.encode('string-escape'),
2424 v.encode('string-escape')))
2423 v.encode('string-escape')))
2425
2424
2426 @command('debugpvec', [], _('A B'))
2425 @command('debugpvec', [], _('A B'))
2427 def debugpvec(ui, repo, a, b=None):
2426 def debugpvec(ui, repo, a, b=None):
2428 ca = scmutil.revsingle(repo, a)
2427 ca = scmutil.revsingle(repo, a)
2429 cb = scmutil.revsingle(repo, b)
2428 cb = scmutil.revsingle(repo, b)
2430 pa = pvec.ctxpvec(ca)
2429 pa = pvec.ctxpvec(ca)
2431 pb = pvec.ctxpvec(cb)
2430 pb = pvec.ctxpvec(cb)
2432 if pa == pb:
2431 if pa == pb:
2433 rel = "="
2432 rel = "="
2434 elif pa > pb:
2433 elif pa > pb:
2435 rel = ">"
2434 rel = ">"
2436 elif pa < pb:
2435 elif pa < pb:
2437 rel = "<"
2436 rel = "<"
2438 elif pa | pb:
2437 elif pa | pb:
2439 rel = "|"
2438 rel = "|"
2440 ui.write(_("a: %s\n") % pa)
2439 ui.write(_("a: %s\n") % pa)
2441 ui.write(_("b: %s\n") % pb)
2440 ui.write(_("b: %s\n") % pb)
2442 ui.write(_("depth(a): %d depth(b): %d\n") % (pa._depth, pb._depth))
2441 ui.write(_("depth(a): %d depth(b): %d\n") % (pa._depth, pb._depth))
2443 ui.write(_("delta: %d hdist: %d distance: %d relation: %s\n") %
2442 ui.write(_("delta: %d hdist: %d distance: %d relation: %s\n") %
2444 (abs(pa._depth - pb._depth), pvec._hamming(pa._vec, pb._vec),
2443 (abs(pa._depth - pb._depth), pvec._hamming(pa._vec, pb._vec),
2445 pa.distance(pb), rel))
2444 pa.distance(pb), rel))
2446
2445
2447 @command('debugrebuilddirstate|debugrebuildstate',
2446 @command('debugrebuilddirstate|debugrebuildstate',
2448 [('r', 'rev', '', _('revision to rebuild to'), _('REV')),
2447 [('r', 'rev', '', _('revision to rebuild to'), _('REV')),
2449 ('', 'minimal', None, _('only rebuild files that are inconsistent with '
2448 ('', 'minimal', None, _('only rebuild files that are inconsistent with '
2450 'the working copy parent')),
2449 'the working copy parent')),
2451 ],
2450 ],
2452 _('[-r REV]'))
2451 _('[-r REV]'))
2453 def debugrebuilddirstate(ui, repo, rev, **opts):
2452 def debugrebuilddirstate(ui, repo, rev, **opts):
2454 """rebuild the dirstate as it would look like for the given revision
2453 """rebuild the dirstate as it would look like for the given revision
2455
2454
2456 If no revision is specified the first current parent will be used.
2455 If no revision is specified the first current parent will be used.
2457
2456
2458 The dirstate will be set to the files of the given revision.
2457 The dirstate will be set to the files of the given revision.
2459 The actual working directory content or existing dirstate
2458 The actual working directory content or existing dirstate
2460 information such as adds or removes is not considered.
2459 information such as adds or removes is not considered.
2461
2460
2462 ``minimal`` will only rebuild the dirstate status for files that claim to be
2461 ``minimal`` will only rebuild the dirstate status for files that claim to be
2463 tracked but are not in the parent manifest, or that exist in the parent
2462 tracked but are not in the parent manifest, or that exist in the parent
2464 manifest but are not in the dirstate. It will not change adds, removes, or
2463 manifest but are not in the dirstate. It will not change adds, removes, or
2465 modified files that are in the working copy parent.
2464 modified files that are in the working copy parent.
2466
2465
2467 One use of this command is to make the next :hg:`status` invocation
2466 One use of this command is to make the next :hg:`status` invocation
2468 check the actual file content.
2467 check the actual file content.
2469 """
2468 """
2470 ctx = scmutil.revsingle(repo, rev)
2469 ctx = scmutil.revsingle(repo, rev)
2471 with repo.wlock():
2470 with repo.wlock():
2472 dirstate = repo.dirstate
2471 dirstate = repo.dirstate
2473 changedfiles = None
2472 changedfiles = None
2474 # See command doc for what minimal does.
2473 # See command doc for what minimal does.
2475 if opts.get('minimal'):
2474 if opts.get('minimal'):
2476 manifestfiles = set(ctx.manifest().keys())
2475 manifestfiles = set(ctx.manifest().keys())
2477 dirstatefiles = set(dirstate)
2476 dirstatefiles = set(dirstate)
2478 manifestonly = manifestfiles - dirstatefiles
2477 manifestonly = manifestfiles - dirstatefiles
2479 dsonly = dirstatefiles - manifestfiles
2478 dsonly = dirstatefiles - manifestfiles
2480 dsnotadded = set(f for f in dsonly if dirstate[f] != 'a')
2479 dsnotadded = set(f for f in dsonly if dirstate[f] != 'a')
2481 changedfiles = manifestonly | dsnotadded
2480 changedfiles = manifestonly | dsnotadded
2482
2481
2483 dirstate.rebuild(ctx.node(), ctx.manifest(), changedfiles)
2482 dirstate.rebuild(ctx.node(), ctx.manifest(), changedfiles)
2484
2483
2485 @command('debugrebuildfncache', [], '')
2484 @command('debugrebuildfncache', [], '')
2486 def debugrebuildfncache(ui, repo):
2485 def debugrebuildfncache(ui, repo):
2487 """rebuild the fncache file"""
2486 """rebuild the fncache file"""
2488 repair.rebuildfncache(ui, repo)
2487 repair.rebuildfncache(ui, repo)
2489
2488
2490 @command('debugrename',
2489 @command('debugrename',
2491 [('r', 'rev', '', _('revision to debug'), _('REV'))],
2490 [('r', 'rev', '', _('revision to debug'), _('REV'))],
2492 _('[-r REV] FILE'))
2491 _('[-r REV] FILE'))
2493 def debugrename(ui, repo, file1, *pats, **opts):
2492 def debugrename(ui, repo, file1, *pats, **opts):
2494 """dump rename information"""
2493 """dump rename information"""
2495
2494
2496 ctx = scmutil.revsingle(repo, opts.get('rev'))
2495 ctx = scmutil.revsingle(repo, opts.get('rev'))
2497 m = scmutil.match(ctx, (file1,) + pats, opts)
2496 m = scmutil.match(ctx, (file1,) + pats, opts)
2498 for abs in ctx.walk(m):
2497 for abs in ctx.walk(m):
2499 fctx = ctx[abs]
2498 fctx = ctx[abs]
2500 o = fctx.filelog().renamed(fctx.filenode())
2499 o = fctx.filelog().renamed(fctx.filenode())
2501 rel = m.rel(abs)
2500 rel = m.rel(abs)
2502 if o:
2501 if o:
2503 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
2502 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
2504 else:
2503 else:
2505 ui.write(_("%s not renamed\n") % rel)
2504 ui.write(_("%s not renamed\n") % rel)
2506
2505
2507 @command('debugrevlog', debugrevlogopts +
2506 @command('debugrevlog', debugrevlogopts +
2508 [('d', 'dump', False, _('dump index data'))],
2507 [('d', 'dump', False, _('dump index data'))],
2509 _('-c|-m|FILE'),
2508 _('-c|-m|FILE'),
2510 optionalrepo=True)
2509 optionalrepo=True)
2511 def debugrevlog(ui, repo, file_=None, **opts):
2510 def debugrevlog(ui, repo, file_=None, **opts):
2512 """show data and statistics about a revlog"""
2511 """show data and statistics about a revlog"""
2513 r = cmdutil.openrevlog(repo, 'debugrevlog', file_, opts)
2512 r = cmdutil.openrevlog(repo, 'debugrevlog', file_, opts)
2514
2513
2515 if opts.get("dump"):
2514 if opts.get("dump"):
2516 numrevs = len(r)
2515 numrevs = len(r)
2517 ui.write(("# rev p1rev p2rev start end deltastart base p1 p2"
2516 ui.write(("# rev p1rev p2rev start end deltastart base p1 p2"
2518 " rawsize totalsize compression heads chainlen\n"))
2517 " rawsize totalsize compression heads chainlen\n"))
2519 ts = 0
2518 ts = 0
2520 heads = set()
2519 heads = set()
2521
2520
2522 for rev in xrange(numrevs):
2521 for rev in xrange(numrevs):
2523 dbase = r.deltaparent(rev)
2522 dbase = r.deltaparent(rev)
2524 if dbase == -1:
2523 if dbase == -1:
2525 dbase = rev
2524 dbase = rev
2526 cbase = r.chainbase(rev)
2525 cbase = r.chainbase(rev)
2527 clen = r.chainlen(rev)
2526 clen = r.chainlen(rev)
2528 p1, p2 = r.parentrevs(rev)
2527 p1, p2 = r.parentrevs(rev)
2529 rs = r.rawsize(rev)
2528 rs = r.rawsize(rev)
2530 ts = ts + rs
2529 ts = ts + rs
2531 heads -= set(r.parentrevs(rev))
2530 heads -= set(r.parentrevs(rev))
2532 heads.add(rev)
2531 heads.add(rev)
2533 try:
2532 try:
2534 compression = ts / r.end(rev)
2533 compression = ts / r.end(rev)
2535 except ZeroDivisionError:
2534 except ZeroDivisionError:
2536 compression = 0
2535 compression = 0
2537 ui.write("%5d %5d %5d %5d %5d %10d %4d %4d %4d %7d %9d "
2536 ui.write("%5d %5d %5d %5d %5d %10d %4d %4d %4d %7d %9d "
2538 "%11d %5d %8d\n" %
2537 "%11d %5d %8d\n" %
2539 (rev, p1, p2, r.start(rev), r.end(rev),
2538 (rev, p1, p2, r.start(rev), r.end(rev),
2540 r.start(dbase), r.start(cbase),
2539 r.start(dbase), r.start(cbase),
2541 r.start(p1), r.start(p2),
2540 r.start(p1), r.start(p2),
2542 rs, ts, compression, len(heads), clen))
2541 rs, ts, compression, len(heads), clen))
2543 return 0
2542 return 0
2544
2543
2545 v = r.version
2544 v = r.version
2546 format = v & 0xFFFF
2545 format = v & 0xFFFF
2547 flags = []
2546 flags = []
2548 gdelta = False
2547 gdelta = False
2549 if v & revlog.REVLOGNGINLINEDATA:
2548 if v & revlog.REVLOGNGINLINEDATA:
2550 flags.append('inline')
2549 flags.append('inline')
2551 if v & revlog.REVLOGGENERALDELTA:
2550 if v & revlog.REVLOGGENERALDELTA:
2552 gdelta = True
2551 gdelta = True
2553 flags.append('generaldelta')
2552 flags.append('generaldelta')
2554 if not flags:
2553 if not flags:
2555 flags = ['(none)']
2554 flags = ['(none)']
2556
2555
2557 nummerges = 0
2556 nummerges = 0
2558 numfull = 0
2557 numfull = 0
2559 numprev = 0
2558 numprev = 0
2560 nump1 = 0
2559 nump1 = 0
2561 nump2 = 0
2560 nump2 = 0
2562 numother = 0
2561 numother = 0
2563 nump1prev = 0
2562 nump1prev = 0
2564 nump2prev = 0
2563 nump2prev = 0
2565 chainlengths = []
2564 chainlengths = []
2566
2565
2567 datasize = [None, 0, 0]
2566 datasize = [None, 0, 0]
2568 fullsize = [None, 0, 0]
2567 fullsize = [None, 0, 0]
2569 deltasize = [None, 0, 0]
2568 deltasize = [None, 0, 0]
2570 chunktypecounts = {}
2569 chunktypecounts = {}
2571 chunktypesizes = {}
2570 chunktypesizes = {}
2572
2571
2573 def addsize(size, l):
2572 def addsize(size, l):
2574 if l[0] is None or size < l[0]:
2573 if l[0] is None or size < l[0]:
2575 l[0] = size
2574 l[0] = size
2576 if size > l[1]:
2575 if size > l[1]:
2577 l[1] = size
2576 l[1] = size
2578 l[2] += size
2577 l[2] += size
2579
2578
2580 numrevs = len(r)
2579 numrevs = len(r)
2581 for rev in xrange(numrevs):
2580 for rev in xrange(numrevs):
2582 p1, p2 = r.parentrevs(rev)
2581 p1, p2 = r.parentrevs(rev)
2583 delta = r.deltaparent(rev)
2582 delta = r.deltaparent(rev)
2584 if format > 0:
2583 if format > 0:
2585 addsize(r.rawsize(rev), datasize)
2584 addsize(r.rawsize(rev), datasize)
2586 if p2 != nullrev:
2585 if p2 != nullrev:
2587 nummerges += 1
2586 nummerges += 1
2588 size = r.length(rev)
2587 size = r.length(rev)
2589 if delta == nullrev:
2588 if delta == nullrev:
2590 chainlengths.append(0)
2589 chainlengths.append(0)
2591 numfull += 1
2590 numfull += 1
2592 addsize(size, fullsize)
2591 addsize(size, fullsize)
2593 else:
2592 else:
2594 chainlengths.append(chainlengths[delta] + 1)
2593 chainlengths.append(chainlengths[delta] + 1)
2595 addsize(size, deltasize)
2594 addsize(size, deltasize)
2596 if delta == rev - 1:
2595 if delta == rev - 1:
2597 numprev += 1
2596 numprev += 1
2598 if delta == p1:
2597 if delta == p1:
2599 nump1prev += 1
2598 nump1prev += 1
2600 elif delta == p2:
2599 elif delta == p2:
2601 nump2prev += 1
2600 nump2prev += 1
2602 elif delta == p1:
2601 elif delta == p1:
2603 nump1 += 1
2602 nump1 += 1
2604 elif delta == p2:
2603 elif delta == p2:
2605 nump2 += 1
2604 nump2 += 1
2606 elif delta != nullrev:
2605 elif delta != nullrev:
2607 numother += 1
2606 numother += 1
2608
2607
2609 # Obtain data on the raw chunks in the revlog.
2608 # Obtain data on the raw chunks in the revlog.
2610 chunk = r._chunkraw(rev, rev)[1]
2609 chunk = r._chunkraw(rev, rev)[1]
2611 if chunk:
2610 if chunk:
2612 chunktype = chunk[0]
2611 chunktype = chunk[0]
2613 else:
2612 else:
2614 chunktype = 'empty'
2613 chunktype = 'empty'
2615
2614
2616 if chunktype not in chunktypecounts:
2615 if chunktype not in chunktypecounts:
2617 chunktypecounts[chunktype] = 0
2616 chunktypecounts[chunktype] = 0
2618 chunktypesizes[chunktype] = 0
2617 chunktypesizes[chunktype] = 0
2619
2618
2620 chunktypecounts[chunktype] += 1
2619 chunktypecounts[chunktype] += 1
2621 chunktypesizes[chunktype] += size
2620 chunktypesizes[chunktype] += size
2622
2621
2623 # Adjust size min value for empty cases
2622 # Adjust size min value for empty cases
2624 for size in (datasize, fullsize, deltasize):
2623 for size in (datasize, fullsize, deltasize):
2625 if size[0] is None:
2624 if size[0] is None:
2626 size[0] = 0
2625 size[0] = 0
2627
2626
2628 numdeltas = numrevs - numfull
2627 numdeltas = numrevs - numfull
2629 numoprev = numprev - nump1prev - nump2prev
2628 numoprev = numprev - nump1prev - nump2prev
2630 totalrawsize = datasize[2]
2629 totalrawsize = datasize[2]
2631 datasize[2] /= numrevs
2630 datasize[2] /= numrevs
2632 fulltotal = fullsize[2]
2631 fulltotal = fullsize[2]
2633 fullsize[2] /= numfull
2632 fullsize[2] /= numfull
2634 deltatotal = deltasize[2]
2633 deltatotal = deltasize[2]
2635 if numrevs - numfull > 0:
2634 if numrevs - numfull > 0:
2636 deltasize[2] /= numrevs - numfull
2635 deltasize[2] /= numrevs - numfull
2637 totalsize = fulltotal + deltatotal
2636 totalsize = fulltotal + deltatotal
2638 avgchainlen = sum(chainlengths) / numrevs
2637 avgchainlen = sum(chainlengths) / numrevs
2639 maxchainlen = max(chainlengths)
2638 maxchainlen = max(chainlengths)
2640 compratio = 1
2639 compratio = 1
2641 if totalsize:
2640 if totalsize:
2642 compratio = totalrawsize / totalsize
2641 compratio = totalrawsize / totalsize
2643
2642
2644 basedfmtstr = '%%%dd\n'
2643 basedfmtstr = '%%%dd\n'
2645 basepcfmtstr = '%%%dd %s(%%5.2f%%%%)\n'
2644 basepcfmtstr = '%%%dd %s(%%5.2f%%%%)\n'
2646
2645
2647 def dfmtstr(max):
2646 def dfmtstr(max):
2648 return basedfmtstr % len(str(max))
2647 return basedfmtstr % len(str(max))
2649 def pcfmtstr(max, padding=0):
2648 def pcfmtstr(max, padding=0):
2650 return basepcfmtstr % (len(str(max)), ' ' * padding)
2649 return basepcfmtstr % (len(str(max)), ' ' * padding)
2651
2650
2652 def pcfmt(value, total):
2651 def pcfmt(value, total):
2653 if total:
2652 if total:
2654 return (value, 100 * float(value) / total)
2653 return (value, 100 * float(value) / total)
2655 else:
2654 else:
2656 return value, 100.0
2655 return value, 100.0
2657
2656
2658 ui.write(('format : %d\n') % format)
2657 ui.write(('format : %d\n') % format)
2659 ui.write(('flags : %s\n') % ', '.join(flags))
2658 ui.write(('flags : %s\n') % ', '.join(flags))
2660
2659
2661 ui.write('\n')
2660 ui.write('\n')
2662 fmt = pcfmtstr(totalsize)
2661 fmt = pcfmtstr(totalsize)
2663 fmt2 = dfmtstr(totalsize)
2662 fmt2 = dfmtstr(totalsize)
2664 ui.write(('revisions : ') + fmt2 % numrevs)
2663 ui.write(('revisions : ') + fmt2 % numrevs)
2665 ui.write((' merges : ') + fmt % pcfmt(nummerges, numrevs))
2664 ui.write((' merges : ') + fmt % pcfmt(nummerges, numrevs))
2666 ui.write((' normal : ') + fmt % pcfmt(numrevs - nummerges, numrevs))
2665 ui.write((' normal : ') + fmt % pcfmt(numrevs - nummerges, numrevs))
2667 ui.write(('revisions : ') + fmt2 % numrevs)
2666 ui.write(('revisions : ') + fmt2 % numrevs)
2668 ui.write((' full : ') + fmt % pcfmt(numfull, numrevs))
2667 ui.write((' full : ') + fmt % pcfmt(numfull, numrevs))
2669 ui.write((' deltas : ') + fmt % pcfmt(numdeltas, numrevs))
2668 ui.write((' deltas : ') + fmt % pcfmt(numdeltas, numrevs))
2670 ui.write(('revision size : ') + fmt2 % totalsize)
2669 ui.write(('revision size : ') + fmt2 % totalsize)
2671 ui.write((' full : ') + fmt % pcfmt(fulltotal, totalsize))
2670 ui.write((' full : ') + fmt % pcfmt(fulltotal, totalsize))
2672 ui.write((' deltas : ') + fmt % pcfmt(deltatotal, totalsize))
2671 ui.write((' deltas : ') + fmt % pcfmt(deltatotal, totalsize))
2673
2672
2674 def fmtchunktype(chunktype):
2673 def fmtchunktype(chunktype):
2675 if chunktype == 'empty':
2674 if chunktype == 'empty':
2676 return ' %s : ' % chunktype
2675 return ' %s : ' % chunktype
2677 elif chunktype in string.ascii_letters:
2676 elif chunktype in string.ascii_letters:
2678 return ' 0x%s (%s) : ' % (hex(chunktype), chunktype)
2677 return ' 0x%s (%s) : ' % (hex(chunktype), chunktype)
2679 else:
2678 else:
2680 return ' 0x%s : ' % hex(chunktype)
2679 return ' 0x%s : ' % hex(chunktype)
2681
2680
2682 ui.write('\n')
2681 ui.write('\n')
2683 ui.write(('chunks : ') + fmt2 % numrevs)
2682 ui.write(('chunks : ') + fmt2 % numrevs)
2684 for chunktype in sorted(chunktypecounts):
2683 for chunktype in sorted(chunktypecounts):
2685 ui.write(fmtchunktype(chunktype))
2684 ui.write(fmtchunktype(chunktype))
2686 ui.write(fmt % pcfmt(chunktypecounts[chunktype], numrevs))
2685 ui.write(fmt % pcfmt(chunktypecounts[chunktype], numrevs))
2687 ui.write(('chunks size : ') + fmt2 % totalsize)
2686 ui.write(('chunks size : ') + fmt2 % totalsize)
2688 for chunktype in sorted(chunktypecounts):
2687 for chunktype in sorted(chunktypecounts):
2689 ui.write(fmtchunktype(chunktype))
2688 ui.write(fmtchunktype(chunktype))
2690 ui.write(fmt % pcfmt(chunktypesizes[chunktype], totalsize))
2689 ui.write(fmt % pcfmt(chunktypesizes[chunktype], totalsize))
2691
2690
2692 ui.write('\n')
2691 ui.write('\n')
2693 fmt = dfmtstr(max(avgchainlen, compratio))
2692 fmt = dfmtstr(max(avgchainlen, compratio))
2694 ui.write(('avg chain length : ') + fmt % avgchainlen)
2693 ui.write(('avg chain length : ') + fmt % avgchainlen)
2695 ui.write(('max chain length : ') + fmt % maxchainlen)
2694 ui.write(('max chain length : ') + fmt % maxchainlen)
2696 ui.write(('compression ratio : ') + fmt % compratio)
2695 ui.write(('compression ratio : ') + fmt % compratio)
2697
2696
2698 if format > 0:
2697 if format > 0:
2699 ui.write('\n')
2698 ui.write('\n')
2700 ui.write(('uncompressed data size (min/max/avg) : %d / %d / %d\n')
2699 ui.write(('uncompressed data size (min/max/avg) : %d / %d / %d\n')
2701 % tuple(datasize))
2700 % tuple(datasize))
2702 ui.write(('full revision size (min/max/avg) : %d / %d / %d\n')
2701 ui.write(('full revision size (min/max/avg) : %d / %d / %d\n')
2703 % tuple(fullsize))
2702 % tuple(fullsize))
2704 ui.write(('delta size (min/max/avg) : %d / %d / %d\n')
2703 ui.write(('delta size (min/max/avg) : %d / %d / %d\n')
2705 % tuple(deltasize))
2704 % tuple(deltasize))
2706
2705
2707 if numdeltas > 0:
2706 if numdeltas > 0:
2708 ui.write('\n')
2707 ui.write('\n')
2709 fmt = pcfmtstr(numdeltas)
2708 fmt = pcfmtstr(numdeltas)
2710 fmt2 = pcfmtstr(numdeltas, 4)
2709 fmt2 = pcfmtstr(numdeltas, 4)
2711 ui.write(('deltas against prev : ') + fmt % pcfmt(numprev, numdeltas))
2710 ui.write(('deltas against prev : ') + fmt % pcfmt(numprev, numdeltas))
2712 if numprev > 0:
2711 if numprev > 0:
2713 ui.write((' where prev = p1 : ') + fmt2 % pcfmt(nump1prev,
2712 ui.write((' where prev = p1 : ') + fmt2 % pcfmt(nump1prev,
2714 numprev))
2713 numprev))
2715 ui.write((' where prev = p2 : ') + fmt2 % pcfmt(nump2prev,
2714 ui.write((' where prev = p2 : ') + fmt2 % pcfmt(nump2prev,
2716 numprev))
2715 numprev))
2717 ui.write((' other : ') + fmt2 % pcfmt(numoprev,
2716 ui.write((' other : ') + fmt2 % pcfmt(numoprev,
2718 numprev))
2717 numprev))
2719 if gdelta:
2718 if gdelta:
2720 ui.write(('deltas against p1 : ')
2719 ui.write(('deltas against p1 : ')
2721 + fmt % pcfmt(nump1, numdeltas))
2720 + fmt % pcfmt(nump1, numdeltas))
2722 ui.write(('deltas against p2 : ')
2721 ui.write(('deltas against p2 : ')
2723 + fmt % pcfmt(nump2, numdeltas))
2722 + fmt % pcfmt(nump2, numdeltas))
2724 ui.write(('deltas against other : ') + fmt % pcfmt(numother,
2723 ui.write(('deltas against other : ') + fmt % pcfmt(numother,
2725 numdeltas))
2724 numdeltas))
2726
2725
2727 @command('debugrevspec',
2726 @command('debugrevspec',
2728 [('', 'optimize', None,
2727 [('', 'optimize', None,
2729 _('print parsed tree after optimizing (DEPRECATED)')),
2728 _('print parsed tree after optimizing (DEPRECATED)')),
2730 ('p', 'show-stage', [],
2729 ('p', 'show-stage', [],
2731 _('print parsed tree at the given stage'), _('NAME')),
2730 _('print parsed tree at the given stage'), _('NAME')),
2732 ('', 'no-optimized', False, _('evaluate tree without optimization')),
2731 ('', 'no-optimized', False, _('evaluate tree without optimization')),
2733 ('', 'verify-optimized', False, _('verify optimized result')),
2732 ('', 'verify-optimized', False, _('verify optimized result')),
2734 ],
2733 ],
2735 ('REVSPEC'))
2734 ('REVSPEC'))
2736 def debugrevspec(ui, repo, expr, **opts):
2735 def debugrevspec(ui, repo, expr, **opts):
2737 """parse and apply a revision specification
2736 """parse and apply a revision specification
2738
2737
2739 Use -p/--show-stage option to print the parsed tree at the given stages.
2738 Use -p/--show-stage option to print the parsed tree at the given stages.
2740 Use -p all to print tree at every stage.
2739 Use -p all to print tree at every stage.
2741
2740
2742 Use --verify-optimized to compare the optimized result with the unoptimized
2741 Use --verify-optimized to compare the optimized result with the unoptimized
2743 one. Returns 1 if the optimized result differs.
2742 one. Returns 1 if the optimized result differs.
2744 """
2743 """
2745 stages = [
2744 stages = [
2746 ('parsed', lambda tree: tree),
2745 ('parsed', lambda tree: tree),
2747 ('expanded', lambda tree: revset.expandaliases(ui, tree)),
2746 ('expanded', lambda tree: revset.expandaliases(ui, tree)),
2748 ('concatenated', revset.foldconcat),
2747 ('concatenated', revset.foldconcat),
2749 ('analyzed', revset.analyze),
2748 ('analyzed', revset.analyze),
2750 ('optimized', revset.optimize),
2749 ('optimized', revset.optimize),
2751 ]
2750 ]
2752 if opts['no_optimized']:
2751 if opts['no_optimized']:
2753 stages = stages[:-1]
2752 stages = stages[:-1]
2754 if opts['verify_optimized'] and opts['no_optimized']:
2753 if opts['verify_optimized'] and opts['no_optimized']:
2755 raise error.Abort(_('cannot use --verify-optimized with '
2754 raise error.Abort(_('cannot use --verify-optimized with '
2756 '--no-optimized'))
2755 '--no-optimized'))
2757 stagenames = set(n for n, f in stages)
2756 stagenames = set(n for n, f in stages)
2758
2757
2759 showalways = set()
2758 showalways = set()
2760 showchanged = set()
2759 showchanged = set()
2761 if ui.verbose and not opts['show_stage']:
2760 if ui.verbose and not opts['show_stage']:
2762 # show parsed tree by --verbose (deprecated)
2761 # show parsed tree by --verbose (deprecated)
2763 showalways.add('parsed')
2762 showalways.add('parsed')
2764 showchanged.update(['expanded', 'concatenated'])
2763 showchanged.update(['expanded', 'concatenated'])
2765 if opts['optimize']:
2764 if opts['optimize']:
2766 showalways.add('optimized')
2765 showalways.add('optimized')
2767 if opts['show_stage'] and opts['optimize']:
2766 if opts['show_stage'] and opts['optimize']:
2768 raise error.Abort(_('cannot use --optimize with --show-stage'))
2767 raise error.Abort(_('cannot use --optimize with --show-stage'))
2769 if opts['show_stage'] == ['all']:
2768 if opts['show_stage'] == ['all']:
2770 showalways.update(stagenames)
2769 showalways.update(stagenames)
2771 else:
2770 else:
2772 for n in opts['show_stage']:
2771 for n in opts['show_stage']:
2773 if n not in stagenames:
2772 if n not in stagenames:
2774 raise error.Abort(_('invalid stage name: %s') % n)
2773 raise error.Abort(_('invalid stage name: %s') % n)
2775 showalways.update(opts['show_stage'])
2774 showalways.update(opts['show_stage'])
2776
2775
2777 treebystage = {}
2776 treebystage = {}
2778 printedtree = None
2777 printedtree = None
2779 tree = revset.parse(expr, lookup=repo.__contains__)
2778 tree = revset.parse(expr, lookup=repo.__contains__)
2780 for n, f in stages:
2779 for n, f in stages:
2781 treebystage[n] = tree = f(tree)
2780 treebystage[n] = tree = f(tree)
2782 if n in showalways or (n in showchanged and tree != printedtree):
2781 if n in showalways or (n in showchanged and tree != printedtree):
2783 if opts['show_stage'] or n != 'parsed':
2782 if opts['show_stage'] or n != 'parsed':
2784 ui.write(("* %s:\n") % n)
2783 ui.write(("* %s:\n") % n)
2785 ui.write(revset.prettyformat(tree), "\n")
2784 ui.write(revset.prettyformat(tree), "\n")
2786 printedtree = tree
2785 printedtree = tree
2787
2786
2788 if opts['verify_optimized']:
2787 if opts['verify_optimized']:
2789 arevs = revset.makematcher(treebystage['analyzed'])(repo)
2788 arevs = revset.makematcher(treebystage['analyzed'])(repo)
2790 brevs = revset.makematcher(treebystage['optimized'])(repo)
2789 brevs = revset.makematcher(treebystage['optimized'])(repo)
2791 if ui.verbose:
2790 if ui.verbose:
2792 ui.note(("* analyzed set:\n"), revset.prettyformatset(arevs), "\n")
2791 ui.note(("* analyzed set:\n"), revset.prettyformatset(arevs), "\n")
2793 ui.note(("* optimized set:\n"), revset.prettyformatset(brevs), "\n")
2792 ui.note(("* optimized set:\n"), revset.prettyformatset(brevs), "\n")
2794 arevs = list(arevs)
2793 arevs = list(arevs)
2795 brevs = list(brevs)
2794 brevs = list(brevs)
2796 if arevs == brevs:
2795 if arevs == brevs:
2797 return 0
2796 return 0
2798 ui.write(('--- analyzed\n'), label='diff.file_a')
2797 ui.write(('--- analyzed\n'), label='diff.file_a')
2799 ui.write(('+++ optimized\n'), label='diff.file_b')
2798 ui.write(('+++ optimized\n'), label='diff.file_b')
2800 sm = difflib.SequenceMatcher(None, arevs, brevs)
2799 sm = difflib.SequenceMatcher(None, arevs, brevs)
2801 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
2800 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
2802 if tag in ('delete', 'replace'):
2801 if tag in ('delete', 'replace'):
2803 for c in arevs[alo:ahi]:
2802 for c in arevs[alo:ahi]:
2804 ui.write('-%s\n' % c, label='diff.deleted')
2803 ui.write('-%s\n' % c, label='diff.deleted')
2805 if tag in ('insert', 'replace'):
2804 if tag in ('insert', 'replace'):
2806 for c in brevs[blo:bhi]:
2805 for c in brevs[blo:bhi]:
2807 ui.write('+%s\n' % c, label='diff.inserted')
2806 ui.write('+%s\n' % c, label='diff.inserted')
2808 if tag == 'equal':
2807 if tag == 'equal':
2809 for c in arevs[alo:ahi]:
2808 for c in arevs[alo:ahi]:
2810 ui.write(' %s\n' % c)
2809 ui.write(' %s\n' % c)
2811 return 1
2810 return 1
2812
2811
2813 func = revset.makematcher(tree)
2812 func = revset.makematcher(tree)
2814 revs = func(repo)
2813 revs = func(repo)
2815 if ui.verbose:
2814 if ui.verbose:
2816 ui.note(("* set:\n"), revset.prettyformatset(revs), "\n")
2815 ui.note(("* set:\n"), revset.prettyformatset(revs), "\n")
2817 for c in revs:
2816 for c in revs:
2818 ui.write("%s\n" % c)
2817 ui.write("%s\n" % c)
2819
2818
2820 @command('debugsetparents', [], _('REV1 [REV2]'))
2819 @command('debugsetparents', [], _('REV1 [REV2]'))
2821 def debugsetparents(ui, repo, rev1, rev2=None):
2820 def debugsetparents(ui, repo, rev1, rev2=None):
2822 """manually set the parents of the current working directory
2821 """manually set the parents of the current working directory
2823
2822
2824 This is useful for writing repository conversion tools, but should
2823 This is useful for writing repository conversion tools, but should
2825 be used with care. For example, neither the working directory nor the
2824 be used with care. For example, neither the working directory nor the
2826 dirstate is updated, so file status may be incorrect after running this
2825 dirstate is updated, so file status may be incorrect after running this
2827 command.
2826 command.
2828
2827
2829 Returns 0 on success.
2828 Returns 0 on success.
2830 """
2829 """
2831
2830
2832 r1 = scmutil.revsingle(repo, rev1).node()
2831 r1 = scmutil.revsingle(repo, rev1).node()
2833 r2 = scmutil.revsingle(repo, rev2, 'null').node()
2832 r2 = scmutil.revsingle(repo, rev2, 'null').node()
2834
2833
2835 with repo.wlock():
2834 with repo.wlock():
2836 repo.setparents(r1, r2)
2835 repo.setparents(r1, r2)
2837
2836
2838 @command('debugdirstate|debugstate',
2837 @command('debugdirstate|debugstate',
2839 [('', 'nodates', None, _('do not display the saved mtime')),
2838 [('', 'nodates', None, _('do not display the saved mtime')),
2840 ('', 'datesort', None, _('sort by saved mtime'))],
2839 ('', 'datesort', None, _('sort by saved mtime'))],
2841 _('[OPTION]...'))
2840 _('[OPTION]...'))
2842 def debugstate(ui, repo, **opts):
2841 def debugstate(ui, repo, **opts):
2843 """show the contents of the current dirstate"""
2842 """show the contents of the current dirstate"""
2844
2843
2845 nodates = opts.get('nodates')
2844 nodates = opts.get('nodates')
2846 datesort = opts.get('datesort')
2845 datesort = opts.get('datesort')
2847
2846
2848 timestr = ""
2847 timestr = ""
2849 if datesort:
2848 if datesort:
2850 keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
2849 keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
2851 else:
2850 else:
2852 keyfunc = None # sort by filename
2851 keyfunc = None # sort by filename
2853 for file_, ent in sorted(repo.dirstate._map.iteritems(), key=keyfunc):
2852 for file_, ent in sorted(repo.dirstate._map.iteritems(), key=keyfunc):
2854 if ent[3] == -1:
2853 if ent[3] == -1:
2855 timestr = 'unset '
2854 timestr = 'unset '
2856 elif nodates:
2855 elif nodates:
2857 timestr = 'set '
2856 timestr = 'set '
2858 else:
2857 else:
2859 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
2858 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
2860 time.localtime(ent[3]))
2859 time.localtime(ent[3]))
2861 if ent[1] & 0o20000:
2860 if ent[1] & 0o20000:
2862 mode = 'lnk'
2861 mode = 'lnk'
2863 else:
2862 else:
2864 mode = '%3o' % (ent[1] & 0o777 & ~util.umask)
2863 mode = '%3o' % (ent[1] & 0o777 & ~util.umask)
2865 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
2864 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
2866 for f in repo.dirstate.copies():
2865 for f in repo.dirstate.copies():
2867 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
2866 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
2868
2867
2869 @command('debugsub',
2868 @command('debugsub',
2870 [('r', 'rev', '',
2869 [('r', 'rev', '',
2871 _('revision to check'), _('REV'))],
2870 _('revision to check'), _('REV'))],
2872 _('[-r REV] [REV]'))
2871 _('[-r REV] [REV]'))
2873 def debugsub(ui, repo, rev=None):
2872 def debugsub(ui, repo, rev=None):
2874 ctx = scmutil.revsingle(repo, rev, None)
2873 ctx = scmutil.revsingle(repo, rev, None)
2875 for k, v in sorted(ctx.substate.items()):
2874 for k, v in sorted(ctx.substate.items()):
2876 ui.write(('path %s\n') % k)
2875 ui.write(('path %s\n') % k)
2877 ui.write((' source %s\n') % v[0])
2876 ui.write((' source %s\n') % v[0])
2878 ui.write((' revision %s\n') % v[1])
2877 ui.write((' revision %s\n') % v[1])
2879
2878
2880 @command('debugsuccessorssets',
2879 @command('debugsuccessorssets',
2881 [],
2880 [],
2882 _('[REV]'))
2881 _('[REV]'))
2883 def debugsuccessorssets(ui, repo, *revs):
2882 def debugsuccessorssets(ui, repo, *revs):
2884 """show set of successors for revision
2883 """show set of successors for revision
2885
2884
2886 A successors set of changeset A is a consistent group of revisions that
2885 A successors set of changeset A is a consistent group of revisions that
2887 succeed A. It contains non-obsolete changesets only.
2886 succeed A. It contains non-obsolete changesets only.
2888
2887
2889 In most cases a changeset A has a single successors set containing a single
2888 In most cases a changeset A has a single successors set containing a single
2890 successor (changeset A replaced by A').
2889 successor (changeset A replaced by A').
2891
2890
2892 A changeset that is made obsolete with no successors are called "pruned".
2891 A changeset that is made obsolete with no successors are called "pruned".
2893 Such changesets have no successors sets at all.
2892 Such changesets have no successors sets at all.
2894
2893
2895 A changeset that has been "split" will have a successors set containing
2894 A changeset that has been "split" will have a successors set containing
2896 more than one successor.
2895 more than one successor.
2897
2896
2898 A changeset that has been rewritten in multiple different ways is called
2897 A changeset that has been rewritten in multiple different ways is called
2899 "divergent". Such changesets have multiple successor sets (each of which
2898 "divergent". Such changesets have multiple successor sets (each of which
2900 may also be split, i.e. have multiple successors).
2899 may also be split, i.e. have multiple successors).
2901
2900
2902 Results are displayed as follows::
2901 Results are displayed as follows::
2903
2902
2904 <rev1>
2903 <rev1>
2905 <successors-1A>
2904 <successors-1A>
2906 <rev2>
2905 <rev2>
2907 <successors-2A>
2906 <successors-2A>
2908 <successors-2B1> <successors-2B2> <successors-2B3>
2907 <successors-2B1> <successors-2B2> <successors-2B3>
2909
2908
2910 Here rev2 has two possible (i.e. divergent) successors sets. The first
2909 Here rev2 has two possible (i.e. divergent) successors sets. The first
2911 holds one element, whereas the second holds three (i.e. the changeset has
2910 holds one element, whereas the second holds three (i.e. the changeset has
2912 been split).
2911 been split).
2913 """
2912 """
2914 # passed to successorssets caching computation from one call to another
2913 # passed to successorssets caching computation from one call to another
2915 cache = {}
2914 cache = {}
2916 ctx2str = str
2915 ctx2str = str
2917 node2str = short
2916 node2str = short
2918 if ui.debug():
2917 if ui.debug():
2919 def ctx2str(ctx):
2918 def ctx2str(ctx):
2920 return ctx.hex()
2919 return ctx.hex()
2921 node2str = hex
2920 node2str = hex
2922 for rev in scmutil.revrange(repo, revs):
2921 for rev in scmutil.revrange(repo, revs):
2923 ctx = repo[rev]
2922 ctx = repo[rev]
2924 ui.write('%s\n'% ctx2str(ctx))
2923 ui.write('%s\n'% ctx2str(ctx))
2925 for succsset in obsolete.successorssets(repo, ctx.node(), cache):
2924 for succsset in obsolete.successorssets(repo, ctx.node(), cache):
2926 if succsset:
2925 if succsset:
2927 ui.write(' ')
2926 ui.write(' ')
2928 ui.write(node2str(succsset[0]))
2927 ui.write(node2str(succsset[0]))
2929 for node in succsset[1:]:
2928 for node in succsset[1:]:
2930 ui.write(' ')
2929 ui.write(' ')
2931 ui.write(node2str(node))
2930 ui.write(node2str(node))
2932 ui.write('\n')
2931 ui.write('\n')
2933
2932
2934 @command('debugtemplate',
2933 @command('debugtemplate',
2935 [('r', 'rev', [], _('apply template on changesets'), _('REV')),
2934 [('r', 'rev', [], _('apply template on changesets'), _('REV')),
2936 ('D', 'define', [], _('define template keyword'), _('KEY=VALUE'))],
2935 ('D', 'define', [], _('define template keyword'), _('KEY=VALUE'))],
2937 _('[-r REV]... [-D KEY=VALUE]... TEMPLATE'),
2936 _('[-r REV]... [-D KEY=VALUE]... TEMPLATE'),
2938 optionalrepo=True)
2937 optionalrepo=True)
2939 def debugtemplate(ui, repo, tmpl, **opts):
2938 def debugtemplate(ui, repo, tmpl, **opts):
2940 """parse and apply a template
2939 """parse and apply a template
2941
2940
2942 If -r/--rev is given, the template is processed as a log template and
2941 If -r/--rev is given, the template is processed as a log template and
2943 applied to the given changesets. Otherwise, it is processed as a generic
2942 applied to the given changesets. Otherwise, it is processed as a generic
2944 template.
2943 template.
2945
2944
2946 Use --verbose to print the parsed tree.
2945 Use --verbose to print the parsed tree.
2947 """
2946 """
2948 revs = None
2947 revs = None
2949 if opts['rev']:
2948 if opts['rev']:
2950 if repo is None:
2949 if repo is None:
2951 raise error.RepoError(_('there is no Mercurial repository here '
2950 raise error.RepoError(_('there is no Mercurial repository here '
2952 '(.hg not found)'))
2951 '(.hg not found)'))
2953 revs = scmutil.revrange(repo, opts['rev'])
2952 revs = scmutil.revrange(repo, opts['rev'])
2954
2953
2955 props = {}
2954 props = {}
2956 for d in opts['define']:
2955 for d in opts['define']:
2957 try:
2956 try:
2958 k, v = (e.strip() for e in d.split('=', 1))
2957 k, v = (e.strip() for e in d.split('=', 1))
2959 if not k:
2958 if not k:
2960 raise ValueError
2959 raise ValueError
2961 props[k] = v
2960 props[k] = v
2962 except ValueError:
2961 except ValueError:
2963 raise error.Abort(_('malformed keyword definition: %s') % d)
2962 raise error.Abort(_('malformed keyword definition: %s') % d)
2964
2963
2965 if ui.verbose:
2964 if ui.verbose:
2966 aliases = ui.configitems('templatealias')
2965 aliases = ui.configitems('templatealias')
2967 tree = templater.parse(tmpl)
2966 tree = templater.parse(tmpl)
2968 ui.note(templater.prettyformat(tree), '\n')
2967 ui.note(templater.prettyformat(tree), '\n')
2969 newtree = templater.expandaliases(tree, aliases)
2968 newtree = templater.expandaliases(tree, aliases)
2970 if newtree != tree:
2969 if newtree != tree:
2971 ui.note(("* expanded:\n"), templater.prettyformat(newtree), '\n')
2970 ui.note(("* expanded:\n"), templater.prettyformat(newtree), '\n')
2972
2971
2973 mapfile = None
2972 mapfile = None
2974 if revs is None:
2973 if revs is None:
2975 k = 'debugtemplate'
2974 k = 'debugtemplate'
2976 t = formatter.maketemplater(ui, k, tmpl)
2975 t = formatter.maketemplater(ui, k, tmpl)
2977 ui.write(templater.stringify(t(k, **props)))
2976 ui.write(templater.stringify(t(k, **props)))
2978 else:
2977 else:
2979 displayer = cmdutil.changeset_templater(ui, repo, None, opts, tmpl,
2978 displayer = cmdutil.changeset_templater(ui, repo, None, opts, tmpl,
2980 mapfile, buffered=False)
2979 mapfile, buffered=False)
2981 for r in revs:
2980 for r in revs:
2982 displayer.show(repo[r], **props)
2981 displayer.show(repo[r], **props)
2983 displayer.close()
2982 displayer.close()
2984
2983
2985 @command('debugwalk', walkopts, _('[OPTION]... [FILE]...'), inferrepo=True)
2984 @command('debugwalk', walkopts, _('[OPTION]... [FILE]...'), inferrepo=True)
2986 def debugwalk(ui, repo, *pats, **opts):
2985 def debugwalk(ui, repo, *pats, **opts):
2987 """show how files match on given patterns"""
2986 """show how files match on given patterns"""
2988 m = scmutil.match(repo[None], pats, opts)
2987 m = scmutil.match(repo[None], pats, opts)
2989 items = list(repo.walk(m))
2988 items = list(repo.walk(m))
2990 if not items:
2989 if not items:
2991 return
2990 return
2992 f = lambda fn: fn
2991 f = lambda fn: fn
2993 if ui.configbool('ui', 'slash') and pycompat.ossep != '/':
2992 if ui.configbool('ui', 'slash') and pycompat.ossep != '/':
2994 f = lambda fn: util.normpath(fn)
2993 f = lambda fn: util.normpath(fn)
2995 fmt = 'f %%-%ds %%-%ds %%s' % (
2994 fmt = 'f %%-%ds %%-%ds %%s' % (
2996 max([len(abs) for abs in items]),
2995 max([len(abs) for abs in items]),
2997 max([len(m.rel(abs)) for abs in items]))
2996 max([len(m.rel(abs)) for abs in items]))
2998 for abs in items:
2997 for abs in items:
2999 line = fmt % (abs, f(m.rel(abs)), m.exact(abs) and 'exact' or '')
2998 line = fmt % (abs, f(m.rel(abs)), m.exact(abs) and 'exact' or '')
3000 ui.write("%s\n" % line.rstrip())
2999 ui.write("%s\n" % line.rstrip())
3001
3000
3002 @command('debugwireargs',
3001 @command('debugwireargs',
3003 [('', 'three', '', 'three'),
3002 [('', 'three', '', 'three'),
3004 ('', 'four', '', 'four'),
3003 ('', 'four', '', 'four'),
3005 ('', 'five', '', 'five'),
3004 ('', 'five', '', 'five'),
3006 ] + remoteopts,
3005 ] + remoteopts,
3007 _('REPO [OPTIONS]... [ONE [TWO]]'),
3006 _('REPO [OPTIONS]... [ONE [TWO]]'),
3008 norepo=True)
3007 norepo=True)
3009 def debugwireargs(ui, repopath, *vals, **opts):
3008 def debugwireargs(ui, repopath, *vals, **opts):
3010 repo = hg.peer(ui, opts, repopath)
3009 repo = hg.peer(ui, opts, repopath)
3011 for opt in remoteopts:
3010 for opt in remoteopts:
3012 del opts[opt[1]]
3011 del opts[opt[1]]
3013 args = {}
3012 args = {}
3014 for k, v in opts.iteritems():
3013 for k, v in opts.iteritems():
3015 if v:
3014 if v:
3016 args[k] = v
3015 args[k] = v
3017 # run twice to check that we don't mess up the stream for the next command
3016 # run twice to check that we don't mess up the stream for the next command
3018 res1 = repo.debugwireargs(*vals, **args)
3017 res1 = repo.debugwireargs(*vals, **args)
3019 res2 = repo.debugwireargs(*vals, **args)
3018 res2 = repo.debugwireargs(*vals, **args)
3020 ui.write("%s\n" % res1)
3019 ui.write("%s\n" % res1)
3021 if res1 != res2:
3020 if res1 != res2:
3022 ui.warn("%s\n" % res2)
3021 ui.warn("%s\n" % res2)
3023
3022
3024 @command('^diff',
3023 @command('^diff',
3025 [('r', 'rev', [], _('revision'), _('REV')),
3024 [('r', 'rev', [], _('revision'), _('REV')),
3026 ('c', 'change', '', _('change made by revision'), _('REV'))
3025 ('c', 'change', '', _('change made by revision'), _('REV'))
3027 ] + diffopts + diffopts2 + walkopts + subrepoopts,
3026 ] + diffopts + diffopts2 + walkopts + subrepoopts,
3028 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
3027 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
3029 inferrepo=True)
3028 inferrepo=True)
3030 def diff(ui, repo, *pats, **opts):
3029 def diff(ui, repo, *pats, **opts):
3031 """diff repository (or selected files)
3030 """diff repository (or selected files)
3032
3031
3033 Show differences between revisions for the specified files.
3032 Show differences between revisions for the specified files.
3034
3033
3035 Differences between files are shown using the unified diff format.
3034 Differences between files are shown using the unified diff format.
3036
3035
3037 .. note::
3036 .. note::
3038
3037
3039 :hg:`diff` may generate unexpected results for merges, as it will
3038 :hg:`diff` may generate unexpected results for merges, as it will
3040 default to comparing against the working directory's first
3039 default to comparing against the working directory's first
3041 parent changeset if no revisions are specified.
3040 parent changeset if no revisions are specified.
3042
3041
3043 When two revision arguments are given, then changes are shown
3042 When two revision arguments are given, then changes are shown
3044 between those revisions. If only one revision is specified then
3043 between those revisions. If only one revision is specified then
3045 that revision is compared to the working directory, and, when no
3044 that revision is compared to the working directory, and, when no
3046 revisions are specified, the working directory files are compared
3045 revisions are specified, the working directory files are compared
3047 to its first parent.
3046 to its first parent.
3048
3047
3049 Alternatively you can specify -c/--change with a revision to see
3048 Alternatively you can specify -c/--change with a revision to see
3050 the changes in that changeset relative to its first parent.
3049 the changes in that changeset relative to its first parent.
3051
3050
3052 Without the -a/--text option, diff will avoid generating diffs of
3051 Without the -a/--text option, diff will avoid generating diffs of
3053 files it detects as binary. With -a, diff will generate a diff
3052 files it detects as binary. With -a, diff will generate a diff
3054 anyway, probably with undesirable results.
3053 anyway, probably with undesirable results.
3055
3054
3056 Use the -g/--git option to generate diffs in the git extended diff
3055 Use the -g/--git option to generate diffs in the git extended diff
3057 format. For more information, read :hg:`help diffs`.
3056 format. For more information, read :hg:`help diffs`.
3058
3057
3059 .. container:: verbose
3058 .. container:: verbose
3060
3059
3061 Examples:
3060 Examples:
3062
3061
3063 - compare a file in the current working directory to its parent::
3062 - compare a file in the current working directory to its parent::
3064
3063
3065 hg diff foo.c
3064 hg diff foo.c
3066
3065
3067 - compare two historical versions of a directory, with rename info::
3066 - compare two historical versions of a directory, with rename info::
3068
3067
3069 hg diff --git -r 1.0:1.2 lib/
3068 hg diff --git -r 1.0:1.2 lib/
3070
3069
3071 - get change stats relative to the last change on some date::
3070 - get change stats relative to the last change on some date::
3072
3071
3073 hg diff --stat -r "date('may 2')"
3072 hg diff --stat -r "date('may 2')"
3074
3073
3075 - diff all newly-added files that contain a keyword::
3074 - diff all newly-added files that contain a keyword::
3076
3075
3077 hg diff "set:added() and grep(GNU)"
3076 hg diff "set:added() and grep(GNU)"
3078
3077
3079 - compare a revision and its parents::
3078 - compare a revision and its parents::
3080
3079
3081 hg diff -c 9353 # compare against first parent
3080 hg diff -c 9353 # compare against first parent
3082 hg diff -r 9353^:9353 # same using revset syntax
3081 hg diff -r 9353^:9353 # same using revset syntax
3083 hg diff -r 9353^2:9353 # compare against the second parent
3082 hg diff -r 9353^2:9353 # compare against the second parent
3084
3083
3085 Returns 0 on success.
3084 Returns 0 on success.
3086 """
3085 """
3087
3086
3088 revs = opts.get('rev')
3087 revs = opts.get('rev')
3089 change = opts.get('change')
3088 change = opts.get('change')
3090 stat = opts.get('stat')
3089 stat = opts.get('stat')
3091 reverse = opts.get('reverse')
3090 reverse = opts.get('reverse')
3092
3091
3093 if revs and change:
3092 if revs and change:
3094 msg = _('cannot specify --rev and --change at the same time')
3093 msg = _('cannot specify --rev and --change at the same time')
3095 raise error.Abort(msg)
3094 raise error.Abort(msg)
3096 elif change:
3095 elif change:
3097 node2 = scmutil.revsingle(repo, change, None).node()
3096 node2 = scmutil.revsingle(repo, change, None).node()
3098 node1 = repo[node2].p1().node()
3097 node1 = repo[node2].p1().node()
3099 else:
3098 else:
3100 node1, node2 = scmutil.revpair(repo, revs)
3099 node1, node2 = scmutil.revpair(repo, revs)
3101
3100
3102 if reverse:
3101 if reverse:
3103 node1, node2 = node2, node1
3102 node1, node2 = node2, node1
3104
3103
3105 diffopts = patch.diffallopts(ui, opts)
3104 diffopts = patch.diffallopts(ui, opts)
3106 m = scmutil.match(repo[node2], pats, opts)
3105 m = scmutil.match(repo[node2], pats, opts)
3107 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
3106 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
3108 listsubrepos=opts.get('subrepos'),
3107 listsubrepos=opts.get('subrepos'),
3109 root=opts.get('root'))
3108 root=opts.get('root'))
3110
3109
3111 @command('^export',
3110 @command('^export',
3112 [('o', 'output', '',
3111 [('o', 'output', '',
3113 _('print output to file with formatted name'), _('FORMAT')),
3112 _('print output to file with formatted name'), _('FORMAT')),
3114 ('', 'switch-parent', None, _('diff against the second parent')),
3113 ('', 'switch-parent', None, _('diff against the second parent')),
3115 ('r', 'rev', [], _('revisions to export'), _('REV')),
3114 ('r', 'rev', [], _('revisions to export'), _('REV')),
3116 ] + diffopts,
3115 ] + diffopts,
3117 _('[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'))
3116 _('[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'))
3118 def export(ui, repo, *changesets, **opts):
3117 def export(ui, repo, *changesets, **opts):
3119 """dump the header and diffs for one or more changesets
3118 """dump the header and diffs for one or more changesets
3120
3119
3121 Print the changeset header and diffs for one or more revisions.
3120 Print the changeset header and diffs for one or more revisions.
3122 If no revision is given, the parent of the working directory is used.
3121 If no revision is given, the parent of the working directory is used.
3123
3122
3124 The information shown in the changeset header is: author, date,
3123 The information shown in the changeset header is: author, date,
3125 branch name (if non-default), changeset hash, parent(s) and commit
3124 branch name (if non-default), changeset hash, parent(s) and commit
3126 comment.
3125 comment.
3127
3126
3128 .. note::
3127 .. note::
3129
3128
3130 :hg:`export` may generate unexpected diff output for merge
3129 :hg:`export` may generate unexpected diff output for merge
3131 changesets, as it will compare the merge changeset against its
3130 changesets, as it will compare the merge changeset against its
3132 first parent only.
3131 first parent only.
3133
3132
3134 Output may be to a file, in which case the name of the file is
3133 Output may be to a file, in which case the name of the file is
3135 given using a format string. The formatting rules are as follows:
3134 given using a format string. The formatting rules are as follows:
3136
3135
3137 :``%%``: literal "%" character
3136 :``%%``: literal "%" character
3138 :``%H``: changeset hash (40 hexadecimal digits)
3137 :``%H``: changeset hash (40 hexadecimal digits)
3139 :``%N``: number of patches being generated
3138 :``%N``: number of patches being generated
3140 :``%R``: changeset revision number
3139 :``%R``: changeset revision number
3141 :``%b``: basename of the exporting repository
3140 :``%b``: basename of the exporting repository
3142 :``%h``: short-form changeset hash (12 hexadecimal digits)
3141 :``%h``: short-form changeset hash (12 hexadecimal digits)
3143 :``%m``: first line of the commit message (only alphanumeric characters)
3142 :``%m``: first line of the commit message (only alphanumeric characters)
3144 :``%n``: zero-padded sequence number, starting at 1
3143 :``%n``: zero-padded sequence number, starting at 1
3145 :``%r``: zero-padded changeset revision number
3144 :``%r``: zero-padded changeset revision number
3146
3145
3147 Without the -a/--text option, export will avoid generating diffs
3146 Without the -a/--text option, export will avoid generating diffs
3148 of files it detects as binary. With -a, export will generate a
3147 of files it detects as binary. With -a, export will generate a
3149 diff anyway, probably with undesirable results.
3148 diff anyway, probably with undesirable results.
3150
3149
3151 Use the -g/--git option to generate diffs in the git extended diff
3150 Use the -g/--git option to generate diffs in the git extended diff
3152 format. See :hg:`help diffs` for more information.
3151 format. See :hg:`help diffs` for more information.
3153
3152
3154 With the --switch-parent option, the diff will be against the
3153 With the --switch-parent option, the diff will be against the
3155 second parent. It can be useful to review a merge.
3154 second parent. It can be useful to review a merge.
3156
3155
3157 .. container:: verbose
3156 .. container:: verbose
3158
3157
3159 Examples:
3158 Examples:
3160
3159
3161 - use export and import to transplant a bugfix to the current
3160 - use export and import to transplant a bugfix to the current
3162 branch::
3161 branch::
3163
3162
3164 hg export -r 9353 | hg import -
3163 hg export -r 9353 | hg import -
3165
3164
3166 - export all the changesets between two revisions to a file with
3165 - export all the changesets between two revisions to a file with
3167 rename information::
3166 rename information::
3168
3167
3169 hg export --git -r 123:150 > changes.txt
3168 hg export --git -r 123:150 > changes.txt
3170
3169
3171 - split outgoing changes into a series of patches with
3170 - split outgoing changes into a series of patches with
3172 descriptive names::
3171 descriptive names::
3173
3172
3174 hg export -r "outgoing()" -o "%n-%m.patch"
3173 hg export -r "outgoing()" -o "%n-%m.patch"
3175
3174
3176 Returns 0 on success.
3175 Returns 0 on success.
3177 """
3176 """
3178 changesets += tuple(opts.get('rev', []))
3177 changesets += tuple(opts.get('rev', []))
3179 if not changesets:
3178 if not changesets:
3180 changesets = ['.']
3179 changesets = ['.']
3181 revs = scmutil.revrange(repo, changesets)
3180 revs = scmutil.revrange(repo, changesets)
3182 if not revs:
3181 if not revs:
3183 raise error.Abort(_("export requires at least one changeset"))
3182 raise error.Abort(_("export requires at least one changeset"))
3184 if len(revs) > 1:
3183 if len(revs) > 1:
3185 ui.note(_('exporting patches:\n'))
3184 ui.note(_('exporting patches:\n'))
3186 else:
3185 else:
3187 ui.note(_('exporting patch:\n'))
3186 ui.note(_('exporting patch:\n'))
3188 cmdutil.export(repo, revs, template=opts.get('output'),
3187 cmdutil.export(repo, revs, template=opts.get('output'),
3189 switch_parent=opts.get('switch_parent'),
3188 switch_parent=opts.get('switch_parent'),
3190 opts=patch.diffallopts(ui, opts))
3189 opts=patch.diffallopts(ui, opts))
3191
3190
3192 @command('files',
3191 @command('files',
3193 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3192 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3194 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3193 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3195 ] + walkopts + formatteropts + subrepoopts,
3194 ] + walkopts + formatteropts + subrepoopts,
3196 _('[OPTION]... [FILE]...'))
3195 _('[OPTION]... [FILE]...'))
3197 def files(ui, repo, *pats, **opts):
3196 def files(ui, repo, *pats, **opts):
3198 """list tracked files
3197 """list tracked files
3199
3198
3200 Print files under Mercurial control in the working directory or
3199 Print files under Mercurial control in the working directory or
3201 specified revision for given files (excluding removed files).
3200 specified revision for given files (excluding removed files).
3202 Files can be specified as filenames or filesets.
3201 Files can be specified as filenames or filesets.
3203
3202
3204 If no files are given to match, this command prints the names
3203 If no files are given to match, this command prints the names
3205 of all files under Mercurial control.
3204 of all files under Mercurial control.
3206
3205
3207 .. container:: verbose
3206 .. container:: verbose
3208
3207
3209 Examples:
3208 Examples:
3210
3209
3211 - list all files under the current directory::
3210 - list all files under the current directory::
3212
3211
3213 hg files .
3212 hg files .
3214
3213
3215 - shows sizes and flags for current revision::
3214 - shows sizes and flags for current revision::
3216
3215
3217 hg files -vr .
3216 hg files -vr .
3218
3217
3219 - list all files named README::
3218 - list all files named README::
3220
3219
3221 hg files -I "**/README"
3220 hg files -I "**/README"
3222
3221
3223 - list all binary files::
3222 - list all binary files::
3224
3223
3225 hg files "set:binary()"
3224 hg files "set:binary()"
3226
3225
3227 - find files containing a regular expression::
3226 - find files containing a regular expression::
3228
3227
3229 hg files "set:grep('bob')"
3228 hg files "set:grep('bob')"
3230
3229
3231 - search tracked file contents with xargs and grep::
3230 - search tracked file contents with xargs and grep::
3232
3231
3233 hg files -0 | xargs -0 grep foo
3232 hg files -0 | xargs -0 grep foo
3234
3233
3235 See :hg:`help patterns` and :hg:`help filesets` for more information
3234 See :hg:`help patterns` and :hg:`help filesets` for more information
3236 on specifying file patterns.
3235 on specifying file patterns.
3237
3236
3238 Returns 0 if a match is found, 1 otherwise.
3237 Returns 0 if a match is found, 1 otherwise.
3239
3238
3240 """
3239 """
3241 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
3240 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
3242
3241
3243 end = '\n'
3242 end = '\n'
3244 if opts.get('print0'):
3243 if opts.get('print0'):
3245 end = '\0'
3244 end = '\0'
3246 fmt = '%s' + end
3245 fmt = '%s' + end
3247
3246
3248 m = scmutil.match(ctx, pats, opts)
3247 m = scmutil.match(ctx, pats, opts)
3249 with ui.formatter('files', opts) as fm:
3248 with ui.formatter('files', opts) as fm:
3250 return cmdutil.files(ui, ctx, m, fm, fmt, opts.get('subrepos'))
3249 return cmdutil.files(ui, ctx, m, fm, fmt, opts.get('subrepos'))
3251
3250
3252 @command('^forget', walkopts, _('[OPTION]... FILE...'), inferrepo=True)
3251 @command('^forget', walkopts, _('[OPTION]... FILE...'), inferrepo=True)
3253 def forget(ui, repo, *pats, **opts):
3252 def forget(ui, repo, *pats, **opts):
3254 """forget the specified files on the next commit
3253 """forget the specified files on the next commit
3255
3254
3256 Mark the specified files so they will no longer be tracked
3255 Mark the specified files so they will no longer be tracked
3257 after the next commit.
3256 after the next commit.
3258
3257
3259 This only removes files from the current branch, not from the
3258 This only removes files from the current branch, not from the
3260 entire project history, and it does not delete them from the
3259 entire project history, and it does not delete them from the
3261 working directory.
3260 working directory.
3262
3261
3263 To delete the file from the working directory, see :hg:`remove`.
3262 To delete the file from the working directory, see :hg:`remove`.
3264
3263
3265 To undo a forget before the next commit, see :hg:`add`.
3264 To undo a forget before the next commit, see :hg:`add`.
3266
3265
3267 .. container:: verbose
3266 .. container:: verbose
3268
3267
3269 Examples:
3268 Examples:
3270
3269
3271 - forget newly-added binary files::
3270 - forget newly-added binary files::
3272
3271
3273 hg forget "set:added() and binary()"
3272 hg forget "set:added() and binary()"
3274
3273
3275 - forget files that would be excluded by .hgignore::
3274 - forget files that would be excluded by .hgignore::
3276
3275
3277 hg forget "set:hgignore()"
3276 hg forget "set:hgignore()"
3278
3277
3279 Returns 0 on success.
3278 Returns 0 on success.
3280 """
3279 """
3281
3280
3282 if not pats:
3281 if not pats:
3283 raise error.Abort(_('no files specified'))
3282 raise error.Abort(_('no files specified'))
3284
3283
3285 m = scmutil.match(repo[None], pats, opts)
3284 m = scmutil.match(repo[None], pats, opts)
3286 rejected = cmdutil.forget(ui, repo, m, prefix="", explicitonly=False)[0]
3285 rejected = cmdutil.forget(ui, repo, m, prefix="", explicitonly=False)[0]
3287 return rejected and 1 or 0
3286 return rejected and 1 or 0
3288
3287
3289 @command(
3288 @command(
3290 'graft',
3289 'graft',
3291 [('r', 'rev', [], _('revisions to graft'), _('REV')),
3290 [('r', 'rev', [], _('revisions to graft'), _('REV')),
3292 ('c', 'continue', False, _('resume interrupted graft')),
3291 ('c', 'continue', False, _('resume interrupted graft')),
3293 ('e', 'edit', False, _('invoke editor on commit messages')),
3292 ('e', 'edit', False, _('invoke editor on commit messages')),
3294 ('', 'log', None, _('append graft info to log message')),
3293 ('', 'log', None, _('append graft info to log message')),
3295 ('f', 'force', False, _('force graft')),
3294 ('f', 'force', False, _('force graft')),
3296 ('D', 'currentdate', False,
3295 ('D', 'currentdate', False,
3297 _('record the current date as commit date')),
3296 _('record the current date as commit date')),
3298 ('U', 'currentuser', False,
3297 ('U', 'currentuser', False,
3299 _('record the current user as committer'), _('DATE'))]
3298 _('record the current user as committer'), _('DATE'))]
3300 + commitopts2 + mergetoolopts + dryrunopts,
3299 + commitopts2 + mergetoolopts + dryrunopts,
3301 _('[OPTION]... [-r REV]... REV...'))
3300 _('[OPTION]... [-r REV]... REV...'))
3302 def graft(ui, repo, *revs, **opts):
3301 def graft(ui, repo, *revs, **opts):
3303 '''copy changes from other branches onto the current branch
3302 '''copy changes from other branches onto the current branch
3304
3303
3305 This command uses Mercurial's merge logic to copy individual
3304 This command uses Mercurial's merge logic to copy individual
3306 changes from other branches without merging branches in the
3305 changes from other branches without merging branches in the
3307 history graph. This is sometimes known as 'backporting' or
3306 history graph. This is sometimes known as 'backporting' or
3308 'cherry-picking'. By default, graft will copy user, date, and
3307 'cherry-picking'. By default, graft will copy user, date, and
3309 description from the source changesets.
3308 description from the source changesets.
3310
3309
3311 Changesets that are ancestors of the current revision, that have
3310 Changesets that are ancestors of the current revision, that have
3312 already been grafted, or that are merges will be skipped.
3311 already been grafted, or that are merges will be skipped.
3313
3312
3314 If --log is specified, log messages will have a comment appended
3313 If --log is specified, log messages will have a comment appended
3315 of the form::
3314 of the form::
3316
3315
3317 (grafted from CHANGESETHASH)
3316 (grafted from CHANGESETHASH)
3318
3317
3319 If --force is specified, revisions will be grafted even if they
3318 If --force is specified, revisions will be grafted even if they
3320 are already ancestors of or have been grafted to the destination.
3319 are already ancestors of or have been grafted to the destination.
3321 This is useful when the revisions have since been backed out.
3320 This is useful when the revisions have since been backed out.
3322
3321
3323 If a graft merge results in conflicts, the graft process is
3322 If a graft merge results in conflicts, the graft process is
3324 interrupted so that the current merge can be manually resolved.
3323 interrupted so that the current merge can be manually resolved.
3325 Once all conflicts are addressed, the graft process can be
3324 Once all conflicts are addressed, the graft process can be
3326 continued with the -c/--continue option.
3325 continued with the -c/--continue option.
3327
3326
3328 .. note::
3327 .. note::
3329
3328
3330 The -c/--continue option does not reapply earlier options, except
3329 The -c/--continue option does not reapply earlier options, except
3331 for --force.
3330 for --force.
3332
3331
3333 .. container:: verbose
3332 .. container:: verbose
3334
3333
3335 Examples:
3334 Examples:
3336
3335
3337 - copy a single change to the stable branch and edit its description::
3336 - copy a single change to the stable branch and edit its description::
3338
3337
3339 hg update stable
3338 hg update stable
3340 hg graft --edit 9393
3339 hg graft --edit 9393
3341
3340
3342 - graft a range of changesets with one exception, updating dates::
3341 - graft a range of changesets with one exception, updating dates::
3343
3342
3344 hg graft -D "2085::2093 and not 2091"
3343 hg graft -D "2085::2093 and not 2091"
3345
3344
3346 - continue a graft after resolving conflicts::
3345 - continue a graft after resolving conflicts::
3347
3346
3348 hg graft -c
3347 hg graft -c
3349
3348
3350 - show the source of a grafted changeset::
3349 - show the source of a grafted changeset::
3351
3350
3352 hg log --debug -r .
3351 hg log --debug -r .
3353
3352
3354 - show revisions sorted by date::
3353 - show revisions sorted by date::
3355
3354
3356 hg log -r "sort(all(), date)"
3355 hg log -r "sort(all(), date)"
3357
3356
3358 See :hg:`help revisions` and :hg:`help revsets` for more about
3357 See :hg:`help revisions` and :hg:`help revsets` for more about
3359 specifying revisions.
3358 specifying revisions.
3360
3359
3361 Returns 0 on successful completion.
3360 Returns 0 on successful completion.
3362 '''
3361 '''
3363 with repo.wlock():
3362 with repo.wlock():
3364 return _dograft(ui, repo, *revs, **opts)
3363 return _dograft(ui, repo, *revs, **opts)
3365
3364
3366 def _dograft(ui, repo, *revs, **opts):
3365 def _dograft(ui, repo, *revs, **opts):
3367 if revs and opts.get('rev'):
3366 if revs and opts.get('rev'):
3368 ui.warn(_('warning: inconsistent use of --rev might give unexpected '
3367 ui.warn(_('warning: inconsistent use of --rev might give unexpected '
3369 'revision ordering!\n'))
3368 'revision ordering!\n'))
3370
3369
3371 revs = list(revs)
3370 revs = list(revs)
3372 revs.extend(opts.get('rev'))
3371 revs.extend(opts.get('rev'))
3373
3372
3374 if not opts.get('user') and opts.get('currentuser'):
3373 if not opts.get('user') and opts.get('currentuser'):
3375 opts['user'] = ui.username()
3374 opts['user'] = ui.username()
3376 if not opts.get('date') and opts.get('currentdate'):
3375 if not opts.get('date') and opts.get('currentdate'):
3377 opts['date'] = "%d %d" % util.makedate()
3376 opts['date'] = "%d %d" % util.makedate()
3378
3377
3379 editor = cmdutil.getcommiteditor(editform='graft', **opts)
3378 editor = cmdutil.getcommiteditor(editform='graft', **opts)
3380
3379
3381 cont = False
3380 cont = False
3382 if opts.get('continue'):
3381 if opts.get('continue'):
3383 cont = True
3382 cont = True
3384 if revs:
3383 if revs:
3385 raise error.Abort(_("can't specify --continue and revisions"))
3384 raise error.Abort(_("can't specify --continue and revisions"))
3386 # read in unfinished revisions
3385 # read in unfinished revisions
3387 try:
3386 try:
3388 nodes = repo.vfs.read('graftstate').splitlines()
3387 nodes = repo.vfs.read('graftstate').splitlines()
3389 revs = [repo[node].rev() for node in nodes]
3388 revs = [repo[node].rev() for node in nodes]
3390 except IOError as inst:
3389 except IOError as inst:
3391 if inst.errno != errno.ENOENT:
3390 if inst.errno != errno.ENOENT:
3392 raise
3391 raise
3393 cmdutil.wrongtooltocontinue(repo, _('graft'))
3392 cmdutil.wrongtooltocontinue(repo, _('graft'))
3394 else:
3393 else:
3395 cmdutil.checkunfinished(repo)
3394 cmdutil.checkunfinished(repo)
3396 cmdutil.bailifchanged(repo)
3395 cmdutil.bailifchanged(repo)
3397 if not revs:
3396 if not revs:
3398 raise error.Abort(_('no revisions specified'))
3397 raise error.Abort(_('no revisions specified'))
3399 revs = scmutil.revrange(repo, revs)
3398 revs = scmutil.revrange(repo, revs)
3400
3399
3401 skipped = set()
3400 skipped = set()
3402 # check for merges
3401 # check for merges
3403 for rev in repo.revs('%ld and merge()', revs):
3402 for rev in repo.revs('%ld and merge()', revs):
3404 ui.warn(_('skipping ungraftable merge revision %s\n') % rev)
3403 ui.warn(_('skipping ungraftable merge revision %s\n') % rev)
3405 skipped.add(rev)
3404 skipped.add(rev)
3406 revs = [r for r in revs if r not in skipped]
3405 revs = [r for r in revs if r not in skipped]
3407 if not revs:
3406 if not revs:
3408 return -1
3407 return -1
3409
3408
3410 # Don't check in the --continue case, in effect retaining --force across
3409 # Don't check in the --continue case, in effect retaining --force across
3411 # --continues. That's because without --force, any revisions we decided to
3410 # --continues. That's because without --force, any revisions we decided to
3412 # skip would have been filtered out here, so they wouldn't have made their
3411 # skip would have been filtered out here, so they wouldn't have made their
3413 # way to the graftstate. With --force, any revisions we would have otherwise
3412 # way to the graftstate. With --force, any revisions we would have otherwise
3414 # skipped would not have been filtered out, and if they hadn't been applied
3413 # skipped would not have been filtered out, and if they hadn't been applied
3415 # already, they'd have been in the graftstate.
3414 # already, they'd have been in the graftstate.
3416 if not (cont or opts.get('force')):
3415 if not (cont or opts.get('force')):
3417 # check for ancestors of dest branch
3416 # check for ancestors of dest branch
3418 crev = repo['.'].rev()
3417 crev = repo['.'].rev()
3419 ancestors = repo.changelog.ancestors([crev], inclusive=True)
3418 ancestors = repo.changelog.ancestors([crev], inclusive=True)
3420 # XXX make this lazy in the future
3419 # XXX make this lazy in the future
3421 # don't mutate while iterating, create a copy
3420 # don't mutate while iterating, create a copy
3422 for rev in list(revs):
3421 for rev in list(revs):
3423 if rev in ancestors:
3422 if rev in ancestors:
3424 ui.warn(_('skipping ancestor revision %d:%s\n') %
3423 ui.warn(_('skipping ancestor revision %d:%s\n') %
3425 (rev, repo[rev]))
3424 (rev, repo[rev]))
3426 # XXX remove on list is slow
3425 # XXX remove on list is slow
3427 revs.remove(rev)
3426 revs.remove(rev)
3428 if not revs:
3427 if not revs:
3429 return -1
3428 return -1
3430
3429
3431 # analyze revs for earlier grafts
3430 # analyze revs for earlier grafts
3432 ids = {}
3431 ids = {}
3433 for ctx in repo.set("%ld", revs):
3432 for ctx in repo.set("%ld", revs):
3434 ids[ctx.hex()] = ctx.rev()
3433 ids[ctx.hex()] = ctx.rev()
3435 n = ctx.extra().get('source')
3434 n = ctx.extra().get('source')
3436 if n:
3435 if n:
3437 ids[n] = ctx.rev()
3436 ids[n] = ctx.rev()
3438
3437
3439 # check ancestors for earlier grafts
3438 # check ancestors for earlier grafts
3440 ui.debug('scanning for duplicate grafts\n')
3439 ui.debug('scanning for duplicate grafts\n')
3441
3440
3442 for rev in repo.changelog.findmissingrevs(revs, [crev]):
3441 for rev in repo.changelog.findmissingrevs(revs, [crev]):
3443 ctx = repo[rev]
3442 ctx = repo[rev]
3444 n = ctx.extra().get('source')
3443 n = ctx.extra().get('source')
3445 if n in ids:
3444 if n in ids:
3446 try:
3445 try:
3447 r = repo[n].rev()
3446 r = repo[n].rev()
3448 except error.RepoLookupError:
3447 except error.RepoLookupError:
3449 r = None
3448 r = None
3450 if r in revs:
3449 if r in revs:
3451 ui.warn(_('skipping revision %d:%s '
3450 ui.warn(_('skipping revision %d:%s '
3452 '(already grafted to %d:%s)\n')
3451 '(already grafted to %d:%s)\n')
3453 % (r, repo[r], rev, ctx))
3452 % (r, repo[r], rev, ctx))
3454 revs.remove(r)
3453 revs.remove(r)
3455 elif ids[n] in revs:
3454 elif ids[n] in revs:
3456 if r is None:
3455 if r is None:
3457 ui.warn(_('skipping already grafted revision %d:%s '
3456 ui.warn(_('skipping already grafted revision %d:%s '
3458 '(%d:%s also has unknown origin %s)\n')
3457 '(%d:%s also has unknown origin %s)\n')
3459 % (ids[n], repo[ids[n]], rev, ctx, n[:12]))
3458 % (ids[n], repo[ids[n]], rev, ctx, n[:12]))
3460 else:
3459 else:
3461 ui.warn(_('skipping already grafted revision %d:%s '
3460 ui.warn(_('skipping already grafted revision %d:%s '
3462 '(%d:%s also has origin %d:%s)\n')
3461 '(%d:%s also has origin %d:%s)\n')
3463 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12]))
3462 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12]))
3464 revs.remove(ids[n])
3463 revs.remove(ids[n])
3465 elif ctx.hex() in ids:
3464 elif ctx.hex() in ids:
3466 r = ids[ctx.hex()]
3465 r = ids[ctx.hex()]
3467 ui.warn(_('skipping already grafted revision %d:%s '
3466 ui.warn(_('skipping already grafted revision %d:%s '
3468 '(was grafted from %d:%s)\n') %
3467 '(was grafted from %d:%s)\n') %
3469 (r, repo[r], rev, ctx))
3468 (r, repo[r], rev, ctx))
3470 revs.remove(r)
3469 revs.remove(r)
3471 if not revs:
3470 if not revs:
3472 return -1
3471 return -1
3473
3472
3474 for pos, ctx in enumerate(repo.set("%ld", revs)):
3473 for pos, ctx in enumerate(repo.set("%ld", revs)):
3475 desc = '%d:%s "%s"' % (ctx.rev(), ctx,
3474 desc = '%d:%s "%s"' % (ctx.rev(), ctx,
3476 ctx.description().split('\n', 1)[0])
3475 ctx.description().split('\n', 1)[0])
3477 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
3476 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
3478 if names:
3477 if names:
3479 desc += ' (%s)' % ' '.join(names)
3478 desc += ' (%s)' % ' '.join(names)
3480 ui.status(_('grafting %s\n') % desc)
3479 ui.status(_('grafting %s\n') % desc)
3481 if opts.get('dry_run'):
3480 if opts.get('dry_run'):
3482 continue
3481 continue
3483
3482
3484 source = ctx.extra().get('source')
3483 source = ctx.extra().get('source')
3485 extra = {}
3484 extra = {}
3486 if source:
3485 if source:
3487 extra['source'] = source
3486 extra['source'] = source
3488 extra['intermediate-source'] = ctx.hex()
3487 extra['intermediate-source'] = ctx.hex()
3489 else:
3488 else:
3490 extra['source'] = ctx.hex()
3489 extra['source'] = ctx.hex()
3491 user = ctx.user()
3490 user = ctx.user()
3492 if opts.get('user'):
3491 if opts.get('user'):
3493 user = opts['user']
3492 user = opts['user']
3494 date = ctx.date()
3493 date = ctx.date()
3495 if opts.get('date'):
3494 if opts.get('date'):
3496 date = opts['date']
3495 date = opts['date']
3497 message = ctx.description()
3496 message = ctx.description()
3498 if opts.get('log'):
3497 if opts.get('log'):
3499 message += '\n(grafted from %s)' % ctx.hex()
3498 message += '\n(grafted from %s)' % ctx.hex()
3500
3499
3501 # we don't merge the first commit when continuing
3500 # we don't merge the first commit when continuing
3502 if not cont:
3501 if not cont:
3503 # perform the graft merge with p1(rev) as 'ancestor'
3502 # perform the graft merge with p1(rev) as 'ancestor'
3504 try:
3503 try:
3505 # ui.forcemerge is an internal variable, do not document
3504 # ui.forcemerge is an internal variable, do not document
3506 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
3505 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
3507 'graft')
3506 'graft')
3508 stats = mergemod.graft(repo, ctx, ctx.p1(),
3507 stats = mergemod.graft(repo, ctx, ctx.p1(),
3509 ['local', 'graft'])
3508 ['local', 'graft'])
3510 finally:
3509 finally:
3511 repo.ui.setconfig('ui', 'forcemerge', '', 'graft')
3510 repo.ui.setconfig('ui', 'forcemerge', '', 'graft')
3512 # report any conflicts
3511 # report any conflicts
3513 if stats and stats[3] > 0:
3512 if stats and stats[3] > 0:
3514 # write out state for --continue
3513 # write out state for --continue
3515 nodelines = [repo[rev].hex() + "\n" for rev in revs[pos:]]
3514 nodelines = [repo[rev].hex() + "\n" for rev in revs[pos:]]
3516 repo.vfs.write('graftstate', ''.join(nodelines))
3515 repo.vfs.write('graftstate', ''.join(nodelines))
3517 extra = ''
3516 extra = ''
3518 if opts.get('user'):
3517 if opts.get('user'):
3519 extra += ' --user %s' % util.shellquote(opts['user'])
3518 extra += ' --user %s' % util.shellquote(opts['user'])
3520 if opts.get('date'):
3519 if opts.get('date'):
3521 extra += ' --date %s' % util.shellquote(opts['date'])
3520 extra += ' --date %s' % util.shellquote(opts['date'])
3522 if opts.get('log'):
3521 if opts.get('log'):
3523 extra += ' --log'
3522 extra += ' --log'
3524 hint=_("use 'hg resolve' and 'hg graft --continue%s'") % extra
3523 hint=_("use 'hg resolve' and 'hg graft --continue%s'") % extra
3525 raise error.Abort(
3524 raise error.Abort(
3526 _("unresolved conflicts, can't continue"),
3525 _("unresolved conflicts, can't continue"),
3527 hint=hint)
3526 hint=hint)
3528 else:
3527 else:
3529 cont = False
3528 cont = False
3530
3529
3531 # commit
3530 # commit
3532 node = repo.commit(text=message, user=user,
3531 node = repo.commit(text=message, user=user,
3533 date=date, extra=extra, editor=editor)
3532 date=date, extra=extra, editor=editor)
3534 if node is None:
3533 if node is None:
3535 ui.warn(
3534 ui.warn(
3536 _('note: graft of %d:%s created no changes to commit\n') %
3535 _('note: graft of %d:%s created no changes to commit\n') %
3537 (ctx.rev(), ctx))
3536 (ctx.rev(), ctx))
3538
3537
3539 # remove state when we complete successfully
3538 # remove state when we complete successfully
3540 if not opts.get('dry_run'):
3539 if not opts.get('dry_run'):
3541 util.unlinkpath(repo.join('graftstate'), ignoremissing=True)
3540 util.unlinkpath(repo.join('graftstate'), ignoremissing=True)
3542
3541
3543 return 0
3542 return 0
3544
3543
3545 @command('grep',
3544 @command('grep',
3546 [('0', 'print0', None, _('end fields with NUL')),
3545 [('0', 'print0', None, _('end fields with NUL')),
3547 ('', 'all', None, _('print all revisions that match')),
3546 ('', 'all', None, _('print all revisions that match')),
3548 ('a', 'text', None, _('treat all files as text')),
3547 ('a', 'text', None, _('treat all files as text')),
3549 ('f', 'follow', None,
3548 ('f', 'follow', None,
3550 _('follow changeset history,'
3549 _('follow changeset history,'
3551 ' or file history across copies and renames')),
3550 ' or file history across copies and renames')),
3552 ('i', 'ignore-case', None, _('ignore case when matching')),
3551 ('i', 'ignore-case', None, _('ignore case when matching')),
3553 ('l', 'files-with-matches', None,
3552 ('l', 'files-with-matches', None,
3554 _('print only filenames and revisions that match')),
3553 _('print only filenames and revisions that match')),
3555 ('n', 'line-number', None, _('print matching line numbers')),
3554 ('n', 'line-number', None, _('print matching line numbers')),
3556 ('r', 'rev', [],
3555 ('r', 'rev', [],
3557 _('only search files changed within revision range'), _('REV')),
3556 _('only search files changed within revision range'), _('REV')),
3558 ('u', 'user', None, _('list the author (long with -v)')),
3557 ('u', 'user', None, _('list the author (long with -v)')),
3559 ('d', 'date', None, _('list the date (short with -q)')),
3558 ('d', 'date', None, _('list the date (short with -q)')),
3560 ] + formatteropts + walkopts,
3559 ] + formatteropts + walkopts,
3561 _('[OPTION]... PATTERN [FILE]...'),
3560 _('[OPTION]... PATTERN [FILE]...'),
3562 inferrepo=True)
3561 inferrepo=True)
3563 def grep(ui, repo, pattern, *pats, **opts):
3562 def grep(ui, repo, pattern, *pats, **opts):
3564 """search revision history for a pattern in specified files
3563 """search revision history for a pattern in specified files
3565
3564
3566 Search revision history for a regular expression in the specified
3565 Search revision history for a regular expression in the specified
3567 files or the entire project.
3566 files or the entire project.
3568
3567
3569 By default, grep prints the most recent revision number for each
3568 By default, grep prints the most recent revision number for each
3570 file in which it finds a match. To get it to print every revision
3569 file in which it finds a match. To get it to print every revision
3571 that contains a change in match status ("-" for a match that becomes
3570 that contains a change in match status ("-" for a match that becomes
3572 a non-match, or "+" for a non-match that becomes a match), use the
3571 a non-match, or "+" for a non-match that becomes a match), use the
3573 --all flag.
3572 --all flag.
3574
3573
3575 PATTERN can be any Python (roughly Perl-compatible) regular
3574 PATTERN can be any Python (roughly Perl-compatible) regular
3576 expression.
3575 expression.
3577
3576
3578 If no FILEs are specified (and -f/--follow isn't set), all files in
3577 If no FILEs are specified (and -f/--follow isn't set), all files in
3579 the repository are searched, including those that don't exist in the
3578 the repository are searched, including those that don't exist in the
3580 current branch or have been deleted in a prior changeset.
3579 current branch or have been deleted in a prior changeset.
3581
3580
3582 Returns 0 if a match is found, 1 otherwise.
3581 Returns 0 if a match is found, 1 otherwise.
3583 """
3582 """
3584 reflags = re.M
3583 reflags = re.M
3585 if opts.get('ignore_case'):
3584 if opts.get('ignore_case'):
3586 reflags |= re.I
3585 reflags |= re.I
3587 try:
3586 try:
3588 regexp = util.re.compile(pattern, reflags)
3587 regexp = util.re.compile(pattern, reflags)
3589 except re.error as inst:
3588 except re.error as inst:
3590 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
3589 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
3591 return 1
3590 return 1
3592 sep, eol = ':', '\n'
3591 sep, eol = ':', '\n'
3593 if opts.get('print0'):
3592 if opts.get('print0'):
3594 sep = eol = '\0'
3593 sep = eol = '\0'
3595
3594
3596 getfile = util.lrucachefunc(repo.file)
3595 getfile = util.lrucachefunc(repo.file)
3597
3596
3598 def matchlines(body):
3597 def matchlines(body):
3599 begin = 0
3598 begin = 0
3600 linenum = 0
3599 linenum = 0
3601 while begin < len(body):
3600 while begin < len(body):
3602 match = regexp.search(body, begin)
3601 match = regexp.search(body, begin)
3603 if not match:
3602 if not match:
3604 break
3603 break
3605 mstart, mend = match.span()
3604 mstart, mend = match.span()
3606 linenum += body.count('\n', begin, mstart) + 1
3605 linenum += body.count('\n', begin, mstart) + 1
3607 lstart = body.rfind('\n', begin, mstart) + 1 or begin
3606 lstart = body.rfind('\n', begin, mstart) + 1 or begin
3608 begin = body.find('\n', mend) + 1 or len(body) + 1
3607 begin = body.find('\n', mend) + 1 or len(body) + 1
3609 lend = begin - 1
3608 lend = begin - 1
3610 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
3609 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
3611
3610
3612 class linestate(object):
3611 class linestate(object):
3613 def __init__(self, line, linenum, colstart, colend):
3612 def __init__(self, line, linenum, colstart, colend):
3614 self.line = line
3613 self.line = line
3615 self.linenum = linenum
3614 self.linenum = linenum
3616 self.colstart = colstart
3615 self.colstart = colstart
3617 self.colend = colend
3616 self.colend = colend
3618
3617
3619 def __hash__(self):
3618 def __hash__(self):
3620 return hash((self.linenum, self.line))
3619 return hash((self.linenum, self.line))
3621
3620
3622 def __eq__(self, other):
3621 def __eq__(self, other):
3623 return self.line == other.line
3622 return self.line == other.line
3624
3623
3625 def findpos(self):
3624 def findpos(self):
3626 """Iterate all (start, end) indices of matches"""
3625 """Iterate all (start, end) indices of matches"""
3627 yield self.colstart, self.colend
3626 yield self.colstart, self.colend
3628 p = self.colend
3627 p = self.colend
3629 while p < len(self.line):
3628 while p < len(self.line):
3630 m = regexp.search(self.line, p)
3629 m = regexp.search(self.line, p)
3631 if not m:
3630 if not m:
3632 break
3631 break
3633 yield m.span()
3632 yield m.span()
3634 p = m.end()
3633 p = m.end()
3635
3634
3636 matches = {}
3635 matches = {}
3637 copies = {}
3636 copies = {}
3638 def grepbody(fn, rev, body):
3637 def grepbody(fn, rev, body):
3639 matches[rev].setdefault(fn, [])
3638 matches[rev].setdefault(fn, [])
3640 m = matches[rev][fn]
3639 m = matches[rev][fn]
3641 for lnum, cstart, cend, line in matchlines(body):
3640 for lnum, cstart, cend, line in matchlines(body):
3642 s = linestate(line, lnum, cstart, cend)
3641 s = linestate(line, lnum, cstart, cend)
3643 m.append(s)
3642 m.append(s)
3644
3643
3645 def difflinestates(a, b):
3644 def difflinestates(a, b):
3646 sm = difflib.SequenceMatcher(None, a, b)
3645 sm = difflib.SequenceMatcher(None, a, b)
3647 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
3646 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
3648 if tag == 'insert':
3647 if tag == 'insert':
3649 for i in xrange(blo, bhi):
3648 for i in xrange(blo, bhi):
3650 yield ('+', b[i])
3649 yield ('+', b[i])
3651 elif tag == 'delete':
3650 elif tag == 'delete':
3652 for i in xrange(alo, ahi):
3651 for i in xrange(alo, ahi):
3653 yield ('-', a[i])
3652 yield ('-', a[i])
3654 elif tag == 'replace':
3653 elif tag == 'replace':
3655 for i in xrange(alo, ahi):
3654 for i in xrange(alo, ahi):
3656 yield ('-', a[i])
3655 yield ('-', a[i])
3657 for i in xrange(blo, bhi):
3656 for i in xrange(blo, bhi):
3658 yield ('+', b[i])
3657 yield ('+', b[i])
3659
3658
3660 def display(fm, fn, ctx, pstates, states):
3659 def display(fm, fn, ctx, pstates, states):
3661 rev = ctx.rev()
3660 rev = ctx.rev()
3662 if fm.isplain():
3661 if fm.isplain():
3663 formatuser = ui.shortuser
3662 formatuser = ui.shortuser
3664 else:
3663 else:
3665 formatuser = str
3664 formatuser = str
3666 if ui.quiet:
3665 if ui.quiet:
3667 datefmt = '%Y-%m-%d'
3666 datefmt = '%Y-%m-%d'
3668 else:
3667 else:
3669 datefmt = '%a %b %d %H:%M:%S %Y %1%2'
3668 datefmt = '%a %b %d %H:%M:%S %Y %1%2'
3670 found = False
3669 found = False
3671 @util.cachefunc
3670 @util.cachefunc
3672 def binary():
3671 def binary():
3673 flog = getfile(fn)
3672 flog = getfile(fn)
3674 return util.binary(flog.read(ctx.filenode(fn)))
3673 return util.binary(flog.read(ctx.filenode(fn)))
3675
3674
3676 fieldnamemap = {'filename': 'file', 'linenumber': 'line_number'}
3675 fieldnamemap = {'filename': 'file', 'linenumber': 'line_number'}
3677 if opts.get('all'):
3676 if opts.get('all'):
3678 iter = difflinestates(pstates, states)
3677 iter = difflinestates(pstates, states)
3679 else:
3678 else:
3680 iter = [('', l) for l in states]
3679 iter = [('', l) for l in states]
3681 for change, l in iter:
3680 for change, l in iter:
3682 fm.startitem()
3681 fm.startitem()
3683 fm.data(node=fm.hexfunc(ctx.node()))
3682 fm.data(node=fm.hexfunc(ctx.node()))
3684 cols = [
3683 cols = [
3685 ('filename', fn, True),
3684 ('filename', fn, True),
3686 ('rev', rev, True),
3685 ('rev', rev, True),
3687 ('linenumber', l.linenum, opts.get('line_number')),
3686 ('linenumber', l.linenum, opts.get('line_number')),
3688 ]
3687 ]
3689 if opts.get('all'):
3688 if opts.get('all'):
3690 cols.append(('change', change, True))
3689 cols.append(('change', change, True))
3691 cols.extend([
3690 cols.extend([
3692 ('user', formatuser(ctx.user()), opts.get('user')),
3691 ('user', formatuser(ctx.user()), opts.get('user')),
3693 ('date', fm.formatdate(ctx.date(), datefmt), opts.get('date')),
3692 ('date', fm.formatdate(ctx.date(), datefmt), opts.get('date')),
3694 ])
3693 ])
3695 lastcol = next(name for name, data, cond in reversed(cols) if cond)
3694 lastcol = next(name for name, data, cond in reversed(cols) if cond)
3696 for name, data, cond in cols:
3695 for name, data, cond in cols:
3697 field = fieldnamemap.get(name, name)
3696 field = fieldnamemap.get(name, name)
3698 fm.condwrite(cond, field, '%s', data, label='grep.%s' % name)
3697 fm.condwrite(cond, field, '%s', data, label='grep.%s' % name)
3699 if cond and name != lastcol:
3698 if cond and name != lastcol:
3700 fm.plain(sep, label='grep.sep')
3699 fm.plain(sep, label='grep.sep')
3701 if not opts.get('files_with_matches'):
3700 if not opts.get('files_with_matches'):
3702 fm.plain(sep, label='grep.sep')
3701 fm.plain(sep, label='grep.sep')
3703 if not opts.get('text') and binary():
3702 if not opts.get('text') and binary():
3704 fm.plain(_(" Binary file matches"))
3703 fm.plain(_(" Binary file matches"))
3705 else:
3704 else:
3706 displaymatches(fm.nested('texts'), l)
3705 displaymatches(fm.nested('texts'), l)
3707 fm.plain(eol)
3706 fm.plain(eol)
3708 found = True
3707 found = True
3709 if opts.get('files_with_matches'):
3708 if opts.get('files_with_matches'):
3710 break
3709 break
3711 return found
3710 return found
3712
3711
3713 def displaymatches(fm, l):
3712 def displaymatches(fm, l):
3714 p = 0
3713 p = 0
3715 for s, e in l.findpos():
3714 for s, e in l.findpos():
3716 if p < s:
3715 if p < s:
3717 fm.startitem()
3716 fm.startitem()
3718 fm.write('text', '%s', l.line[p:s])
3717 fm.write('text', '%s', l.line[p:s])
3719 fm.data(matched=False)
3718 fm.data(matched=False)
3720 fm.startitem()
3719 fm.startitem()
3721 fm.write('text', '%s', l.line[s:e], label='grep.match')
3720 fm.write('text', '%s', l.line[s:e], label='grep.match')
3722 fm.data(matched=True)
3721 fm.data(matched=True)
3723 p = e
3722 p = e
3724 if p < len(l.line):
3723 if p < len(l.line):
3725 fm.startitem()
3724 fm.startitem()
3726 fm.write('text', '%s', l.line[p:])
3725 fm.write('text', '%s', l.line[p:])
3727 fm.data(matched=False)
3726 fm.data(matched=False)
3728 fm.end()
3727 fm.end()
3729
3728
3730 skip = {}
3729 skip = {}
3731 revfiles = {}
3730 revfiles = {}
3732 matchfn = scmutil.match(repo[None], pats, opts)
3731 matchfn = scmutil.match(repo[None], pats, opts)
3733 found = False
3732 found = False
3734 follow = opts.get('follow')
3733 follow = opts.get('follow')
3735
3734
3736 def prep(ctx, fns):
3735 def prep(ctx, fns):
3737 rev = ctx.rev()
3736 rev = ctx.rev()
3738 pctx = ctx.p1()
3737 pctx = ctx.p1()
3739 parent = pctx.rev()
3738 parent = pctx.rev()
3740 matches.setdefault(rev, {})
3739 matches.setdefault(rev, {})
3741 matches.setdefault(parent, {})
3740 matches.setdefault(parent, {})
3742 files = revfiles.setdefault(rev, [])
3741 files = revfiles.setdefault(rev, [])
3743 for fn in fns:
3742 for fn in fns:
3744 flog = getfile(fn)
3743 flog = getfile(fn)
3745 try:
3744 try:
3746 fnode = ctx.filenode(fn)
3745 fnode = ctx.filenode(fn)
3747 except error.LookupError:
3746 except error.LookupError:
3748 continue
3747 continue
3749
3748
3750 copied = flog.renamed(fnode)
3749 copied = flog.renamed(fnode)
3751 copy = follow and copied and copied[0]
3750 copy = follow and copied and copied[0]
3752 if copy:
3751 if copy:
3753 copies.setdefault(rev, {})[fn] = copy
3752 copies.setdefault(rev, {})[fn] = copy
3754 if fn in skip:
3753 if fn in skip:
3755 if copy:
3754 if copy:
3756 skip[copy] = True
3755 skip[copy] = True
3757 continue
3756 continue
3758 files.append(fn)
3757 files.append(fn)
3759
3758
3760 if fn not in matches[rev]:
3759 if fn not in matches[rev]:
3761 grepbody(fn, rev, flog.read(fnode))
3760 grepbody(fn, rev, flog.read(fnode))
3762
3761
3763 pfn = copy or fn
3762 pfn = copy or fn
3764 if pfn not in matches[parent]:
3763 if pfn not in matches[parent]:
3765 try:
3764 try:
3766 fnode = pctx.filenode(pfn)
3765 fnode = pctx.filenode(pfn)
3767 grepbody(pfn, parent, flog.read(fnode))
3766 grepbody(pfn, parent, flog.read(fnode))
3768 except error.LookupError:
3767 except error.LookupError:
3769 pass
3768 pass
3770
3769
3771 fm = ui.formatter('grep', opts)
3770 fm = ui.formatter('grep', opts)
3772 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
3771 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
3773 rev = ctx.rev()
3772 rev = ctx.rev()
3774 parent = ctx.p1().rev()
3773 parent = ctx.p1().rev()
3775 for fn in sorted(revfiles.get(rev, [])):
3774 for fn in sorted(revfiles.get(rev, [])):
3776 states = matches[rev][fn]
3775 states = matches[rev][fn]
3777 copy = copies.get(rev, {}).get(fn)
3776 copy = copies.get(rev, {}).get(fn)
3778 if fn in skip:
3777 if fn in skip:
3779 if copy:
3778 if copy:
3780 skip[copy] = True
3779 skip[copy] = True
3781 continue
3780 continue
3782 pstates = matches.get(parent, {}).get(copy or fn, [])
3781 pstates = matches.get(parent, {}).get(copy or fn, [])
3783 if pstates or states:
3782 if pstates or states:
3784 r = display(fm, fn, ctx, pstates, states)
3783 r = display(fm, fn, ctx, pstates, states)
3785 found = found or r
3784 found = found or r
3786 if r and not opts.get('all'):
3785 if r and not opts.get('all'):
3787 skip[fn] = True
3786 skip[fn] = True
3788 if copy:
3787 if copy:
3789 skip[copy] = True
3788 skip[copy] = True
3790 del matches[rev]
3789 del matches[rev]
3791 del revfiles[rev]
3790 del revfiles[rev]
3792 fm.end()
3791 fm.end()
3793
3792
3794 return not found
3793 return not found
3795
3794
3796 @command('heads',
3795 @command('heads',
3797 [('r', 'rev', '',
3796 [('r', 'rev', '',
3798 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
3797 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
3799 ('t', 'topo', False, _('show topological heads only')),
3798 ('t', 'topo', False, _('show topological heads only')),
3800 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
3799 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
3801 ('c', 'closed', False, _('show normal and closed branch heads')),
3800 ('c', 'closed', False, _('show normal and closed branch heads')),
3802 ] + templateopts,
3801 ] + templateopts,
3803 _('[-ct] [-r STARTREV] [REV]...'))
3802 _('[-ct] [-r STARTREV] [REV]...'))
3804 def heads(ui, repo, *branchrevs, **opts):
3803 def heads(ui, repo, *branchrevs, **opts):
3805 """show branch heads
3804 """show branch heads
3806
3805
3807 With no arguments, show all open branch heads in the repository.
3806 With no arguments, show all open branch heads in the repository.
3808 Branch heads are changesets that have no descendants on the
3807 Branch heads are changesets that have no descendants on the
3809 same branch. They are where development generally takes place and
3808 same branch. They are where development generally takes place and
3810 are the usual targets for update and merge operations.
3809 are the usual targets for update and merge operations.
3811
3810
3812 If one or more REVs are given, only open branch heads on the
3811 If one or more REVs are given, only open branch heads on the
3813 branches associated with the specified changesets are shown. This
3812 branches associated with the specified changesets are shown. This
3814 means that you can use :hg:`heads .` to see the heads on the
3813 means that you can use :hg:`heads .` to see the heads on the
3815 currently checked-out branch.
3814 currently checked-out branch.
3816
3815
3817 If -c/--closed is specified, also show branch heads marked closed
3816 If -c/--closed is specified, also show branch heads marked closed
3818 (see :hg:`commit --close-branch`).
3817 (see :hg:`commit --close-branch`).
3819
3818
3820 If STARTREV is specified, only those heads that are descendants of
3819 If STARTREV is specified, only those heads that are descendants of
3821 STARTREV will be displayed.
3820 STARTREV will be displayed.
3822
3821
3823 If -t/--topo is specified, named branch mechanics will be ignored and only
3822 If -t/--topo is specified, named branch mechanics will be ignored and only
3824 topological heads (changesets with no children) will be shown.
3823 topological heads (changesets with no children) will be shown.
3825
3824
3826 Returns 0 if matching heads are found, 1 if not.
3825 Returns 0 if matching heads are found, 1 if not.
3827 """
3826 """
3828
3827
3829 start = None
3828 start = None
3830 if 'rev' in opts:
3829 if 'rev' in opts:
3831 start = scmutil.revsingle(repo, opts['rev'], None).node()
3830 start = scmutil.revsingle(repo, opts['rev'], None).node()
3832
3831
3833 if opts.get('topo'):
3832 if opts.get('topo'):
3834 heads = [repo[h] for h in repo.heads(start)]
3833 heads = [repo[h] for h in repo.heads(start)]
3835 else:
3834 else:
3836 heads = []
3835 heads = []
3837 for branch in repo.branchmap():
3836 for branch in repo.branchmap():
3838 heads += repo.branchheads(branch, start, opts.get('closed'))
3837 heads += repo.branchheads(branch, start, opts.get('closed'))
3839 heads = [repo[h] for h in heads]
3838 heads = [repo[h] for h in heads]
3840
3839
3841 if branchrevs:
3840 if branchrevs:
3842 branches = set(repo[br].branch() for br in branchrevs)
3841 branches = set(repo[br].branch() for br in branchrevs)
3843 heads = [h for h in heads if h.branch() in branches]
3842 heads = [h for h in heads if h.branch() in branches]
3844
3843
3845 if opts.get('active') and branchrevs:
3844 if opts.get('active') and branchrevs:
3846 dagheads = repo.heads(start)
3845 dagheads = repo.heads(start)
3847 heads = [h for h in heads if h.node() in dagheads]
3846 heads = [h for h in heads if h.node() in dagheads]
3848
3847
3849 if branchrevs:
3848 if branchrevs:
3850 haveheads = set(h.branch() for h in heads)
3849 haveheads = set(h.branch() for h in heads)
3851 if branches - haveheads:
3850 if branches - haveheads:
3852 headless = ', '.join(b for b in branches - haveheads)
3851 headless = ', '.join(b for b in branches - haveheads)
3853 msg = _('no open branch heads found on branches %s')
3852 msg = _('no open branch heads found on branches %s')
3854 if opts.get('rev'):
3853 if opts.get('rev'):
3855 msg += _(' (started at %s)') % opts['rev']
3854 msg += _(' (started at %s)') % opts['rev']
3856 ui.warn((msg + '\n') % headless)
3855 ui.warn((msg + '\n') % headless)
3857
3856
3858 if not heads:
3857 if not heads:
3859 return 1
3858 return 1
3860
3859
3861 heads = sorted(heads, key=lambda x: -x.rev())
3860 heads = sorted(heads, key=lambda x: -x.rev())
3862 displayer = cmdutil.show_changeset(ui, repo, opts)
3861 displayer = cmdutil.show_changeset(ui, repo, opts)
3863 for ctx in heads:
3862 for ctx in heads:
3864 displayer.show(ctx)
3863 displayer.show(ctx)
3865 displayer.close()
3864 displayer.close()
3866
3865
3867 @command('help',
3866 @command('help',
3868 [('e', 'extension', None, _('show only help for extensions')),
3867 [('e', 'extension', None, _('show only help for extensions')),
3869 ('c', 'command', None, _('show only help for commands')),
3868 ('c', 'command', None, _('show only help for commands')),
3870 ('k', 'keyword', None, _('show topics matching keyword')),
3869 ('k', 'keyword', None, _('show topics matching keyword')),
3871 ('s', 'system', [], _('show help for specific platform(s)')),
3870 ('s', 'system', [], _('show help for specific platform(s)')),
3872 ],
3871 ],
3873 _('[-ecks] [TOPIC]'),
3872 _('[-ecks] [TOPIC]'),
3874 norepo=True)
3873 norepo=True)
3875 def help_(ui, name=None, **opts):
3874 def help_(ui, name=None, **opts):
3876 """show help for a given topic or a help overview
3875 """show help for a given topic or a help overview
3877
3876
3878 With no arguments, print a list of commands with short help messages.
3877 With no arguments, print a list of commands with short help messages.
3879
3878
3880 Given a topic, extension, or command name, print help for that
3879 Given a topic, extension, or command name, print help for that
3881 topic.
3880 topic.
3882
3881
3883 Returns 0 if successful.
3882 Returns 0 if successful.
3884 """
3883 """
3885
3884
3886 textwidth = ui.configint('ui', 'textwidth', 78)
3885 textwidth = ui.configint('ui', 'textwidth', 78)
3887 termwidth = ui.termwidth() - 2
3886 termwidth = ui.termwidth() - 2
3888 if textwidth <= 0 or termwidth < textwidth:
3887 if textwidth <= 0 or termwidth < textwidth:
3889 textwidth = termwidth
3888 textwidth = termwidth
3890
3889
3891 keep = opts.get('system') or []
3890 keep = opts.get('system') or []
3892 if len(keep) == 0:
3891 if len(keep) == 0:
3893 if pycompat.sysplatform.startswith('win'):
3892 if pycompat.sysplatform.startswith('win'):
3894 keep.append('windows')
3893 keep.append('windows')
3895 elif pycompat.sysplatform == 'OpenVMS':
3894 elif pycompat.sysplatform == 'OpenVMS':
3896 keep.append('vms')
3895 keep.append('vms')
3897 elif pycompat.sysplatform == 'plan9':
3896 elif pycompat.sysplatform == 'plan9':
3898 keep.append('plan9')
3897 keep.append('plan9')
3899 else:
3898 else:
3900 keep.append('unix')
3899 keep.append('unix')
3901 keep.append(pycompat.sysplatform.lower())
3900 keep.append(pycompat.sysplatform.lower())
3902 if ui.verbose:
3901 if ui.verbose:
3903 keep.append('verbose')
3902 keep.append('verbose')
3904
3903
3905 section = None
3904 section = None
3906 subtopic = None
3905 subtopic = None
3907 if name and '.' in name:
3906 if name and '.' in name:
3908 name, remaining = name.split('.', 1)
3907 name, remaining = name.split('.', 1)
3909 remaining = encoding.lower(remaining)
3908 remaining = encoding.lower(remaining)
3910 if '.' in remaining:
3909 if '.' in remaining:
3911 subtopic, section = remaining.split('.', 1)
3910 subtopic, section = remaining.split('.', 1)
3912 else:
3911 else:
3913 if name in help.subtopics:
3912 if name in help.subtopics:
3914 subtopic = remaining
3913 subtopic = remaining
3915 else:
3914 else:
3916 section = remaining
3915 section = remaining
3917
3916
3918 text = help.help_(ui, name, subtopic=subtopic, **opts)
3917 text = help.help_(ui, name, subtopic=subtopic, **opts)
3919
3918
3920 formatted, pruned = minirst.format(text, textwidth, keep=keep,
3919 formatted, pruned = minirst.format(text, textwidth, keep=keep,
3921 section=section)
3920 section=section)
3922
3921
3923 # We could have been given a weird ".foo" section without a name
3922 # We could have been given a weird ".foo" section without a name
3924 # to look for, or we could have simply failed to found "foo.bar"
3923 # to look for, or we could have simply failed to found "foo.bar"
3925 # because bar isn't a section of foo
3924 # because bar isn't a section of foo
3926 if section and not (formatted and name):
3925 if section and not (formatted and name):
3927 raise error.Abort(_("help section not found"))
3926 raise error.Abort(_("help section not found"))
3928
3927
3929 if 'verbose' in pruned:
3928 if 'verbose' in pruned:
3930 keep.append('omitted')
3929 keep.append('omitted')
3931 else:
3930 else:
3932 keep.append('notomitted')
3931 keep.append('notomitted')
3933 formatted, pruned = minirst.format(text, textwidth, keep=keep,
3932 formatted, pruned = minirst.format(text, textwidth, keep=keep,
3934 section=section)
3933 section=section)
3935 ui.write(formatted)
3934 ui.write(formatted)
3936
3935
3937
3936
3938 @command('identify|id',
3937 @command('identify|id',
3939 [('r', 'rev', '',
3938 [('r', 'rev', '',
3940 _('identify the specified revision'), _('REV')),
3939 _('identify the specified revision'), _('REV')),
3941 ('n', 'num', None, _('show local revision number')),
3940 ('n', 'num', None, _('show local revision number')),
3942 ('i', 'id', None, _('show global revision id')),
3941 ('i', 'id', None, _('show global revision id')),
3943 ('b', 'branch', None, _('show branch')),
3942 ('b', 'branch', None, _('show branch')),
3944 ('t', 'tags', None, _('show tags')),
3943 ('t', 'tags', None, _('show tags')),
3945 ('B', 'bookmarks', None, _('show bookmarks')),
3944 ('B', 'bookmarks', None, _('show bookmarks')),
3946 ] + remoteopts,
3945 ] + remoteopts,
3947 _('[-nibtB] [-r REV] [SOURCE]'),
3946 _('[-nibtB] [-r REV] [SOURCE]'),
3948 optionalrepo=True)
3947 optionalrepo=True)
3949 def identify(ui, repo, source=None, rev=None,
3948 def identify(ui, repo, source=None, rev=None,
3950 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
3949 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
3951 """identify the working directory or specified revision
3950 """identify the working directory or specified revision
3952
3951
3953 Print a summary identifying the repository state at REV using one or
3952 Print a summary identifying the repository state at REV using one or
3954 two parent hash identifiers, followed by a "+" if the working
3953 two parent hash identifiers, followed by a "+" if the working
3955 directory has uncommitted changes, the branch name (if not default),
3954 directory has uncommitted changes, the branch name (if not default),
3956 a list of tags, and a list of bookmarks.
3955 a list of tags, and a list of bookmarks.
3957
3956
3958 When REV is not given, print a summary of the current state of the
3957 When REV is not given, print a summary of the current state of the
3959 repository.
3958 repository.
3960
3959
3961 Specifying a path to a repository root or Mercurial bundle will
3960 Specifying a path to a repository root or Mercurial bundle will
3962 cause lookup to operate on that repository/bundle.
3961 cause lookup to operate on that repository/bundle.
3963
3962
3964 .. container:: verbose
3963 .. container:: verbose
3965
3964
3966 Examples:
3965 Examples:
3967
3966
3968 - generate a build identifier for the working directory::
3967 - generate a build identifier for the working directory::
3969
3968
3970 hg id --id > build-id.dat
3969 hg id --id > build-id.dat
3971
3970
3972 - find the revision corresponding to a tag::
3971 - find the revision corresponding to a tag::
3973
3972
3974 hg id -n -r 1.3
3973 hg id -n -r 1.3
3975
3974
3976 - check the most recent revision of a remote repository::
3975 - check the most recent revision of a remote repository::
3977
3976
3978 hg id -r tip https://www.mercurial-scm.org/repo/hg/
3977 hg id -r tip https://www.mercurial-scm.org/repo/hg/
3979
3978
3980 See :hg:`log` for generating more information about specific revisions,
3979 See :hg:`log` for generating more information about specific revisions,
3981 including full hash identifiers.
3980 including full hash identifiers.
3982
3981
3983 Returns 0 if successful.
3982 Returns 0 if successful.
3984 """
3983 """
3985
3984
3986 if not repo and not source:
3985 if not repo and not source:
3987 raise error.Abort(_("there is no Mercurial repository here "
3986 raise error.Abort(_("there is no Mercurial repository here "
3988 "(.hg not found)"))
3987 "(.hg not found)"))
3989
3988
3990 if ui.debugflag:
3989 if ui.debugflag:
3991 hexfunc = hex
3990 hexfunc = hex
3992 else:
3991 else:
3993 hexfunc = short
3992 hexfunc = short
3994 default = not (num or id or branch or tags or bookmarks)
3993 default = not (num or id or branch or tags or bookmarks)
3995 output = []
3994 output = []
3996 revs = []
3995 revs = []
3997
3996
3998 if source:
3997 if source:
3999 source, branches = hg.parseurl(ui.expandpath(source))
3998 source, branches = hg.parseurl(ui.expandpath(source))
4000 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
3999 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
4001 repo = peer.local()
4000 repo = peer.local()
4002 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
4001 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
4003
4002
4004 if not repo:
4003 if not repo:
4005 if num or branch or tags:
4004 if num or branch or tags:
4006 raise error.Abort(
4005 raise error.Abort(
4007 _("can't query remote revision number, branch, or tags"))
4006 _("can't query remote revision number, branch, or tags"))
4008 if not rev and revs:
4007 if not rev and revs:
4009 rev = revs[0]
4008 rev = revs[0]
4010 if not rev:
4009 if not rev:
4011 rev = "tip"
4010 rev = "tip"
4012
4011
4013 remoterev = peer.lookup(rev)
4012 remoterev = peer.lookup(rev)
4014 if default or id:
4013 if default or id:
4015 output = [hexfunc(remoterev)]
4014 output = [hexfunc(remoterev)]
4016
4015
4017 def getbms():
4016 def getbms():
4018 bms = []
4017 bms = []
4019
4018
4020 if 'bookmarks' in peer.listkeys('namespaces'):
4019 if 'bookmarks' in peer.listkeys('namespaces'):
4021 hexremoterev = hex(remoterev)
4020 hexremoterev = hex(remoterev)
4022 bms = [bm for bm, bmr in peer.listkeys('bookmarks').iteritems()
4021 bms = [bm for bm, bmr in peer.listkeys('bookmarks').iteritems()
4023 if bmr == hexremoterev]
4022 if bmr == hexremoterev]
4024
4023
4025 return sorted(bms)
4024 return sorted(bms)
4026
4025
4027 if bookmarks:
4026 if bookmarks:
4028 output.extend(getbms())
4027 output.extend(getbms())
4029 elif default and not ui.quiet:
4028 elif default and not ui.quiet:
4030 # multiple bookmarks for a single parent separated by '/'
4029 # multiple bookmarks for a single parent separated by '/'
4031 bm = '/'.join(getbms())
4030 bm = '/'.join(getbms())
4032 if bm:
4031 if bm:
4033 output.append(bm)
4032 output.append(bm)
4034 else:
4033 else:
4035 ctx = scmutil.revsingle(repo, rev, None)
4034 ctx = scmutil.revsingle(repo, rev, None)
4036
4035
4037 if ctx.rev() is None:
4036 if ctx.rev() is None:
4038 ctx = repo[None]
4037 ctx = repo[None]
4039 parents = ctx.parents()
4038 parents = ctx.parents()
4040 taglist = []
4039 taglist = []
4041 for p in parents:
4040 for p in parents:
4042 taglist.extend(p.tags())
4041 taglist.extend(p.tags())
4043
4042
4044 changed = ""
4043 changed = ""
4045 if default or id or num:
4044 if default or id or num:
4046 if (any(repo.status())
4045 if (any(repo.status())
4047 or any(ctx.sub(s).dirty() for s in ctx.substate)):
4046 or any(ctx.sub(s).dirty() for s in ctx.substate)):
4048 changed = '+'
4047 changed = '+'
4049 if default or id:
4048 if default or id:
4050 output = ["%s%s" %
4049 output = ["%s%s" %
4051 ('+'.join([hexfunc(p.node()) for p in parents]), changed)]
4050 ('+'.join([hexfunc(p.node()) for p in parents]), changed)]
4052 if num:
4051 if num:
4053 output.append("%s%s" %
4052 output.append("%s%s" %
4054 ('+'.join([str(p.rev()) for p in parents]), changed))
4053 ('+'.join([str(p.rev()) for p in parents]), changed))
4055 else:
4054 else:
4056 if default or id:
4055 if default or id:
4057 output = [hexfunc(ctx.node())]
4056 output = [hexfunc(ctx.node())]
4058 if num:
4057 if num:
4059 output.append(str(ctx.rev()))
4058 output.append(str(ctx.rev()))
4060 taglist = ctx.tags()
4059 taglist = ctx.tags()
4061
4060
4062 if default and not ui.quiet:
4061 if default and not ui.quiet:
4063 b = ctx.branch()
4062 b = ctx.branch()
4064 if b != 'default':
4063 if b != 'default':
4065 output.append("(%s)" % b)
4064 output.append("(%s)" % b)
4066
4065
4067 # multiple tags for a single parent separated by '/'
4066 # multiple tags for a single parent separated by '/'
4068 t = '/'.join(taglist)
4067 t = '/'.join(taglist)
4069 if t:
4068 if t:
4070 output.append(t)
4069 output.append(t)
4071
4070
4072 # multiple bookmarks for a single parent separated by '/'
4071 # multiple bookmarks for a single parent separated by '/'
4073 bm = '/'.join(ctx.bookmarks())
4072 bm = '/'.join(ctx.bookmarks())
4074 if bm:
4073 if bm:
4075 output.append(bm)
4074 output.append(bm)
4076 else:
4075 else:
4077 if branch:
4076 if branch:
4078 output.append(ctx.branch())
4077 output.append(ctx.branch())
4079
4078
4080 if tags:
4079 if tags:
4081 output.extend(taglist)
4080 output.extend(taglist)
4082
4081
4083 if bookmarks:
4082 if bookmarks:
4084 output.extend(ctx.bookmarks())
4083 output.extend(ctx.bookmarks())
4085
4084
4086 ui.write("%s\n" % ' '.join(output))
4085 ui.write("%s\n" % ' '.join(output))
4087
4086
4088 @command('import|patch',
4087 @command('import|patch',
4089 [('p', 'strip', 1,
4088 [('p', 'strip', 1,
4090 _('directory strip option for patch. This has the same '
4089 _('directory strip option for patch. This has the same '
4091 'meaning as the corresponding patch option'), _('NUM')),
4090 'meaning as the corresponding patch option'), _('NUM')),
4092 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
4091 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
4093 ('e', 'edit', False, _('invoke editor on commit messages')),
4092 ('e', 'edit', False, _('invoke editor on commit messages')),
4094 ('f', 'force', None,
4093 ('f', 'force', None,
4095 _('skip check for outstanding uncommitted changes (DEPRECATED)')),
4094 _('skip check for outstanding uncommitted changes (DEPRECATED)')),
4096 ('', 'no-commit', None,
4095 ('', 'no-commit', None,
4097 _("don't commit, just update the working directory")),
4096 _("don't commit, just update the working directory")),
4098 ('', 'bypass', None,
4097 ('', 'bypass', None,
4099 _("apply patch without touching the working directory")),
4098 _("apply patch without touching the working directory")),
4100 ('', 'partial', None,
4099 ('', 'partial', None,
4101 _('commit even if some hunks fail')),
4100 _('commit even if some hunks fail')),
4102 ('', 'exact', None,
4101 ('', 'exact', None,
4103 _('abort if patch would apply lossily')),
4102 _('abort if patch would apply lossily')),
4104 ('', 'prefix', '',
4103 ('', 'prefix', '',
4105 _('apply patch to subdirectory'), _('DIR')),
4104 _('apply patch to subdirectory'), _('DIR')),
4106 ('', 'import-branch', None,
4105 ('', 'import-branch', None,
4107 _('use any branch information in patch (implied by --exact)'))] +
4106 _('use any branch information in patch (implied by --exact)'))] +
4108 commitopts + commitopts2 + similarityopts,
4107 commitopts + commitopts2 + similarityopts,
4109 _('[OPTION]... PATCH...'))
4108 _('[OPTION]... PATCH...'))
4110 def import_(ui, repo, patch1=None, *patches, **opts):
4109 def import_(ui, repo, patch1=None, *patches, **opts):
4111 """import an ordered set of patches
4110 """import an ordered set of patches
4112
4111
4113 Import a list of patches and commit them individually (unless
4112 Import a list of patches and commit them individually (unless
4114 --no-commit is specified).
4113 --no-commit is specified).
4115
4114
4116 To read a patch from standard input, use "-" as the patch name. If
4115 To read a patch from standard input, use "-" as the patch name. If
4117 a URL is specified, the patch will be downloaded from there.
4116 a URL is specified, the patch will be downloaded from there.
4118
4117
4119 Import first applies changes to the working directory (unless
4118 Import first applies changes to the working directory (unless
4120 --bypass is specified), import will abort if there are outstanding
4119 --bypass is specified), import will abort if there are outstanding
4121 changes.
4120 changes.
4122
4121
4123 Use --bypass to apply and commit patches directly to the
4122 Use --bypass to apply and commit patches directly to the
4124 repository, without affecting the working directory. Without
4123 repository, without affecting the working directory. Without
4125 --exact, patches will be applied on top of the working directory
4124 --exact, patches will be applied on top of the working directory
4126 parent revision.
4125 parent revision.
4127
4126
4128 You can import a patch straight from a mail message. Even patches
4127 You can import a patch straight from a mail message. Even patches
4129 as attachments work (to use the body part, it must have type
4128 as attachments work (to use the body part, it must have type
4130 text/plain or text/x-patch). From and Subject headers of email
4129 text/plain or text/x-patch). From and Subject headers of email
4131 message are used as default committer and commit message. All
4130 message are used as default committer and commit message. All
4132 text/plain body parts before first diff are added to the commit
4131 text/plain body parts before first diff are added to the commit
4133 message.
4132 message.
4134
4133
4135 If the imported patch was generated by :hg:`export`, user and
4134 If the imported patch was generated by :hg:`export`, user and
4136 description from patch override values from message headers and
4135 description from patch override values from message headers and
4137 body. Values given on command line with -m/--message and -u/--user
4136 body. Values given on command line with -m/--message and -u/--user
4138 override these.
4137 override these.
4139
4138
4140 If --exact is specified, import will set the working directory to
4139 If --exact is specified, import will set the working directory to
4141 the parent of each patch before applying it, and will abort if the
4140 the parent of each patch before applying it, and will abort if the
4142 resulting changeset has a different ID than the one recorded in
4141 resulting changeset has a different ID than the one recorded in
4143 the patch. This will guard against various ways that portable
4142 the patch. This will guard against various ways that portable
4144 patch formats and mail systems might fail to transfer Mercurial
4143 patch formats and mail systems might fail to transfer Mercurial
4145 data or metadata. See :hg:`bundle` for lossless transmission.
4144 data or metadata. See :hg:`bundle` for lossless transmission.
4146
4145
4147 Use --partial to ensure a changeset will be created from the patch
4146 Use --partial to ensure a changeset will be created from the patch
4148 even if some hunks fail to apply. Hunks that fail to apply will be
4147 even if some hunks fail to apply. Hunks that fail to apply will be
4149 written to a <target-file>.rej file. Conflicts can then be resolved
4148 written to a <target-file>.rej file. Conflicts can then be resolved
4150 by hand before :hg:`commit --amend` is run to update the created
4149 by hand before :hg:`commit --amend` is run to update the created
4151 changeset. This flag exists to let people import patches that
4150 changeset. This flag exists to let people import patches that
4152 partially apply without losing the associated metadata (author,
4151 partially apply without losing the associated metadata (author,
4153 date, description, ...).
4152 date, description, ...).
4154
4153
4155 .. note::
4154 .. note::
4156
4155
4157 When no hunks apply cleanly, :hg:`import --partial` will create
4156 When no hunks apply cleanly, :hg:`import --partial` will create
4158 an empty changeset, importing only the patch metadata.
4157 an empty changeset, importing only the patch metadata.
4159
4158
4160 With -s/--similarity, hg will attempt to discover renames and
4159 With -s/--similarity, hg will attempt to discover renames and
4161 copies in the patch in the same way as :hg:`addremove`.
4160 copies in the patch in the same way as :hg:`addremove`.
4162
4161
4163 It is possible to use external patch programs to perform the patch
4162 It is possible to use external patch programs to perform the patch
4164 by setting the ``ui.patch`` configuration option. For the default
4163 by setting the ``ui.patch`` configuration option. For the default
4165 internal tool, the fuzz can also be configured via ``patch.fuzz``.
4164 internal tool, the fuzz can also be configured via ``patch.fuzz``.
4166 See :hg:`help config` for more information about configuration
4165 See :hg:`help config` for more information about configuration
4167 files and how to use these options.
4166 files and how to use these options.
4168
4167
4169 See :hg:`help dates` for a list of formats valid for -d/--date.
4168 See :hg:`help dates` for a list of formats valid for -d/--date.
4170
4169
4171 .. container:: verbose
4170 .. container:: verbose
4172
4171
4173 Examples:
4172 Examples:
4174
4173
4175 - import a traditional patch from a website and detect renames::
4174 - import a traditional patch from a website and detect renames::
4176
4175
4177 hg import -s 80 http://example.com/bugfix.patch
4176 hg import -s 80 http://example.com/bugfix.patch
4178
4177
4179 - import a changeset from an hgweb server::
4178 - import a changeset from an hgweb server::
4180
4179
4181 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
4180 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
4182
4181
4183 - import all the patches in an Unix-style mbox::
4182 - import all the patches in an Unix-style mbox::
4184
4183
4185 hg import incoming-patches.mbox
4184 hg import incoming-patches.mbox
4186
4185
4187 - attempt to exactly restore an exported changeset (not always
4186 - attempt to exactly restore an exported changeset (not always
4188 possible)::
4187 possible)::
4189
4188
4190 hg import --exact proposed-fix.patch
4189 hg import --exact proposed-fix.patch
4191
4190
4192 - use an external tool to apply a patch which is too fuzzy for
4191 - use an external tool to apply a patch which is too fuzzy for
4193 the default internal tool.
4192 the default internal tool.
4194
4193
4195 hg import --config ui.patch="patch --merge" fuzzy.patch
4194 hg import --config ui.patch="patch --merge" fuzzy.patch
4196
4195
4197 - change the default fuzzing from 2 to a less strict 7
4196 - change the default fuzzing from 2 to a less strict 7
4198
4197
4199 hg import --config ui.fuzz=7 fuzz.patch
4198 hg import --config ui.fuzz=7 fuzz.patch
4200
4199
4201 Returns 0 on success, 1 on partial success (see --partial).
4200 Returns 0 on success, 1 on partial success (see --partial).
4202 """
4201 """
4203
4202
4204 if not patch1:
4203 if not patch1:
4205 raise error.Abort(_('need at least one patch to import'))
4204 raise error.Abort(_('need at least one patch to import'))
4206
4205
4207 patches = (patch1,) + patches
4206 patches = (patch1,) + patches
4208
4207
4209 date = opts.get('date')
4208 date = opts.get('date')
4210 if date:
4209 if date:
4211 opts['date'] = util.parsedate(date)
4210 opts['date'] = util.parsedate(date)
4212
4211
4213 exact = opts.get('exact')
4212 exact = opts.get('exact')
4214 update = not opts.get('bypass')
4213 update = not opts.get('bypass')
4215 if not update and opts.get('no_commit'):
4214 if not update and opts.get('no_commit'):
4216 raise error.Abort(_('cannot use --no-commit with --bypass'))
4215 raise error.Abort(_('cannot use --no-commit with --bypass'))
4217 try:
4216 try:
4218 sim = float(opts.get('similarity') or 0)
4217 sim = float(opts.get('similarity') or 0)
4219 except ValueError:
4218 except ValueError:
4220 raise error.Abort(_('similarity must be a number'))
4219 raise error.Abort(_('similarity must be a number'))
4221 if sim < 0 or sim > 100:
4220 if sim < 0 or sim > 100:
4222 raise error.Abort(_('similarity must be between 0 and 100'))
4221 raise error.Abort(_('similarity must be between 0 and 100'))
4223 if sim and not update:
4222 if sim and not update:
4224 raise error.Abort(_('cannot use --similarity with --bypass'))
4223 raise error.Abort(_('cannot use --similarity with --bypass'))
4225 if exact:
4224 if exact:
4226 if opts.get('edit'):
4225 if opts.get('edit'):
4227 raise error.Abort(_('cannot use --exact with --edit'))
4226 raise error.Abort(_('cannot use --exact with --edit'))
4228 if opts.get('prefix'):
4227 if opts.get('prefix'):
4229 raise error.Abort(_('cannot use --exact with --prefix'))
4228 raise error.Abort(_('cannot use --exact with --prefix'))
4230
4229
4231 base = opts["base"]
4230 base = opts["base"]
4232 wlock = dsguard = lock = tr = None
4231 wlock = dsguard = lock = tr = None
4233 msgs = []
4232 msgs = []
4234 ret = 0
4233 ret = 0
4235
4234
4236
4235
4237 try:
4236 try:
4238 wlock = repo.wlock()
4237 wlock = repo.wlock()
4239
4238
4240 if update:
4239 if update:
4241 cmdutil.checkunfinished(repo)
4240 cmdutil.checkunfinished(repo)
4242 if (exact or not opts.get('force')):
4241 if (exact or not opts.get('force')):
4243 cmdutil.bailifchanged(repo)
4242 cmdutil.bailifchanged(repo)
4244
4243
4245 if not opts.get('no_commit'):
4244 if not opts.get('no_commit'):
4246 lock = repo.lock()
4245 lock = repo.lock()
4247 tr = repo.transaction('import')
4246 tr = repo.transaction('import')
4248 else:
4247 else:
4249 dsguard = dirstateguard.dirstateguard(repo, 'import')
4248 dsguard = dirstateguard.dirstateguard(repo, 'import')
4250 parents = repo[None].parents()
4249 parents = repo[None].parents()
4251 for patchurl in patches:
4250 for patchurl in patches:
4252 if patchurl == '-':
4251 if patchurl == '-':
4253 ui.status(_('applying patch from stdin\n'))
4252 ui.status(_('applying patch from stdin\n'))
4254 patchfile = ui.fin
4253 patchfile = ui.fin
4255 patchurl = 'stdin' # for error message
4254 patchurl = 'stdin' # for error message
4256 else:
4255 else:
4257 patchurl = os.path.join(base, patchurl)
4256 patchurl = os.path.join(base, patchurl)
4258 ui.status(_('applying %s\n') % patchurl)
4257 ui.status(_('applying %s\n') % patchurl)
4259 patchfile = hg.openpath(ui, patchurl)
4258 patchfile = hg.openpath(ui, patchurl)
4260
4259
4261 haspatch = False
4260 haspatch = False
4262 for hunk in patch.split(patchfile):
4261 for hunk in patch.split(patchfile):
4263 (msg, node, rej) = cmdutil.tryimportone(ui, repo, hunk,
4262 (msg, node, rej) = cmdutil.tryimportone(ui, repo, hunk,
4264 parents, opts,
4263 parents, opts,
4265 msgs, hg.clean)
4264 msgs, hg.clean)
4266 if msg:
4265 if msg:
4267 haspatch = True
4266 haspatch = True
4268 ui.note(msg + '\n')
4267 ui.note(msg + '\n')
4269 if update or exact:
4268 if update or exact:
4270 parents = repo[None].parents()
4269 parents = repo[None].parents()
4271 else:
4270 else:
4272 parents = [repo[node]]
4271 parents = [repo[node]]
4273 if rej:
4272 if rej:
4274 ui.write_err(_("patch applied partially\n"))
4273 ui.write_err(_("patch applied partially\n"))
4275 ui.write_err(_("(fix the .rej files and run "
4274 ui.write_err(_("(fix the .rej files and run "
4276 "`hg commit --amend`)\n"))
4275 "`hg commit --amend`)\n"))
4277 ret = 1
4276 ret = 1
4278 break
4277 break
4279
4278
4280 if not haspatch:
4279 if not haspatch:
4281 raise error.Abort(_('%s: no diffs found') % patchurl)
4280 raise error.Abort(_('%s: no diffs found') % patchurl)
4282
4281
4283 if tr:
4282 if tr:
4284 tr.close()
4283 tr.close()
4285 if msgs:
4284 if msgs:
4286 repo.savecommitmessage('\n* * *\n'.join(msgs))
4285 repo.savecommitmessage('\n* * *\n'.join(msgs))
4287 if dsguard:
4286 if dsguard:
4288 dsguard.close()
4287 dsguard.close()
4289 return ret
4288 return ret
4290 finally:
4289 finally:
4291 if tr:
4290 if tr:
4292 tr.release()
4291 tr.release()
4293 release(lock, dsguard, wlock)
4292 release(lock, dsguard, wlock)
4294
4293
4295 @command('incoming|in',
4294 @command('incoming|in',
4296 [('f', 'force', None,
4295 [('f', 'force', None,
4297 _('run even if remote repository is unrelated')),
4296 _('run even if remote repository is unrelated')),
4298 ('n', 'newest-first', None, _('show newest record first')),
4297 ('n', 'newest-first', None, _('show newest record first')),
4299 ('', 'bundle', '',
4298 ('', 'bundle', '',
4300 _('file to store the bundles into'), _('FILE')),
4299 _('file to store the bundles into'), _('FILE')),
4301 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
4300 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
4302 ('B', 'bookmarks', False, _("compare bookmarks")),
4301 ('B', 'bookmarks', False, _("compare bookmarks")),
4303 ('b', 'branch', [],
4302 ('b', 'branch', [],
4304 _('a specific branch you would like to pull'), _('BRANCH')),
4303 _('a specific branch you would like to pull'), _('BRANCH')),
4305 ] + logopts + remoteopts + subrepoopts,
4304 ] + logopts + remoteopts + subrepoopts,
4306 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
4305 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
4307 def incoming(ui, repo, source="default", **opts):
4306 def incoming(ui, repo, source="default", **opts):
4308 """show new changesets found in source
4307 """show new changesets found in source
4309
4308
4310 Show new changesets found in the specified path/URL or the default
4309 Show new changesets found in the specified path/URL or the default
4311 pull location. These are the changesets that would have been pulled
4310 pull location. These are the changesets that would have been pulled
4312 if a pull at the time you issued this command.
4311 if a pull at the time you issued this command.
4313
4312
4314 See pull for valid source format details.
4313 See pull for valid source format details.
4315
4314
4316 .. container:: verbose
4315 .. container:: verbose
4317
4316
4318 With -B/--bookmarks, the result of bookmark comparison between
4317 With -B/--bookmarks, the result of bookmark comparison between
4319 local and remote repositories is displayed. With -v/--verbose,
4318 local and remote repositories is displayed. With -v/--verbose,
4320 status is also displayed for each bookmark like below::
4319 status is also displayed for each bookmark like below::
4321
4320
4322 BM1 01234567890a added
4321 BM1 01234567890a added
4323 BM2 1234567890ab advanced
4322 BM2 1234567890ab advanced
4324 BM3 234567890abc diverged
4323 BM3 234567890abc diverged
4325 BM4 34567890abcd changed
4324 BM4 34567890abcd changed
4326
4325
4327 The action taken locally when pulling depends on the
4326 The action taken locally when pulling depends on the
4328 status of each bookmark:
4327 status of each bookmark:
4329
4328
4330 :``added``: pull will create it
4329 :``added``: pull will create it
4331 :``advanced``: pull will update it
4330 :``advanced``: pull will update it
4332 :``diverged``: pull will create a divergent bookmark
4331 :``diverged``: pull will create a divergent bookmark
4333 :``changed``: result depends on remote changesets
4332 :``changed``: result depends on remote changesets
4334
4333
4335 From the point of view of pulling behavior, bookmark
4334 From the point of view of pulling behavior, bookmark
4336 existing only in the remote repository are treated as ``added``,
4335 existing only in the remote repository are treated as ``added``,
4337 even if it is in fact locally deleted.
4336 even if it is in fact locally deleted.
4338
4337
4339 .. container:: verbose
4338 .. container:: verbose
4340
4339
4341 For remote repository, using --bundle avoids downloading the
4340 For remote repository, using --bundle avoids downloading the
4342 changesets twice if the incoming is followed by a pull.
4341 changesets twice if the incoming is followed by a pull.
4343
4342
4344 Examples:
4343 Examples:
4345
4344
4346 - show incoming changes with patches and full description::
4345 - show incoming changes with patches and full description::
4347
4346
4348 hg incoming -vp
4347 hg incoming -vp
4349
4348
4350 - show incoming changes excluding merges, store a bundle::
4349 - show incoming changes excluding merges, store a bundle::
4351
4350
4352 hg in -vpM --bundle incoming.hg
4351 hg in -vpM --bundle incoming.hg
4353 hg pull incoming.hg
4352 hg pull incoming.hg
4354
4353
4355 - briefly list changes inside a bundle::
4354 - briefly list changes inside a bundle::
4356
4355
4357 hg in changes.hg -T "{desc|firstline}\\n"
4356 hg in changes.hg -T "{desc|firstline}\\n"
4358
4357
4359 Returns 0 if there are incoming changes, 1 otherwise.
4358 Returns 0 if there are incoming changes, 1 otherwise.
4360 """
4359 """
4361 if opts.get('graph'):
4360 if opts.get('graph'):
4362 cmdutil.checkunsupportedgraphflags([], opts)
4361 cmdutil.checkunsupportedgraphflags([], opts)
4363 def display(other, chlist, displayer):
4362 def display(other, chlist, displayer):
4364 revdag = cmdutil.graphrevs(other, chlist, opts)
4363 revdag = cmdutil.graphrevs(other, chlist, opts)
4365 cmdutil.displaygraph(ui, repo, revdag, displayer,
4364 cmdutil.displaygraph(ui, repo, revdag, displayer,
4366 graphmod.asciiedges)
4365 graphmod.asciiedges)
4367
4366
4368 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
4367 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
4369 return 0
4368 return 0
4370
4369
4371 if opts.get('bundle') and opts.get('subrepos'):
4370 if opts.get('bundle') and opts.get('subrepos'):
4372 raise error.Abort(_('cannot combine --bundle and --subrepos'))
4371 raise error.Abort(_('cannot combine --bundle and --subrepos'))
4373
4372
4374 if opts.get('bookmarks'):
4373 if opts.get('bookmarks'):
4375 source, branches = hg.parseurl(ui.expandpath(source),
4374 source, branches = hg.parseurl(ui.expandpath(source),
4376 opts.get('branch'))
4375 opts.get('branch'))
4377 other = hg.peer(repo, opts, source)
4376 other = hg.peer(repo, opts, source)
4378 if 'bookmarks' not in other.listkeys('namespaces'):
4377 if 'bookmarks' not in other.listkeys('namespaces'):
4379 ui.warn(_("remote doesn't support bookmarks\n"))
4378 ui.warn(_("remote doesn't support bookmarks\n"))
4380 return 0
4379 return 0
4381 ui.status(_('comparing with %s\n') % util.hidepassword(source))
4380 ui.status(_('comparing with %s\n') % util.hidepassword(source))
4382 return bookmarks.incoming(ui, repo, other)
4381 return bookmarks.incoming(ui, repo, other)
4383
4382
4384 repo._subtoppath = ui.expandpath(source)
4383 repo._subtoppath = ui.expandpath(source)
4385 try:
4384 try:
4386 return hg.incoming(ui, repo, source, opts)
4385 return hg.incoming(ui, repo, source, opts)
4387 finally:
4386 finally:
4388 del repo._subtoppath
4387 del repo._subtoppath
4389
4388
4390
4389
4391 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'),
4390 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'),
4392 norepo=True)
4391 norepo=True)
4393 def init(ui, dest=".", **opts):
4392 def init(ui, dest=".", **opts):
4394 """create a new repository in the given directory
4393 """create a new repository in the given directory
4395
4394
4396 Initialize a new repository in the given directory. If the given
4395 Initialize a new repository in the given directory. If the given
4397 directory does not exist, it will be created.
4396 directory does not exist, it will be created.
4398
4397
4399 If no directory is given, the current directory is used.
4398 If no directory is given, the current directory is used.
4400
4399
4401 It is possible to specify an ``ssh://`` URL as the destination.
4400 It is possible to specify an ``ssh://`` URL as the destination.
4402 See :hg:`help urls` for more information.
4401 See :hg:`help urls` for more information.
4403
4402
4404 Returns 0 on success.
4403 Returns 0 on success.
4405 """
4404 """
4406 hg.peer(ui, opts, ui.expandpath(dest), create=True)
4405 hg.peer(ui, opts, ui.expandpath(dest), create=True)
4407
4406
4408 @command('locate',
4407 @command('locate',
4409 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
4408 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
4410 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
4409 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
4411 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
4410 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
4412 ] + walkopts,
4411 ] + walkopts,
4413 _('[OPTION]... [PATTERN]...'))
4412 _('[OPTION]... [PATTERN]...'))
4414 def locate(ui, repo, *pats, **opts):
4413 def locate(ui, repo, *pats, **opts):
4415 """locate files matching specific patterns (DEPRECATED)
4414 """locate files matching specific patterns (DEPRECATED)
4416
4415
4417 Print files under Mercurial control in the working directory whose
4416 Print files under Mercurial control in the working directory whose
4418 names match the given patterns.
4417 names match the given patterns.
4419
4418
4420 By default, this command searches all directories in the working
4419 By default, this command searches all directories in the working
4421 directory. To search just the current directory and its
4420 directory. To search just the current directory and its
4422 subdirectories, use "--include .".
4421 subdirectories, use "--include .".
4423
4422
4424 If no patterns are given to match, this command prints the names
4423 If no patterns are given to match, this command prints the names
4425 of all files under Mercurial control in the working directory.
4424 of all files under Mercurial control in the working directory.
4426
4425
4427 If you want to feed the output of this command into the "xargs"
4426 If you want to feed the output of this command into the "xargs"
4428 command, use the -0 option to both this command and "xargs". This
4427 command, use the -0 option to both this command and "xargs". This
4429 will avoid the problem of "xargs" treating single filenames that
4428 will avoid the problem of "xargs" treating single filenames that
4430 contain whitespace as multiple filenames.
4429 contain whitespace as multiple filenames.
4431
4430
4432 See :hg:`help files` for a more versatile command.
4431 See :hg:`help files` for a more versatile command.
4433
4432
4434 Returns 0 if a match is found, 1 otherwise.
4433 Returns 0 if a match is found, 1 otherwise.
4435 """
4434 """
4436 if opts.get('print0'):
4435 if opts.get('print0'):
4437 end = '\0'
4436 end = '\0'
4438 else:
4437 else:
4439 end = '\n'
4438 end = '\n'
4440 rev = scmutil.revsingle(repo, opts.get('rev'), None).node()
4439 rev = scmutil.revsingle(repo, opts.get('rev'), None).node()
4441
4440
4442 ret = 1
4441 ret = 1
4443 ctx = repo[rev]
4442 ctx = repo[rev]
4444 m = scmutil.match(ctx, pats, opts, default='relglob',
4443 m = scmutil.match(ctx, pats, opts, default='relglob',
4445 badfn=lambda x, y: False)
4444 badfn=lambda x, y: False)
4446
4445
4447 for abs in ctx.matches(m):
4446 for abs in ctx.matches(m):
4448 if opts.get('fullpath'):
4447 if opts.get('fullpath'):
4449 ui.write(repo.wjoin(abs), end)
4448 ui.write(repo.wjoin(abs), end)
4450 else:
4449 else:
4451 ui.write(((pats and m.rel(abs)) or abs), end)
4450 ui.write(((pats and m.rel(abs)) or abs), end)
4452 ret = 0
4451 ret = 0
4453
4452
4454 return ret
4453 return ret
4455
4454
4456 @command('^log|history',
4455 @command('^log|history',
4457 [('f', 'follow', None,
4456 [('f', 'follow', None,
4458 _('follow changeset history, or file history across copies and renames')),
4457 _('follow changeset history, or file history across copies and renames')),
4459 ('', 'follow-first', None,
4458 ('', 'follow-first', None,
4460 _('only follow the first parent of merge changesets (DEPRECATED)')),
4459 _('only follow the first parent of merge changesets (DEPRECATED)')),
4461 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
4460 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
4462 ('C', 'copies', None, _('show copied files')),
4461 ('C', 'copies', None, _('show copied files')),
4463 ('k', 'keyword', [],
4462 ('k', 'keyword', [],
4464 _('do case-insensitive search for a given text'), _('TEXT')),
4463 _('do case-insensitive search for a given text'), _('TEXT')),
4465 ('r', 'rev', [], _('show the specified revision or revset'), _('REV')),
4464 ('r', 'rev', [], _('show the specified revision or revset'), _('REV')),
4466 ('', 'removed', None, _('include revisions where files were removed')),
4465 ('', 'removed', None, _('include revisions where files were removed')),
4467 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
4466 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
4468 ('u', 'user', [], _('revisions committed by user'), _('USER')),
4467 ('u', 'user', [], _('revisions committed by user'), _('USER')),
4469 ('', 'only-branch', [],
4468 ('', 'only-branch', [],
4470 _('show only changesets within the given named branch (DEPRECATED)'),
4469 _('show only changesets within the given named branch (DEPRECATED)'),
4471 _('BRANCH')),
4470 _('BRANCH')),
4472 ('b', 'branch', [],
4471 ('b', 'branch', [],
4473 _('show changesets within the given named branch'), _('BRANCH')),
4472 _('show changesets within the given named branch'), _('BRANCH')),
4474 ('P', 'prune', [],
4473 ('P', 'prune', [],
4475 _('do not display revision or any of its ancestors'), _('REV')),
4474 _('do not display revision or any of its ancestors'), _('REV')),
4476 ] + logopts + walkopts,
4475 ] + logopts + walkopts,
4477 _('[OPTION]... [FILE]'),
4476 _('[OPTION]... [FILE]'),
4478 inferrepo=True)
4477 inferrepo=True)
4479 def log(ui, repo, *pats, **opts):
4478 def log(ui, repo, *pats, **opts):
4480 """show revision history of entire repository or files
4479 """show revision history of entire repository or files
4481
4480
4482 Print the revision history of the specified files or the entire
4481 Print the revision history of the specified files or the entire
4483 project.
4482 project.
4484
4483
4485 If no revision range is specified, the default is ``tip:0`` unless
4484 If no revision range is specified, the default is ``tip:0`` unless
4486 --follow is set, in which case the working directory parent is
4485 --follow is set, in which case the working directory parent is
4487 used as the starting revision.
4486 used as the starting revision.
4488
4487
4489 File history is shown without following rename or copy history of
4488 File history is shown without following rename or copy history of
4490 files. Use -f/--follow with a filename to follow history across
4489 files. Use -f/--follow with a filename to follow history across
4491 renames and copies. --follow without a filename will only show
4490 renames and copies. --follow without a filename will only show
4492 ancestors or descendants of the starting revision.
4491 ancestors or descendants of the starting revision.
4493
4492
4494 By default this command prints revision number and changeset id,
4493 By default this command prints revision number and changeset id,
4495 tags, non-trivial parents, user, date and time, and a summary for
4494 tags, non-trivial parents, user, date and time, and a summary for
4496 each commit. When the -v/--verbose switch is used, the list of
4495 each commit. When the -v/--verbose switch is used, the list of
4497 changed files and full commit message are shown.
4496 changed files and full commit message are shown.
4498
4497
4499 With --graph the revisions are shown as an ASCII art DAG with the most
4498 With --graph the revisions are shown as an ASCII art DAG with the most
4500 recent changeset at the top.
4499 recent changeset at the top.
4501 'o' is a changeset, '@' is a working directory parent, 'x' is obsolete,
4500 'o' is a changeset, '@' is a working directory parent, 'x' is obsolete,
4502 and '+' represents a fork where the changeset from the lines below is a
4501 and '+' represents a fork where the changeset from the lines below is a
4503 parent of the 'o' merge on the same line.
4502 parent of the 'o' merge on the same line.
4504
4503
4505 .. note::
4504 .. note::
4506
4505
4507 :hg:`log --patch` may generate unexpected diff output for merge
4506 :hg:`log --patch` may generate unexpected diff output for merge
4508 changesets, as it will only compare the merge changeset against
4507 changesets, as it will only compare the merge changeset against
4509 its first parent. Also, only files different from BOTH parents
4508 its first parent. Also, only files different from BOTH parents
4510 will appear in files:.
4509 will appear in files:.
4511
4510
4512 .. note::
4511 .. note::
4513
4512
4514 For performance reasons, :hg:`log FILE` may omit duplicate changes
4513 For performance reasons, :hg:`log FILE` may omit duplicate changes
4515 made on branches and will not show removals or mode changes. To
4514 made on branches and will not show removals or mode changes. To
4516 see all such changes, use the --removed switch.
4515 see all such changes, use the --removed switch.
4517
4516
4518 .. container:: verbose
4517 .. container:: verbose
4519
4518
4520 Some examples:
4519 Some examples:
4521
4520
4522 - changesets with full descriptions and file lists::
4521 - changesets with full descriptions and file lists::
4523
4522
4524 hg log -v
4523 hg log -v
4525
4524
4526 - changesets ancestral to the working directory::
4525 - changesets ancestral to the working directory::
4527
4526
4528 hg log -f
4527 hg log -f
4529
4528
4530 - last 10 commits on the current branch::
4529 - last 10 commits on the current branch::
4531
4530
4532 hg log -l 10 -b .
4531 hg log -l 10 -b .
4533
4532
4534 - changesets showing all modifications of a file, including removals::
4533 - changesets showing all modifications of a file, including removals::
4535
4534
4536 hg log --removed file.c
4535 hg log --removed file.c
4537
4536
4538 - all changesets that touch a directory, with diffs, excluding merges::
4537 - all changesets that touch a directory, with diffs, excluding merges::
4539
4538
4540 hg log -Mp lib/
4539 hg log -Mp lib/
4541
4540
4542 - all revision numbers that match a keyword::
4541 - all revision numbers that match a keyword::
4543
4542
4544 hg log -k bug --template "{rev}\\n"
4543 hg log -k bug --template "{rev}\\n"
4545
4544
4546 - the full hash identifier of the working directory parent::
4545 - the full hash identifier of the working directory parent::
4547
4546
4548 hg log -r . --template "{node}\\n"
4547 hg log -r . --template "{node}\\n"
4549
4548
4550 - list available log templates::
4549 - list available log templates::
4551
4550
4552 hg log -T list
4551 hg log -T list
4553
4552
4554 - check if a given changeset is included in a tagged release::
4553 - check if a given changeset is included in a tagged release::
4555
4554
4556 hg log -r "a21ccf and ancestor(1.9)"
4555 hg log -r "a21ccf and ancestor(1.9)"
4557
4556
4558 - find all changesets by some user in a date range::
4557 - find all changesets by some user in a date range::
4559
4558
4560 hg log -k alice -d "may 2008 to jul 2008"
4559 hg log -k alice -d "may 2008 to jul 2008"
4561
4560
4562 - summary of all changesets after the last tag::
4561 - summary of all changesets after the last tag::
4563
4562
4564 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
4563 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
4565
4564
4566 See :hg:`help dates` for a list of formats valid for -d/--date.
4565 See :hg:`help dates` for a list of formats valid for -d/--date.
4567
4566
4568 See :hg:`help revisions` and :hg:`help revsets` for more about
4567 See :hg:`help revisions` and :hg:`help revsets` for more about
4569 specifying and ordering revisions.
4568 specifying and ordering revisions.
4570
4569
4571 See :hg:`help templates` for more about pre-packaged styles and
4570 See :hg:`help templates` for more about pre-packaged styles and
4572 specifying custom templates.
4571 specifying custom templates.
4573
4572
4574 Returns 0 on success.
4573 Returns 0 on success.
4575
4574
4576 """
4575 """
4577 if opts.get('follow') and opts.get('rev'):
4576 if opts.get('follow') and opts.get('rev'):
4578 opts['rev'] = [revset.formatspec('reverse(::%lr)', opts.get('rev'))]
4577 opts['rev'] = [revset.formatspec('reverse(::%lr)', opts.get('rev'))]
4579 del opts['follow']
4578 del opts['follow']
4580
4579
4581 if opts.get('graph'):
4580 if opts.get('graph'):
4582 return cmdutil.graphlog(ui, repo, *pats, **opts)
4581 return cmdutil.graphlog(ui, repo, *pats, **opts)
4583
4582
4584 revs, expr, filematcher = cmdutil.getlogrevs(repo, pats, opts)
4583 revs, expr, filematcher = cmdutil.getlogrevs(repo, pats, opts)
4585 limit = cmdutil.loglimit(opts)
4584 limit = cmdutil.loglimit(opts)
4586 count = 0
4585 count = 0
4587
4586
4588 getrenamed = None
4587 getrenamed = None
4589 if opts.get('copies'):
4588 if opts.get('copies'):
4590 endrev = None
4589 endrev = None
4591 if opts.get('rev'):
4590 if opts.get('rev'):
4592 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
4591 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
4593 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
4592 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
4594
4593
4595 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
4594 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
4596 for rev in revs:
4595 for rev in revs:
4597 if count == limit:
4596 if count == limit:
4598 break
4597 break
4599 ctx = repo[rev]
4598 ctx = repo[rev]
4600 copies = None
4599 copies = None
4601 if getrenamed is not None and rev:
4600 if getrenamed is not None and rev:
4602 copies = []
4601 copies = []
4603 for fn in ctx.files():
4602 for fn in ctx.files():
4604 rename = getrenamed(fn, rev)
4603 rename = getrenamed(fn, rev)
4605 if rename:
4604 if rename:
4606 copies.append((fn, rename[0]))
4605 copies.append((fn, rename[0]))
4607 if filematcher:
4606 if filematcher:
4608 revmatchfn = filematcher(ctx.rev())
4607 revmatchfn = filematcher(ctx.rev())
4609 else:
4608 else:
4610 revmatchfn = None
4609 revmatchfn = None
4611 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
4610 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
4612 if displayer.flush(ctx):
4611 if displayer.flush(ctx):
4613 count += 1
4612 count += 1
4614
4613
4615 displayer.close()
4614 displayer.close()
4616
4615
4617 @command('manifest',
4616 @command('manifest',
4618 [('r', 'rev', '', _('revision to display'), _('REV')),
4617 [('r', 'rev', '', _('revision to display'), _('REV')),
4619 ('', 'all', False, _("list files from all revisions"))]
4618 ('', 'all', False, _("list files from all revisions"))]
4620 + formatteropts,
4619 + formatteropts,
4621 _('[-r REV]'))
4620 _('[-r REV]'))
4622 def manifest(ui, repo, node=None, rev=None, **opts):
4621 def manifest(ui, repo, node=None, rev=None, **opts):
4623 """output the current or given revision of the project manifest
4622 """output the current or given revision of the project manifest
4624
4623
4625 Print a list of version controlled files for the given revision.
4624 Print a list of version controlled files for the given revision.
4626 If no revision is given, the first parent of the working directory
4625 If no revision is given, the first parent of the working directory
4627 is used, or the null revision if no revision is checked out.
4626 is used, or the null revision if no revision is checked out.
4628
4627
4629 With -v, print file permissions, symlink and executable bits.
4628 With -v, print file permissions, symlink and executable bits.
4630 With --debug, print file revision hashes.
4629 With --debug, print file revision hashes.
4631
4630
4632 If option --all is specified, the list of all files from all revisions
4631 If option --all is specified, the list of all files from all revisions
4633 is printed. This includes deleted and renamed files.
4632 is printed. This includes deleted and renamed files.
4634
4633
4635 Returns 0 on success.
4634 Returns 0 on success.
4636 """
4635 """
4637
4636
4638 fm = ui.formatter('manifest', opts)
4637 fm = ui.formatter('manifest', opts)
4639
4638
4640 if opts.get('all'):
4639 if opts.get('all'):
4641 if rev or node:
4640 if rev or node:
4642 raise error.Abort(_("can't specify a revision with --all"))
4641 raise error.Abort(_("can't specify a revision with --all"))
4643
4642
4644 res = []
4643 res = []
4645 prefix = "data/"
4644 prefix = "data/"
4646 suffix = ".i"
4645 suffix = ".i"
4647 plen = len(prefix)
4646 plen = len(prefix)
4648 slen = len(suffix)
4647 slen = len(suffix)
4649 with repo.lock():
4648 with repo.lock():
4650 for fn, b, size in repo.store.datafiles():
4649 for fn, b, size in repo.store.datafiles():
4651 if size != 0 and fn[-slen:] == suffix and fn[:plen] == prefix:
4650 if size != 0 and fn[-slen:] == suffix and fn[:plen] == prefix:
4652 res.append(fn[plen:-slen])
4651 res.append(fn[plen:-slen])
4653 for f in res:
4652 for f in res:
4654 fm.startitem()
4653 fm.startitem()
4655 fm.write("path", '%s\n', f)
4654 fm.write("path", '%s\n', f)
4656 fm.end()
4655 fm.end()
4657 return
4656 return
4658
4657
4659 if rev and node:
4658 if rev and node:
4660 raise error.Abort(_("please specify just one revision"))
4659 raise error.Abort(_("please specify just one revision"))
4661
4660
4662 if not node:
4661 if not node:
4663 node = rev
4662 node = rev
4664
4663
4665 char = {'l': '@', 'x': '*', '': ''}
4664 char = {'l': '@', 'x': '*', '': ''}
4666 mode = {'l': '644', 'x': '755', '': '644'}
4665 mode = {'l': '644', 'x': '755', '': '644'}
4667 ctx = scmutil.revsingle(repo, node)
4666 ctx = scmutil.revsingle(repo, node)
4668 mf = ctx.manifest()
4667 mf = ctx.manifest()
4669 for f in ctx:
4668 for f in ctx:
4670 fm.startitem()
4669 fm.startitem()
4671 fl = ctx[f].flags()
4670 fl = ctx[f].flags()
4672 fm.condwrite(ui.debugflag, 'hash', '%s ', hex(mf[f]))
4671 fm.condwrite(ui.debugflag, 'hash', '%s ', hex(mf[f]))
4673 fm.condwrite(ui.verbose, 'mode type', '%s %1s ', mode[fl], char[fl])
4672 fm.condwrite(ui.verbose, 'mode type', '%s %1s ', mode[fl], char[fl])
4674 fm.write('path', '%s\n', f)
4673 fm.write('path', '%s\n', f)
4675 fm.end()
4674 fm.end()
4676
4675
4677 @command('^merge',
4676 @command('^merge',
4678 [('f', 'force', None,
4677 [('f', 'force', None,
4679 _('force a merge including outstanding changes (DEPRECATED)')),
4678 _('force a merge including outstanding changes (DEPRECATED)')),
4680 ('r', 'rev', '', _('revision to merge'), _('REV')),
4679 ('r', 'rev', '', _('revision to merge'), _('REV')),
4681 ('P', 'preview', None,
4680 ('P', 'preview', None,
4682 _('review revisions to merge (no merge is performed)'))
4681 _('review revisions to merge (no merge is performed)'))
4683 ] + mergetoolopts,
4682 ] + mergetoolopts,
4684 _('[-P] [[-r] REV]'))
4683 _('[-P] [[-r] REV]'))
4685 def merge(ui, repo, node=None, **opts):
4684 def merge(ui, repo, node=None, **opts):
4686 """merge another revision into working directory
4685 """merge another revision into working directory
4687
4686
4688 The current working directory is updated with all changes made in
4687 The current working directory is updated with all changes made in
4689 the requested revision since the last common predecessor revision.
4688 the requested revision since the last common predecessor revision.
4690
4689
4691 Files that changed between either parent are marked as changed for
4690 Files that changed between either parent are marked as changed for
4692 the next commit and a commit must be performed before any further
4691 the next commit and a commit must be performed before any further
4693 updates to the repository are allowed. The next commit will have
4692 updates to the repository are allowed. The next commit will have
4694 two parents.
4693 two parents.
4695
4694
4696 ``--tool`` can be used to specify the merge tool used for file
4695 ``--tool`` can be used to specify the merge tool used for file
4697 merges. It overrides the HGMERGE environment variable and your
4696 merges. It overrides the HGMERGE environment variable and your
4698 configuration files. See :hg:`help merge-tools` for options.
4697 configuration files. See :hg:`help merge-tools` for options.
4699
4698
4700 If no revision is specified, the working directory's parent is a
4699 If no revision is specified, the working directory's parent is a
4701 head revision, and the current branch contains exactly one other
4700 head revision, and the current branch contains exactly one other
4702 head, the other head is merged with by default. Otherwise, an
4701 head, the other head is merged with by default. Otherwise, an
4703 explicit revision with which to merge with must be provided.
4702 explicit revision with which to merge with must be provided.
4704
4703
4705 See :hg:`help resolve` for information on handling file conflicts.
4704 See :hg:`help resolve` for information on handling file conflicts.
4706
4705
4707 To undo an uncommitted merge, use :hg:`update --clean .` which
4706 To undo an uncommitted merge, use :hg:`update --clean .` which
4708 will check out a clean copy of the original merge parent, losing
4707 will check out a clean copy of the original merge parent, losing
4709 all changes.
4708 all changes.
4710
4709
4711 Returns 0 on success, 1 if there are unresolved files.
4710 Returns 0 on success, 1 if there are unresolved files.
4712 """
4711 """
4713
4712
4714 if opts.get('rev') and node:
4713 if opts.get('rev') and node:
4715 raise error.Abort(_("please specify just one revision"))
4714 raise error.Abort(_("please specify just one revision"))
4716 if not node:
4715 if not node:
4717 node = opts.get('rev')
4716 node = opts.get('rev')
4718
4717
4719 if node:
4718 if node:
4720 node = scmutil.revsingle(repo, node).node()
4719 node = scmutil.revsingle(repo, node).node()
4721
4720
4722 if not node:
4721 if not node:
4723 node = repo[destutil.destmerge(repo)].node()
4722 node = repo[destutil.destmerge(repo)].node()
4724
4723
4725 if opts.get('preview'):
4724 if opts.get('preview'):
4726 # find nodes that are ancestors of p2 but not of p1
4725 # find nodes that are ancestors of p2 but not of p1
4727 p1 = repo.lookup('.')
4726 p1 = repo.lookup('.')
4728 p2 = repo.lookup(node)
4727 p2 = repo.lookup(node)
4729 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4728 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4730
4729
4731 displayer = cmdutil.show_changeset(ui, repo, opts)
4730 displayer = cmdutil.show_changeset(ui, repo, opts)
4732 for node in nodes:
4731 for node in nodes:
4733 displayer.show(repo[node])
4732 displayer.show(repo[node])
4734 displayer.close()
4733 displayer.close()
4735 return 0
4734 return 0
4736
4735
4737 try:
4736 try:
4738 # ui.forcemerge is an internal variable, do not document
4737 # ui.forcemerge is an internal variable, do not document
4739 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''), 'merge')
4738 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''), 'merge')
4740 force = opts.get('force')
4739 force = opts.get('force')
4741 labels = ['working copy', 'merge rev']
4740 labels = ['working copy', 'merge rev']
4742 return hg.merge(repo, node, force=force, mergeforce=force,
4741 return hg.merge(repo, node, force=force, mergeforce=force,
4743 labels=labels)
4742 labels=labels)
4744 finally:
4743 finally:
4745 ui.setconfig('ui', 'forcemerge', '', 'merge')
4744 ui.setconfig('ui', 'forcemerge', '', 'merge')
4746
4745
4747 @command('outgoing|out',
4746 @command('outgoing|out',
4748 [('f', 'force', None, _('run even when the destination is unrelated')),
4747 [('f', 'force', None, _('run even when the destination is unrelated')),
4749 ('r', 'rev', [],
4748 ('r', 'rev', [],
4750 _('a changeset intended to be included in the destination'), _('REV')),
4749 _('a changeset intended to be included in the destination'), _('REV')),
4751 ('n', 'newest-first', None, _('show newest record first')),
4750 ('n', 'newest-first', None, _('show newest record first')),
4752 ('B', 'bookmarks', False, _('compare bookmarks')),
4751 ('B', 'bookmarks', False, _('compare bookmarks')),
4753 ('b', 'branch', [], _('a specific branch you would like to push'),
4752 ('b', 'branch', [], _('a specific branch you would like to push'),
4754 _('BRANCH')),
4753 _('BRANCH')),
4755 ] + logopts + remoteopts + subrepoopts,
4754 ] + logopts + remoteopts + subrepoopts,
4756 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
4755 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
4757 def outgoing(ui, repo, dest=None, **opts):
4756 def outgoing(ui, repo, dest=None, **opts):
4758 """show changesets not found in the destination
4757 """show changesets not found in the destination
4759
4758
4760 Show changesets not found in the specified destination repository
4759 Show changesets not found in the specified destination repository
4761 or the default push location. These are the changesets that would
4760 or the default push location. These are the changesets that would
4762 be pushed if a push was requested.
4761 be pushed if a push was requested.
4763
4762
4764 See pull for details of valid destination formats.
4763 See pull for details of valid destination formats.
4765
4764
4766 .. container:: verbose
4765 .. container:: verbose
4767
4766
4768 With -B/--bookmarks, the result of bookmark comparison between
4767 With -B/--bookmarks, the result of bookmark comparison between
4769 local and remote repositories is displayed. With -v/--verbose,
4768 local and remote repositories is displayed. With -v/--verbose,
4770 status is also displayed for each bookmark like below::
4769 status is also displayed for each bookmark like below::
4771
4770
4772 BM1 01234567890a added
4771 BM1 01234567890a added
4773 BM2 deleted
4772 BM2 deleted
4774 BM3 234567890abc advanced
4773 BM3 234567890abc advanced
4775 BM4 34567890abcd diverged
4774 BM4 34567890abcd diverged
4776 BM5 4567890abcde changed
4775 BM5 4567890abcde changed
4777
4776
4778 The action taken when pushing depends on the
4777 The action taken when pushing depends on the
4779 status of each bookmark:
4778 status of each bookmark:
4780
4779
4781 :``added``: push with ``-B`` will create it
4780 :``added``: push with ``-B`` will create it
4782 :``deleted``: push with ``-B`` will delete it
4781 :``deleted``: push with ``-B`` will delete it
4783 :``advanced``: push will update it
4782 :``advanced``: push will update it
4784 :``diverged``: push with ``-B`` will update it
4783 :``diverged``: push with ``-B`` will update it
4785 :``changed``: push with ``-B`` will update it
4784 :``changed``: push with ``-B`` will update it
4786
4785
4787 From the point of view of pushing behavior, bookmarks
4786 From the point of view of pushing behavior, bookmarks
4788 existing only in the remote repository are treated as
4787 existing only in the remote repository are treated as
4789 ``deleted``, even if it is in fact added remotely.
4788 ``deleted``, even if it is in fact added remotely.
4790
4789
4791 Returns 0 if there are outgoing changes, 1 otherwise.
4790 Returns 0 if there are outgoing changes, 1 otherwise.
4792 """
4791 """
4793 if opts.get('graph'):
4792 if opts.get('graph'):
4794 cmdutil.checkunsupportedgraphflags([], opts)
4793 cmdutil.checkunsupportedgraphflags([], opts)
4795 o, other = hg._outgoing(ui, repo, dest, opts)
4794 o, other = hg._outgoing(ui, repo, dest, opts)
4796 if not o:
4795 if not o:
4797 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4796 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4798 return
4797 return
4799
4798
4800 revdag = cmdutil.graphrevs(repo, o, opts)
4799 revdag = cmdutil.graphrevs(repo, o, opts)
4801 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
4800 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
4802 cmdutil.displaygraph(ui, repo, revdag, displayer, graphmod.asciiedges)
4801 cmdutil.displaygraph(ui, repo, revdag, displayer, graphmod.asciiedges)
4803 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4802 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4804 return 0
4803 return 0
4805
4804
4806 if opts.get('bookmarks'):
4805 if opts.get('bookmarks'):
4807 dest = ui.expandpath(dest or 'default-push', dest or 'default')
4806 dest = ui.expandpath(dest or 'default-push', dest or 'default')
4808 dest, branches = hg.parseurl(dest, opts.get('branch'))
4807 dest, branches = hg.parseurl(dest, opts.get('branch'))
4809 other = hg.peer(repo, opts, dest)
4808 other = hg.peer(repo, opts, dest)
4810 if 'bookmarks' not in other.listkeys('namespaces'):
4809 if 'bookmarks' not in other.listkeys('namespaces'):
4811 ui.warn(_("remote doesn't support bookmarks\n"))
4810 ui.warn(_("remote doesn't support bookmarks\n"))
4812 return 0
4811 return 0
4813 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
4812 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
4814 return bookmarks.outgoing(ui, repo, other)
4813 return bookmarks.outgoing(ui, repo, other)
4815
4814
4816 repo._subtoppath = ui.expandpath(dest or 'default-push', dest or 'default')
4815 repo._subtoppath = ui.expandpath(dest or 'default-push', dest or 'default')
4817 try:
4816 try:
4818 return hg.outgoing(ui, repo, dest, opts)
4817 return hg.outgoing(ui, repo, dest, opts)
4819 finally:
4818 finally:
4820 del repo._subtoppath
4819 del repo._subtoppath
4821
4820
4822 @command('parents',
4821 @command('parents',
4823 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
4822 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
4824 ] + templateopts,
4823 ] + templateopts,
4825 _('[-r REV] [FILE]'),
4824 _('[-r REV] [FILE]'),
4826 inferrepo=True)
4825 inferrepo=True)
4827 def parents(ui, repo, file_=None, **opts):
4826 def parents(ui, repo, file_=None, **opts):
4828 """show the parents of the working directory or revision (DEPRECATED)
4827 """show the parents of the working directory or revision (DEPRECATED)
4829
4828
4830 Print the working directory's parent revisions. If a revision is
4829 Print the working directory's parent revisions. If a revision is
4831 given via -r/--rev, the parent of that revision will be printed.
4830 given via -r/--rev, the parent of that revision will be printed.
4832 If a file argument is given, the revision in which the file was
4831 If a file argument is given, the revision in which the file was
4833 last changed (before the working directory revision or the
4832 last changed (before the working directory revision or the
4834 argument to --rev if given) is printed.
4833 argument to --rev if given) is printed.
4835
4834
4836 This command is equivalent to::
4835 This command is equivalent to::
4837
4836
4838 hg log -r "p1()+p2()" or
4837 hg log -r "p1()+p2()" or
4839 hg log -r "p1(REV)+p2(REV)" or
4838 hg log -r "p1(REV)+p2(REV)" or
4840 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
4839 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
4841 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
4840 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
4842
4841
4843 See :hg:`summary` and :hg:`help revsets` for related information.
4842 See :hg:`summary` and :hg:`help revsets` for related information.
4844
4843
4845 Returns 0 on success.
4844 Returns 0 on success.
4846 """
4845 """
4847
4846
4848 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
4847 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
4849
4848
4850 if file_:
4849 if file_:
4851 m = scmutil.match(ctx, (file_,), opts)
4850 m = scmutil.match(ctx, (file_,), opts)
4852 if m.anypats() or len(m.files()) != 1:
4851 if m.anypats() or len(m.files()) != 1:
4853 raise error.Abort(_('can only specify an explicit filename'))
4852 raise error.Abort(_('can only specify an explicit filename'))
4854 file_ = m.files()[0]
4853 file_ = m.files()[0]
4855 filenodes = []
4854 filenodes = []
4856 for cp in ctx.parents():
4855 for cp in ctx.parents():
4857 if not cp:
4856 if not cp:
4858 continue
4857 continue
4859 try:
4858 try:
4860 filenodes.append(cp.filenode(file_))
4859 filenodes.append(cp.filenode(file_))
4861 except error.LookupError:
4860 except error.LookupError:
4862 pass
4861 pass
4863 if not filenodes:
4862 if not filenodes:
4864 raise error.Abort(_("'%s' not found in manifest!") % file_)
4863 raise error.Abort(_("'%s' not found in manifest!") % file_)
4865 p = []
4864 p = []
4866 for fn in filenodes:
4865 for fn in filenodes:
4867 fctx = repo.filectx(file_, fileid=fn)
4866 fctx = repo.filectx(file_, fileid=fn)
4868 p.append(fctx.node())
4867 p.append(fctx.node())
4869 else:
4868 else:
4870 p = [cp.node() for cp in ctx.parents()]
4869 p = [cp.node() for cp in ctx.parents()]
4871
4870
4872 displayer = cmdutil.show_changeset(ui, repo, opts)
4871 displayer = cmdutil.show_changeset(ui, repo, opts)
4873 for n in p:
4872 for n in p:
4874 if n != nullid:
4873 if n != nullid:
4875 displayer.show(repo[n])
4874 displayer.show(repo[n])
4876 displayer.close()
4875 displayer.close()
4877
4876
4878 @command('paths', formatteropts, _('[NAME]'), optionalrepo=True)
4877 @command('paths', formatteropts, _('[NAME]'), optionalrepo=True)
4879 def paths(ui, repo, search=None, **opts):
4878 def paths(ui, repo, search=None, **opts):
4880 """show aliases for remote repositories
4879 """show aliases for remote repositories
4881
4880
4882 Show definition of symbolic path name NAME. If no name is given,
4881 Show definition of symbolic path name NAME. If no name is given,
4883 show definition of all available names.
4882 show definition of all available names.
4884
4883
4885 Option -q/--quiet suppresses all output when searching for NAME
4884 Option -q/--quiet suppresses all output when searching for NAME
4886 and shows only the path names when listing all definitions.
4885 and shows only the path names when listing all definitions.
4887
4886
4888 Path names are defined in the [paths] section of your
4887 Path names are defined in the [paths] section of your
4889 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
4888 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
4890 repository, ``.hg/hgrc`` is used, too.
4889 repository, ``.hg/hgrc`` is used, too.
4891
4890
4892 The path names ``default`` and ``default-push`` have a special
4891 The path names ``default`` and ``default-push`` have a special
4893 meaning. When performing a push or pull operation, they are used
4892 meaning. When performing a push or pull operation, they are used
4894 as fallbacks if no location is specified on the command-line.
4893 as fallbacks if no location is specified on the command-line.
4895 When ``default-push`` is set, it will be used for push and
4894 When ``default-push`` is set, it will be used for push and
4896 ``default`` will be used for pull; otherwise ``default`` is used
4895 ``default`` will be used for pull; otherwise ``default`` is used
4897 as the fallback for both. When cloning a repository, the clone
4896 as the fallback for both. When cloning a repository, the clone
4898 source is written as ``default`` in ``.hg/hgrc``.
4897 source is written as ``default`` in ``.hg/hgrc``.
4899
4898
4900 .. note::
4899 .. note::
4901
4900
4902 ``default`` and ``default-push`` apply to all inbound (e.g.
4901 ``default`` and ``default-push`` apply to all inbound (e.g.
4903 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
4902 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
4904 and :hg:`bundle`) operations.
4903 and :hg:`bundle`) operations.
4905
4904
4906 See :hg:`help urls` for more information.
4905 See :hg:`help urls` for more information.
4907
4906
4908 Returns 0 on success.
4907 Returns 0 on success.
4909 """
4908 """
4910 if search:
4909 if search:
4911 pathitems = [(name, path) for name, path in ui.paths.iteritems()
4910 pathitems = [(name, path) for name, path in ui.paths.iteritems()
4912 if name == search]
4911 if name == search]
4913 else:
4912 else:
4914 pathitems = sorted(ui.paths.iteritems())
4913 pathitems = sorted(ui.paths.iteritems())
4915
4914
4916 fm = ui.formatter('paths', opts)
4915 fm = ui.formatter('paths', opts)
4917 if fm.isplain():
4916 if fm.isplain():
4918 hidepassword = util.hidepassword
4917 hidepassword = util.hidepassword
4919 else:
4918 else:
4920 hidepassword = str
4919 hidepassword = str
4921 if ui.quiet:
4920 if ui.quiet:
4922 namefmt = '%s\n'
4921 namefmt = '%s\n'
4923 else:
4922 else:
4924 namefmt = '%s = '
4923 namefmt = '%s = '
4925 showsubopts = not search and not ui.quiet
4924 showsubopts = not search and not ui.quiet
4926
4925
4927 for name, path in pathitems:
4926 for name, path in pathitems:
4928 fm.startitem()
4927 fm.startitem()
4929 fm.condwrite(not search, 'name', namefmt, name)
4928 fm.condwrite(not search, 'name', namefmt, name)
4930 fm.condwrite(not ui.quiet, 'url', '%s\n', hidepassword(path.rawloc))
4929 fm.condwrite(not ui.quiet, 'url', '%s\n', hidepassword(path.rawloc))
4931 for subopt, value in sorted(path.suboptions.items()):
4930 for subopt, value in sorted(path.suboptions.items()):
4932 assert subopt not in ('name', 'url')
4931 assert subopt not in ('name', 'url')
4933 if showsubopts:
4932 if showsubopts:
4934 fm.plain('%s:%s = ' % (name, subopt))
4933 fm.plain('%s:%s = ' % (name, subopt))
4935 fm.condwrite(showsubopts, subopt, '%s\n', value)
4934 fm.condwrite(showsubopts, subopt, '%s\n', value)
4936
4935
4937 fm.end()
4936 fm.end()
4938
4937
4939 if search and not pathitems:
4938 if search and not pathitems:
4940 if not ui.quiet:
4939 if not ui.quiet:
4941 ui.warn(_("not found!\n"))
4940 ui.warn(_("not found!\n"))
4942 return 1
4941 return 1
4943 else:
4942 else:
4944 return 0
4943 return 0
4945
4944
4946 @command('phase',
4945 @command('phase',
4947 [('p', 'public', False, _('set changeset phase to public')),
4946 [('p', 'public', False, _('set changeset phase to public')),
4948 ('d', 'draft', False, _('set changeset phase to draft')),
4947 ('d', 'draft', False, _('set changeset phase to draft')),
4949 ('s', 'secret', False, _('set changeset phase to secret')),
4948 ('s', 'secret', False, _('set changeset phase to secret')),
4950 ('f', 'force', False, _('allow to move boundary backward')),
4949 ('f', 'force', False, _('allow to move boundary backward')),
4951 ('r', 'rev', [], _('target revision'), _('REV')),
4950 ('r', 'rev', [], _('target revision'), _('REV')),
4952 ],
4951 ],
4953 _('[-p|-d|-s] [-f] [-r] [REV...]'))
4952 _('[-p|-d|-s] [-f] [-r] [REV...]'))
4954 def phase(ui, repo, *revs, **opts):
4953 def phase(ui, repo, *revs, **opts):
4955 """set or show the current phase name
4954 """set or show the current phase name
4956
4955
4957 With no argument, show the phase name of the current revision(s).
4956 With no argument, show the phase name of the current revision(s).
4958
4957
4959 With one of -p/--public, -d/--draft or -s/--secret, change the
4958 With one of -p/--public, -d/--draft or -s/--secret, change the
4960 phase value of the specified revisions.
4959 phase value of the specified revisions.
4961
4960
4962 Unless -f/--force is specified, :hg:`phase` won't move changeset from a
4961 Unless -f/--force is specified, :hg:`phase` won't move changeset from a
4963 lower phase to an higher phase. Phases are ordered as follows::
4962 lower phase to an higher phase. Phases are ordered as follows::
4964
4963
4965 public < draft < secret
4964 public < draft < secret
4966
4965
4967 Returns 0 on success, 1 if some phases could not be changed.
4966 Returns 0 on success, 1 if some phases could not be changed.
4968
4967
4969 (For more information about the phases concept, see :hg:`help phases`.)
4968 (For more information about the phases concept, see :hg:`help phases`.)
4970 """
4969 """
4971 # search for a unique phase argument
4970 # search for a unique phase argument
4972 targetphase = None
4971 targetphase = None
4973 for idx, name in enumerate(phases.phasenames):
4972 for idx, name in enumerate(phases.phasenames):
4974 if opts[name]:
4973 if opts[name]:
4975 if targetphase is not None:
4974 if targetphase is not None:
4976 raise error.Abort(_('only one phase can be specified'))
4975 raise error.Abort(_('only one phase can be specified'))
4977 targetphase = idx
4976 targetphase = idx
4978
4977
4979 # look for specified revision
4978 # look for specified revision
4980 revs = list(revs)
4979 revs = list(revs)
4981 revs.extend(opts['rev'])
4980 revs.extend(opts['rev'])
4982 if not revs:
4981 if not revs:
4983 # display both parents as the second parent phase can influence
4982 # display both parents as the second parent phase can influence
4984 # the phase of a merge commit
4983 # the phase of a merge commit
4985 revs = [c.rev() for c in repo[None].parents()]
4984 revs = [c.rev() for c in repo[None].parents()]
4986
4985
4987 revs = scmutil.revrange(repo, revs)
4986 revs = scmutil.revrange(repo, revs)
4988
4987
4989 lock = None
4988 lock = None
4990 ret = 0
4989 ret = 0
4991 if targetphase is None:
4990 if targetphase is None:
4992 # display
4991 # display
4993 for r in revs:
4992 for r in revs:
4994 ctx = repo[r]
4993 ctx = repo[r]
4995 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
4994 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
4996 else:
4995 else:
4997 tr = None
4996 tr = None
4998 lock = repo.lock()
4997 lock = repo.lock()
4999 try:
4998 try:
5000 tr = repo.transaction("phase")
4999 tr = repo.transaction("phase")
5001 # set phase
5000 # set phase
5002 if not revs:
5001 if not revs:
5003 raise error.Abort(_('empty revision set'))
5002 raise error.Abort(_('empty revision set'))
5004 nodes = [repo[r].node() for r in revs]
5003 nodes = [repo[r].node() for r in revs]
5005 # moving revision from public to draft may hide them
5004 # moving revision from public to draft may hide them
5006 # We have to check result on an unfiltered repository
5005 # We have to check result on an unfiltered repository
5007 unfi = repo.unfiltered()
5006 unfi = repo.unfiltered()
5008 getphase = unfi._phasecache.phase
5007 getphase = unfi._phasecache.phase
5009 olddata = [getphase(unfi, r) for r in unfi]
5008 olddata = [getphase(unfi, r) for r in unfi]
5010 phases.advanceboundary(repo, tr, targetphase, nodes)
5009 phases.advanceboundary(repo, tr, targetphase, nodes)
5011 if opts['force']:
5010 if opts['force']:
5012 phases.retractboundary(repo, tr, targetphase, nodes)
5011 phases.retractboundary(repo, tr, targetphase, nodes)
5013 tr.close()
5012 tr.close()
5014 finally:
5013 finally:
5015 if tr is not None:
5014 if tr is not None:
5016 tr.release()
5015 tr.release()
5017 lock.release()
5016 lock.release()
5018 getphase = unfi._phasecache.phase
5017 getphase = unfi._phasecache.phase
5019 newdata = [getphase(unfi, r) for r in unfi]
5018 newdata = [getphase(unfi, r) for r in unfi]
5020 changes = sum(newdata[r] != olddata[r] for r in unfi)
5019 changes = sum(newdata[r] != olddata[r] for r in unfi)
5021 cl = unfi.changelog
5020 cl = unfi.changelog
5022 rejected = [n for n in nodes
5021 rejected = [n for n in nodes
5023 if newdata[cl.rev(n)] < targetphase]
5022 if newdata[cl.rev(n)] < targetphase]
5024 if rejected:
5023 if rejected:
5025 ui.warn(_('cannot move %i changesets to a higher '
5024 ui.warn(_('cannot move %i changesets to a higher '
5026 'phase, use --force\n') % len(rejected))
5025 'phase, use --force\n') % len(rejected))
5027 ret = 1
5026 ret = 1
5028 if changes:
5027 if changes:
5029 msg = _('phase changed for %i changesets\n') % changes
5028 msg = _('phase changed for %i changesets\n') % changes
5030 if ret:
5029 if ret:
5031 ui.status(msg)
5030 ui.status(msg)
5032 else:
5031 else:
5033 ui.note(msg)
5032 ui.note(msg)
5034 else:
5033 else:
5035 ui.warn(_('no phases changed\n'))
5034 ui.warn(_('no phases changed\n'))
5036 return ret
5035 return ret
5037
5036
5038 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
5037 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
5039 """Run after a changegroup has been added via pull/unbundle
5038 """Run after a changegroup has been added via pull/unbundle
5040
5039
5041 This takes arguments below:
5040 This takes arguments below:
5042
5041
5043 :modheads: change of heads by pull/unbundle
5042 :modheads: change of heads by pull/unbundle
5044 :optupdate: updating working directory is needed or not
5043 :optupdate: updating working directory is needed or not
5045 :checkout: update destination revision (or None to default destination)
5044 :checkout: update destination revision (or None to default destination)
5046 :brev: a name, which might be a bookmark to be activated after updating
5045 :brev: a name, which might be a bookmark to be activated after updating
5047 """
5046 """
5048 if modheads == 0:
5047 if modheads == 0:
5049 return
5048 return
5050 if optupdate:
5049 if optupdate:
5051 try:
5050 try:
5052 return hg.updatetotally(ui, repo, checkout, brev)
5051 return hg.updatetotally(ui, repo, checkout, brev)
5053 except error.UpdateAbort as inst:
5052 except error.UpdateAbort as inst:
5054 msg = _("not updating: %s") % str(inst)
5053 msg = _("not updating: %s") % str(inst)
5055 hint = inst.hint
5054 hint = inst.hint
5056 raise error.UpdateAbort(msg, hint=hint)
5055 raise error.UpdateAbort(msg, hint=hint)
5057 if modheads > 1:
5056 if modheads > 1:
5058 currentbranchheads = len(repo.branchheads())
5057 currentbranchheads = len(repo.branchheads())
5059 if currentbranchheads == modheads:
5058 if currentbranchheads == modheads:
5060 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
5059 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
5061 elif currentbranchheads > 1:
5060 elif currentbranchheads > 1:
5062 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to "
5061 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to "
5063 "merge)\n"))
5062 "merge)\n"))
5064 else:
5063 else:
5065 ui.status(_("(run 'hg heads' to see heads)\n"))
5064 ui.status(_("(run 'hg heads' to see heads)\n"))
5066 else:
5065 else:
5067 ui.status(_("(run 'hg update' to get a working copy)\n"))
5066 ui.status(_("(run 'hg update' to get a working copy)\n"))
5068
5067
5069 @command('^pull',
5068 @command('^pull',
5070 [('u', 'update', None,
5069 [('u', 'update', None,
5071 _('update to new branch head if changesets were pulled')),
5070 _('update to new branch head if changesets were pulled')),
5072 ('f', 'force', None, _('run even when remote repository is unrelated')),
5071 ('f', 'force', None, _('run even when remote repository is unrelated')),
5073 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
5072 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
5074 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
5073 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
5075 ('b', 'branch', [], _('a specific branch you would like to pull'),
5074 ('b', 'branch', [], _('a specific branch you would like to pull'),
5076 _('BRANCH')),
5075 _('BRANCH')),
5077 ] + remoteopts,
5076 ] + remoteopts,
5078 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
5077 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
5079 def pull(ui, repo, source="default", **opts):
5078 def pull(ui, repo, source="default", **opts):
5080 """pull changes from the specified source
5079 """pull changes from the specified source
5081
5080
5082 Pull changes from a remote repository to a local one.
5081 Pull changes from a remote repository to a local one.
5083
5082
5084 This finds all changes from the repository at the specified path
5083 This finds all changes from the repository at the specified path
5085 or URL and adds them to a local repository (the current one unless
5084 or URL and adds them to a local repository (the current one unless
5086 -R is specified). By default, this does not update the copy of the
5085 -R is specified). By default, this does not update the copy of the
5087 project in the working directory.
5086 project in the working directory.
5088
5087
5089 Use :hg:`incoming` if you want to see what would have been added
5088 Use :hg:`incoming` if you want to see what would have been added
5090 by a pull at the time you issued this command. If you then decide
5089 by a pull at the time you issued this command. If you then decide
5091 to add those changes to the repository, you should use :hg:`pull
5090 to add those changes to the repository, you should use :hg:`pull
5092 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
5091 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
5093
5092
5094 If SOURCE is omitted, the 'default' path will be used.
5093 If SOURCE is omitted, the 'default' path will be used.
5095 See :hg:`help urls` for more information.
5094 See :hg:`help urls` for more information.
5096
5095
5097 Specifying bookmark as ``.`` is equivalent to specifying the active
5096 Specifying bookmark as ``.`` is equivalent to specifying the active
5098 bookmark's name.
5097 bookmark's name.
5099
5098
5100 Returns 0 on success, 1 if an update had unresolved files.
5099 Returns 0 on success, 1 if an update had unresolved files.
5101 """
5100 """
5102 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
5101 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
5103 ui.status(_('pulling from %s\n') % util.hidepassword(source))
5102 ui.status(_('pulling from %s\n') % util.hidepassword(source))
5104 other = hg.peer(repo, opts, source)
5103 other = hg.peer(repo, opts, source)
5105 try:
5104 try:
5106 revs, checkout = hg.addbranchrevs(repo, other, branches,
5105 revs, checkout = hg.addbranchrevs(repo, other, branches,
5107 opts.get('rev'))
5106 opts.get('rev'))
5108
5107
5109
5108
5110 pullopargs = {}
5109 pullopargs = {}
5111 if opts.get('bookmark'):
5110 if opts.get('bookmark'):
5112 if not revs:
5111 if not revs:
5113 revs = []
5112 revs = []
5114 # The list of bookmark used here is not the one used to actually
5113 # The list of bookmark used here is not the one used to actually
5115 # update the bookmark name. This can result in the revision pulled
5114 # update the bookmark name. This can result in the revision pulled
5116 # not ending up with the name of the bookmark because of a race
5115 # not ending up with the name of the bookmark because of a race
5117 # condition on the server. (See issue 4689 for details)
5116 # condition on the server. (See issue 4689 for details)
5118 remotebookmarks = other.listkeys('bookmarks')
5117 remotebookmarks = other.listkeys('bookmarks')
5119 pullopargs['remotebookmarks'] = remotebookmarks
5118 pullopargs['remotebookmarks'] = remotebookmarks
5120 for b in opts['bookmark']:
5119 for b in opts['bookmark']:
5121 b = repo._bookmarks.expandname(b)
5120 b = repo._bookmarks.expandname(b)
5122 if b not in remotebookmarks:
5121 if b not in remotebookmarks:
5123 raise error.Abort(_('remote bookmark %s not found!') % b)
5122 raise error.Abort(_('remote bookmark %s not found!') % b)
5124 revs.append(remotebookmarks[b])
5123 revs.append(remotebookmarks[b])
5125
5124
5126 if revs:
5125 if revs:
5127 try:
5126 try:
5128 # When 'rev' is a bookmark name, we cannot guarantee that it
5127 # When 'rev' is a bookmark name, we cannot guarantee that it
5129 # will be updated with that name because of a race condition
5128 # will be updated with that name because of a race condition
5130 # server side. (See issue 4689 for details)
5129 # server side. (See issue 4689 for details)
5131 oldrevs = revs
5130 oldrevs = revs
5132 revs = [] # actually, nodes
5131 revs = [] # actually, nodes
5133 for r in oldrevs:
5132 for r in oldrevs:
5134 node = other.lookup(r)
5133 node = other.lookup(r)
5135 revs.append(node)
5134 revs.append(node)
5136 if r == checkout:
5135 if r == checkout:
5137 checkout = node
5136 checkout = node
5138 except error.CapabilityError:
5137 except error.CapabilityError:
5139 err = _("other repository doesn't support revision lookup, "
5138 err = _("other repository doesn't support revision lookup, "
5140 "so a rev cannot be specified.")
5139 "so a rev cannot be specified.")
5141 raise error.Abort(err)
5140 raise error.Abort(err)
5142
5141
5143 pullopargs.update(opts.get('opargs', {}))
5142 pullopargs.update(opts.get('opargs', {}))
5144 modheads = exchange.pull(repo, other, heads=revs,
5143 modheads = exchange.pull(repo, other, heads=revs,
5145 force=opts.get('force'),
5144 force=opts.get('force'),
5146 bookmarks=opts.get('bookmark', ()),
5145 bookmarks=opts.get('bookmark', ()),
5147 opargs=pullopargs).cgresult
5146 opargs=pullopargs).cgresult
5148
5147
5149 # brev is a name, which might be a bookmark to be activated at
5148 # brev is a name, which might be a bookmark to be activated at
5150 # the end of the update. In other words, it is an explicit
5149 # the end of the update. In other words, it is an explicit
5151 # destination of the update
5150 # destination of the update
5152 brev = None
5151 brev = None
5153
5152
5154 if checkout:
5153 if checkout:
5155 checkout = str(repo.changelog.rev(checkout))
5154 checkout = str(repo.changelog.rev(checkout))
5156
5155
5157 # order below depends on implementation of
5156 # order below depends on implementation of
5158 # hg.addbranchrevs(). opts['bookmark'] is ignored,
5157 # hg.addbranchrevs(). opts['bookmark'] is ignored,
5159 # because 'checkout' is determined without it.
5158 # because 'checkout' is determined without it.
5160 if opts.get('rev'):
5159 if opts.get('rev'):
5161 brev = opts['rev'][0]
5160 brev = opts['rev'][0]
5162 elif opts.get('branch'):
5161 elif opts.get('branch'):
5163 brev = opts['branch'][0]
5162 brev = opts['branch'][0]
5164 else:
5163 else:
5165 brev = branches[0]
5164 brev = branches[0]
5166 repo._subtoppath = source
5165 repo._subtoppath = source
5167 try:
5166 try:
5168 ret = postincoming(ui, repo, modheads, opts.get('update'),
5167 ret = postincoming(ui, repo, modheads, opts.get('update'),
5169 checkout, brev)
5168 checkout, brev)
5170
5169
5171 finally:
5170 finally:
5172 del repo._subtoppath
5171 del repo._subtoppath
5173
5172
5174 finally:
5173 finally:
5175 other.close()
5174 other.close()
5176 return ret
5175 return ret
5177
5176
5178 @command('^push',
5177 @command('^push',
5179 [('f', 'force', None, _('force push')),
5178 [('f', 'force', None, _('force push')),
5180 ('r', 'rev', [],
5179 ('r', 'rev', [],
5181 _('a changeset intended to be included in the destination'),
5180 _('a changeset intended to be included in the destination'),
5182 _('REV')),
5181 _('REV')),
5183 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
5182 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
5184 ('b', 'branch', [],
5183 ('b', 'branch', [],
5185 _('a specific branch you would like to push'), _('BRANCH')),
5184 _('a specific branch you would like to push'), _('BRANCH')),
5186 ('', 'new-branch', False, _('allow pushing a new branch')),
5185 ('', 'new-branch', False, _('allow pushing a new branch')),
5187 ] + remoteopts,
5186 ] + remoteopts,
5188 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
5187 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
5189 def push(ui, repo, dest=None, **opts):
5188 def push(ui, repo, dest=None, **opts):
5190 """push changes to the specified destination
5189 """push changes to the specified destination
5191
5190
5192 Push changesets from the local repository to the specified
5191 Push changesets from the local repository to the specified
5193 destination.
5192 destination.
5194
5193
5195 This operation is symmetrical to pull: it is identical to a pull
5194 This operation is symmetrical to pull: it is identical to a pull
5196 in the destination repository from the current one.
5195 in the destination repository from the current one.
5197
5196
5198 By default, push will not allow creation of new heads at the
5197 By default, push will not allow creation of new heads at the
5199 destination, since multiple heads would make it unclear which head
5198 destination, since multiple heads would make it unclear which head
5200 to use. In this situation, it is recommended to pull and merge
5199 to use. In this situation, it is recommended to pull and merge
5201 before pushing.
5200 before pushing.
5202
5201
5203 Use --new-branch if you want to allow push to create a new named
5202 Use --new-branch if you want to allow push to create a new named
5204 branch that is not present at the destination. This allows you to
5203 branch that is not present at the destination. This allows you to
5205 only create a new branch without forcing other changes.
5204 only create a new branch without forcing other changes.
5206
5205
5207 .. note::
5206 .. note::
5208
5207
5209 Extra care should be taken with the -f/--force option,
5208 Extra care should be taken with the -f/--force option,
5210 which will push all new heads on all branches, an action which will
5209 which will push all new heads on all branches, an action which will
5211 almost always cause confusion for collaborators.
5210 almost always cause confusion for collaborators.
5212
5211
5213 If -r/--rev is used, the specified revision and all its ancestors
5212 If -r/--rev is used, the specified revision and all its ancestors
5214 will be pushed to the remote repository.
5213 will be pushed to the remote repository.
5215
5214
5216 If -B/--bookmark is used, the specified bookmarked revision, its
5215 If -B/--bookmark is used, the specified bookmarked revision, its
5217 ancestors, and the bookmark will be pushed to the remote
5216 ancestors, and the bookmark will be pushed to the remote
5218 repository. Specifying ``.`` is equivalent to specifying the active
5217 repository. Specifying ``.`` is equivalent to specifying the active
5219 bookmark's name.
5218 bookmark's name.
5220
5219
5221 Please see :hg:`help urls` for important details about ``ssh://``
5220 Please see :hg:`help urls` for important details about ``ssh://``
5222 URLs. If DESTINATION is omitted, a default path will be used.
5221 URLs. If DESTINATION is omitted, a default path will be used.
5223
5222
5224 Returns 0 if push was successful, 1 if nothing to push.
5223 Returns 0 if push was successful, 1 if nothing to push.
5225 """
5224 """
5226
5225
5227 if opts.get('bookmark'):
5226 if opts.get('bookmark'):
5228 ui.setconfig('bookmarks', 'pushing', opts['bookmark'], 'push')
5227 ui.setconfig('bookmarks', 'pushing', opts['bookmark'], 'push')
5229 for b in opts['bookmark']:
5228 for b in opts['bookmark']:
5230 # translate -B options to -r so changesets get pushed
5229 # translate -B options to -r so changesets get pushed
5231 b = repo._bookmarks.expandname(b)
5230 b = repo._bookmarks.expandname(b)
5232 if b in repo._bookmarks:
5231 if b in repo._bookmarks:
5233 opts.setdefault('rev', []).append(b)
5232 opts.setdefault('rev', []).append(b)
5234 else:
5233 else:
5235 # if we try to push a deleted bookmark, translate it to null
5234 # if we try to push a deleted bookmark, translate it to null
5236 # this lets simultaneous -r, -b options continue working
5235 # this lets simultaneous -r, -b options continue working
5237 opts.setdefault('rev', []).append("null")
5236 opts.setdefault('rev', []).append("null")
5238
5237
5239 path = ui.paths.getpath(dest, default=('default-push', 'default'))
5238 path = ui.paths.getpath(dest, default=('default-push', 'default'))
5240 if not path:
5239 if not path:
5241 raise error.Abort(_('default repository not configured!'),
5240 raise error.Abort(_('default repository not configured!'),
5242 hint=_("see 'hg help config.paths'"))
5241 hint=_("see 'hg help config.paths'"))
5243 dest = path.pushloc or path.loc
5242 dest = path.pushloc or path.loc
5244 branches = (path.branch, opts.get('branch') or [])
5243 branches = (path.branch, opts.get('branch') or [])
5245 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
5244 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
5246 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
5245 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
5247 other = hg.peer(repo, opts, dest)
5246 other = hg.peer(repo, opts, dest)
5248
5247
5249 if revs:
5248 if revs:
5250 revs = [repo.lookup(r) for r in scmutil.revrange(repo, revs)]
5249 revs = [repo.lookup(r) for r in scmutil.revrange(repo, revs)]
5251 if not revs:
5250 if not revs:
5252 raise error.Abort(_("specified revisions evaluate to an empty set"),
5251 raise error.Abort(_("specified revisions evaluate to an empty set"),
5253 hint=_("use different revision arguments"))
5252 hint=_("use different revision arguments"))
5254 elif path.pushrev:
5253 elif path.pushrev:
5255 # It doesn't make any sense to specify ancestor revisions. So limit
5254 # It doesn't make any sense to specify ancestor revisions. So limit
5256 # to DAG heads to make discovery simpler.
5255 # to DAG heads to make discovery simpler.
5257 expr = revset.formatspec('heads(%r)', path.pushrev)
5256 expr = revset.formatspec('heads(%r)', path.pushrev)
5258 revs = scmutil.revrange(repo, [expr])
5257 revs = scmutil.revrange(repo, [expr])
5259 revs = [repo[rev].node() for rev in revs]
5258 revs = [repo[rev].node() for rev in revs]
5260 if not revs:
5259 if not revs:
5261 raise error.Abort(_('default push revset for path evaluates to an '
5260 raise error.Abort(_('default push revset for path evaluates to an '
5262 'empty set'))
5261 'empty set'))
5263
5262
5264 repo._subtoppath = dest
5263 repo._subtoppath = dest
5265 try:
5264 try:
5266 # push subrepos depth-first for coherent ordering
5265 # push subrepos depth-first for coherent ordering
5267 c = repo['']
5266 c = repo['']
5268 subs = c.substate # only repos that are committed
5267 subs = c.substate # only repos that are committed
5269 for s in sorted(subs):
5268 for s in sorted(subs):
5270 result = c.sub(s).push(opts)
5269 result = c.sub(s).push(opts)
5271 if result == 0:
5270 if result == 0:
5272 return not result
5271 return not result
5273 finally:
5272 finally:
5274 del repo._subtoppath
5273 del repo._subtoppath
5275 pushop = exchange.push(repo, other, opts.get('force'), revs=revs,
5274 pushop = exchange.push(repo, other, opts.get('force'), revs=revs,
5276 newbranch=opts.get('new_branch'),
5275 newbranch=opts.get('new_branch'),
5277 bookmarks=opts.get('bookmark', ()),
5276 bookmarks=opts.get('bookmark', ()),
5278 opargs=opts.get('opargs'))
5277 opargs=opts.get('opargs'))
5279
5278
5280 result = not pushop.cgresult
5279 result = not pushop.cgresult
5281
5280
5282 if pushop.bkresult is not None:
5281 if pushop.bkresult is not None:
5283 if pushop.bkresult == 2:
5282 if pushop.bkresult == 2:
5284 result = 2
5283 result = 2
5285 elif not result and pushop.bkresult:
5284 elif not result and pushop.bkresult:
5286 result = 2
5285 result = 2
5287
5286
5288 return result
5287 return result
5289
5288
5290 @command('recover', [])
5289 @command('recover', [])
5291 def recover(ui, repo):
5290 def recover(ui, repo):
5292 """roll back an interrupted transaction
5291 """roll back an interrupted transaction
5293
5292
5294 Recover from an interrupted commit or pull.
5293 Recover from an interrupted commit or pull.
5295
5294
5296 This command tries to fix the repository status after an
5295 This command tries to fix the repository status after an
5297 interrupted operation. It should only be necessary when Mercurial
5296 interrupted operation. It should only be necessary when Mercurial
5298 suggests it.
5297 suggests it.
5299
5298
5300 Returns 0 if successful, 1 if nothing to recover or verify fails.
5299 Returns 0 if successful, 1 if nothing to recover or verify fails.
5301 """
5300 """
5302 if repo.recover():
5301 if repo.recover():
5303 return hg.verify(repo)
5302 return hg.verify(repo)
5304 return 1
5303 return 1
5305
5304
5306 @command('^remove|rm',
5305 @command('^remove|rm',
5307 [('A', 'after', None, _('record delete for missing files')),
5306 [('A', 'after', None, _('record delete for missing files')),
5308 ('f', 'force', None,
5307 ('f', 'force', None,
5309 _('forget added files, delete modified files')),
5308 _('forget added files, delete modified files')),
5310 ] + subrepoopts + walkopts,
5309 ] + subrepoopts + walkopts,
5311 _('[OPTION]... FILE...'),
5310 _('[OPTION]... FILE...'),
5312 inferrepo=True)
5311 inferrepo=True)
5313 def remove(ui, repo, *pats, **opts):
5312 def remove(ui, repo, *pats, **opts):
5314 """remove the specified files on the next commit
5313 """remove the specified files on the next commit
5315
5314
5316 Schedule the indicated files for removal from the current branch.
5315 Schedule the indicated files for removal from the current branch.
5317
5316
5318 This command schedules the files to be removed at the next commit.
5317 This command schedules the files to be removed at the next commit.
5319 To undo a remove before that, see :hg:`revert`. To undo added
5318 To undo a remove before that, see :hg:`revert`. To undo added
5320 files, see :hg:`forget`.
5319 files, see :hg:`forget`.
5321
5320
5322 .. container:: verbose
5321 .. container:: verbose
5323
5322
5324 -A/--after can be used to remove only files that have already
5323 -A/--after can be used to remove only files that have already
5325 been deleted, -f/--force can be used to force deletion, and -Af
5324 been deleted, -f/--force can be used to force deletion, and -Af
5326 can be used to remove files from the next revision without
5325 can be used to remove files from the next revision without
5327 deleting them from the working directory.
5326 deleting them from the working directory.
5328
5327
5329 The following table details the behavior of remove for different
5328 The following table details the behavior of remove for different
5330 file states (columns) and option combinations (rows). The file
5329 file states (columns) and option combinations (rows). The file
5331 states are Added [A], Clean [C], Modified [M] and Missing [!]
5330 states are Added [A], Clean [C], Modified [M] and Missing [!]
5332 (as reported by :hg:`status`). The actions are Warn, Remove
5331 (as reported by :hg:`status`). The actions are Warn, Remove
5333 (from branch) and Delete (from disk):
5332 (from branch) and Delete (from disk):
5334
5333
5335 ========= == == == ==
5334 ========= == == == ==
5336 opt/state A C M !
5335 opt/state A C M !
5337 ========= == == == ==
5336 ========= == == == ==
5338 none W RD W R
5337 none W RD W R
5339 -f R RD RD R
5338 -f R RD RD R
5340 -A W W W R
5339 -A W W W R
5341 -Af R R R R
5340 -Af R R R R
5342 ========= == == == ==
5341 ========= == == == ==
5343
5342
5344 .. note::
5343 .. note::
5345
5344
5346 :hg:`remove` never deletes files in Added [A] state from the
5345 :hg:`remove` never deletes files in Added [A] state from the
5347 working directory, not even if ``--force`` is specified.
5346 working directory, not even if ``--force`` is specified.
5348
5347
5349 Returns 0 on success, 1 if any warnings encountered.
5348 Returns 0 on success, 1 if any warnings encountered.
5350 """
5349 """
5351
5350
5352 after, force = opts.get('after'), opts.get('force')
5351 after, force = opts.get('after'), opts.get('force')
5353 if not pats and not after:
5352 if not pats and not after:
5354 raise error.Abort(_('no files specified'))
5353 raise error.Abort(_('no files specified'))
5355
5354
5356 m = scmutil.match(repo[None], pats, opts)
5355 m = scmutil.match(repo[None], pats, opts)
5357 subrepos = opts.get('subrepos')
5356 subrepos = opts.get('subrepos')
5358 return cmdutil.remove(ui, repo, m, "", after, force, subrepos)
5357 return cmdutil.remove(ui, repo, m, "", after, force, subrepos)
5359
5358
5360 @command('rename|move|mv',
5359 @command('rename|move|mv',
5361 [('A', 'after', None, _('record a rename that has already occurred')),
5360 [('A', 'after', None, _('record a rename that has already occurred')),
5362 ('f', 'force', None, _('forcibly copy over an existing managed file')),
5361 ('f', 'force', None, _('forcibly copy over an existing managed file')),
5363 ] + walkopts + dryrunopts,
5362 ] + walkopts + dryrunopts,
5364 _('[OPTION]... SOURCE... DEST'))
5363 _('[OPTION]... SOURCE... DEST'))
5365 def rename(ui, repo, *pats, **opts):
5364 def rename(ui, repo, *pats, **opts):
5366 """rename files; equivalent of copy + remove
5365 """rename files; equivalent of copy + remove
5367
5366
5368 Mark dest as copies of sources; mark sources for deletion. If dest
5367 Mark dest as copies of sources; mark sources for deletion. If dest
5369 is a directory, copies are put in that directory. If dest is a
5368 is a directory, copies are put in that directory. If dest is a
5370 file, there can only be one source.
5369 file, there can only be one source.
5371
5370
5372 By default, this command copies the contents of files as they
5371 By default, this command copies the contents of files as they
5373 exist in the working directory. If invoked with -A/--after, the
5372 exist in the working directory. If invoked with -A/--after, the
5374 operation is recorded, but no copying is performed.
5373 operation is recorded, but no copying is performed.
5375
5374
5376 This command takes effect at the next commit. To undo a rename
5375 This command takes effect at the next commit. To undo a rename
5377 before that, see :hg:`revert`.
5376 before that, see :hg:`revert`.
5378
5377
5379 Returns 0 on success, 1 if errors are encountered.
5378 Returns 0 on success, 1 if errors are encountered.
5380 """
5379 """
5381 with repo.wlock(False):
5380 with repo.wlock(False):
5382 return cmdutil.copy(ui, repo, pats, opts, rename=True)
5381 return cmdutil.copy(ui, repo, pats, opts, rename=True)
5383
5382
5384 @command('resolve',
5383 @command('resolve',
5385 [('a', 'all', None, _('select all unresolved files')),
5384 [('a', 'all', None, _('select all unresolved files')),
5386 ('l', 'list', None, _('list state of files needing merge')),
5385 ('l', 'list', None, _('list state of files needing merge')),
5387 ('m', 'mark', None, _('mark files as resolved')),
5386 ('m', 'mark', None, _('mark files as resolved')),
5388 ('u', 'unmark', None, _('mark files as unresolved')),
5387 ('u', 'unmark', None, _('mark files as unresolved')),
5389 ('n', 'no-status', None, _('hide status prefix'))]
5388 ('n', 'no-status', None, _('hide status prefix'))]
5390 + mergetoolopts + walkopts + formatteropts,
5389 + mergetoolopts + walkopts + formatteropts,
5391 _('[OPTION]... [FILE]...'),
5390 _('[OPTION]... [FILE]...'),
5392 inferrepo=True)
5391 inferrepo=True)
5393 def resolve(ui, repo, *pats, **opts):
5392 def resolve(ui, repo, *pats, **opts):
5394 """redo merges or set/view the merge status of files
5393 """redo merges or set/view the merge status of files
5395
5394
5396 Merges with unresolved conflicts are often the result of
5395 Merges with unresolved conflicts are often the result of
5397 non-interactive merging using the ``internal:merge`` configuration
5396 non-interactive merging using the ``internal:merge`` configuration
5398 setting, or a command-line merge tool like ``diff3``. The resolve
5397 setting, or a command-line merge tool like ``diff3``. The resolve
5399 command is used to manage the files involved in a merge, after
5398 command is used to manage the files involved in a merge, after
5400 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
5399 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
5401 working directory must have two parents). See :hg:`help
5400 working directory must have two parents). See :hg:`help
5402 merge-tools` for information on configuring merge tools.
5401 merge-tools` for information on configuring merge tools.
5403
5402
5404 The resolve command can be used in the following ways:
5403 The resolve command can be used in the following ways:
5405
5404
5406 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
5405 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
5407 files, discarding any previous merge attempts. Re-merging is not
5406 files, discarding any previous merge attempts. Re-merging is not
5408 performed for files already marked as resolved. Use ``--all/-a``
5407 performed for files already marked as resolved. Use ``--all/-a``
5409 to select all unresolved files. ``--tool`` can be used to specify
5408 to select all unresolved files. ``--tool`` can be used to specify
5410 the merge tool used for the given files. It overrides the HGMERGE
5409 the merge tool used for the given files. It overrides the HGMERGE
5411 environment variable and your configuration files. Previous file
5410 environment variable and your configuration files. Previous file
5412 contents are saved with a ``.orig`` suffix.
5411 contents are saved with a ``.orig`` suffix.
5413
5412
5414 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
5413 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
5415 (e.g. after having manually fixed-up the files). The default is
5414 (e.g. after having manually fixed-up the files). The default is
5416 to mark all unresolved files.
5415 to mark all unresolved files.
5417
5416
5418 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
5417 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
5419 default is to mark all resolved files.
5418 default is to mark all resolved files.
5420
5419
5421 - :hg:`resolve -l`: list files which had or still have conflicts.
5420 - :hg:`resolve -l`: list files which had or still have conflicts.
5422 In the printed list, ``U`` = unresolved and ``R`` = resolved.
5421 In the printed list, ``U`` = unresolved and ``R`` = resolved.
5423
5422
5424 .. note::
5423 .. note::
5425
5424
5426 Mercurial will not let you commit files with unresolved merge
5425 Mercurial will not let you commit files with unresolved merge
5427 conflicts. You must use :hg:`resolve -m ...` before you can
5426 conflicts. You must use :hg:`resolve -m ...` before you can
5428 commit after a conflicting merge.
5427 commit after a conflicting merge.
5429
5428
5430 Returns 0 on success, 1 if any files fail a resolve attempt.
5429 Returns 0 on success, 1 if any files fail a resolve attempt.
5431 """
5430 """
5432
5431
5433 flaglist = 'all mark unmark list no_status'.split()
5432 flaglist = 'all mark unmark list no_status'.split()
5434 all, mark, unmark, show, nostatus = \
5433 all, mark, unmark, show, nostatus = \
5435 [opts.get(o) for o in flaglist]
5434 [opts.get(o) for o in flaglist]
5436
5435
5437 if (show and (mark or unmark)) or (mark and unmark):
5436 if (show and (mark or unmark)) or (mark and unmark):
5438 raise error.Abort(_("too many options specified"))
5437 raise error.Abort(_("too many options specified"))
5439 if pats and all:
5438 if pats and all:
5440 raise error.Abort(_("can't specify --all and patterns"))
5439 raise error.Abort(_("can't specify --all and patterns"))
5441 if not (all or pats or show or mark or unmark):
5440 if not (all or pats or show or mark or unmark):
5442 raise error.Abort(_('no files or directories specified'),
5441 raise error.Abort(_('no files or directories specified'),
5443 hint=('use --all to re-merge all unresolved files'))
5442 hint=('use --all to re-merge all unresolved files'))
5444
5443
5445 if show:
5444 if show:
5446 fm = ui.formatter('resolve', opts)
5445 fm = ui.formatter('resolve', opts)
5447 ms = mergemod.mergestate.read(repo)
5446 ms = mergemod.mergestate.read(repo)
5448 m = scmutil.match(repo[None], pats, opts)
5447 m = scmutil.match(repo[None], pats, opts)
5449 for f in ms:
5448 for f in ms:
5450 if not m(f):
5449 if not m(f):
5451 continue
5450 continue
5452 l = 'resolve.' + {'u': 'unresolved', 'r': 'resolved',
5451 l = 'resolve.' + {'u': 'unresolved', 'r': 'resolved',
5453 'd': 'driverresolved'}[ms[f]]
5452 'd': 'driverresolved'}[ms[f]]
5454 fm.startitem()
5453 fm.startitem()
5455 fm.condwrite(not nostatus, 'status', '%s ', ms[f].upper(), label=l)
5454 fm.condwrite(not nostatus, 'status', '%s ', ms[f].upper(), label=l)
5456 fm.write('path', '%s\n', f, label=l)
5455 fm.write('path', '%s\n', f, label=l)
5457 fm.end()
5456 fm.end()
5458 return 0
5457 return 0
5459
5458
5460 with repo.wlock():
5459 with repo.wlock():
5461 ms = mergemod.mergestate.read(repo)
5460 ms = mergemod.mergestate.read(repo)
5462
5461
5463 if not (ms.active() or repo.dirstate.p2() != nullid):
5462 if not (ms.active() or repo.dirstate.p2() != nullid):
5464 raise error.Abort(
5463 raise error.Abort(
5465 _('resolve command not applicable when not merging'))
5464 _('resolve command not applicable when not merging'))
5466
5465
5467 wctx = repo[None]
5466 wctx = repo[None]
5468
5467
5469 if ms.mergedriver and ms.mdstate() == 'u':
5468 if ms.mergedriver and ms.mdstate() == 'u':
5470 proceed = mergemod.driverpreprocess(repo, ms, wctx)
5469 proceed = mergemod.driverpreprocess(repo, ms, wctx)
5471 ms.commit()
5470 ms.commit()
5472 # allow mark and unmark to go through
5471 # allow mark and unmark to go through
5473 if not mark and not unmark and not proceed:
5472 if not mark and not unmark and not proceed:
5474 return 1
5473 return 1
5475
5474
5476 m = scmutil.match(wctx, pats, opts)
5475 m = scmutil.match(wctx, pats, opts)
5477 ret = 0
5476 ret = 0
5478 didwork = False
5477 didwork = False
5479 runconclude = False
5478 runconclude = False
5480
5479
5481 tocomplete = []
5480 tocomplete = []
5482 for f in ms:
5481 for f in ms:
5483 if not m(f):
5482 if not m(f):
5484 continue
5483 continue
5485
5484
5486 didwork = True
5485 didwork = True
5487
5486
5488 # don't let driver-resolved files be marked, and run the conclude
5487 # don't let driver-resolved files be marked, and run the conclude
5489 # step if asked to resolve
5488 # step if asked to resolve
5490 if ms[f] == "d":
5489 if ms[f] == "d":
5491 exact = m.exact(f)
5490 exact = m.exact(f)
5492 if mark:
5491 if mark:
5493 if exact:
5492 if exact:
5494 ui.warn(_('not marking %s as it is driver-resolved\n')
5493 ui.warn(_('not marking %s as it is driver-resolved\n')
5495 % f)
5494 % f)
5496 elif unmark:
5495 elif unmark:
5497 if exact:
5496 if exact:
5498 ui.warn(_('not unmarking %s as it is driver-resolved\n')
5497 ui.warn(_('not unmarking %s as it is driver-resolved\n')
5499 % f)
5498 % f)
5500 else:
5499 else:
5501 runconclude = True
5500 runconclude = True
5502 continue
5501 continue
5503
5502
5504 if mark:
5503 if mark:
5505 ms.mark(f, "r")
5504 ms.mark(f, "r")
5506 elif unmark:
5505 elif unmark:
5507 ms.mark(f, "u")
5506 ms.mark(f, "u")
5508 else:
5507 else:
5509 # backup pre-resolve (merge uses .orig for its own purposes)
5508 # backup pre-resolve (merge uses .orig for its own purposes)
5510 a = repo.wjoin(f)
5509 a = repo.wjoin(f)
5511 try:
5510 try:
5512 util.copyfile(a, a + ".resolve")
5511 util.copyfile(a, a + ".resolve")
5513 except (IOError, OSError) as inst:
5512 except (IOError, OSError) as inst:
5514 if inst.errno != errno.ENOENT:
5513 if inst.errno != errno.ENOENT:
5515 raise
5514 raise
5516
5515
5517 try:
5516 try:
5518 # preresolve file
5517 # preresolve file
5519 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
5518 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
5520 'resolve')
5519 'resolve')
5521 complete, r = ms.preresolve(f, wctx)
5520 complete, r = ms.preresolve(f, wctx)
5522 if not complete:
5521 if not complete:
5523 tocomplete.append(f)
5522 tocomplete.append(f)
5524 elif r:
5523 elif r:
5525 ret = 1
5524 ret = 1
5526 finally:
5525 finally:
5527 ui.setconfig('ui', 'forcemerge', '', 'resolve')
5526 ui.setconfig('ui', 'forcemerge', '', 'resolve')
5528 ms.commit()
5527 ms.commit()
5529
5528
5530 # replace filemerge's .orig file with our resolve file, but only
5529 # replace filemerge's .orig file with our resolve file, but only
5531 # for merges that are complete
5530 # for merges that are complete
5532 if complete:
5531 if complete:
5533 try:
5532 try:
5534 util.rename(a + ".resolve",
5533 util.rename(a + ".resolve",
5535 scmutil.origpath(ui, repo, a))
5534 scmutil.origpath(ui, repo, a))
5536 except OSError as inst:
5535 except OSError as inst:
5537 if inst.errno != errno.ENOENT:
5536 if inst.errno != errno.ENOENT:
5538 raise
5537 raise
5539
5538
5540 for f in tocomplete:
5539 for f in tocomplete:
5541 try:
5540 try:
5542 # resolve file
5541 # resolve file
5543 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
5542 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
5544 'resolve')
5543 'resolve')
5545 r = ms.resolve(f, wctx)
5544 r = ms.resolve(f, wctx)
5546 if r:
5545 if r:
5547 ret = 1
5546 ret = 1
5548 finally:
5547 finally:
5549 ui.setconfig('ui', 'forcemerge', '', 'resolve')
5548 ui.setconfig('ui', 'forcemerge', '', 'resolve')
5550 ms.commit()
5549 ms.commit()
5551
5550
5552 # replace filemerge's .orig file with our resolve file
5551 # replace filemerge's .orig file with our resolve file
5553 a = repo.wjoin(f)
5552 a = repo.wjoin(f)
5554 try:
5553 try:
5555 util.rename(a + ".resolve", scmutil.origpath(ui, repo, a))
5554 util.rename(a + ".resolve", scmutil.origpath(ui, repo, a))
5556 except OSError as inst:
5555 except OSError as inst:
5557 if inst.errno != errno.ENOENT:
5556 if inst.errno != errno.ENOENT:
5558 raise
5557 raise
5559
5558
5560 ms.commit()
5559 ms.commit()
5561 ms.recordactions()
5560 ms.recordactions()
5562
5561
5563 if not didwork and pats:
5562 if not didwork and pats:
5564 hint = None
5563 hint = None
5565 if not any([p for p in pats if p.find(':') >= 0]):
5564 if not any([p for p in pats if p.find(':') >= 0]):
5566 pats = ['path:%s' % p for p in pats]
5565 pats = ['path:%s' % p for p in pats]
5567 m = scmutil.match(wctx, pats, opts)
5566 m = scmutil.match(wctx, pats, opts)
5568 for f in ms:
5567 for f in ms:
5569 if not m(f):
5568 if not m(f):
5570 continue
5569 continue
5571 flags = ''.join(['-%s ' % o[0] for o in flaglist
5570 flags = ''.join(['-%s ' % o[0] for o in flaglist
5572 if opts.get(o)])
5571 if opts.get(o)])
5573 hint = _("(try: hg resolve %s%s)\n") % (
5572 hint = _("(try: hg resolve %s%s)\n") % (
5574 flags,
5573 flags,
5575 ' '.join(pats))
5574 ' '.join(pats))
5576 break
5575 break
5577 ui.warn(_("arguments do not match paths that need resolving\n"))
5576 ui.warn(_("arguments do not match paths that need resolving\n"))
5578 if hint:
5577 if hint:
5579 ui.warn(hint)
5578 ui.warn(hint)
5580 elif ms.mergedriver and ms.mdstate() != 's':
5579 elif ms.mergedriver and ms.mdstate() != 's':
5581 # run conclude step when either a driver-resolved file is requested
5580 # run conclude step when either a driver-resolved file is requested
5582 # or there are no driver-resolved files
5581 # or there are no driver-resolved files
5583 # we can't use 'ret' to determine whether any files are unresolved
5582 # we can't use 'ret' to determine whether any files are unresolved
5584 # because we might not have tried to resolve some
5583 # because we might not have tried to resolve some
5585 if ((runconclude or not list(ms.driverresolved()))
5584 if ((runconclude or not list(ms.driverresolved()))
5586 and not list(ms.unresolved())):
5585 and not list(ms.unresolved())):
5587 proceed = mergemod.driverconclude(repo, ms, wctx)
5586 proceed = mergemod.driverconclude(repo, ms, wctx)
5588 ms.commit()
5587 ms.commit()
5589 if not proceed:
5588 if not proceed:
5590 return 1
5589 return 1
5591
5590
5592 # Nudge users into finishing an unfinished operation
5591 # Nudge users into finishing an unfinished operation
5593 unresolvedf = list(ms.unresolved())
5592 unresolvedf = list(ms.unresolved())
5594 driverresolvedf = list(ms.driverresolved())
5593 driverresolvedf = list(ms.driverresolved())
5595 if not unresolvedf and not driverresolvedf:
5594 if not unresolvedf and not driverresolvedf:
5596 ui.status(_('(no more unresolved files)\n'))
5595 ui.status(_('(no more unresolved files)\n'))
5597 cmdutil.checkafterresolved(repo)
5596 cmdutil.checkafterresolved(repo)
5598 elif not unresolvedf:
5597 elif not unresolvedf:
5599 ui.status(_('(no more unresolved files -- '
5598 ui.status(_('(no more unresolved files -- '
5600 'run "hg resolve --all" to conclude)\n'))
5599 'run "hg resolve --all" to conclude)\n'))
5601
5600
5602 return ret
5601 return ret
5603
5602
5604 @command('revert',
5603 @command('revert',
5605 [('a', 'all', None, _('revert all changes when no arguments given')),
5604 [('a', 'all', None, _('revert all changes when no arguments given')),
5606 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5605 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5607 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
5606 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
5608 ('C', 'no-backup', None, _('do not save backup copies of files')),
5607 ('C', 'no-backup', None, _('do not save backup copies of files')),
5609 ('i', 'interactive', None,
5608 ('i', 'interactive', None,
5610 _('interactively select the changes (EXPERIMENTAL)')),
5609 _('interactively select the changes (EXPERIMENTAL)')),
5611 ] + walkopts + dryrunopts,
5610 ] + walkopts + dryrunopts,
5612 _('[OPTION]... [-r REV] [NAME]...'))
5611 _('[OPTION]... [-r REV] [NAME]...'))
5613 def revert(ui, repo, *pats, **opts):
5612 def revert(ui, repo, *pats, **opts):
5614 """restore files to their checkout state
5613 """restore files to their checkout state
5615
5614
5616 .. note::
5615 .. note::
5617
5616
5618 To check out earlier revisions, you should use :hg:`update REV`.
5617 To check out earlier revisions, you should use :hg:`update REV`.
5619 To cancel an uncommitted merge (and lose your changes),
5618 To cancel an uncommitted merge (and lose your changes),
5620 use :hg:`update --clean .`.
5619 use :hg:`update --clean .`.
5621
5620
5622 With no revision specified, revert the specified files or directories
5621 With no revision specified, revert the specified files or directories
5623 to the contents they had in the parent of the working directory.
5622 to the contents they had in the parent of the working directory.
5624 This restores the contents of files to an unmodified
5623 This restores the contents of files to an unmodified
5625 state and unschedules adds, removes, copies, and renames. If the
5624 state and unschedules adds, removes, copies, and renames. If the
5626 working directory has two parents, you must explicitly specify a
5625 working directory has two parents, you must explicitly specify a
5627 revision.
5626 revision.
5628
5627
5629 Using the -r/--rev or -d/--date options, revert the given files or
5628 Using the -r/--rev or -d/--date options, revert the given files or
5630 directories to their states as of a specific revision. Because
5629 directories to their states as of a specific revision. Because
5631 revert does not change the working directory parents, this will
5630 revert does not change the working directory parents, this will
5632 cause these files to appear modified. This can be helpful to "back
5631 cause these files to appear modified. This can be helpful to "back
5633 out" some or all of an earlier change. See :hg:`backout` for a
5632 out" some or all of an earlier change. See :hg:`backout` for a
5634 related method.
5633 related method.
5635
5634
5636 Modified files are saved with a .orig suffix before reverting.
5635 Modified files are saved with a .orig suffix before reverting.
5637 To disable these backups, use --no-backup. It is possible to store
5636 To disable these backups, use --no-backup. It is possible to store
5638 the backup files in a custom directory relative to the root of the
5637 the backup files in a custom directory relative to the root of the
5639 repository by setting the ``ui.origbackuppath`` configuration
5638 repository by setting the ``ui.origbackuppath`` configuration
5640 option.
5639 option.
5641
5640
5642 See :hg:`help dates` for a list of formats valid for -d/--date.
5641 See :hg:`help dates` for a list of formats valid for -d/--date.
5643
5642
5644 See :hg:`help backout` for a way to reverse the effect of an
5643 See :hg:`help backout` for a way to reverse the effect of an
5645 earlier changeset.
5644 earlier changeset.
5646
5645
5647 Returns 0 on success.
5646 Returns 0 on success.
5648 """
5647 """
5649
5648
5650 if opts.get("date"):
5649 if opts.get("date"):
5651 if opts.get("rev"):
5650 if opts.get("rev"):
5652 raise error.Abort(_("you can't specify a revision and a date"))
5651 raise error.Abort(_("you can't specify a revision and a date"))
5653 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
5652 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
5654
5653
5655 parent, p2 = repo.dirstate.parents()
5654 parent, p2 = repo.dirstate.parents()
5656 if not opts.get('rev') and p2 != nullid:
5655 if not opts.get('rev') and p2 != nullid:
5657 # revert after merge is a trap for new users (issue2915)
5656 # revert after merge is a trap for new users (issue2915)
5658 raise error.Abort(_('uncommitted merge with no revision specified'),
5657 raise error.Abort(_('uncommitted merge with no revision specified'),
5659 hint=_("use 'hg update' or see 'hg help revert'"))
5658 hint=_("use 'hg update' or see 'hg help revert'"))
5660
5659
5661 ctx = scmutil.revsingle(repo, opts.get('rev'))
5660 ctx = scmutil.revsingle(repo, opts.get('rev'))
5662
5661
5663 if (not (pats or opts.get('include') or opts.get('exclude') or
5662 if (not (pats or opts.get('include') or opts.get('exclude') or
5664 opts.get('all') or opts.get('interactive'))):
5663 opts.get('all') or opts.get('interactive'))):
5665 msg = _("no files or directories specified")
5664 msg = _("no files or directories specified")
5666 if p2 != nullid:
5665 if p2 != nullid:
5667 hint = _("uncommitted merge, use --all to discard all changes,"
5666 hint = _("uncommitted merge, use --all to discard all changes,"
5668 " or 'hg update -C .' to abort the merge")
5667 " or 'hg update -C .' to abort the merge")
5669 raise error.Abort(msg, hint=hint)
5668 raise error.Abort(msg, hint=hint)
5670 dirty = any(repo.status())
5669 dirty = any(repo.status())
5671 node = ctx.node()
5670 node = ctx.node()
5672 if node != parent:
5671 if node != parent:
5673 if dirty:
5672 if dirty:
5674 hint = _("uncommitted changes, use --all to discard all"
5673 hint = _("uncommitted changes, use --all to discard all"
5675 " changes, or 'hg update %s' to update") % ctx.rev()
5674 " changes, or 'hg update %s' to update") % ctx.rev()
5676 else:
5675 else:
5677 hint = _("use --all to revert all files,"
5676 hint = _("use --all to revert all files,"
5678 " or 'hg update %s' to update") % ctx.rev()
5677 " or 'hg update %s' to update") % ctx.rev()
5679 elif dirty:
5678 elif dirty:
5680 hint = _("uncommitted changes, use --all to discard all changes")
5679 hint = _("uncommitted changes, use --all to discard all changes")
5681 else:
5680 else:
5682 hint = _("use --all to revert all files")
5681 hint = _("use --all to revert all files")
5683 raise error.Abort(msg, hint=hint)
5682 raise error.Abort(msg, hint=hint)
5684
5683
5685 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats, **opts)
5684 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats, **opts)
5686
5685
5687 @command('rollback', dryrunopts +
5686 @command('rollback', dryrunopts +
5688 [('f', 'force', False, _('ignore safety measures'))])
5687 [('f', 'force', False, _('ignore safety measures'))])
5689 def rollback(ui, repo, **opts):
5688 def rollback(ui, repo, **opts):
5690 """roll back the last transaction (DANGEROUS) (DEPRECATED)
5689 """roll back the last transaction (DANGEROUS) (DEPRECATED)
5691
5690
5692 Please use :hg:`commit --amend` instead of rollback to correct
5691 Please use :hg:`commit --amend` instead of rollback to correct
5693 mistakes in the last commit.
5692 mistakes in the last commit.
5694
5693
5695 This command should be used with care. There is only one level of
5694 This command should be used with care. There is only one level of
5696 rollback, and there is no way to undo a rollback. It will also
5695 rollback, and there is no way to undo a rollback. It will also
5697 restore the dirstate at the time of the last transaction, losing
5696 restore the dirstate at the time of the last transaction, losing
5698 any dirstate changes since that time. This command does not alter
5697 any dirstate changes since that time. This command does not alter
5699 the working directory.
5698 the working directory.
5700
5699
5701 Transactions are used to encapsulate the effects of all commands
5700 Transactions are used to encapsulate the effects of all commands
5702 that create new changesets or propagate existing changesets into a
5701 that create new changesets or propagate existing changesets into a
5703 repository.
5702 repository.
5704
5703
5705 .. container:: verbose
5704 .. container:: verbose
5706
5705
5707 For example, the following commands are transactional, and their
5706 For example, the following commands are transactional, and their
5708 effects can be rolled back:
5707 effects can be rolled back:
5709
5708
5710 - commit
5709 - commit
5711 - import
5710 - import
5712 - pull
5711 - pull
5713 - push (with this repository as the destination)
5712 - push (with this repository as the destination)
5714 - unbundle
5713 - unbundle
5715
5714
5716 To avoid permanent data loss, rollback will refuse to rollback a
5715 To avoid permanent data loss, rollback will refuse to rollback a
5717 commit transaction if it isn't checked out. Use --force to
5716 commit transaction if it isn't checked out. Use --force to
5718 override this protection.
5717 override this protection.
5719
5718
5720 The rollback command can be entirely disabled by setting the
5719 The rollback command can be entirely disabled by setting the
5721 ``ui.rollback`` configuration setting to false. If you're here
5720 ``ui.rollback`` configuration setting to false. If you're here
5722 because you want to use rollback and it's disabled, you can
5721 because you want to use rollback and it's disabled, you can
5723 re-enable the command by setting ``ui.rollback`` to true.
5722 re-enable the command by setting ``ui.rollback`` to true.
5724
5723
5725 This command is not intended for use on public repositories. Once
5724 This command is not intended for use on public repositories. Once
5726 changes are visible for pull by other users, rolling a transaction
5725 changes are visible for pull by other users, rolling a transaction
5727 back locally is ineffective (someone else may already have pulled
5726 back locally is ineffective (someone else may already have pulled
5728 the changes). Furthermore, a race is possible with readers of the
5727 the changes). Furthermore, a race is possible with readers of the
5729 repository; for example an in-progress pull from the repository
5728 repository; for example an in-progress pull from the repository
5730 may fail if a rollback is performed.
5729 may fail if a rollback is performed.
5731
5730
5732 Returns 0 on success, 1 if no rollback data is available.
5731 Returns 0 on success, 1 if no rollback data is available.
5733 """
5732 """
5734 if not ui.configbool('ui', 'rollback', True):
5733 if not ui.configbool('ui', 'rollback', True):
5735 raise error.Abort(_('rollback is disabled because it is unsafe'),
5734 raise error.Abort(_('rollback is disabled because it is unsafe'),
5736 hint=('see `hg help -v rollback` for information'))
5735 hint=('see `hg help -v rollback` for information'))
5737 return repo.rollback(dryrun=opts.get('dry_run'),
5736 return repo.rollback(dryrun=opts.get('dry_run'),
5738 force=opts.get('force'))
5737 force=opts.get('force'))
5739
5738
5740 @command('root', [])
5739 @command('root', [])
5741 def root(ui, repo):
5740 def root(ui, repo):
5742 """print the root (top) of the current working directory
5741 """print the root (top) of the current working directory
5743
5742
5744 Print the root directory of the current repository.
5743 Print the root directory of the current repository.
5745
5744
5746 Returns 0 on success.
5745 Returns 0 on success.
5747 """
5746 """
5748 ui.write(repo.root + "\n")
5747 ui.write(repo.root + "\n")
5749
5748
5750 @command('^serve',
5749 @command('^serve',
5751 [('A', 'accesslog', '', _('name of access log file to write to'),
5750 [('A', 'accesslog', '', _('name of access log file to write to'),
5752 _('FILE')),
5751 _('FILE')),
5753 ('d', 'daemon', None, _('run server in background')),
5752 ('d', 'daemon', None, _('run server in background')),
5754 ('', 'daemon-postexec', [], _('used internally by daemon mode')),
5753 ('', 'daemon-postexec', [], _('used internally by daemon mode')),
5755 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
5754 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
5756 # use string type, then we can check if something was passed
5755 # use string type, then we can check if something was passed
5757 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
5756 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
5758 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
5757 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
5759 _('ADDR')),
5758 _('ADDR')),
5760 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
5759 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
5761 _('PREFIX')),
5760 _('PREFIX')),
5762 ('n', 'name', '',
5761 ('n', 'name', '',
5763 _('name to show in web pages (default: working directory)'), _('NAME')),
5762 _('name to show in web pages (default: working directory)'), _('NAME')),
5764 ('', 'web-conf', '',
5763 ('', 'web-conf', '',
5765 _("name of the hgweb config file (see 'hg help hgweb')"), _('FILE')),
5764 _("name of the hgweb config file (see 'hg help hgweb')"), _('FILE')),
5766 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
5765 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
5767 _('FILE')),
5766 _('FILE')),
5768 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
5767 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
5769 ('', 'stdio', None, _('for remote clients')),
5768 ('', 'stdio', None, _('for remote clients')),
5770 ('', 'cmdserver', '', _('for remote clients'), _('MODE')),
5769 ('', 'cmdserver', '', _('for remote clients'), _('MODE')),
5771 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
5770 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
5772 ('', 'style', '', _('template style to use'), _('STYLE')),
5771 ('', 'style', '', _('template style to use'), _('STYLE')),
5773 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
5772 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
5774 ('', 'certificate', '', _('SSL certificate file'), _('FILE'))],
5773 ('', 'certificate', '', _('SSL certificate file'), _('FILE'))],
5775 _('[OPTION]...'),
5774 _('[OPTION]...'),
5776 optionalrepo=True)
5775 optionalrepo=True)
5777 def serve(ui, repo, **opts):
5776 def serve(ui, repo, **opts):
5778 """start stand-alone webserver
5777 """start stand-alone webserver
5779
5778
5780 Start a local HTTP repository browser and pull server. You can use
5779 Start a local HTTP repository browser and pull server. You can use
5781 this for ad-hoc sharing and browsing of repositories. It is
5780 this for ad-hoc sharing and browsing of repositories. It is
5782 recommended to use a real web server to serve a repository for
5781 recommended to use a real web server to serve a repository for
5783 longer periods of time.
5782 longer periods of time.
5784
5783
5785 Please note that the server does not implement access control.
5784 Please note that the server does not implement access control.
5786 This means that, by default, anybody can read from the server and
5785 This means that, by default, anybody can read from the server and
5787 nobody can write to it by default. Set the ``web.allow_push``
5786 nobody can write to it by default. Set the ``web.allow_push``
5788 option to ``*`` to allow everybody to push to the server. You
5787 option to ``*`` to allow everybody to push to the server. You
5789 should use a real web server if you need to authenticate users.
5788 should use a real web server if you need to authenticate users.
5790
5789
5791 By default, the server logs accesses to stdout and errors to
5790 By default, the server logs accesses to stdout and errors to
5792 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
5791 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
5793 files.
5792 files.
5794
5793
5795 To have the server choose a free port number to listen on, specify
5794 To have the server choose a free port number to listen on, specify
5796 a port number of 0; in this case, the server will print the port
5795 a port number of 0; in this case, the server will print the port
5797 number it uses.
5796 number it uses.
5798
5797
5799 Returns 0 on success.
5798 Returns 0 on success.
5800 """
5799 """
5801
5800
5802 if opts["stdio"] and opts["cmdserver"]:
5801 if opts["stdio"] and opts["cmdserver"]:
5803 raise error.Abort(_("cannot use --stdio with --cmdserver"))
5802 raise error.Abort(_("cannot use --stdio with --cmdserver"))
5804
5803
5805 if opts["stdio"]:
5804 if opts["stdio"]:
5806 if repo is None:
5805 if repo is None:
5807 raise error.RepoError(_("there is no Mercurial repository here"
5806 raise error.RepoError(_("there is no Mercurial repository here"
5808 " (.hg not found)"))
5807 " (.hg not found)"))
5809 s = sshserver.sshserver(ui, repo)
5808 s = sshserver.sshserver(ui, repo)
5810 s.serve_forever()
5809 s.serve_forever()
5811
5810
5812 service = server.createservice(ui, repo, opts)
5811 service = server.createservice(ui, repo, opts)
5813 return server.runservice(opts, initfn=service.init, runfn=service.run)
5812 return server.runservice(opts, initfn=service.init, runfn=service.run)
5814
5813
5815 @command('^status|st',
5814 @command('^status|st',
5816 [('A', 'all', None, _('show status of all files')),
5815 [('A', 'all', None, _('show status of all files')),
5817 ('m', 'modified', None, _('show only modified files')),
5816 ('m', 'modified', None, _('show only modified files')),
5818 ('a', 'added', None, _('show only added files')),
5817 ('a', 'added', None, _('show only added files')),
5819 ('r', 'removed', None, _('show only removed files')),
5818 ('r', 'removed', None, _('show only removed files')),
5820 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
5819 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
5821 ('c', 'clean', None, _('show only files without changes')),
5820 ('c', 'clean', None, _('show only files without changes')),
5822 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
5821 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
5823 ('i', 'ignored', None, _('show only ignored files')),
5822 ('i', 'ignored', None, _('show only ignored files')),
5824 ('n', 'no-status', None, _('hide status prefix')),
5823 ('n', 'no-status', None, _('hide status prefix')),
5825 ('C', 'copies', None, _('show source of copied files')),
5824 ('C', 'copies', None, _('show source of copied files')),
5826 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
5825 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
5827 ('', 'rev', [], _('show difference from revision'), _('REV')),
5826 ('', 'rev', [], _('show difference from revision'), _('REV')),
5828 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
5827 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
5829 ] + walkopts + subrepoopts + formatteropts,
5828 ] + walkopts + subrepoopts + formatteropts,
5830 _('[OPTION]... [FILE]...'),
5829 _('[OPTION]... [FILE]...'),
5831 inferrepo=True)
5830 inferrepo=True)
5832 def status(ui, repo, *pats, **opts):
5831 def status(ui, repo, *pats, **opts):
5833 """show changed files in the working directory
5832 """show changed files in the working directory
5834
5833
5835 Show status of files in the repository. If names are given, only
5834 Show status of files in the repository. If names are given, only
5836 files that match are shown. Files that are clean or ignored or
5835 files that match are shown. Files that are clean or ignored or
5837 the source of a copy/move operation, are not listed unless
5836 the source of a copy/move operation, are not listed unless
5838 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
5837 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
5839 Unless options described with "show only ..." are given, the
5838 Unless options described with "show only ..." are given, the
5840 options -mardu are used.
5839 options -mardu are used.
5841
5840
5842 Option -q/--quiet hides untracked (unknown and ignored) files
5841 Option -q/--quiet hides untracked (unknown and ignored) files
5843 unless explicitly requested with -u/--unknown or -i/--ignored.
5842 unless explicitly requested with -u/--unknown or -i/--ignored.
5844
5843
5845 .. note::
5844 .. note::
5846
5845
5847 :hg:`status` may appear to disagree with diff if permissions have
5846 :hg:`status` may appear to disagree with diff if permissions have
5848 changed or a merge has occurred. The standard diff format does
5847 changed or a merge has occurred. The standard diff format does
5849 not report permission changes and diff only reports changes
5848 not report permission changes and diff only reports changes
5850 relative to one merge parent.
5849 relative to one merge parent.
5851
5850
5852 If one revision is given, it is used as the base revision.
5851 If one revision is given, it is used as the base revision.
5853 If two revisions are given, the differences between them are
5852 If two revisions are given, the differences between them are
5854 shown. The --change option can also be used as a shortcut to list
5853 shown. The --change option can also be used as a shortcut to list
5855 the changed files of a revision from its first parent.
5854 the changed files of a revision from its first parent.
5856
5855
5857 The codes used to show the status of files are::
5856 The codes used to show the status of files are::
5858
5857
5859 M = modified
5858 M = modified
5860 A = added
5859 A = added
5861 R = removed
5860 R = removed
5862 C = clean
5861 C = clean
5863 ! = missing (deleted by non-hg command, but still tracked)
5862 ! = missing (deleted by non-hg command, but still tracked)
5864 ? = not tracked
5863 ? = not tracked
5865 I = ignored
5864 I = ignored
5866 = origin of the previous file (with --copies)
5865 = origin of the previous file (with --copies)
5867
5866
5868 .. container:: verbose
5867 .. container:: verbose
5869
5868
5870 Examples:
5869 Examples:
5871
5870
5872 - show changes in the working directory relative to a
5871 - show changes in the working directory relative to a
5873 changeset::
5872 changeset::
5874
5873
5875 hg status --rev 9353
5874 hg status --rev 9353
5876
5875
5877 - show changes in the working directory relative to the
5876 - show changes in the working directory relative to the
5878 current directory (see :hg:`help patterns` for more information)::
5877 current directory (see :hg:`help patterns` for more information)::
5879
5878
5880 hg status re:
5879 hg status re:
5881
5880
5882 - show all changes including copies in an existing changeset::
5881 - show all changes including copies in an existing changeset::
5883
5882
5884 hg status --copies --change 9353
5883 hg status --copies --change 9353
5885
5884
5886 - get a NUL separated list of added files, suitable for xargs::
5885 - get a NUL separated list of added files, suitable for xargs::
5887
5886
5888 hg status -an0
5887 hg status -an0
5889
5888
5890 Returns 0 on success.
5889 Returns 0 on success.
5891 """
5890 """
5892
5891
5893 revs = opts.get('rev')
5892 revs = opts.get('rev')
5894 change = opts.get('change')
5893 change = opts.get('change')
5895
5894
5896 if revs and change:
5895 if revs and change:
5897 msg = _('cannot specify --rev and --change at the same time')
5896 msg = _('cannot specify --rev and --change at the same time')
5898 raise error.Abort(msg)
5897 raise error.Abort(msg)
5899 elif change:
5898 elif change:
5900 node2 = scmutil.revsingle(repo, change, None).node()
5899 node2 = scmutil.revsingle(repo, change, None).node()
5901 node1 = repo[node2].p1().node()
5900 node1 = repo[node2].p1().node()
5902 else:
5901 else:
5903 node1, node2 = scmutil.revpair(repo, revs)
5902 node1, node2 = scmutil.revpair(repo, revs)
5904
5903
5905 if pats:
5904 if pats:
5906 cwd = repo.getcwd()
5905 cwd = repo.getcwd()
5907 else:
5906 else:
5908 cwd = ''
5907 cwd = ''
5909
5908
5910 if opts.get('print0'):
5909 if opts.get('print0'):
5911 end = '\0'
5910 end = '\0'
5912 else:
5911 else:
5913 end = '\n'
5912 end = '\n'
5914 copy = {}
5913 copy = {}
5915 states = 'modified added removed deleted unknown ignored clean'.split()
5914 states = 'modified added removed deleted unknown ignored clean'.split()
5916 show = [k for k in states if opts.get(k)]
5915 show = [k for k in states if opts.get(k)]
5917 if opts.get('all'):
5916 if opts.get('all'):
5918 show += ui.quiet and (states[:4] + ['clean']) or states
5917 show += ui.quiet and (states[:4] + ['clean']) or states
5919 if not show:
5918 if not show:
5920 if ui.quiet:
5919 if ui.quiet:
5921 show = states[:4]
5920 show = states[:4]
5922 else:
5921 else:
5923 show = states[:5]
5922 show = states[:5]
5924
5923
5925 m = scmutil.match(repo[node2], pats, opts)
5924 m = scmutil.match(repo[node2], pats, opts)
5926 stat = repo.status(node1, node2, m,
5925 stat = repo.status(node1, node2, m,
5927 'ignored' in show, 'clean' in show, 'unknown' in show,
5926 'ignored' in show, 'clean' in show, 'unknown' in show,
5928 opts.get('subrepos'))
5927 opts.get('subrepos'))
5929 changestates = zip(states, 'MAR!?IC', stat)
5928 changestates = zip(states, 'MAR!?IC', stat)
5930
5929
5931 if (opts.get('all') or opts.get('copies')
5930 if (opts.get('all') or opts.get('copies')
5932 or ui.configbool('ui', 'statuscopies')) and not opts.get('no_status'):
5931 or ui.configbool('ui', 'statuscopies')) and not opts.get('no_status'):
5933 copy = copies.pathcopies(repo[node1], repo[node2], m)
5932 copy = copies.pathcopies(repo[node1], repo[node2], m)
5934
5933
5935 fm = ui.formatter('status', opts)
5934 fm = ui.formatter('status', opts)
5936 fmt = '%s' + end
5935 fmt = '%s' + end
5937 showchar = not opts.get('no_status')
5936 showchar = not opts.get('no_status')
5938
5937
5939 for state, char, files in changestates:
5938 for state, char, files in changestates:
5940 if state in show:
5939 if state in show:
5941 label = 'status.' + state
5940 label = 'status.' + state
5942 for f in files:
5941 for f in files:
5943 fm.startitem()
5942 fm.startitem()
5944 fm.condwrite(showchar, 'status', '%s ', char, label=label)
5943 fm.condwrite(showchar, 'status', '%s ', char, label=label)
5945 fm.write('path', fmt, repo.pathto(f, cwd), label=label)
5944 fm.write('path', fmt, repo.pathto(f, cwd), label=label)
5946 if f in copy:
5945 if f in copy:
5947 fm.write("copy", ' %s' + end, repo.pathto(copy[f], cwd),
5946 fm.write("copy", ' %s' + end, repo.pathto(copy[f], cwd),
5948 label='status.copied')
5947 label='status.copied')
5949 fm.end()
5948 fm.end()
5950
5949
5951 @command('^summary|sum',
5950 @command('^summary|sum',
5952 [('', 'remote', None, _('check for push and pull'))], '[--remote]')
5951 [('', 'remote', None, _('check for push and pull'))], '[--remote]')
5953 def summary(ui, repo, **opts):
5952 def summary(ui, repo, **opts):
5954 """summarize working directory state
5953 """summarize working directory state
5955
5954
5956 This generates a brief summary of the working directory state,
5955 This generates a brief summary of the working directory state,
5957 including parents, branch, commit status, phase and available updates.
5956 including parents, branch, commit status, phase and available updates.
5958
5957
5959 With the --remote option, this will check the default paths for
5958 With the --remote option, this will check the default paths for
5960 incoming and outgoing changes. This can be time-consuming.
5959 incoming and outgoing changes. This can be time-consuming.
5961
5960
5962 Returns 0 on success.
5961 Returns 0 on success.
5963 """
5962 """
5964
5963
5965 ctx = repo[None]
5964 ctx = repo[None]
5966 parents = ctx.parents()
5965 parents = ctx.parents()
5967 pnode = parents[0].node()
5966 pnode = parents[0].node()
5968 marks = []
5967 marks = []
5969
5968
5970 ms = None
5969 ms = None
5971 try:
5970 try:
5972 ms = mergemod.mergestate.read(repo)
5971 ms = mergemod.mergestate.read(repo)
5973 except error.UnsupportedMergeRecords as e:
5972 except error.UnsupportedMergeRecords as e:
5974 s = ' '.join(e.recordtypes)
5973 s = ' '.join(e.recordtypes)
5975 ui.warn(
5974 ui.warn(
5976 _('warning: merge state has unsupported record types: %s\n') % s)
5975 _('warning: merge state has unsupported record types: %s\n') % s)
5977 unresolved = 0
5976 unresolved = 0
5978 else:
5977 else:
5979 unresolved = [f for f in ms if ms[f] == 'u']
5978 unresolved = [f for f in ms if ms[f] == 'u']
5980
5979
5981 for p in parents:
5980 for p in parents:
5982 # label with log.changeset (instead of log.parent) since this
5981 # label with log.changeset (instead of log.parent) since this
5983 # shows a working directory parent *changeset*:
5982 # shows a working directory parent *changeset*:
5984 # i18n: column positioning for "hg summary"
5983 # i18n: column positioning for "hg summary"
5985 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
5984 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
5986 label='log.changeset changeset.%s' % p.phasestr())
5985 label='log.changeset changeset.%s' % p.phasestr())
5987 ui.write(' '.join(p.tags()), label='log.tag')
5986 ui.write(' '.join(p.tags()), label='log.tag')
5988 if p.bookmarks():
5987 if p.bookmarks():
5989 marks.extend(p.bookmarks())
5988 marks.extend(p.bookmarks())
5990 if p.rev() == -1:
5989 if p.rev() == -1:
5991 if not len(repo):
5990 if not len(repo):
5992 ui.write(_(' (empty repository)'))
5991 ui.write(_(' (empty repository)'))
5993 else:
5992 else:
5994 ui.write(_(' (no revision checked out)'))
5993 ui.write(_(' (no revision checked out)'))
5995 ui.write('\n')
5994 ui.write('\n')
5996 if p.description():
5995 if p.description():
5997 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
5996 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
5998 label='log.summary')
5997 label='log.summary')
5999
5998
6000 branch = ctx.branch()
5999 branch = ctx.branch()
6001 bheads = repo.branchheads(branch)
6000 bheads = repo.branchheads(branch)
6002 # i18n: column positioning for "hg summary"
6001 # i18n: column positioning for "hg summary"
6003 m = _('branch: %s\n') % branch
6002 m = _('branch: %s\n') % branch
6004 if branch != 'default':
6003 if branch != 'default':
6005 ui.write(m, label='log.branch')
6004 ui.write(m, label='log.branch')
6006 else:
6005 else:
6007 ui.status(m, label='log.branch')
6006 ui.status(m, label='log.branch')
6008
6007
6009 if marks:
6008 if marks:
6010 active = repo._activebookmark
6009 active = repo._activebookmark
6011 # i18n: column positioning for "hg summary"
6010 # i18n: column positioning for "hg summary"
6012 ui.write(_('bookmarks:'), label='log.bookmark')
6011 ui.write(_('bookmarks:'), label='log.bookmark')
6013 if active is not None:
6012 if active is not None:
6014 if active in marks:
6013 if active in marks:
6015 ui.write(' *' + active, label=activebookmarklabel)
6014 ui.write(' *' + active, label=activebookmarklabel)
6016 marks.remove(active)
6015 marks.remove(active)
6017 else:
6016 else:
6018 ui.write(' [%s]' % active, label=activebookmarklabel)
6017 ui.write(' [%s]' % active, label=activebookmarklabel)
6019 for m in marks:
6018 for m in marks:
6020 ui.write(' ' + m, label='log.bookmark')
6019 ui.write(' ' + m, label='log.bookmark')
6021 ui.write('\n', label='log.bookmark')
6020 ui.write('\n', label='log.bookmark')
6022
6021
6023 status = repo.status(unknown=True)
6022 status = repo.status(unknown=True)
6024
6023
6025 c = repo.dirstate.copies()
6024 c = repo.dirstate.copies()
6026 copied, renamed = [], []
6025 copied, renamed = [], []
6027 for d, s in c.iteritems():
6026 for d, s in c.iteritems():
6028 if s in status.removed:
6027 if s in status.removed:
6029 status.removed.remove(s)
6028 status.removed.remove(s)
6030 renamed.append(d)
6029 renamed.append(d)
6031 else:
6030 else:
6032 copied.append(d)
6031 copied.append(d)
6033 if d in status.added:
6032 if d in status.added:
6034 status.added.remove(d)
6033 status.added.remove(d)
6035
6034
6036 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
6035 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
6037
6036
6038 labels = [(ui.label(_('%d modified'), 'status.modified'), status.modified),
6037 labels = [(ui.label(_('%d modified'), 'status.modified'), status.modified),
6039 (ui.label(_('%d added'), 'status.added'), status.added),
6038 (ui.label(_('%d added'), 'status.added'), status.added),
6040 (ui.label(_('%d removed'), 'status.removed'), status.removed),
6039 (ui.label(_('%d removed'), 'status.removed'), status.removed),
6041 (ui.label(_('%d renamed'), 'status.copied'), renamed),
6040 (ui.label(_('%d renamed'), 'status.copied'), renamed),
6042 (ui.label(_('%d copied'), 'status.copied'), copied),
6041 (ui.label(_('%d copied'), 'status.copied'), copied),
6043 (ui.label(_('%d deleted'), 'status.deleted'), status.deleted),
6042 (ui.label(_('%d deleted'), 'status.deleted'), status.deleted),
6044 (ui.label(_('%d unknown'), 'status.unknown'), status.unknown),
6043 (ui.label(_('%d unknown'), 'status.unknown'), status.unknown),
6045 (ui.label(_('%d unresolved'), 'resolve.unresolved'), unresolved),
6044 (ui.label(_('%d unresolved'), 'resolve.unresolved'), unresolved),
6046 (ui.label(_('%d subrepos'), 'status.modified'), subs)]
6045 (ui.label(_('%d subrepos'), 'status.modified'), subs)]
6047 t = []
6046 t = []
6048 for l, s in labels:
6047 for l, s in labels:
6049 if s:
6048 if s:
6050 t.append(l % len(s))
6049 t.append(l % len(s))
6051
6050
6052 t = ', '.join(t)
6051 t = ', '.join(t)
6053 cleanworkdir = False
6052 cleanworkdir = False
6054
6053
6055 if repo.vfs.exists('graftstate'):
6054 if repo.vfs.exists('graftstate'):
6056 t += _(' (graft in progress)')
6055 t += _(' (graft in progress)')
6057 if repo.vfs.exists('updatestate'):
6056 if repo.vfs.exists('updatestate'):
6058 t += _(' (interrupted update)')
6057 t += _(' (interrupted update)')
6059 elif len(parents) > 1:
6058 elif len(parents) > 1:
6060 t += _(' (merge)')
6059 t += _(' (merge)')
6061 elif branch != parents[0].branch():
6060 elif branch != parents[0].branch():
6062 t += _(' (new branch)')
6061 t += _(' (new branch)')
6063 elif (parents[0].closesbranch() and
6062 elif (parents[0].closesbranch() and
6064 pnode in repo.branchheads(branch, closed=True)):
6063 pnode in repo.branchheads(branch, closed=True)):
6065 t += _(' (head closed)')
6064 t += _(' (head closed)')
6066 elif not (status.modified or status.added or status.removed or renamed or
6065 elif not (status.modified or status.added or status.removed or renamed or
6067 copied or subs):
6066 copied or subs):
6068 t += _(' (clean)')
6067 t += _(' (clean)')
6069 cleanworkdir = True
6068 cleanworkdir = True
6070 elif pnode not in bheads:
6069 elif pnode not in bheads:
6071 t += _(' (new branch head)')
6070 t += _(' (new branch head)')
6072
6071
6073 if parents:
6072 if parents:
6074 pendingphase = max(p.phase() for p in parents)
6073 pendingphase = max(p.phase() for p in parents)
6075 else:
6074 else:
6076 pendingphase = phases.public
6075 pendingphase = phases.public
6077
6076
6078 if pendingphase > phases.newcommitphase(ui):
6077 if pendingphase > phases.newcommitphase(ui):
6079 t += ' (%s)' % phases.phasenames[pendingphase]
6078 t += ' (%s)' % phases.phasenames[pendingphase]
6080
6079
6081 if cleanworkdir:
6080 if cleanworkdir:
6082 # i18n: column positioning for "hg summary"
6081 # i18n: column positioning for "hg summary"
6083 ui.status(_('commit: %s\n') % t.strip())
6082 ui.status(_('commit: %s\n') % t.strip())
6084 else:
6083 else:
6085 # i18n: column positioning for "hg summary"
6084 # i18n: column positioning for "hg summary"
6086 ui.write(_('commit: %s\n') % t.strip())
6085 ui.write(_('commit: %s\n') % t.strip())
6087
6086
6088 # all ancestors of branch heads - all ancestors of parent = new csets
6087 # all ancestors of branch heads - all ancestors of parent = new csets
6089 new = len(repo.changelog.findmissing([pctx.node() for pctx in parents],
6088 new = len(repo.changelog.findmissing([pctx.node() for pctx in parents],
6090 bheads))
6089 bheads))
6091
6090
6092 if new == 0:
6091 if new == 0:
6093 # i18n: column positioning for "hg summary"
6092 # i18n: column positioning for "hg summary"
6094 ui.status(_('update: (current)\n'))
6093 ui.status(_('update: (current)\n'))
6095 elif pnode not in bheads:
6094 elif pnode not in bheads:
6096 # i18n: column positioning for "hg summary"
6095 # i18n: column positioning for "hg summary"
6097 ui.write(_('update: %d new changesets (update)\n') % new)
6096 ui.write(_('update: %d new changesets (update)\n') % new)
6098 else:
6097 else:
6099 # i18n: column positioning for "hg summary"
6098 # i18n: column positioning for "hg summary"
6100 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
6099 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
6101 (new, len(bheads)))
6100 (new, len(bheads)))
6102
6101
6103 t = []
6102 t = []
6104 draft = len(repo.revs('draft()'))
6103 draft = len(repo.revs('draft()'))
6105 if draft:
6104 if draft:
6106 t.append(_('%d draft') % draft)
6105 t.append(_('%d draft') % draft)
6107 secret = len(repo.revs('secret()'))
6106 secret = len(repo.revs('secret()'))
6108 if secret:
6107 if secret:
6109 t.append(_('%d secret') % secret)
6108 t.append(_('%d secret') % secret)
6110
6109
6111 if draft or secret:
6110 if draft or secret:
6112 ui.status(_('phases: %s\n') % ', '.join(t))
6111 ui.status(_('phases: %s\n') % ', '.join(t))
6113
6112
6114 if obsolete.isenabled(repo, obsolete.createmarkersopt):
6113 if obsolete.isenabled(repo, obsolete.createmarkersopt):
6115 for trouble in ("unstable", "divergent", "bumped"):
6114 for trouble in ("unstable", "divergent", "bumped"):
6116 numtrouble = len(repo.revs(trouble + "()"))
6115 numtrouble = len(repo.revs(trouble + "()"))
6117 # We write all the possibilities to ease translation
6116 # We write all the possibilities to ease translation
6118 troublemsg = {
6117 troublemsg = {
6119 "unstable": _("unstable: %d changesets"),
6118 "unstable": _("unstable: %d changesets"),
6120 "divergent": _("divergent: %d changesets"),
6119 "divergent": _("divergent: %d changesets"),
6121 "bumped": _("bumped: %d changesets"),
6120 "bumped": _("bumped: %d changesets"),
6122 }
6121 }
6123 if numtrouble > 0:
6122 if numtrouble > 0:
6124 ui.status(troublemsg[trouble] % numtrouble + "\n")
6123 ui.status(troublemsg[trouble] % numtrouble + "\n")
6125
6124
6126 cmdutil.summaryhooks(ui, repo)
6125 cmdutil.summaryhooks(ui, repo)
6127
6126
6128 if opts.get('remote'):
6127 if opts.get('remote'):
6129 needsincoming, needsoutgoing = True, True
6128 needsincoming, needsoutgoing = True, True
6130 else:
6129 else:
6131 needsincoming, needsoutgoing = False, False
6130 needsincoming, needsoutgoing = False, False
6132 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
6131 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
6133 if i:
6132 if i:
6134 needsincoming = True
6133 needsincoming = True
6135 if o:
6134 if o:
6136 needsoutgoing = True
6135 needsoutgoing = True
6137 if not needsincoming and not needsoutgoing:
6136 if not needsincoming and not needsoutgoing:
6138 return
6137 return
6139
6138
6140 def getincoming():
6139 def getincoming():
6141 source, branches = hg.parseurl(ui.expandpath('default'))
6140 source, branches = hg.parseurl(ui.expandpath('default'))
6142 sbranch = branches[0]
6141 sbranch = branches[0]
6143 try:
6142 try:
6144 other = hg.peer(repo, {}, source)
6143 other = hg.peer(repo, {}, source)
6145 except error.RepoError:
6144 except error.RepoError:
6146 if opts.get('remote'):
6145 if opts.get('remote'):
6147 raise
6146 raise
6148 return source, sbranch, None, None, None
6147 return source, sbranch, None, None, None
6149 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
6148 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
6150 if revs:
6149 if revs:
6151 revs = [other.lookup(rev) for rev in revs]
6150 revs = [other.lookup(rev) for rev in revs]
6152 ui.debug('comparing with %s\n' % util.hidepassword(source))
6151 ui.debug('comparing with %s\n' % util.hidepassword(source))
6153 repo.ui.pushbuffer()
6152 repo.ui.pushbuffer()
6154 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
6153 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
6155 repo.ui.popbuffer()
6154 repo.ui.popbuffer()
6156 return source, sbranch, other, commoninc, commoninc[1]
6155 return source, sbranch, other, commoninc, commoninc[1]
6157
6156
6158 if needsincoming:
6157 if needsincoming:
6159 source, sbranch, sother, commoninc, incoming = getincoming()
6158 source, sbranch, sother, commoninc, incoming = getincoming()
6160 else:
6159 else:
6161 source = sbranch = sother = commoninc = incoming = None
6160 source = sbranch = sother = commoninc = incoming = None
6162
6161
6163 def getoutgoing():
6162 def getoutgoing():
6164 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
6163 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
6165 dbranch = branches[0]
6164 dbranch = branches[0]
6166 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
6165 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
6167 if source != dest:
6166 if source != dest:
6168 try:
6167 try:
6169 dother = hg.peer(repo, {}, dest)
6168 dother = hg.peer(repo, {}, dest)
6170 except error.RepoError:
6169 except error.RepoError:
6171 if opts.get('remote'):
6170 if opts.get('remote'):
6172 raise
6171 raise
6173 return dest, dbranch, None, None
6172 return dest, dbranch, None, None
6174 ui.debug('comparing with %s\n' % util.hidepassword(dest))
6173 ui.debug('comparing with %s\n' % util.hidepassword(dest))
6175 elif sother is None:
6174 elif sother is None:
6176 # there is no explicit destination peer, but source one is invalid
6175 # there is no explicit destination peer, but source one is invalid
6177 return dest, dbranch, None, None
6176 return dest, dbranch, None, None
6178 else:
6177 else:
6179 dother = sother
6178 dother = sother
6180 if (source != dest or (sbranch is not None and sbranch != dbranch)):
6179 if (source != dest or (sbranch is not None and sbranch != dbranch)):
6181 common = None
6180 common = None
6182 else:
6181 else:
6183 common = commoninc
6182 common = commoninc
6184 if revs:
6183 if revs:
6185 revs = [repo.lookup(rev) for rev in revs]
6184 revs = [repo.lookup(rev) for rev in revs]
6186 repo.ui.pushbuffer()
6185 repo.ui.pushbuffer()
6187 outgoing = discovery.findcommonoutgoing(repo, dother, onlyheads=revs,
6186 outgoing = discovery.findcommonoutgoing(repo, dother, onlyheads=revs,
6188 commoninc=common)
6187 commoninc=common)
6189 repo.ui.popbuffer()
6188 repo.ui.popbuffer()
6190 return dest, dbranch, dother, outgoing
6189 return dest, dbranch, dother, outgoing
6191
6190
6192 if needsoutgoing:
6191 if needsoutgoing:
6193 dest, dbranch, dother, outgoing = getoutgoing()
6192 dest, dbranch, dother, outgoing = getoutgoing()
6194 else:
6193 else:
6195 dest = dbranch = dother = outgoing = None
6194 dest = dbranch = dother = outgoing = None
6196
6195
6197 if opts.get('remote'):
6196 if opts.get('remote'):
6198 t = []
6197 t = []
6199 if incoming:
6198 if incoming:
6200 t.append(_('1 or more incoming'))
6199 t.append(_('1 or more incoming'))
6201 o = outgoing.missing
6200 o = outgoing.missing
6202 if o:
6201 if o:
6203 t.append(_('%d outgoing') % len(o))
6202 t.append(_('%d outgoing') % len(o))
6204 other = dother or sother
6203 other = dother or sother
6205 if 'bookmarks' in other.listkeys('namespaces'):
6204 if 'bookmarks' in other.listkeys('namespaces'):
6206 counts = bookmarks.summary(repo, other)
6205 counts = bookmarks.summary(repo, other)
6207 if counts[0] > 0:
6206 if counts[0] > 0:
6208 t.append(_('%d incoming bookmarks') % counts[0])
6207 t.append(_('%d incoming bookmarks') % counts[0])
6209 if counts[1] > 0:
6208 if counts[1] > 0:
6210 t.append(_('%d outgoing bookmarks') % counts[1])
6209 t.append(_('%d outgoing bookmarks') % counts[1])
6211
6210
6212 if t:
6211 if t:
6213 # i18n: column positioning for "hg summary"
6212 # i18n: column positioning for "hg summary"
6214 ui.write(_('remote: %s\n') % (', '.join(t)))
6213 ui.write(_('remote: %s\n') % (', '.join(t)))
6215 else:
6214 else:
6216 # i18n: column positioning for "hg summary"
6215 # i18n: column positioning for "hg summary"
6217 ui.status(_('remote: (synced)\n'))
6216 ui.status(_('remote: (synced)\n'))
6218
6217
6219 cmdutil.summaryremotehooks(ui, repo, opts,
6218 cmdutil.summaryremotehooks(ui, repo, opts,
6220 ((source, sbranch, sother, commoninc),
6219 ((source, sbranch, sother, commoninc),
6221 (dest, dbranch, dother, outgoing)))
6220 (dest, dbranch, dother, outgoing)))
6222
6221
6223 @command('tag',
6222 @command('tag',
6224 [('f', 'force', None, _('force tag')),
6223 [('f', 'force', None, _('force tag')),
6225 ('l', 'local', None, _('make the tag local')),
6224 ('l', 'local', None, _('make the tag local')),
6226 ('r', 'rev', '', _('revision to tag'), _('REV')),
6225 ('r', 'rev', '', _('revision to tag'), _('REV')),
6227 ('', 'remove', None, _('remove a tag')),
6226 ('', 'remove', None, _('remove a tag')),
6228 # -l/--local is already there, commitopts cannot be used
6227 # -l/--local is already there, commitopts cannot be used
6229 ('e', 'edit', None, _('invoke editor on commit messages')),
6228 ('e', 'edit', None, _('invoke editor on commit messages')),
6230 ('m', 'message', '', _('use text as commit message'), _('TEXT')),
6229 ('m', 'message', '', _('use text as commit message'), _('TEXT')),
6231 ] + commitopts2,
6230 ] + commitopts2,
6232 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
6231 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
6233 def tag(ui, repo, name1, *names, **opts):
6232 def tag(ui, repo, name1, *names, **opts):
6234 """add one or more tags for the current or given revision
6233 """add one or more tags for the current or given revision
6235
6234
6236 Name a particular revision using <name>.
6235 Name a particular revision using <name>.
6237
6236
6238 Tags are used to name particular revisions of the repository and are
6237 Tags are used to name particular revisions of the repository and are
6239 very useful to compare different revisions, to go back to significant
6238 very useful to compare different revisions, to go back to significant
6240 earlier versions or to mark branch points as releases, etc. Changing
6239 earlier versions or to mark branch points as releases, etc. Changing
6241 an existing tag is normally disallowed; use -f/--force to override.
6240 an existing tag is normally disallowed; use -f/--force to override.
6242
6241
6243 If no revision is given, the parent of the working directory is
6242 If no revision is given, the parent of the working directory is
6244 used.
6243 used.
6245
6244
6246 To facilitate version control, distribution, and merging of tags,
6245 To facilitate version control, distribution, and merging of tags,
6247 they are stored as a file named ".hgtags" which is managed similarly
6246 they are stored as a file named ".hgtags" which is managed similarly
6248 to other project files and can be hand-edited if necessary. This
6247 to other project files and can be hand-edited if necessary. This
6249 also means that tagging creates a new commit. The file
6248 also means that tagging creates a new commit. The file
6250 ".hg/localtags" is used for local tags (not shared among
6249 ".hg/localtags" is used for local tags (not shared among
6251 repositories).
6250 repositories).
6252
6251
6253 Tag commits are usually made at the head of a branch. If the parent
6252 Tag commits are usually made at the head of a branch. If the parent
6254 of the working directory is not a branch head, :hg:`tag` aborts; use
6253 of the working directory is not a branch head, :hg:`tag` aborts; use
6255 -f/--force to force the tag commit to be based on a non-head
6254 -f/--force to force the tag commit to be based on a non-head
6256 changeset.
6255 changeset.
6257
6256
6258 See :hg:`help dates` for a list of formats valid for -d/--date.
6257 See :hg:`help dates` for a list of formats valid for -d/--date.
6259
6258
6260 Since tag names have priority over branch names during revision
6259 Since tag names have priority over branch names during revision
6261 lookup, using an existing branch name as a tag name is discouraged.
6260 lookup, using an existing branch name as a tag name is discouraged.
6262
6261
6263 Returns 0 on success.
6262 Returns 0 on success.
6264 """
6263 """
6265 wlock = lock = None
6264 wlock = lock = None
6266 try:
6265 try:
6267 wlock = repo.wlock()
6266 wlock = repo.wlock()
6268 lock = repo.lock()
6267 lock = repo.lock()
6269 rev_ = "."
6268 rev_ = "."
6270 names = [t.strip() for t in (name1,) + names]
6269 names = [t.strip() for t in (name1,) + names]
6271 if len(names) != len(set(names)):
6270 if len(names) != len(set(names)):
6272 raise error.Abort(_('tag names must be unique'))
6271 raise error.Abort(_('tag names must be unique'))
6273 for n in names:
6272 for n in names:
6274 scmutil.checknewlabel(repo, n, 'tag')
6273 scmutil.checknewlabel(repo, n, 'tag')
6275 if not n:
6274 if not n:
6276 raise error.Abort(_('tag names cannot consist entirely of '
6275 raise error.Abort(_('tag names cannot consist entirely of '
6277 'whitespace'))
6276 'whitespace'))
6278 if opts.get('rev') and opts.get('remove'):
6277 if opts.get('rev') and opts.get('remove'):
6279 raise error.Abort(_("--rev and --remove are incompatible"))
6278 raise error.Abort(_("--rev and --remove are incompatible"))
6280 if opts.get('rev'):
6279 if opts.get('rev'):
6281 rev_ = opts['rev']
6280 rev_ = opts['rev']
6282 message = opts.get('message')
6281 message = opts.get('message')
6283 if opts.get('remove'):
6282 if opts.get('remove'):
6284 if opts.get('local'):
6283 if opts.get('local'):
6285 expectedtype = 'local'
6284 expectedtype = 'local'
6286 else:
6285 else:
6287 expectedtype = 'global'
6286 expectedtype = 'global'
6288
6287
6289 for n in names:
6288 for n in names:
6290 if not repo.tagtype(n):
6289 if not repo.tagtype(n):
6291 raise error.Abort(_("tag '%s' does not exist") % n)
6290 raise error.Abort(_("tag '%s' does not exist") % n)
6292 if repo.tagtype(n) != expectedtype:
6291 if repo.tagtype(n) != expectedtype:
6293 if expectedtype == 'global':
6292 if expectedtype == 'global':
6294 raise error.Abort(_("tag '%s' is not a global tag") % n)
6293 raise error.Abort(_("tag '%s' is not a global tag") % n)
6295 else:
6294 else:
6296 raise error.Abort(_("tag '%s' is not a local tag") % n)
6295 raise error.Abort(_("tag '%s' is not a local tag") % n)
6297 rev_ = 'null'
6296 rev_ = 'null'
6298 if not message:
6297 if not message:
6299 # we don't translate commit messages
6298 # we don't translate commit messages
6300 message = 'Removed tag %s' % ', '.join(names)
6299 message = 'Removed tag %s' % ', '.join(names)
6301 elif not opts.get('force'):
6300 elif not opts.get('force'):
6302 for n in names:
6301 for n in names:
6303 if n in repo.tags():
6302 if n in repo.tags():
6304 raise error.Abort(_("tag '%s' already exists "
6303 raise error.Abort(_("tag '%s' already exists "
6305 "(use -f to force)") % n)
6304 "(use -f to force)") % n)
6306 if not opts.get('local'):
6305 if not opts.get('local'):
6307 p1, p2 = repo.dirstate.parents()
6306 p1, p2 = repo.dirstate.parents()
6308 if p2 != nullid:
6307 if p2 != nullid:
6309 raise error.Abort(_('uncommitted merge'))
6308 raise error.Abort(_('uncommitted merge'))
6310 bheads = repo.branchheads()
6309 bheads = repo.branchheads()
6311 if not opts.get('force') and bheads and p1 not in bheads:
6310 if not opts.get('force') and bheads and p1 not in bheads:
6312 raise error.Abort(_('working directory is not at a branch head '
6311 raise error.Abort(_('working directory is not at a branch head '
6313 '(use -f to force)'))
6312 '(use -f to force)'))
6314 r = scmutil.revsingle(repo, rev_).node()
6313 r = scmutil.revsingle(repo, rev_).node()
6315
6314
6316 if not message:
6315 if not message:
6317 # we don't translate commit messages
6316 # we don't translate commit messages
6318 message = ('Added tag %s for changeset %s' %
6317 message = ('Added tag %s for changeset %s' %
6319 (', '.join(names), short(r)))
6318 (', '.join(names), short(r)))
6320
6319
6321 date = opts.get('date')
6320 date = opts.get('date')
6322 if date:
6321 if date:
6323 date = util.parsedate(date)
6322 date = util.parsedate(date)
6324
6323
6325 if opts.get('remove'):
6324 if opts.get('remove'):
6326 editform = 'tag.remove'
6325 editform = 'tag.remove'
6327 else:
6326 else:
6328 editform = 'tag.add'
6327 editform = 'tag.add'
6329 editor = cmdutil.getcommiteditor(editform=editform, **opts)
6328 editor = cmdutil.getcommiteditor(editform=editform, **opts)
6330
6329
6331 # don't allow tagging the null rev
6330 # don't allow tagging the null rev
6332 if (not opts.get('remove') and
6331 if (not opts.get('remove') and
6333 scmutil.revsingle(repo, rev_).rev() == nullrev):
6332 scmutil.revsingle(repo, rev_).rev() == nullrev):
6334 raise error.Abort(_("cannot tag null revision"))
6333 raise error.Abort(_("cannot tag null revision"))
6335
6334
6336 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date,
6335 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date,
6337 editor=editor)
6336 editor=editor)
6338 finally:
6337 finally:
6339 release(lock, wlock)
6338 release(lock, wlock)
6340
6339
6341 @command('tags', formatteropts, '')
6340 @command('tags', formatteropts, '')
6342 def tags(ui, repo, **opts):
6341 def tags(ui, repo, **opts):
6343 """list repository tags
6342 """list repository tags
6344
6343
6345 This lists both regular and local tags. When the -v/--verbose
6344 This lists both regular and local tags. When the -v/--verbose
6346 switch is used, a third column "local" is printed for local tags.
6345 switch is used, a third column "local" is printed for local tags.
6347 When the -q/--quiet switch is used, only the tag name is printed.
6346 When the -q/--quiet switch is used, only the tag name is printed.
6348
6347
6349 Returns 0 on success.
6348 Returns 0 on success.
6350 """
6349 """
6351
6350
6352 fm = ui.formatter('tags', opts)
6351 fm = ui.formatter('tags', opts)
6353 hexfunc = fm.hexfunc
6352 hexfunc = fm.hexfunc
6354 tagtype = ""
6353 tagtype = ""
6355
6354
6356 for t, n in reversed(repo.tagslist()):
6355 for t, n in reversed(repo.tagslist()):
6357 hn = hexfunc(n)
6356 hn = hexfunc(n)
6358 label = 'tags.normal'
6357 label = 'tags.normal'
6359 tagtype = ''
6358 tagtype = ''
6360 if repo.tagtype(t) == 'local':
6359 if repo.tagtype(t) == 'local':
6361 label = 'tags.local'
6360 label = 'tags.local'
6362 tagtype = 'local'
6361 tagtype = 'local'
6363
6362
6364 fm.startitem()
6363 fm.startitem()
6365 fm.write('tag', '%s', t, label=label)
6364 fm.write('tag', '%s', t, label=label)
6366 fmt = " " * (30 - encoding.colwidth(t)) + ' %5d:%s'
6365 fmt = " " * (30 - encoding.colwidth(t)) + ' %5d:%s'
6367 fm.condwrite(not ui.quiet, 'rev node', fmt,
6366 fm.condwrite(not ui.quiet, 'rev node', fmt,
6368 repo.changelog.rev(n), hn, label=label)
6367 repo.changelog.rev(n), hn, label=label)
6369 fm.condwrite(ui.verbose and tagtype, 'type', ' %s',
6368 fm.condwrite(ui.verbose and tagtype, 'type', ' %s',
6370 tagtype, label=label)
6369 tagtype, label=label)
6371 fm.plain('\n')
6370 fm.plain('\n')
6372 fm.end()
6371 fm.end()
6373
6372
6374 @command('tip',
6373 @command('tip',
6375 [('p', 'patch', None, _('show patch')),
6374 [('p', 'patch', None, _('show patch')),
6376 ('g', 'git', None, _('use git extended diff format')),
6375 ('g', 'git', None, _('use git extended diff format')),
6377 ] + templateopts,
6376 ] + templateopts,
6378 _('[-p] [-g]'))
6377 _('[-p] [-g]'))
6379 def tip(ui, repo, **opts):
6378 def tip(ui, repo, **opts):
6380 """show the tip revision (DEPRECATED)
6379 """show the tip revision (DEPRECATED)
6381
6380
6382 The tip revision (usually just called the tip) is the changeset
6381 The tip revision (usually just called the tip) is the changeset
6383 most recently added to the repository (and therefore the most
6382 most recently added to the repository (and therefore the most
6384 recently changed head).
6383 recently changed head).
6385
6384
6386 If you have just made a commit, that commit will be the tip. If
6385 If you have just made a commit, that commit will be the tip. If
6387 you have just pulled changes from another repository, the tip of
6386 you have just pulled changes from another repository, the tip of
6388 that repository becomes the current tip. The "tip" tag is special
6387 that repository becomes the current tip. The "tip" tag is special
6389 and cannot be renamed or assigned to a different changeset.
6388 and cannot be renamed or assigned to a different changeset.
6390
6389
6391 This command is deprecated, please use :hg:`heads` instead.
6390 This command is deprecated, please use :hg:`heads` instead.
6392
6391
6393 Returns 0 on success.
6392 Returns 0 on success.
6394 """
6393 """
6395 displayer = cmdutil.show_changeset(ui, repo, opts)
6394 displayer = cmdutil.show_changeset(ui, repo, opts)
6396 displayer.show(repo['tip'])
6395 displayer.show(repo['tip'])
6397 displayer.close()
6396 displayer.close()
6398
6397
6399 @command('unbundle',
6398 @command('unbundle',
6400 [('u', 'update', None,
6399 [('u', 'update', None,
6401 _('update to new branch head if changesets were unbundled'))],
6400 _('update to new branch head if changesets were unbundled'))],
6402 _('[-u] FILE...'))
6401 _('[-u] FILE...'))
6403 def unbundle(ui, repo, fname1, *fnames, **opts):
6402 def unbundle(ui, repo, fname1, *fnames, **opts):
6404 """apply one or more changegroup files
6403 """apply one or more changegroup files
6405
6404
6406 Apply one or more compressed changegroup files generated by the
6405 Apply one or more compressed changegroup files generated by the
6407 bundle command.
6406 bundle command.
6408
6407
6409 Returns 0 on success, 1 if an update has unresolved files.
6408 Returns 0 on success, 1 if an update has unresolved files.
6410 """
6409 """
6411 fnames = (fname1,) + fnames
6410 fnames = (fname1,) + fnames
6412
6411
6413 with repo.lock():
6412 with repo.lock():
6414 for fname in fnames:
6413 for fname in fnames:
6415 f = hg.openpath(ui, fname)
6414 f = hg.openpath(ui, fname)
6416 gen = exchange.readbundle(ui, f, fname)
6415 gen = exchange.readbundle(ui, f, fname)
6417 if isinstance(gen, bundle2.unbundle20):
6416 if isinstance(gen, bundle2.unbundle20):
6418 tr = repo.transaction('unbundle')
6417 tr = repo.transaction('unbundle')
6419 try:
6418 try:
6420 op = bundle2.applybundle(repo, gen, tr, source='unbundle',
6419 op = bundle2.applybundle(repo, gen, tr, source='unbundle',
6421 url='bundle:' + fname)
6420 url='bundle:' + fname)
6422 tr.close()
6421 tr.close()
6423 except error.BundleUnknownFeatureError as exc:
6422 except error.BundleUnknownFeatureError as exc:
6424 raise error.Abort(_('%s: unknown bundle feature, %s')
6423 raise error.Abort(_('%s: unknown bundle feature, %s')
6425 % (fname, exc),
6424 % (fname, exc),
6426 hint=_("see https://mercurial-scm.org/"
6425 hint=_("see https://mercurial-scm.org/"
6427 "wiki/BundleFeature for more "
6426 "wiki/BundleFeature for more "
6428 "information"))
6427 "information"))
6429 finally:
6428 finally:
6430 if tr:
6429 if tr:
6431 tr.release()
6430 tr.release()
6432 changes = [r.get('return', 0)
6431 changes = [r.get('return', 0)
6433 for r in op.records['changegroup']]
6432 for r in op.records['changegroup']]
6434 modheads = changegroup.combineresults(changes)
6433 modheads = changegroup.combineresults(changes)
6435 elif isinstance(gen, streamclone.streamcloneapplier):
6434 elif isinstance(gen, streamclone.streamcloneapplier):
6436 raise error.Abort(
6435 raise error.Abort(
6437 _('packed bundles cannot be applied with '
6436 _('packed bundles cannot be applied with '
6438 '"hg unbundle"'),
6437 '"hg unbundle"'),
6439 hint=_('use "hg debugapplystreamclonebundle"'))
6438 hint=_('use "hg debugapplystreamclonebundle"'))
6440 else:
6439 else:
6441 modheads = gen.apply(repo, 'unbundle', 'bundle:' + fname)
6440 modheads = gen.apply(repo, 'unbundle', 'bundle:' + fname)
6442
6441
6443 return postincoming(ui, repo, modheads, opts.get('update'), None, None)
6442 return postincoming(ui, repo, modheads, opts.get('update'), None, None)
6444
6443
6445 @command('^update|up|checkout|co',
6444 @command('^update|up|checkout|co',
6446 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
6445 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
6447 ('c', 'check', None, _('require clean working directory')),
6446 ('c', 'check', None, _('require clean working directory')),
6448 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
6447 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
6449 ('r', 'rev', '', _('revision'), _('REV'))
6448 ('r', 'rev', '', _('revision'), _('REV'))
6450 ] + mergetoolopts,
6449 ] + mergetoolopts,
6451 _('[-c] [-C] [-d DATE] [[-r] REV]'))
6450 _('[-c] [-C] [-d DATE] [[-r] REV]'))
6452 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False,
6451 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False,
6453 tool=None):
6452 tool=None):
6454 """update working directory (or switch revisions)
6453 """update working directory (or switch revisions)
6455
6454
6456 Update the repository's working directory to the specified
6455 Update the repository's working directory to the specified
6457 changeset. If no changeset is specified, update to the tip of the
6456 changeset. If no changeset is specified, update to the tip of the
6458 current named branch and move the active bookmark (see :hg:`help
6457 current named branch and move the active bookmark (see :hg:`help
6459 bookmarks`).
6458 bookmarks`).
6460
6459
6461 Update sets the working directory's parent revision to the specified
6460 Update sets the working directory's parent revision to the specified
6462 changeset (see :hg:`help parents`).
6461 changeset (see :hg:`help parents`).
6463
6462
6464 If the changeset is not a descendant or ancestor of the working
6463 If the changeset is not a descendant or ancestor of the working
6465 directory's parent, the update is aborted. With the -c/--check
6464 directory's parent, the update is aborted. With the -c/--check
6466 option, the working directory is checked for uncommitted changes; if
6465 option, the working directory is checked for uncommitted changes; if
6467 none are found, the working directory is updated to the specified
6466 none are found, the working directory is updated to the specified
6468 changeset.
6467 changeset.
6469
6468
6470 .. container:: verbose
6469 .. container:: verbose
6471
6470
6472 The following rules apply when the working directory contains
6471 The following rules apply when the working directory contains
6473 uncommitted changes:
6472 uncommitted changes:
6474
6473
6475 1. If neither -c/--check nor -C/--clean is specified, and if
6474 1. If neither -c/--check nor -C/--clean is specified, and if
6476 the requested changeset is an ancestor or descendant of
6475 the requested changeset is an ancestor or descendant of
6477 the working directory's parent, the uncommitted changes
6476 the working directory's parent, the uncommitted changes
6478 are merged into the requested changeset and the merged
6477 are merged into the requested changeset and the merged
6479 result is left uncommitted. If the requested changeset is
6478 result is left uncommitted. If the requested changeset is
6480 not an ancestor or descendant (that is, it is on another
6479 not an ancestor or descendant (that is, it is on another
6481 branch), the update is aborted and the uncommitted changes
6480 branch), the update is aborted and the uncommitted changes
6482 are preserved.
6481 are preserved.
6483
6482
6484 2. With the -c/--check option, the update is aborted and the
6483 2. With the -c/--check option, the update is aborted and the
6485 uncommitted changes are preserved.
6484 uncommitted changes are preserved.
6486
6485
6487 3. With the -C/--clean option, uncommitted changes are discarded and
6486 3. With the -C/--clean option, uncommitted changes are discarded and
6488 the working directory is updated to the requested changeset.
6487 the working directory is updated to the requested changeset.
6489
6488
6490 To cancel an uncommitted merge (and lose your changes), use
6489 To cancel an uncommitted merge (and lose your changes), use
6491 :hg:`update --clean .`.
6490 :hg:`update --clean .`.
6492
6491
6493 Use null as the changeset to remove the working directory (like
6492 Use null as the changeset to remove the working directory (like
6494 :hg:`clone -U`).
6493 :hg:`clone -U`).
6495
6494
6496 If you want to revert just one file to an older revision, use
6495 If you want to revert just one file to an older revision, use
6497 :hg:`revert [-r REV] NAME`.
6496 :hg:`revert [-r REV] NAME`.
6498
6497
6499 See :hg:`help dates` for a list of formats valid for -d/--date.
6498 See :hg:`help dates` for a list of formats valid for -d/--date.
6500
6499
6501 Returns 0 on success, 1 if there are unresolved files.
6500 Returns 0 on success, 1 if there are unresolved files.
6502 """
6501 """
6503 if rev and node:
6502 if rev and node:
6504 raise error.Abort(_("please specify just one revision"))
6503 raise error.Abort(_("please specify just one revision"))
6505
6504
6506 if rev is None or rev == '':
6505 if rev is None or rev == '':
6507 rev = node
6506 rev = node
6508
6507
6509 if date and rev is not None:
6508 if date and rev is not None:
6510 raise error.Abort(_("you can't specify a revision and a date"))
6509 raise error.Abort(_("you can't specify a revision and a date"))
6511
6510
6512 if check and clean:
6511 if check and clean:
6513 raise error.Abort(_("cannot specify both -c/--check and -C/--clean"))
6512 raise error.Abort(_("cannot specify both -c/--check and -C/--clean"))
6514
6513
6515 with repo.wlock():
6514 with repo.wlock():
6516 cmdutil.clearunfinished(repo)
6515 cmdutil.clearunfinished(repo)
6517
6516
6518 if date:
6517 if date:
6519 rev = cmdutil.finddate(ui, repo, date)
6518 rev = cmdutil.finddate(ui, repo, date)
6520
6519
6521 # if we defined a bookmark, we have to remember the original name
6520 # if we defined a bookmark, we have to remember the original name
6522 brev = rev
6521 brev = rev
6523 rev = scmutil.revsingle(repo, rev, rev).rev()
6522 rev = scmutil.revsingle(repo, rev, rev).rev()
6524
6523
6525 if check:
6524 if check:
6526 cmdutil.bailifchanged(repo, merge=False)
6525 cmdutil.bailifchanged(repo, merge=False)
6527
6526
6528 repo.ui.setconfig('ui', 'forcemerge', tool, 'update')
6527 repo.ui.setconfig('ui', 'forcemerge', tool, 'update')
6529
6528
6530 return hg.updatetotally(ui, repo, rev, brev, clean=clean, check=check)
6529 return hg.updatetotally(ui, repo, rev, brev, clean=clean, check=check)
6531
6530
6532 @command('verify', [])
6531 @command('verify', [])
6533 def verify(ui, repo):
6532 def verify(ui, repo):
6534 """verify the integrity of the repository
6533 """verify the integrity of the repository
6535
6534
6536 Verify the integrity of the current repository.
6535 Verify the integrity of the current repository.
6537
6536
6538 This will perform an extensive check of the repository's
6537 This will perform an extensive check of the repository's
6539 integrity, validating the hashes and checksums of each entry in
6538 integrity, validating the hashes and checksums of each entry in
6540 the changelog, manifest, and tracked files, as well as the
6539 the changelog, manifest, and tracked files, as well as the
6541 integrity of their crosslinks and indices.
6540 integrity of their crosslinks and indices.
6542
6541
6543 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
6542 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
6544 for more information about recovery from corruption of the
6543 for more information about recovery from corruption of the
6545 repository.
6544 repository.
6546
6545
6547 Returns 0 on success, 1 if errors are encountered.
6546 Returns 0 on success, 1 if errors are encountered.
6548 """
6547 """
6549 return hg.verify(repo)
6548 return hg.verify(repo)
6550
6549
6551 @command('version', [] + formatteropts, norepo=True)
6550 @command('version', [] + formatteropts, norepo=True)
6552 def version_(ui, **opts):
6551 def version_(ui, **opts):
6553 """output version and copyright information"""
6552 """output version and copyright information"""
6554 fm = ui.formatter("version", opts)
6553 fm = ui.formatter("version", opts)
6555 fm.startitem()
6554 fm.startitem()
6556 fm.write("ver", _("Mercurial Distributed SCM (version %s)\n"),
6555 fm.write("ver", _("Mercurial Distributed SCM (version %s)\n"),
6557 util.version())
6556 util.version())
6558 license = _(
6557 license = _(
6559 "(see https://mercurial-scm.org for more information)\n"
6558 "(see https://mercurial-scm.org for more information)\n"
6560 "\nCopyright (C) 2005-2016 Matt Mackall and others\n"
6559 "\nCopyright (C) 2005-2016 Matt Mackall and others\n"
6561 "This is free software; see the source for copying conditions. "
6560 "This is free software; see the source for copying conditions. "
6562 "There is NO\nwarranty; "
6561 "There is NO\nwarranty; "
6563 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
6562 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
6564 )
6563 )
6565 if not ui.quiet:
6564 if not ui.quiet:
6566 fm.plain(license)
6565 fm.plain(license)
6567
6566
6568 if ui.verbose:
6567 if ui.verbose:
6569 fm.plain(_("\nEnabled extensions:\n\n"))
6568 fm.plain(_("\nEnabled extensions:\n\n"))
6570 # format names and versions into columns
6569 # format names and versions into columns
6571 names = []
6570 names = []
6572 vers = []
6571 vers = []
6573 isinternals = []
6572 isinternals = []
6574 for name, module in extensions.extensions():
6573 for name, module in extensions.extensions():
6575 names.append(name)
6574 names.append(name)
6576 vers.append(extensions.moduleversion(module) or None)
6575 vers.append(extensions.moduleversion(module) or None)
6577 isinternals.append(extensions.ismoduleinternal(module))
6576 isinternals.append(extensions.ismoduleinternal(module))
6578 fn = fm.nested("extensions")
6577 fn = fm.nested("extensions")
6579 if names:
6578 if names:
6580 namefmt = " %%-%ds " % max(len(n) for n in names)
6579 namefmt = " %%-%ds " % max(len(n) for n in names)
6581 places = [_("external"), _("internal")]
6580 places = [_("external"), _("internal")]
6582 for n, v, p in zip(names, vers, isinternals):
6581 for n, v, p in zip(names, vers, isinternals):
6583 fn.startitem()
6582 fn.startitem()
6584 fn.condwrite(ui.verbose, "name", namefmt, n)
6583 fn.condwrite(ui.verbose, "name", namefmt, n)
6585 if ui.verbose:
6584 if ui.verbose:
6586 fn.plain("%s " % places[p])
6585 fn.plain("%s " % places[p])
6587 fn.data(bundled=p)
6586 fn.data(bundled=p)
6588 fn.condwrite(ui.verbose and v, "ver", "%s", v)
6587 fn.condwrite(ui.verbose and v, "ver", "%s", v)
6589 if ui.verbose:
6588 if ui.verbose:
6590 fn.plain("\n")
6589 fn.plain("\n")
6591 fn.end()
6590 fn.end()
6592 fm.end()
6591 fm.end()
6593
6592
6594 def loadcmdtable(ui, name, cmdtable):
6593 def loadcmdtable(ui, name, cmdtable):
6595 """Load command functions from specified cmdtable
6594 """Load command functions from specified cmdtable
6596 """
6595 """
6597 overrides = [cmd for cmd in cmdtable if cmd in table]
6596 overrides = [cmd for cmd in cmdtable if cmd in table]
6598 if overrides:
6597 if overrides:
6599 ui.warn(_("extension '%s' overrides commands: %s\n")
6598 ui.warn(_("extension '%s' overrides commands: %s\n")
6600 % (name, " ".join(overrides)))
6599 % (name, " ".join(overrides)))
6601 table.update(cmdtable)
6600 table.update(cmdtable)
@@ -1,889 +1,888 b''
1 # dispatch.py - command dispatching for mercurial
1 # dispatch.py - command dispatching for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import, print_function
8 from __future__ import absolute_import, print_function
9
9
10 import atexit
10 import atexit
11 import difflib
11 import difflib
12 import errno
12 import errno
13 import getopt
13 import getopt
14 import os
14 import os
15 import pdb
15 import pdb
16 import re
16 import re
17 import shlex
18 import signal
17 import signal
19 import sys
18 import sys
20 import time
19 import time
21 import traceback
20 import traceback
22
21
23
22
24 from .i18n import _
23 from .i18n import _
25
24
26 from . import (
25 from . import (
27 cmdutil,
26 cmdutil,
28 color,
27 color,
29 commands,
28 commands,
30 debugcommands,
29 debugcommands,
31 demandimport,
30 demandimport,
32 encoding,
31 encoding,
33 error,
32 error,
34 extensions,
33 extensions,
35 fancyopts,
34 fancyopts,
36 fileset,
35 fileset,
37 hg,
36 hg,
38 hook,
37 hook,
39 profiling,
38 profiling,
40 pycompat,
39 pycompat,
41 revset,
40 revset,
42 scmutil,
41 scmutil,
43 templatefilters,
42 templatefilters,
44 templatekw,
43 templatekw,
45 templater,
44 templater,
46 ui as uimod,
45 ui as uimod,
47 util,
46 util,
48 )
47 )
49
48
50 class request(object):
49 class request(object):
51 def __init__(self, args, ui=None, repo=None, fin=None, fout=None,
50 def __init__(self, args, ui=None, repo=None, fin=None, fout=None,
52 ferr=None):
51 ferr=None):
53 self.args = args
52 self.args = args
54 self.ui = ui
53 self.ui = ui
55 self.repo = repo
54 self.repo = repo
56
55
57 # input/output/error streams
56 # input/output/error streams
58 self.fin = fin
57 self.fin = fin
59 self.fout = fout
58 self.fout = fout
60 self.ferr = ferr
59 self.ferr = ferr
61
60
62 def run():
61 def run():
63 "run the command in sys.argv"
62 "run the command in sys.argv"
64 sys.exit((dispatch(request(pycompat.sysargv[1:])) or 0) & 255)
63 sys.exit((dispatch(request(pycompat.sysargv[1:])) or 0) & 255)
65
64
66 def _getsimilar(symbols, value):
65 def _getsimilar(symbols, value):
67 sim = lambda x: difflib.SequenceMatcher(None, value, x).ratio()
66 sim = lambda x: difflib.SequenceMatcher(None, value, x).ratio()
68 # The cutoff for similarity here is pretty arbitrary. It should
67 # The cutoff for similarity here is pretty arbitrary. It should
69 # probably be investigated and tweaked.
68 # probably be investigated and tweaked.
70 return [s for s in symbols if sim(s) > 0.6]
69 return [s for s in symbols if sim(s) > 0.6]
71
70
72 def _reportsimilar(write, similar):
71 def _reportsimilar(write, similar):
73 if len(similar) == 1:
72 if len(similar) == 1:
74 write(_("(did you mean %s?)\n") % similar[0])
73 write(_("(did you mean %s?)\n") % similar[0])
75 elif similar:
74 elif similar:
76 ss = ", ".join(sorted(similar))
75 ss = ", ".join(sorted(similar))
77 write(_("(did you mean one of %s?)\n") % ss)
76 write(_("(did you mean one of %s?)\n") % ss)
78
77
79 def _formatparse(write, inst):
78 def _formatparse(write, inst):
80 similar = []
79 similar = []
81 if isinstance(inst, error.UnknownIdentifier):
80 if isinstance(inst, error.UnknownIdentifier):
82 # make sure to check fileset first, as revset can invoke fileset
81 # make sure to check fileset first, as revset can invoke fileset
83 similar = _getsimilar(inst.symbols, inst.function)
82 similar = _getsimilar(inst.symbols, inst.function)
84 if len(inst.args) > 1:
83 if len(inst.args) > 1:
85 write(_("hg: parse error at %s: %s\n") %
84 write(_("hg: parse error at %s: %s\n") %
86 (inst.args[1], inst.args[0]))
85 (inst.args[1], inst.args[0]))
87 if (inst.args[0][0] == ' '):
86 if (inst.args[0][0] == ' '):
88 write(_("unexpected leading whitespace\n"))
87 write(_("unexpected leading whitespace\n"))
89 else:
88 else:
90 write(_("hg: parse error: %s\n") % inst.args[0])
89 write(_("hg: parse error: %s\n") % inst.args[0])
91 _reportsimilar(write, similar)
90 _reportsimilar(write, similar)
92 if inst.hint:
91 if inst.hint:
93 write(_("(%s)\n") % inst.hint)
92 write(_("(%s)\n") % inst.hint)
94
93
95 def dispatch(req):
94 def dispatch(req):
96 "run the command specified in req.args"
95 "run the command specified in req.args"
97 if req.ferr:
96 if req.ferr:
98 ferr = req.ferr
97 ferr = req.ferr
99 elif req.ui:
98 elif req.ui:
100 ferr = req.ui.ferr
99 ferr = req.ui.ferr
101 else:
100 else:
102 ferr = util.stderr
101 ferr = util.stderr
103
102
104 try:
103 try:
105 if not req.ui:
104 if not req.ui:
106 req.ui = uimod.ui.load()
105 req.ui = uimod.ui.load()
107 if '--traceback' in req.args:
106 if '--traceback' in req.args:
108 req.ui.setconfig('ui', 'traceback', 'on', '--traceback')
107 req.ui.setconfig('ui', 'traceback', 'on', '--traceback')
109
108
110 # set ui streams from the request
109 # set ui streams from the request
111 if req.fin:
110 if req.fin:
112 req.ui.fin = req.fin
111 req.ui.fin = req.fin
113 if req.fout:
112 if req.fout:
114 req.ui.fout = req.fout
113 req.ui.fout = req.fout
115 if req.ferr:
114 if req.ferr:
116 req.ui.ferr = req.ferr
115 req.ui.ferr = req.ferr
117 except error.Abort as inst:
116 except error.Abort as inst:
118 ferr.write(_("abort: %s\n") % inst)
117 ferr.write(_("abort: %s\n") % inst)
119 if inst.hint:
118 if inst.hint:
120 ferr.write(_("(%s)\n") % inst.hint)
119 ferr.write(_("(%s)\n") % inst.hint)
121 return -1
120 return -1
122 except error.ParseError as inst:
121 except error.ParseError as inst:
123 _formatparse(ferr.write, inst)
122 _formatparse(ferr.write, inst)
124 return -1
123 return -1
125
124
126 msg = ' '.join(' ' in a and repr(a) or a for a in req.args)
125 msg = ' '.join(' ' in a and repr(a) or a for a in req.args)
127 starttime = time.time()
126 starttime = time.time()
128 ret = None
127 ret = None
129 try:
128 try:
130 ret = _runcatch(req)
129 ret = _runcatch(req)
131 except KeyboardInterrupt:
130 except KeyboardInterrupt:
132 try:
131 try:
133 req.ui.warn(_("interrupted!\n"))
132 req.ui.warn(_("interrupted!\n"))
134 except IOError as inst:
133 except IOError as inst:
135 if inst.errno != errno.EPIPE:
134 if inst.errno != errno.EPIPE:
136 raise
135 raise
137 ret = -1
136 ret = -1
138 finally:
137 finally:
139 duration = time.time() - starttime
138 duration = time.time() - starttime
140 req.ui.flush()
139 req.ui.flush()
141 req.ui.log("commandfinish", "%s exited %s after %0.2f seconds\n",
140 req.ui.log("commandfinish", "%s exited %s after %0.2f seconds\n",
142 msg, ret or 0, duration)
141 msg, ret or 0, duration)
143 return ret
142 return ret
144
143
145 def _runcatch(req):
144 def _runcatch(req):
146 def catchterm(*args):
145 def catchterm(*args):
147 raise error.SignalInterrupt
146 raise error.SignalInterrupt
148
147
149 ui = req.ui
148 ui = req.ui
150 try:
149 try:
151 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
150 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
152 num = getattr(signal, name, None)
151 num = getattr(signal, name, None)
153 if num:
152 if num:
154 signal.signal(num, catchterm)
153 signal.signal(num, catchterm)
155 except ValueError:
154 except ValueError:
156 pass # happens if called in a thread
155 pass # happens if called in a thread
157
156
158 def _runcatchfunc():
157 def _runcatchfunc():
159 try:
158 try:
160 debugger = 'pdb'
159 debugger = 'pdb'
161 debugtrace = {
160 debugtrace = {
162 'pdb' : pdb.set_trace
161 'pdb' : pdb.set_trace
163 }
162 }
164 debugmortem = {
163 debugmortem = {
165 'pdb' : pdb.post_mortem
164 'pdb' : pdb.post_mortem
166 }
165 }
167
166
168 # read --config before doing anything else
167 # read --config before doing anything else
169 # (e.g. to change trust settings for reading .hg/hgrc)
168 # (e.g. to change trust settings for reading .hg/hgrc)
170 cfgs = _parseconfig(req.ui, _earlygetopt(['--config'], req.args))
169 cfgs = _parseconfig(req.ui, _earlygetopt(['--config'], req.args))
171
170
172 if req.repo:
171 if req.repo:
173 # copy configs that were passed on the cmdline (--config) to
172 # copy configs that were passed on the cmdline (--config) to
174 # the repo ui
173 # the repo ui
175 for sec, name, val in cfgs:
174 for sec, name, val in cfgs:
176 req.repo.ui.setconfig(sec, name, val, source='--config')
175 req.repo.ui.setconfig(sec, name, val, source='--config')
177
176
178 # developer config: ui.debugger
177 # developer config: ui.debugger
179 debugger = ui.config("ui", "debugger")
178 debugger = ui.config("ui", "debugger")
180 debugmod = pdb
179 debugmod = pdb
181 if not debugger or ui.plain():
180 if not debugger or ui.plain():
182 # if we are in HGPLAIN mode, then disable custom debugging
181 # if we are in HGPLAIN mode, then disable custom debugging
183 debugger = 'pdb'
182 debugger = 'pdb'
184 elif '--debugger' in req.args:
183 elif '--debugger' in req.args:
185 # This import can be slow for fancy debuggers, so only
184 # This import can be slow for fancy debuggers, so only
186 # do it when absolutely necessary, i.e. when actual
185 # do it when absolutely necessary, i.e. when actual
187 # debugging has been requested
186 # debugging has been requested
188 with demandimport.deactivated():
187 with demandimport.deactivated():
189 try:
188 try:
190 debugmod = __import__(debugger)
189 debugmod = __import__(debugger)
191 except ImportError:
190 except ImportError:
192 pass # Leave debugmod = pdb
191 pass # Leave debugmod = pdb
193
192
194 debugtrace[debugger] = debugmod.set_trace
193 debugtrace[debugger] = debugmod.set_trace
195 debugmortem[debugger] = debugmod.post_mortem
194 debugmortem[debugger] = debugmod.post_mortem
196
195
197 # enter the debugger before command execution
196 # enter the debugger before command execution
198 if '--debugger' in req.args:
197 if '--debugger' in req.args:
199 ui.warn(_("entering debugger - "
198 ui.warn(_("entering debugger - "
200 "type c to continue starting hg or h for help\n"))
199 "type c to continue starting hg or h for help\n"))
201
200
202 if (debugger != 'pdb' and
201 if (debugger != 'pdb' and
203 debugtrace[debugger] == debugtrace['pdb']):
202 debugtrace[debugger] == debugtrace['pdb']):
204 ui.warn(_("%s debugger specified "
203 ui.warn(_("%s debugger specified "
205 "but its module was not found\n") % debugger)
204 "but its module was not found\n") % debugger)
206 with demandimport.deactivated():
205 with demandimport.deactivated():
207 debugtrace[debugger]()
206 debugtrace[debugger]()
208 try:
207 try:
209 return _dispatch(req)
208 return _dispatch(req)
210 finally:
209 finally:
211 ui.flush()
210 ui.flush()
212 except: # re-raises
211 except: # re-raises
213 # enter the debugger when we hit an exception
212 # enter the debugger when we hit an exception
214 if '--debugger' in req.args:
213 if '--debugger' in req.args:
215 traceback.print_exc()
214 traceback.print_exc()
216 debugmortem[debugger](sys.exc_info()[2])
215 debugmortem[debugger](sys.exc_info()[2])
217 ui.traceback()
216 ui.traceback()
218 raise
217 raise
219
218
220 return callcatch(ui, _runcatchfunc)
219 return callcatch(ui, _runcatchfunc)
221
220
222 def callcatch(ui, func):
221 def callcatch(ui, func):
223 """like scmutil.callcatch but handles more high-level exceptions about
222 """like scmutil.callcatch but handles more high-level exceptions about
224 config parsing and commands. besides, use handlecommandexception to handle
223 config parsing and commands. besides, use handlecommandexception to handle
225 uncaught exceptions.
224 uncaught exceptions.
226 """
225 """
227 try:
226 try:
228 return scmutil.callcatch(ui, func)
227 return scmutil.callcatch(ui, func)
229 except error.AmbiguousCommand as inst:
228 except error.AmbiguousCommand as inst:
230 ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
229 ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
231 (inst.args[0], " ".join(inst.args[1])))
230 (inst.args[0], " ".join(inst.args[1])))
232 except error.CommandError as inst:
231 except error.CommandError as inst:
233 if inst.args[0]:
232 if inst.args[0]:
234 ui.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
233 ui.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
235 commands.help_(ui, inst.args[0], full=False, command=True)
234 commands.help_(ui, inst.args[0], full=False, command=True)
236 else:
235 else:
237 ui.warn(_("hg: %s\n") % inst.args[1])
236 ui.warn(_("hg: %s\n") % inst.args[1])
238 commands.help_(ui, 'shortlist')
237 commands.help_(ui, 'shortlist')
239 except error.ParseError as inst:
238 except error.ParseError as inst:
240 _formatparse(ui.warn, inst)
239 _formatparse(ui.warn, inst)
241 return -1
240 return -1
242 except error.UnknownCommand as inst:
241 except error.UnknownCommand as inst:
243 ui.warn(_("hg: unknown command '%s'\n") % inst.args[0])
242 ui.warn(_("hg: unknown command '%s'\n") % inst.args[0])
244 try:
243 try:
245 # check if the command is in a disabled extension
244 # check if the command is in a disabled extension
246 # (but don't check for extensions themselves)
245 # (but don't check for extensions themselves)
247 commands.help_(ui, inst.args[0], unknowncmd=True)
246 commands.help_(ui, inst.args[0], unknowncmd=True)
248 except (error.UnknownCommand, error.Abort):
247 except (error.UnknownCommand, error.Abort):
249 suggested = False
248 suggested = False
250 if len(inst.args) == 2:
249 if len(inst.args) == 2:
251 sim = _getsimilar(inst.args[1], inst.args[0])
250 sim = _getsimilar(inst.args[1], inst.args[0])
252 if sim:
251 if sim:
253 _reportsimilar(ui.warn, sim)
252 _reportsimilar(ui.warn, sim)
254 suggested = True
253 suggested = True
255 if not suggested:
254 if not suggested:
256 commands.help_(ui, 'shortlist')
255 commands.help_(ui, 'shortlist')
257 except IOError:
256 except IOError:
258 raise
257 raise
259 except KeyboardInterrupt:
258 except KeyboardInterrupt:
260 raise
259 raise
261 except: # probably re-raises
260 except: # probably re-raises
262 if not handlecommandexception(ui):
261 if not handlecommandexception(ui):
263 raise
262 raise
264
263
265 return -1
264 return -1
266
265
267 def aliasargs(fn, givenargs):
266 def aliasargs(fn, givenargs):
268 args = getattr(fn, 'args', [])
267 args = getattr(fn, 'args', [])
269 if args:
268 if args:
270 cmd = ' '.join(map(util.shellquote, args))
269 cmd = ' '.join(map(util.shellquote, args))
271
270
272 nums = []
271 nums = []
273 def replacer(m):
272 def replacer(m):
274 num = int(m.group(1)) - 1
273 num = int(m.group(1)) - 1
275 nums.append(num)
274 nums.append(num)
276 if num < len(givenargs):
275 if num < len(givenargs):
277 return givenargs[num]
276 return givenargs[num]
278 raise error.Abort(_('too few arguments for command alias'))
277 raise error.Abort(_('too few arguments for command alias'))
279 cmd = re.sub(r'\$(\d+|\$)', replacer, cmd)
278 cmd = re.sub(r'\$(\d+|\$)', replacer, cmd)
280 givenargs = [x for i, x in enumerate(givenargs)
279 givenargs = [x for i, x in enumerate(givenargs)
281 if i not in nums]
280 if i not in nums]
282 args = shlex.split(cmd)
281 args = pycompat.shlexsplit(cmd)
283 return args + givenargs
282 return args + givenargs
284
283
285 def aliasinterpolate(name, args, cmd):
284 def aliasinterpolate(name, args, cmd):
286 '''interpolate args into cmd for shell aliases
285 '''interpolate args into cmd for shell aliases
287
286
288 This also handles $0, $@ and "$@".
287 This also handles $0, $@ and "$@".
289 '''
288 '''
290 # util.interpolate can't deal with "$@" (with quotes) because it's only
289 # util.interpolate can't deal with "$@" (with quotes) because it's only
291 # built to match prefix + patterns.
290 # built to match prefix + patterns.
292 replacemap = dict(('$%d' % (i + 1), arg) for i, arg in enumerate(args))
291 replacemap = dict(('$%d' % (i + 1), arg) for i, arg in enumerate(args))
293 replacemap['$0'] = name
292 replacemap['$0'] = name
294 replacemap['$$'] = '$'
293 replacemap['$$'] = '$'
295 replacemap['$@'] = ' '.join(args)
294 replacemap['$@'] = ' '.join(args)
296 # Typical Unix shells interpolate "$@" (with quotes) as all the positional
295 # Typical Unix shells interpolate "$@" (with quotes) as all the positional
297 # parameters, separated out into words. Emulate the same behavior here by
296 # parameters, separated out into words. Emulate the same behavior here by
298 # quoting the arguments individually. POSIX shells will then typically
297 # quoting the arguments individually. POSIX shells will then typically
299 # tokenize each argument into exactly one word.
298 # tokenize each argument into exactly one word.
300 replacemap['"$@"'] = ' '.join(util.shellquote(arg) for arg in args)
299 replacemap['"$@"'] = ' '.join(util.shellquote(arg) for arg in args)
301 # escape '\$' for regex
300 # escape '\$' for regex
302 regex = '|'.join(replacemap.keys()).replace('$', r'\$')
301 regex = '|'.join(replacemap.keys()).replace('$', r'\$')
303 r = re.compile(regex)
302 r = re.compile(regex)
304 return r.sub(lambda x: replacemap[x.group()], cmd)
303 return r.sub(lambda x: replacemap[x.group()], cmd)
305
304
306 class cmdalias(object):
305 class cmdalias(object):
307 def __init__(self, name, definition, cmdtable, source):
306 def __init__(self, name, definition, cmdtable, source):
308 self.name = self.cmd = name
307 self.name = self.cmd = name
309 self.cmdname = ''
308 self.cmdname = ''
310 self.definition = definition
309 self.definition = definition
311 self.fn = None
310 self.fn = None
312 self.givenargs = []
311 self.givenargs = []
313 self.opts = []
312 self.opts = []
314 self.help = ''
313 self.help = ''
315 self.badalias = None
314 self.badalias = None
316 self.unknowncmd = False
315 self.unknowncmd = False
317 self.source = source
316 self.source = source
318
317
319 try:
318 try:
320 aliases, entry = cmdutil.findcmd(self.name, cmdtable)
319 aliases, entry = cmdutil.findcmd(self.name, cmdtable)
321 for alias, e in cmdtable.iteritems():
320 for alias, e in cmdtable.iteritems():
322 if e is entry:
321 if e is entry:
323 self.cmd = alias
322 self.cmd = alias
324 break
323 break
325 self.shadows = True
324 self.shadows = True
326 except error.UnknownCommand:
325 except error.UnknownCommand:
327 self.shadows = False
326 self.shadows = False
328
327
329 if not self.definition:
328 if not self.definition:
330 self.badalias = _("no definition for alias '%s'") % self.name
329 self.badalias = _("no definition for alias '%s'") % self.name
331 return
330 return
332
331
333 if self.definition.startswith('!'):
332 if self.definition.startswith('!'):
334 self.shell = True
333 self.shell = True
335 def fn(ui, *args):
334 def fn(ui, *args):
336 env = {'HG_ARGS': ' '.join((self.name,) + args)}
335 env = {'HG_ARGS': ' '.join((self.name,) + args)}
337 def _checkvar(m):
336 def _checkvar(m):
338 if m.groups()[0] == '$':
337 if m.groups()[0] == '$':
339 return m.group()
338 return m.group()
340 elif int(m.groups()[0]) <= len(args):
339 elif int(m.groups()[0]) <= len(args):
341 return m.group()
340 return m.group()
342 else:
341 else:
343 ui.debug("No argument found for substitution "
342 ui.debug("No argument found for substitution "
344 "of %i variable in alias '%s' definition."
343 "of %i variable in alias '%s' definition."
345 % (int(m.groups()[0]), self.name))
344 % (int(m.groups()[0]), self.name))
346 return ''
345 return ''
347 cmd = re.sub(r'\$(\d+|\$)', _checkvar, self.definition[1:])
346 cmd = re.sub(r'\$(\d+|\$)', _checkvar, self.definition[1:])
348 cmd = aliasinterpolate(self.name, args, cmd)
347 cmd = aliasinterpolate(self.name, args, cmd)
349 return ui.system(cmd, environ=env)
348 return ui.system(cmd, environ=env)
350 self.fn = fn
349 self.fn = fn
351 return
350 return
352
351
353 try:
352 try:
354 args = shlex.split(self.definition)
353 args = pycompat.shlexsplit(self.definition)
355 except ValueError as inst:
354 except ValueError as inst:
356 self.badalias = (_("error in definition for alias '%s': %s")
355 self.badalias = (_("error in definition for alias '%s': %s")
357 % (self.name, inst))
356 % (self.name, inst))
358 return
357 return
359 self.cmdname = cmd = args.pop(0)
358 self.cmdname = cmd = args.pop(0)
360 self.givenargs = args
359 self.givenargs = args
361
360
362 for invalidarg in ("--cwd", "-R", "--repository", "--repo", "--config"):
361 for invalidarg in ("--cwd", "-R", "--repository", "--repo", "--config"):
363 if _earlygetopt([invalidarg], args):
362 if _earlygetopt([invalidarg], args):
364 self.badalias = (_("error in definition for alias '%s': %s may "
363 self.badalias = (_("error in definition for alias '%s': %s may "
365 "only be given on the command line")
364 "only be given on the command line")
366 % (self.name, invalidarg))
365 % (self.name, invalidarg))
367 return
366 return
368
367
369 try:
368 try:
370 tableentry = cmdutil.findcmd(cmd, cmdtable, False)[1]
369 tableentry = cmdutil.findcmd(cmd, cmdtable, False)[1]
371 if len(tableentry) > 2:
370 if len(tableentry) > 2:
372 self.fn, self.opts, self.help = tableentry
371 self.fn, self.opts, self.help = tableentry
373 else:
372 else:
374 self.fn, self.opts = tableentry
373 self.fn, self.opts = tableentry
375
374
376 if self.help.startswith("hg " + cmd):
375 if self.help.startswith("hg " + cmd):
377 # drop prefix in old-style help lines so hg shows the alias
376 # drop prefix in old-style help lines so hg shows the alias
378 self.help = self.help[4 + len(cmd):]
377 self.help = self.help[4 + len(cmd):]
379 self.__doc__ = self.fn.__doc__
378 self.__doc__ = self.fn.__doc__
380
379
381 except error.UnknownCommand:
380 except error.UnknownCommand:
382 self.badalias = (_("alias '%s' resolves to unknown command '%s'")
381 self.badalias = (_("alias '%s' resolves to unknown command '%s'")
383 % (self.name, cmd))
382 % (self.name, cmd))
384 self.unknowncmd = True
383 self.unknowncmd = True
385 except error.AmbiguousCommand:
384 except error.AmbiguousCommand:
386 self.badalias = (_("alias '%s' resolves to ambiguous command '%s'")
385 self.badalias = (_("alias '%s' resolves to ambiguous command '%s'")
387 % (self.name, cmd))
386 % (self.name, cmd))
388
387
389 @property
388 @property
390 def args(self):
389 def args(self):
391 args = map(util.expandpath, self.givenargs)
390 args = map(util.expandpath, self.givenargs)
392 return aliasargs(self.fn, args)
391 return aliasargs(self.fn, args)
393
392
394 def __getattr__(self, name):
393 def __getattr__(self, name):
395 adefaults = {'norepo': True, 'optionalrepo': False, 'inferrepo': False}
394 adefaults = {'norepo': True, 'optionalrepo': False, 'inferrepo': False}
396 if name not in adefaults:
395 if name not in adefaults:
397 raise AttributeError(name)
396 raise AttributeError(name)
398 if self.badalias or util.safehasattr(self, 'shell'):
397 if self.badalias or util.safehasattr(self, 'shell'):
399 return adefaults[name]
398 return adefaults[name]
400 return getattr(self.fn, name)
399 return getattr(self.fn, name)
401
400
402 def __call__(self, ui, *args, **opts):
401 def __call__(self, ui, *args, **opts):
403 if self.badalias:
402 if self.badalias:
404 hint = None
403 hint = None
405 if self.unknowncmd:
404 if self.unknowncmd:
406 try:
405 try:
407 # check if the command is in a disabled extension
406 # check if the command is in a disabled extension
408 cmd, ext = extensions.disabledcmd(ui, self.cmdname)[:2]
407 cmd, ext = extensions.disabledcmd(ui, self.cmdname)[:2]
409 hint = _("'%s' is provided by '%s' extension") % (cmd, ext)
408 hint = _("'%s' is provided by '%s' extension") % (cmd, ext)
410 except error.UnknownCommand:
409 except error.UnknownCommand:
411 pass
410 pass
412 raise error.Abort(self.badalias, hint=hint)
411 raise error.Abort(self.badalias, hint=hint)
413 if self.shadows:
412 if self.shadows:
414 ui.debug("alias '%s' shadows command '%s'\n" %
413 ui.debug("alias '%s' shadows command '%s'\n" %
415 (self.name, self.cmdname))
414 (self.name, self.cmdname))
416
415
417 ui.log('commandalias', "alias '%s' expands to '%s'\n",
416 ui.log('commandalias', "alias '%s' expands to '%s'\n",
418 self.name, self.definition)
417 self.name, self.definition)
419 if util.safehasattr(self, 'shell'):
418 if util.safehasattr(self, 'shell'):
420 return self.fn(ui, *args, **opts)
419 return self.fn(ui, *args, **opts)
421 else:
420 else:
422 try:
421 try:
423 return util.checksignature(self.fn)(ui, *args, **opts)
422 return util.checksignature(self.fn)(ui, *args, **opts)
424 except error.SignatureError:
423 except error.SignatureError:
425 args = ' '.join([self.cmdname] + self.args)
424 args = ' '.join([self.cmdname] + self.args)
426 ui.debug("alias '%s' expands to '%s'\n" % (self.name, args))
425 ui.debug("alias '%s' expands to '%s'\n" % (self.name, args))
427 raise
426 raise
428
427
429 def addaliases(ui, cmdtable):
428 def addaliases(ui, cmdtable):
430 # aliases are processed after extensions have been loaded, so they
429 # aliases are processed after extensions have been loaded, so they
431 # may use extension commands. Aliases can also use other alias definitions,
430 # may use extension commands. Aliases can also use other alias definitions,
432 # but only if they have been defined prior to the current definition.
431 # but only if they have been defined prior to the current definition.
433 for alias, definition in ui.configitems('alias'):
432 for alias, definition in ui.configitems('alias'):
434 source = ui.configsource('alias', alias)
433 source = ui.configsource('alias', alias)
435 aliasdef = cmdalias(alias, definition, cmdtable, source)
434 aliasdef = cmdalias(alias, definition, cmdtable, source)
436
435
437 try:
436 try:
438 olddef = cmdtable[aliasdef.cmd][0]
437 olddef = cmdtable[aliasdef.cmd][0]
439 if olddef.definition == aliasdef.definition:
438 if olddef.definition == aliasdef.definition:
440 continue
439 continue
441 except (KeyError, AttributeError):
440 except (KeyError, AttributeError):
442 # definition might not exist or it might not be a cmdalias
441 # definition might not exist or it might not be a cmdalias
443 pass
442 pass
444
443
445 cmdtable[aliasdef.name] = (aliasdef, aliasdef.opts, aliasdef.help)
444 cmdtable[aliasdef.name] = (aliasdef, aliasdef.opts, aliasdef.help)
446
445
447 def _parse(ui, args):
446 def _parse(ui, args):
448 options = {}
447 options = {}
449 cmdoptions = {}
448 cmdoptions = {}
450
449
451 try:
450 try:
452 args = fancyopts.fancyopts(args, commands.globalopts, options)
451 args = fancyopts.fancyopts(args, commands.globalopts, options)
453 except getopt.GetoptError as inst:
452 except getopt.GetoptError as inst:
454 raise error.CommandError(None, inst)
453 raise error.CommandError(None, inst)
455
454
456 if args:
455 if args:
457 cmd, args = args[0], args[1:]
456 cmd, args = args[0], args[1:]
458 aliases, entry = cmdutil.findcmd(cmd, commands.table,
457 aliases, entry = cmdutil.findcmd(cmd, commands.table,
459 ui.configbool("ui", "strict"))
458 ui.configbool("ui", "strict"))
460 cmd = aliases[0]
459 cmd = aliases[0]
461 args = aliasargs(entry[0], args)
460 args = aliasargs(entry[0], args)
462 defaults = ui.config("defaults", cmd)
461 defaults = ui.config("defaults", cmd)
463 if defaults:
462 if defaults:
464 args = map(util.expandpath, shlex.split(defaults)) + args
463 args = map(util.expandpath, pycompat.shlexsplit(defaults)) + args
465 c = list(entry[1])
464 c = list(entry[1])
466 else:
465 else:
467 cmd = None
466 cmd = None
468 c = []
467 c = []
469
468
470 # combine global options into local
469 # combine global options into local
471 for o in commands.globalopts:
470 for o in commands.globalopts:
472 c.append((o[0], o[1], options[o[1]], o[3]))
471 c.append((o[0], o[1], options[o[1]], o[3]))
473
472
474 try:
473 try:
475 args = fancyopts.fancyopts(args, c, cmdoptions, gnu=True)
474 args = fancyopts.fancyopts(args, c, cmdoptions, gnu=True)
476 except getopt.GetoptError as inst:
475 except getopt.GetoptError as inst:
477 raise error.CommandError(cmd, inst)
476 raise error.CommandError(cmd, inst)
478
477
479 # separate global options back out
478 # separate global options back out
480 for o in commands.globalopts:
479 for o in commands.globalopts:
481 n = o[1]
480 n = o[1]
482 options[n] = cmdoptions[n]
481 options[n] = cmdoptions[n]
483 del cmdoptions[n]
482 del cmdoptions[n]
484
483
485 return (cmd, cmd and entry[0] or None, args, options, cmdoptions)
484 return (cmd, cmd and entry[0] or None, args, options, cmdoptions)
486
485
487 def _parseconfig(ui, config):
486 def _parseconfig(ui, config):
488 """parse the --config options from the command line"""
487 """parse the --config options from the command line"""
489 configs = []
488 configs = []
490
489
491 for cfg in config:
490 for cfg in config:
492 try:
491 try:
493 name, value = [cfgelem.strip()
492 name, value = [cfgelem.strip()
494 for cfgelem in cfg.split('=', 1)]
493 for cfgelem in cfg.split('=', 1)]
495 section, name = name.split('.', 1)
494 section, name = name.split('.', 1)
496 if not section or not name:
495 if not section or not name:
497 raise IndexError
496 raise IndexError
498 ui.setconfig(section, name, value, '--config')
497 ui.setconfig(section, name, value, '--config')
499 configs.append((section, name, value))
498 configs.append((section, name, value))
500 except (IndexError, ValueError):
499 except (IndexError, ValueError):
501 raise error.Abort(_('malformed --config option: %r '
500 raise error.Abort(_('malformed --config option: %r '
502 '(use --config section.name=value)') % cfg)
501 '(use --config section.name=value)') % cfg)
503
502
504 return configs
503 return configs
505
504
506 def _earlygetopt(aliases, args):
505 def _earlygetopt(aliases, args):
507 """Return list of values for an option (or aliases).
506 """Return list of values for an option (or aliases).
508
507
509 The values are listed in the order they appear in args.
508 The values are listed in the order they appear in args.
510 The options and values are removed from args.
509 The options and values are removed from args.
511
510
512 >>> args = ['x', '--cwd', 'foo', 'y']
511 >>> args = ['x', '--cwd', 'foo', 'y']
513 >>> _earlygetopt(['--cwd'], args), args
512 >>> _earlygetopt(['--cwd'], args), args
514 (['foo'], ['x', 'y'])
513 (['foo'], ['x', 'y'])
515
514
516 >>> args = ['x', '--cwd=bar', 'y']
515 >>> args = ['x', '--cwd=bar', 'y']
517 >>> _earlygetopt(['--cwd'], args), args
516 >>> _earlygetopt(['--cwd'], args), args
518 (['bar'], ['x', 'y'])
517 (['bar'], ['x', 'y'])
519
518
520 >>> args = ['x', '-R', 'foo', 'y']
519 >>> args = ['x', '-R', 'foo', 'y']
521 >>> _earlygetopt(['-R'], args), args
520 >>> _earlygetopt(['-R'], args), args
522 (['foo'], ['x', 'y'])
521 (['foo'], ['x', 'y'])
523
522
524 >>> args = ['x', '-Rbar', 'y']
523 >>> args = ['x', '-Rbar', 'y']
525 >>> _earlygetopt(['-R'], args), args
524 >>> _earlygetopt(['-R'], args), args
526 (['bar'], ['x', 'y'])
525 (['bar'], ['x', 'y'])
527 """
526 """
528 try:
527 try:
529 argcount = args.index("--")
528 argcount = args.index("--")
530 except ValueError:
529 except ValueError:
531 argcount = len(args)
530 argcount = len(args)
532 shortopts = [opt for opt in aliases if len(opt) == 2]
531 shortopts = [opt for opt in aliases if len(opt) == 2]
533 values = []
532 values = []
534 pos = 0
533 pos = 0
535 while pos < argcount:
534 while pos < argcount:
536 fullarg = arg = args[pos]
535 fullarg = arg = args[pos]
537 equals = arg.find('=')
536 equals = arg.find('=')
538 if equals > -1:
537 if equals > -1:
539 arg = arg[:equals]
538 arg = arg[:equals]
540 if arg in aliases:
539 if arg in aliases:
541 del args[pos]
540 del args[pos]
542 if equals > -1:
541 if equals > -1:
543 values.append(fullarg[equals + 1:])
542 values.append(fullarg[equals + 1:])
544 argcount -= 1
543 argcount -= 1
545 else:
544 else:
546 if pos + 1 >= argcount:
545 if pos + 1 >= argcount:
547 # ignore and let getopt report an error if there is no value
546 # ignore and let getopt report an error if there is no value
548 break
547 break
549 values.append(args.pop(pos))
548 values.append(args.pop(pos))
550 argcount -= 2
549 argcount -= 2
551 elif arg[:2] in shortopts:
550 elif arg[:2] in shortopts:
552 # short option can have no following space, e.g. hg log -Rfoo
551 # short option can have no following space, e.g. hg log -Rfoo
553 values.append(args.pop(pos)[2:])
552 values.append(args.pop(pos)[2:])
554 argcount -= 1
553 argcount -= 1
555 else:
554 else:
556 pos += 1
555 pos += 1
557 return values
556 return values
558
557
559 def runcommand(lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions):
558 def runcommand(lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions):
560 # run pre-hook, and abort if it fails
559 # run pre-hook, and abort if it fails
561 hook.hook(lui, repo, "pre-%s" % cmd, True, args=" ".join(fullargs),
560 hook.hook(lui, repo, "pre-%s" % cmd, True, args=" ".join(fullargs),
562 pats=cmdpats, opts=cmdoptions)
561 pats=cmdpats, opts=cmdoptions)
563 try:
562 try:
564 ret = _runcommand(ui, options, cmd, d)
563 ret = _runcommand(ui, options, cmd, d)
565 # run post-hook, passing command result
564 # run post-hook, passing command result
566 hook.hook(lui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
565 hook.hook(lui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
567 result=ret, pats=cmdpats, opts=cmdoptions)
566 result=ret, pats=cmdpats, opts=cmdoptions)
568 except Exception:
567 except Exception:
569 # run failure hook and re-raise
568 # run failure hook and re-raise
570 hook.hook(lui, repo, "fail-%s" % cmd, False, args=" ".join(fullargs),
569 hook.hook(lui, repo, "fail-%s" % cmd, False, args=" ".join(fullargs),
571 pats=cmdpats, opts=cmdoptions)
570 pats=cmdpats, opts=cmdoptions)
572 raise
571 raise
573 return ret
572 return ret
574
573
575 def _getlocal(ui, rpath, wd=None):
574 def _getlocal(ui, rpath, wd=None):
576 """Return (path, local ui object) for the given target path.
575 """Return (path, local ui object) for the given target path.
577
576
578 Takes paths in [cwd]/.hg/hgrc into account."
577 Takes paths in [cwd]/.hg/hgrc into account."
579 """
578 """
580 if wd is None:
579 if wd is None:
581 try:
580 try:
582 wd = pycompat.getcwd()
581 wd = pycompat.getcwd()
583 except OSError as e:
582 except OSError as e:
584 raise error.Abort(_("error getting current working directory: %s") %
583 raise error.Abort(_("error getting current working directory: %s") %
585 e.strerror)
584 e.strerror)
586 path = cmdutil.findrepo(wd) or ""
585 path = cmdutil.findrepo(wd) or ""
587 if not path:
586 if not path:
588 lui = ui
587 lui = ui
589 else:
588 else:
590 lui = ui.copy()
589 lui = ui.copy()
591 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
590 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
592
591
593 if rpath and rpath[-1]:
592 if rpath and rpath[-1]:
594 path = lui.expandpath(rpath[-1])
593 path = lui.expandpath(rpath[-1])
595 lui = ui.copy()
594 lui = ui.copy()
596 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
595 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
597
596
598 return path, lui
597 return path, lui
599
598
600 def _checkshellalias(lui, ui, args):
599 def _checkshellalias(lui, ui, args):
601 """Return the function to run the shell alias, if it is required"""
600 """Return the function to run the shell alias, if it is required"""
602 options = {}
601 options = {}
603
602
604 try:
603 try:
605 args = fancyopts.fancyopts(args, commands.globalopts, options)
604 args = fancyopts.fancyopts(args, commands.globalopts, options)
606 except getopt.GetoptError:
605 except getopt.GetoptError:
607 return
606 return
608
607
609 if not args:
608 if not args:
610 return
609 return
611
610
612 cmdtable = commands.table
611 cmdtable = commands.table
613
612
614 cmd = args[0]
613 cmd = args[0]
615 try:
614 try:
616 strict = ui.configbool("ui", "strict")
615 strict = ui.configbool("ui", "strict")
617 aliases, entry = cmdutil.findcmd(cmd, cmdtable, strict)
616 aliases, entry = cmdutil.findcmd(cmd, cmdtable, strict)
618 except (error.AmbiguousCommand, error.UnknownCommand):
617 except (error.AmbiguousCommand, error.UnknownCommand):
619 return
618 return
620
619
621 cmd = aliases[0]
620 cmd = aliases[0]
622 fn = entry[0]
621 fn = entry[0]
623
622
624 if cmd and util.safehasattr(fn, 'shell'):
623 if cmd and util.safehasattr(fn, 'shell'):
625 d = lambda: fn(ui, *args[1:])
624 d = lambda: fn(ui, *args[1:])
626 return lambda: runcommand(lui, None, cmd, args[:1], ui, options, d,
625 return lambda: runcommand(lui, None, cmd, args[:1], ui, options, d,
627 [], {})
626 [], {})
628
627
629 _loaded = set()
628 _loaded = set()
630
629
631 # list of (objname, loadermod, loadername) tuple:
630 # list of (objname, loadermod, loadername) tuple:
632 # - objname is the name of an object in extension module, from which
631 # - objname is the name of an object in extension module, from which
633 # extra information is loaded
632 # extra information is loaded
634 # - loadermod is the module where loader is placed
633 # - loadermod is the module where loader is placed
635 # - loadername is the name of the function, which takes (ui, extensionname,
634 # - loadername is the name of the function, which takes (ui, extensionname,
636 # extraobj) arguments
635 # extraobj) arguments
637 extraloaders = [
636 extraloaders = [
638 ('cmdtable', commands, 'loadcmdtable'),
637 ('cmdtable', commands, 'loadcmdtable'),
639 ('colortable', color, 'loadcolortable'),
638 ('colortable', color, 'loadcolortable'),
640 ('filesetpredicate', fileset, 'loadpredicate'),
639 ('filesetpredicate', fileset, 'loadpredicate'),
641 ('revsetpredicate', revset, 'loadpredicate'),
640 ('revsetpredicate', revset, 'loadpredicate'),
642 ('templatefilter', templatefilters, 'loadfilter'),
641 ('templatefilter', templatefilters, 'loadfilter'),
643 ('templatefunc', templater, 'loadfunction'),
642 ('templatefunc', templater, 'loadfunction'),
644 ('templatekeyword', templatekw, 'loadkeyword'),
643 ('templatekeyword', templatekw, 'loadkeyword'),
645 ]
644 ]
646
645
647 def _dispatch(req):
646 def _dispatch(req):
648 args = req.args
647 args = req.args
649 ui = req.ui
648 ui = req.ui
650
649
651 # check for cwd
650 # check for cwd
652 cwd = _earlygetopt(['--cwd'], args)
651 cwd = _earlygetopt(['--cwd'], args)
653 if cwd:
652 if cwd:
654 os.chdir(cwd[-1])
653 os.chdir(cwd[-1])
655
654
656 rpath = _earlygetopt(["-R", "--repository", "--repo"], args)
655 rpath = _earlygetopt(["-R", "--repository", "--repo"], args)
657 path, lui = _getlocal(ui, rpath)
656 path, lui = _getlocal(ui, rpath)
658
657
659 # Configure extensions in phases: uisetup, extsetup, cmdtable, and
658 # Configure extensions in phases: uisetup, extsetup, cmdtable, and
660 # reposetup. Programs like TortoiseHg will call _dispatch several
659 # reposetup. Programs like TortoiseHg will call _dispatch several
661 # times so we keep track of configured extensions in _loaded.
660 # times so we keep track of configured extensions in _loaded.
662 extensions.loadall(lui)
661 extensions.loadall(lui)
663 exts = [ext for ext in extensions.extensions() if ext[0] not in _loaded]
662 exts = [ext for ext in extensions.extensions() if ext[0] not in _loaded]
664 # Propagate any changes to lui.__class__ by extensions
663 # Propagate any changes to lui.__class__ by extensions
665 ui.__class__ = lui.__class__
664 ui.__class__ = lui.__class__
666
665
667 # (uisetup and extsetup are handled in extensions.loadall)
666 # (uisetup and extsetup are handled in extensions.loadall)
668
667
669 for name, module in exts:
668 for name, module in exts:
670 for objname, loadermod, loadername in extraloaders:
669 for objname, loadermod, loadername in extraloaders:
671 extraobj = getattr(module, objname, None)
670 extraobj = getattr(module, objname, None)
672 if extraobj is not None:
671 if extraobj is not None:
673 getattr(loadermod, loadername)(ui, name, extraobj)
672 getattr(loadermod, loadername)(ui, name, extraobj)
674 _loaded.add(name)
673 _loaded.add(name)
675
674
676 # (reposetup is handled in hg.repository)
675 # (reposetup is handled in hg.repository)
677
676
678 # Side-effect of accessing is debugcommands module is guaranteed to be
677 # Side-effect of accessing is debugcommands module is guaranteed to be
679 # imported and commands.table is populated.
678 # imported and commands.table is populated.
680 debugcommands.command
679 debugcommands.command
681
680
682 addaliases(lui, commands.table)
681 addaliases(lui, commands.table)
683
682
684 # All aliases and commands are completely defined, now.
683 # All aliases and commands are completely defined, now.
685 # Check abbreviation/ambiguity of shell alias.
684 # Check abbreviation/ambiguity of shell alias.
686 shellaliasfn = _checkshellalias(lui, ui, args)
685 shellaliasfn = _checkshellalias(lui, ui, args)
687 if shellaliasfn:
686 if shellaliasfn:
688 with profiling.maybeprofile(lui):
687 with profiling.maybeprofile(lui):
689 return shellaliasfn()
688 return shellaliasfn()
690
689
691 # check for fallback encoding
690 # check for fallback encoding
692 fallback = lui.config('ui', 'fallbackencoding')
691 fallback = lui.config('ui', 'fallbackencoding')
693 if fallback:
692 if fallback:
694 encoding.fallbackencoding = fallback
693 encoding.fallbackencoding = fallback
695
694
696 fullargs = args
695 fullargs = args
697 cmd, func, args, options, cmdoptions = _parse(lui, args)
696 cmd, func, args, options, cmdoptions = _parse(lui, args)
698
697
699 if options["config"]:
698 if options["config"]:
700 raise error.Abort(_("option --config may not be abbreviated!"))
699 raise error.Abort(_("option --config may not be abbreviated!"))
701 if options["cwd"]:
700 if options["cwd"]:
702 raise error.Abort(_("option --cwd may not be abbreviated!"))
701 raise error.Abort(_("option --cwd may not be abbreviated!"))
703 if options["repository"]:
702 if options["repository"]:
704 raise error.Abort(_(
703 raise error.Abort(_(
705 "option -R has to be separated from other options (e.g. not -qR) "
704 "option -R has to be separated from other options (e.g. not -qR) "
706 "and --repository may only be abbreviated as --repo!"))
705 "and --repository may only be abbreviated as --repo!"))
707
706
708 if options["encoding"]:
707 if options["encoding"]:
709 encoding.encoding = options["encoding"]
708 encoding.encoding = options["encoding"]
710 if options["encodingmode"]:
709 if options["encodingmode"]:
711 encoding.encodingmode = options["encodingmode"]
710 encoding.encodingmode = options["encodingmode"]
712 if options["time"]:
711 if options["time"]:
713 def get_times():
712 def get_times():
714 t = os.times()
713 t = os.times()
715 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
714 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
716 t = (t[0], t[1], t[2], t[3], time.clock())
715 t = (t[0], t[1], t[2], t[3], time.clock())
717 return t
716 return t
718 s = get_times()
717 s = get_times()
719 def print_time():
718 def print_time():
720 t = get_times()
719 t = get_times()
721 ui.warn(_("time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
720 ui.warn(_("time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
722 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
721 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
723 atexit.register(print_time)
722 atexit.register(print_time)
724
723
725 uis = set([ui, lui])
724 uis = set([ui, lui])
726
725
727 if req.repo:
726 if req.repo:
728 uis.add(req.repo.ui)
727 uis.add(req.repo.ui)
729
728
730 if options['verbose'] or options['debug'] or options['quiet']:
729 if options['verbose'] or options['debug'] or options['quiet']:
731 for opt in ('verbose', 'debug', 'quiet'):
730 for opt in ('verbose', 'debug', 'quiet'):
732 val = str(bool(options[opt]))
731 val = str(bool(options[opt]))
733 for ui_ in uis:
732 for ui_ in uis:
734 ui_.setconfig('ui', opt, val, '--' + opt)
733 ui_.setconfig('ui', opt, val, '--' + opt)
735
734
736 if options['profile']:
735 if options['profile']:
737 for ui_ in uis:
736 for ui_ in uis:
738 ui_.setconfig('profiling', 'enabled', 'true', '--profile')
737 ui_.setconfig('profiling', 'enabled', 'true', '--profile')
739
738
740 if options['traceback']:
739 if options['traceback']:
741 for ui_ in uis:
740 for ui_ in uis:
742 ui_.setconfig('ui', 'traceback', 'on', '--traceback')
741 ui_.setconfig('ui', 'traceback', 'on', '--traceback')
743
742
744 if options['noninteractive']:
743 if options['noninteractive']:
745 for ui_ in uis:
744 for ui_ in uis:
746 ui_.setconfig('ui', 'interactive', 'off', '-y')
745 ui_.setconfig('ui', 'interactive', 'off', '-y')
747
746
748 if cmdoptions.get('insecure', False):
747 if cmdoptions.get('insecure', False):
749 for ui_ in uis:
748 for ui_ in uis:
750 ui_.insecureconnections = True
749 ui_.insecureconnections = True
751
750
752 if options['version']:
751 if options['version']:
753 return commands.version_(ui)
752 return commands.version_(ui)
754 if options['help']:
753 if options['help']:
755 return commands.help_(ui, cmd, command=cmd is not None)
754 return commands.help_(ui, cmd, command=cmd is not None)
756 elif not cmd:
755 elif not cmd:
757 return commands.help_(ui, 'shortlist')
756 return commands.help_(ui, 'shortlist')
758
757
759 with profiling.maybeprofile(lui):
758 with profiling.maybeprofile(lui):
760 repo = None
759 repo = None
761 cmdpats = args[:]
760 cmdpats = args[:]
762 if not func.norepo:
761 if not func.norepo:
763 # use the repo from the request only if we don't have -R
762 # use the repo from the request only if we don't have -R
764 if not rpath and not cwd:
763 if not rpath and not cwd:
765 repo = req.repo
764 repo = req.repo
766
765
767 if repo:
766 if repo:
768 # set the descriptors of the repo ui to those of ui
767 # set the descriptors of the repo ui to those of ui
769 repo.ui.fin = ui.fin
768 repo.ui.fin = ui.fin
770 repo.ui.fout = ui.fout
769 repo.ui.fout = ui.fout
771 repo.ui.ferr = ui.ferr
770 repo.ui.ferr = ui.ferr
772 else:
771 else:
773 try:
772 try:
774 repo = hg.repository(ui, path=path)
773 repo = hg.repository(ui, path=path)
775 if not repo.local():
774 if not repo.local():
776 raise error.Abort(_("repository '%s' is not local")
775 raise error.Abort(_("repository '%s' is not local")
777 % path)
776 % path)
778 repo.ui.setconfig("bundle", "mainreporoot", repo.root,
777 repo.ui.setconfig("bundle", "mainreporoot", repo.root,
779 'repo')
778 'repo')
780 except error.RequirementError:
779 except error.RequirementError:
781 raise
780 raise
782 except error.RepoError:
781 except error.RepoError:
783 if rpath and rpath[-1]: # invalid -R path
782 if rpath and rpath[-1]: # invalid -R path
784 raise
783 raise
785 if not func.optionalrepo:
784 if not func.optionalrepo:
786 if func.inferrepo and args and not path:
785 if func.inferrepo and args and not path:
787 # try to infer -R from command args
786 # try to infer -R from command args
788 repos = map(cmdutil.findrepo, args)
787 repos = map(cmdutil.findrepo, args)
789 guess = repos[0]
788 guess = repos[0]
790 if guess and repos.count(guess) == len(repos):
789 if guess and repos.count(guess) == len(repos):
791 req.args = ['--repository', guess] + fullargs
790 req.args = ['--repository', guess] + fullargs
792 return _dispatch(req)
791 return _dispatch(req)
793 if not path:
792 if not path:
794 raise error.RepoError(_("no repository found in"
793 raise error.RepoError(_("no repository found in"
795 " '%s' (.hg not found)")
794 " '%s' (.hg not found)")
796 % pycompat.getcwd())
795 % pycompat.getcwd())
797 raise
796 raise
798 if repo:
797 if repo:
799 ui = repo.ui
798 ui = repo.ui
800 if options['hidden']:
799 if options['hidden']:
801 repo = repo.unfiltered()
800 repo = repo.unfiltered()
802 args.insert(0, repo)
801 args.insert(0, repo)
803 elif rpath:
802 elif rpath:
804 ui.warn(_("warning: --repository ignored\n"))
803 ui.warn(_("warning: --repository ignored\n"))
805
804
806 msg = ' '.join(' ' in a and repr(a) or a for a in fullargs)
805 msg = ' '.join(' ' in a and repr(a) or a for a in fullargs)
807 ui.log("command", '%s\n', msg)
806 ui.log("command", '%s\n', msg)
808 strcmdopt = pycompat.strkwargs(cmdoptions)
807 strcmdopt = pycompat.strkwargs(cmdoptions)
809 d = lambda: util.checksignature(func)(ui, *args, **strcmdopt)
808 d = lambda: util.checksignature(func)(ui, *args, **strcmdopt)
810 try:
809 try:
811 return runcommand(lui, repo, cmd, fullargs, ui, options, d,
810 return runcommand(lui, repo, cmd, fullargs, ui, options, d,
812 cmdpats, cmdoptions)
811 cmdpats, cmdoptions)
813 finally:
812 finally:
814 if repo and repo != req.repo:
813 if repo and repo != req.repo:
815 repo.close()
814 repo.close()
816
815
817 def _runcommand(ui, options, cmd, cmdfunc):
816 def _runcommand(ui, options, cmd, cmdfunc):
818 """Run a command function, possibly with profiling enabled."""
817 """Run a command function, possibly with profiling enabled."""
819 try:
818 try:
820 return cmdfunc()
819 return cmdfunc()
821 except error.SignatureError:
820 except error.SignatureError:
822 raise error.CommandError(cmd, _('invalid arguments'))
821 raise error.CommandError(cmd, _('invalid arguments'))
823
822
824 def _exceptionwarning(ui):
823 def _exceptionwarning(ui):
825 """Produce a warning message for the current active exception"""
824 """Produce a warning message for the current active exception"""
826
825
827 # For compatibility checking, we discard the portion of the hg
826 # For compatibility checking, we discard the portion of the hg
828 # version after the + on the assumption that if a "normal
827 # version after the + on the assumption that if a "normal
829 # user" is running a build with a + in it the packager
828 # user" is running a build with a + in it the packager
830 # probably built from fairly close to a tag and anyone with a
829 # probably built from fairly close to a tag and anyone with a
831 # 'make local' copy of hg (where the version number can be out
830 # 'make local' copy of hg (where the version number can be out
832 # of date) will be clueful enough to notice the implausible
831 # of date) will be clueful enough to notice the implausible
833 # version number and try updating.
832 # version number and try updating.
834 ct = util.versiontuple(n=2)
833 ct = util.versiontuple(n=2)
835 worst = None, ct, ''
834 worst = None, ct, ''
836 if ui.config('ui', 'supportcontact', None) is None:
835 if ui.config('ui', 'supportcontact', None) is None:
837 for name, mod in extensions.extensions():
836 for name, mod in extensions.extensions():
838 testedwith = getattr(mod, 'testedwith', '')
837 testedwith = getattr(mod, 'testedwith', '')
839 report = getattr(mod, 'buglink', _('the extension author.'))
838 report = getattr(mod, 'buglink', _('the extension author.'))
840 if not testedwith.strip():
839 if not testedwith.strip():
841 # We found an untested extension. It's likely the culprit.
840 # We found an untested extension. It's likely the culprit.
842 worst = name, 'unknown', report
841 worst = name, 'unknown', report
843 break
842 break
844
843
845 # Never blame on extensions bundled with Mercurial.
844 # Never blame on extensions bundled with Mercurial.
846 if extensions.ismoduleinternal(mod):
845 if extensions.ismoduleinternal(mod):
847 continue
846 continue
848
847
849 tested = [util.versiontuple(t, 2) for t in testedwith.split()]
848 tested = [util.versiontuple(t, 2) for t in testedwith.split()]
850 if ct in tested:
849 if ct in tested:
851 continue
850 continue
852
851
853 lower = [t for t in tested if t < ct]
852 lower = [t for t in tested if t < ct]
854 nearest = max(lower or tested)
853 nearest = max(lower or tested)
855 if worst[0] is None or nearest < worst[1]:
854 if worst[0] is None or nearest < worst[1]:
856 worst = name, nearest, report
855 worst = name, nearest, report
857 if worst[0] is not None:
856 if worst[0] is not None:
858 name, testedwith, report = worst
857 name, testedwith, report = worst
859 if not isinstance(testedwith, str):
858 if not isinstance(testedwith, str):
860 testedwith = '.'.join([str(c) for c in testedwith])
859 testedwith = '.'.join([str(c) for c in testedwith])
861 warning = (_('** Unknown exception encountered with '
860 warning = (_('** Unknown exception encountered with '
862 'possibly-broken third-party extension %s\n'
861 'possibly-broken third-party extension %s\n'
863 '** which supports versions %s of Mercurial.\n'
862 '** which supports versions %s of Mercurial.\n'
864 '** Please disable %s and try your action again.\n'
863 '** Please disable %s and try your action again.\n'
865 '** If that fixes the bug please report it to %s\n')
864 '** If that fixes the bug please report it to %s\n')
866 % (name, testedwith, name, report))
865 % (name, testedwith, name, report))
867 else:
866 else:
868 bugtracker = ui.config('ui', 'supportcontact', None)
867 bugtracker = ui.config('ui', 'supportcontact', None)
869 if bugtracker is None:
868 if bugtracker is None:
870 bugtracker = _("https://mercurial-scm.org/wiki/BugTracker")
869 bugtracker = _("https://mercurial-scm.org/wiki/BugTracker")
871 warning = (_("** unknown exception encountered, "
870 warning = (_("** unknown exception encountered, "
872 "please report by visiting\n** ") + bugtracker + '\n')
871 "please report by visiting\n** ") + bugtracker + '\n')
873 warning += ((_("** Python %s\n") % sys.version.replace('\n', '')) +
872 warning += ((_("** Python %s\n") % sys.version.replace('\n', '')) +
874 (_("** Mercurial Distributed SCM (version %s)\n") %
873 (_("** Mercurial Distributed SCM (version %s)\n") %
875 util.version()) +
874 util.version()) +
876 (_("** Extensions loaded: %s\n") %
875 (_("** Extensions loaded: %s\n") %
877 ", ".join([x[0] for x in extensions.extensions()])))
876 ", ".join([x[0] for x in extensions.extensions()])))
878 return warning
877 return warning
879
878
880 def handlecommandexception(ui):
879 def handlecommandexception(ui):
881 """Produce a warning message for broken commands
880 """Produce a warning message for broken commands
882
881
883 Called when handling an exception; the exception is reraised if
882 Called when handling an exception; the exception is reraised if
884 this function returns False, ignored otherwise.
883 this function returns False, ignored otherwise.
885 """
884 """
886 warning = _exceptionwarning(ui)
885 warning = _exceptionwarning(ui)
887 ui.log("commandexception", "%s\n%s\n", warning, traceback.format_exc())
886 ui.log("commandexception", "%s\n%s\n", warning, traceback.format_exc())
888 ui.warn(warning)
887 ui.warn(warning)
889 return False # re-raise the exception
888 return False # re-raise the exception
@@ -1,283 +1,293 b''
1 # pycompat.py - portability shim for python 3
1 # pycompat.py - portability shim for python 3
2 #
2 #
3 # This software may be used and distributed according to the terms of the
3 # This software may be used and distributed according to the terms of the
4 # GNU General Public License version 2 or any later version.
4 # GNU General Public License version 2 or any later version.
5
5
6 """Mercurial portability shim for python 3.
6 """Mercurial portability shim for python 3.
7
7
8 This contains aliases to hide python version-specific details from the core.
8 This contains aliases to hide python version-specific details from the core.
9 """
9 """
10
10
11 from __future__ import absolute_import
11 from __future__ import absolute_import
12
12
13 import getopt
13 import getopt
14 import os
14 import os
15 import shlex
15 import sys
16 import sys
16
17
17 ispy3 = (sys.version_info[0] >= 3)
18 ispy3 = (sys.version_info[0] >= 3)
18
19
19 if not ispy3:
20 if not ispy3:
20 import cPickle as pickle
21 import cPickle as pickle
21 import cStringIO as io
22 import cStringIO as io
22 import httplib
23 import httplib
23 import Queue as _queue
24 import Queue as _queue
24 import SocketServer as socketserver
25 import SocketServer as socketserver
25 import urlparse
26 import urlparse
26 urlunquote = urlparse.unquote
27 urlunquote = urlparse.unquote
27 import xmlrpclib
28 import xmlrpclib
28 else:
29 else:
29 import http.client as httplib
30 import http.client as httplib
30 import io
31 import io
31 import pickle
32 import pickle
32 import queue as _queue
33 import queue as _queue
33 import socketserver
34 import socketserver
34 import urllib.parse as urlparse
35 import urllib.parse as urlparse
35 urlunquote = urlparse.unquote_to_bytes
36 urlunquote = urlparse.unquote_to_bytes
36 import xmlrpc.client as xmlrpclib
37 import xmlrpc.client as xmlrpclib
37
38
38 if ispy3:
39 if ispy3:
39 import builtins
40 import builtins
40 import functools
41 import functools
41 fsencode = os.fsencode
42 fsencode = os.fsencode
42 fsdecode = os.fsdecode
43 fsdecode = os.fsdecode
43 # A bytes version of os.name.
44 # A bytes version of os.name.
44 osname = os.name.encode('ascii')
45 osname = os.name.encode('ascii')
45 ospathsep = os.pathsep.encode('ascii')
46 ospathsep = os.pathsep.encode('ascii')
46 ossep = os.sep.encode('ascii')
47 ossep = os.sep.encode('ascii')
47 osaltsep = os.altsep
48 osaltsep = os.altsep
48 osgetenv = os.getenvb
49 osgetenv = os.getenvb
49 if osaltsep:
50 if osaltsep:
50 osaltsep = osaltsep.encode('ascii')
51 osaltsep = osaltsep.encode('ascii')
51 # os.getcwd() on Python 3 returns string, but it has os.getcwdb() which
52 # os.getcwd() on Python 3 returns string, but it has os.getcwdb() which
52 # returns bytes.
53 # returns bytes.
53 getcwd = os.getcwdb
54 getcwd = os.getcwdb
54 sysplatform = sys.platform.encode('ascii')
55 sysplatform = sys.platform.encode('ascii')
55 sysexecutable = sys.executable
56 sysexecutable = sys.executable
56 if sysexecutable:
57 if sysexecutable:
57 sysexecutable = os.fsencode(sysexecutable)
58 sysexecutable = os.fsencode(sysexecutable)
58
59
59 # TODO: .buffer might not exist if std streams were replaced; we'll need
60 # TODO: .buffer might not exist if std streams were replaced; we'll need
60 # a silly wrapper to make a bytes stream backed by a unicode one.
61 # a silly wrapper to make a bytes stream backed by a unicode one.
61 stdin = sys.stdin.buffer
62 stdin = sys.stdin.buffer
62 stdout = sys.stdout.buffer
63 stdout = sys.stdout.buffer
63 stderr = sys.stderr.buffer
64 stderr = sys.stderr.buffer
64
65
65 # Since Python 3 converts argv to wchar_t type by Py_DecodeLocale() on Unix,
66 # Since Python 3 converts argv to wchar_t type by Py_DecodeLocale() on Unix,
66 # we can use os.fsencode() to get back bytes argv.
67 # we can use os.fsencode() to get back bytes argv.
67 #
68 #
68 # https://hg.python.org/cpython/file/v3.5.1/Programs/python.c#l55
69 # https://hg.python.org/cpython/file/v3.5.1/Programs/python.c#l55
69 #
70 #
70 # TODO: On Windows, the native argv is wchar_t, so we'll need a different
71 # TODO: On Windows, the native argv is wchar_t, so we'll need a different
71 # workaround to simulate the Python 2 (i.e. ANSI Win32 API) behavior.
72 # workaround to simulate the Python 2 (i.e. ANSI Win32 API) behavior.
72 sysargv = list(map(os.fsencode, sys.argv))
73 sysargv = list(map(os.fsencode, sys.argv))
73
74
74 def sysstr(s):
75 def sysstr(s):
75 """Return a keyword str to be passed to Python functions such as
76 """Return a keyword str to be passed to Python functions such as
76 getattr() and str.encode()
77 getattr() and str.encode()
77
78
78 This never raises UnicodeDecodeError. Non-ascii characters are
79 This never raises UnicodeDecodeError. Non-ascii characters are
79 considered invalid and mapped to arbitrary but unique code points
80 considered invalid and mapped to arbitrary but unique code points
80 such that 'sysstr(a) != sysstr(b)' for all 'a != b'.
81 such that 'sysstr(a) != sysstr(b)' for all 'a != b'.
81 """
82 """
82 if isinstance(s, builtins.str):
83 if isinstance(s, builtins.str):
83 return s
84 return s
84 return s.decode(u'latin-1')
85 return s.decode(u'latin-1')
85
86
86 def _wrapattrfunc(f):
87 def _wrapattrfunc(f):
87 @functools.wraps(f)
88 @functools.wraps(f)
88 def w(object, name, *args):
89 def w(object, name, *args):
89 return f(object, sysstr(name), *args)
90 return f(object, sysstr(name), *args)
90 return w
91 return w
91
92
92 # these wrappers are automagically imported by hgloader
93 # these wrappers are automagically imported by hgloader
93 delattr = _wrapattrfunc(builtins.delattr)
94 delattr = _wrapattrfunc(builtins.delattr)
94 getattr = _wrapattrfunc(builtins.getattr)
95 getattr = _wrapattrfunc(builtins.getattr)
95 hasattr = _wrapattrfunc(builtins.hasattr)
96 hasattr = _wrapattrfunc(builtins.hasattr)
96 setattr = _wrapattrfunc(builtins.setattr)
97 setattr = _wrapattrfunc(builtins.setattr)
97 xrange = builtins.range
98 xrange = builtins.range
98
99
99 # getopt.getopt() on Python 3 deals with unicodes internally so we cannot
100 # getopt.getopt() on Python 3 deals with unicodes internally so we cannot
100 # pass bytes there. Passing unicodes will result in unicodes as return
101 # pass bytes there. Passing unicodes will result in unicodes as return
101 # values which we need to convert again to bytes.
102 # values which we need to convert again to bytes.
102 def getoptb(args, shortlist, namelist):
103 def getoptb(args, shortlist, namelist):
103 args = [a.decode('latin-1') for a in args]
104 args = [a.decode('latin-1') for a in args]
104 shortlist = shortlist.decode('latin-1')
105 shortlist = shortlist.decode('latin-1')
105 namelist = [a.decode('latin-1') for a in namelist]
106 namelist = [a.decode('latin-1') for a in namelist]
106 opts, args = getopt.getopt(args, shortlist, namelist)
107 opts, args = getopt.getopt(args, shortlist, namelist)
107 opts = [(a[0].encode('latin-1'), a[1].encode('latin-1'))
108 opts = [(a[0].encode('latin-1'), a[1].encode('latin-1'))
108 for a in opts]
109 for a in opts]
109 args = [a.encode('latin-1') for a in args]
110 args = [a.encode('latin-1') for a in args]
110 return opts, args
111 return opts, args
111
112
112 # keys of keyword arguments in Python need to be strings which are unicodes
113 # keys of keyword arguments in Python need to be strings which are unicodes
113 # Python 3. This function takes keyword arguments, convert the keys to str.
114 # Python 3. This function takes keyword arguments, convert the keys to str.
114 def strkwargs(dic):
115 def strkwargs(dic):
115 dic = dict((k.decode('latin-1'), v) for k, v in dic.iteritems())
116 dic = dict((k.decode('latin-1'), v) for k, v in dic.iteritems())
116 return dic
117 return dic
117
118
118 # keys of keyword arguments need to be unicode while passing into
119 # keys of keyword arguments need to be unicode while passing into
119 # a function. This function helps us to convert those keys back to bytes
120 # a function. This function helps us to convert those keys back to bytes
120 # again as we need to deal with bytes.
121 # again as we need to deal with bytes.
121 def byteskwargs(dic):
122 def byteskwargs(dic):
122 dic = dict((k.encode('latin-1'), v) for k, v in dic.iteritems())
123 dic = dict((k.encode('latin-1'), v) for k, v in dic.iteritems())
123 return dic
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 else:
134 else:
126 def sysstr(s):
135 def sysstr(s):
127 return s
136 return s
128
137
129 # Partial backport from os.py in Python 3, which only accepts bytes.
138 # Partial backport from os.py in Python 3, which only accepts bytes.
130 # In Python 2, our paths should only ever be bytes, a unicode path
139 # In Python 2, our paths should only ever be bytes, a unicode path
131 # indicates a bug.
140 # indicates a bug.
132 def fsencode(filename):
141 def fsencode(filename):
133 if isinstance(filename, str):
142 if isinstance(filename, str):
134 return filename
143 return filename
135 else:
144 else:
136 raise TypeError(
145 raise TypeError(
137 "expect str, not %s" % type(filename).__name__)
146 "expect str, not %s" % type(filename).__name__)
138
147
139 # In Python 2, fsdecode() has a very chance to receive bytes. So it's
148 # In Python 2, fsdecode() has a very chance to receive bytes. So it's
140 # better not to touch Python 2 part as it's already working fine.
149 # better not to touch Python 2 part as it's already working fine.
141 def fsdecode(filename):
150 def fsdecode(filename):
142 return filename
151 return filename
143
152
144 def getoptb(args, shortlist, namelist):
153 def getoptb(args, shortlist, namelist):
145 return getopt.getopt(args, shortlist, namelist)
154 return getopt.getopt(args, shortlist, namelist)
146
155
147 def strkwargs(dic):
156 def strkwargs(dic):
148 return dic
157 return dic
149
158
150 def byteskwargs(dic):
159 def byteskwargs(dic):
151 return dic
160 return dic
152
161
153 osname = os.name
162 osname = os.name
154 ospathsep = os.pathsep
163 ospathsep = os.pathsep
155 ossep = os.sep
164 ossep = os.sep
156 osaltsep = os.altsep
165 osaltsep = os.altsep
157 stdin = sys.stdin
166 stdin = sys.stdin
158 stdout = sys.stdout
167 stdout = sys.stdout
159 stderr = sys.stderr
168 stderr = sys.stderr
160 sysargv = sys.argv
169 sysargv = sys.argv
161 sysplatform = sys.platform
170 sysplatform = sys.platform
162 getcwd = os.getcwd
171 getcwd = os.getcwd
163 osgetenv = os.getenv
172 osgetenv = os.getenv
164 sysexecutable = sys.executable
173 sysexecutable = sys.executable
174 shlexsplit = shlex.split
165
175
166 stringio = io.StringIO
176 stringio = io.StringIO
167 empty = _queue.Empty
177 empty = _queue.Empty
168 queue = _queue.Queue
178 queue = _queue.Queue
169
179
170 class _pycompatstub(object):
180 class _pycompatstub(object):
171 def __init__(self):
181 def __init__(self):
172 self._aliases = {}
182 self._aliases = {}
173
183
174 def _registeraliases(self, origin, items):
184 def _registeraliases(self, origin, items):
175 """Add items that will be populated at the first access"""
185 """Add items that will be populated at the first access"""
176 items = map(sysstr, items)
186 items = map(sysstr, items)
177 self._aliases.update(
187 self._aliases.update(
178 (item.replace(sysstr('_'), sysstr('')).lower(), (origin, item))
188 (item.replace(sysstr('_'), sysstr('')).lower(), (origin, item))
179 for item in items)
189 for item in items)
180
190
181 def __getattr__(self, name):
191 def __getattr__(self, name):
182 try:
192 try:
183 origin, item = self._aliases[name]
193 origin, item = self._aliases[name]
184 except KeyError:
194 except KeyError:
185 raise AttributeError(name)
195 raise AttributeError(name)
186 self.__dict__[name] = obj = getattr(origin, item)
196 self.__dict__[name] = obj = getattr(origin, item)
187 return obj
197 return obj
188
198
189 httpserver = _pycompatstub()
199 httpserver = _pycompatstub()
190 urlreq = _pycompatstub()
200 urlreq = _pycompatstub()
191 urlerr = _pycompatstub()
201 urlerr = _pycompatstub()
192 if not ispy3:
202 if not ispy3:
193 import BaseHTTPServer
203 import BaseHTTPServer
194 import CGIHTTPServer
204 import CGIHTTPServer
195 import SimpleHTTPServer
205 import SimpleHTTPServer
196 import urllib2
206 import urllib2
197 import urllib
207 import urllib
198 urlreq._registeraliases(urllib, (
208 urlreq._registeraliases(urllib, (
199 "addclosehook",
209 "addclosehook",
200 "addinfourl",
210 "addinfourl",
201 "ftpwrapper",
211 "ftpwrapper",
202 "pathname2url",
212 "pathname2url",
203 "quote",
213 "quote",
204 "splitattr",
214 "splitattr",
205 "splitpasswd",
215 "splitpasswd",
206 "splitport",
216 "splitport",
207 "splituser",
217 "splituser",
208 "unquote",
218 "unquote",
209 "url2pathname",
219 "url2pathname",
210 "urlencode",
220 "urlencode",
211 ))
221 ))
212 urlreq._registeraliases(urllib2, (
222 urlreq._registeraliases(urllib2, (
213 "AbstractHTTPHandler",
223 "AbstractHTTPHandler",
214 "BaseHandler",
224 "BaseHandler",
215 "build_opener",
225 "build_opener",
216 "FileHandler",
226 "FileHandler",
217 "FTPHandler",
227 "FTPHandler",
218 "HTTPBasicAuthHandler",
228 "HTTPBasicAuthHandler",
219 "HTTPDigestAuthHandler",
229 "HTTPDigestAuthHandler",
220 "HTTPHandler",
230 "HTTPHandler",
221 "HTTPPasswordMgrWithDefaultRealm",
231 "HTTPPasswordMgrWithDefaultRealm",
222 "HTTPSHandler",
232 "HTTPSHandler",
223 "install_opener",
233 "install_opener",
224 "ProxyHandler",
234 "ProxyHandler",
225 "Request",
235 "Request",
226 "urlopen",
236 "urlopen",
227 ))
237 ))
228 urlerr._registeraliases(urllib2, (
238 urlerr._registeraliases(urllib2, (
229 "HTTPError",
239 "HTTPError",
230 "URLError",
240 "URLError",
231 ))
241 ))
232 httpserver._registeraliases(BaseHTTPServer, (
242 httpserver._registeraliases(BaseHTTPServer, (
233 "HTTPServer",
243 "HTTPServer",
234 "BaseHTTPRequestHandler",
244 "BaseHTTPRequestHandler",
235 ))
245 ))
236 httpserver._registeraliases(SimpleHTTPServer, (
246 httpserver._registeraliases(SimpleHTTPServer, (
237 "SimpleHTTPRequestHandler",
247 "SimpleHTTPRequestHandler",
238 ))
248 ))
239 httpserver._registeraliases(CGIHTTPServer, (
249 httpserver._registeraliases(CGIHTTPServer, (
240 "CGIHTTPRequestHandler",
250 "CGIHTTPRequestHandler",
241 ))
251 ))
242
252
243 else:
253 else:
244 import urllib.request
254 import urllib.request
245 urlreq._registeraliases(urllib.request, (
255 urlreq._registeraliases(urllib.request, (
246 "AbstractHTTPHandler",
256 "AbstractHTTPHandler",
247 "addclosehook",
257 "addclosehook",
248 "addinfourl",
258 "addinfourl",
249 "BaseHandler",
259 "BaseHandler",
250 "build_opener",
260 "build_opener",
251 "FileHandler",
261 "FileHandler",
252 "FTPHandler",
262 "FTPHandler",
253 "ftpwrapper",
263 "ftpwrapper",
254 "HTTPHandler",
264 "HTTPHandler",
255 "HTTPSHandler",
265 "HTTPSHandler",
256 "install_opener",
266 "install_opener",
257 "pathname2url",
267 "pathname2url",
258 "HTTPBasicAuthHandler",
268 "HTTPBasicAuthHandler",
259 "HTTPDigestAuthHandler",
269 "HTTPDigestAuthHandler",
260 "HTTPPasswordMgrWithDefaultRealm",
270 "HTTPPasswordMgrWithDefaultRealm",
261 "ProxyHandler",
271 "ProxyHandler",
262 "quote",
272 "quote",
263 "Request",
273 "Request",
264 "splitattr",
274 "splitattr",
265 "splitpasswd",
275 "splitpasswd",
266 "splitport",
276 "splitport",
267 "splituser",
277 "splituser",
268 "unquote",
278 "unquote",
269 "url2pathname",
279 "url2pathname",
270 "urlopen",
280 "urlopen",
271 ))
281 ))
272 import urllib.error
282 import urllib.error
273 urlerr._registeraliases(urllib.error, (
283 urlerr._registeraliases(urllib.error, (
274 "HTTPError",
284 "HTTPError",
275 "URLError",
285 "URLError",
276 ))
286 ))
277 import http.server
287 import http.server
278 httpserver._registeraliases(http.server, (
288 httpserver._registeraliases(http.server, (
279 "HTTPServer",
289 "HTTPServer",
280 "BaseHTTPRequestHandler",
290 "BaseHTTPRequestHandler",
281 "SimpleHTTPRequestHandler",
291 "SimpleHTTPRequestHandler",
282 "CGIHTTPRequestHandler",
292 "CGIHTTPRequestHandler",
283 ))
293 ))
General Comments 0
You need to be logged in to leave comments. Login now