##// END OF EJS Templates
dispatch: verify result of early command parsing...
Yuya Nishihara -
r35063:02845f74 stable
parent child Browse files
Show More
@@ -1,5587 +1,5591 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 sys
14 import sys
15
15
16 from .i18n import _
16 from .i18n import _
17 from .node import (
17 from .node import (
18 hex,
18 hex,
19 nullid,
19 nullid,
20 nullrev,
20 nullrev,
21 short,
21 short,
22 )
22 )
23 from . import (
23 from . import (
24 archival,
24 archival,
25 bookmarks,
25 bookmarks,
26 bundle2,
26 bundle2,
27 changegroup,
27 changegroup,
28 cmdutil,
28 cmdutil,
29 copies,
29 copies,
30 debugcommands as debugcommandsmod,
30 debugcommands as debugcommandsmod,
31 destutil,
31 destutil,
32 dirstateguard,
32 dirstateguard,
33 discovery,
33 discovery,
34 encoding,
34 encoding,
35 error,
35 error,
36 exchange,
36 exchange,
37 extensions,
37 extensions,
38 formatter,
38 formatter,
39 graphmod,
39 graphmod,
40 hbisect,
40 hbisect,
41 help,
41 help,
42 hg,
42 hg,
43 lock as lockmod,
43 lock as lockmod,
44 merge as mergemod,
44 merge as mergemod,
45 obsolete,
45 obsolete,
46 patch,
46 patch,
47 phases,
47 phases,
48 pycompat,
48 pycompat,
49 rcutil,
49 rcutil,
50 registrar,
50 registrar,
51 revsetlang,
51 revsetlang,
52 scmutil,
52 scmutil,
53 server,
53 server,
54 sshserver,
54 sshserver,
55 streamclone,
55 streamclone,
56 tags as tagsmod,
56 tags as tagsmod,
57 templatekw,
57 templatekw,
58 ui as uimod,
58 ui as uimod,
59 util,
59 util,
60 )
60 )
61
61
62 release = lockmod.release
62 release = lockmod.release
63
63
64 table = {}
64 table = {}
65 table.update(debugcommandsmod.command._table)
65 table.update(debugcommandsmod.command._table)
66
66
67 command = registrar.command(table)
67 command = registrar.command(table)
68
68
69 # common command options
69 # common command options
70
70
71 globalopts = [
71 globalopts = [
72 ('R', 'repository', '',
72 ('R', 'repository', '',
73 _('repository root directory or name of overlay bundle file'),
73 _('repository root directory or name of overlay bundle file'),
74 _('REPO')),
74 _('REPO')),
75 ('', 'cwd', '',
75 ('', 'cwd', '',
76 _('change working directory'), _('DIR')),
76 _('change working directory'), _('DIR')),
77 ('y', 'noninteractive', None,
77 ('y', 'noninteractive', None,
78 _('do not prompt, automatically pick the first choice for all prompts')),
78 _('do not prompt, automatically pick the first choice for all prompts')),
79 ('q', 'quiet', None, _('suppress output')),
79 ('q', 'quiet', None, _('suppress output')),
80 ('v', 'verbose', None, _('enable additional output')),
80 ('v', 'verbose', None, _('enable additional output')),
81 ('', 'color', '',
81 ('', 'color', '',
82 # i18n: 'always', 'auto', 'never', and 'debug' are keywords
82 # i18n: 'always', 'auto', 'never', and 'debug' are keywords
83 # and should not be translated
83 # and should not be translated
84 _("when to colorize (boolean, always, auto, never, or debug)"),
84 _("when to colorize (boolean, always, auto, never, or debug)"),
85 _('TYPE')),
85 _('TYPE')),
86 ('', 'config', [],
86 ('', 'config', [],
87 _('set/override config option (use \'section.name=value\')'),
87 _('set/override config option (use \'section.name=value\')'),
88 _('CONFIG')),
88 _('CONFIG')),
89 ('', 'debug', None, _('enable debugging output')),
89 ('', 'debug', None, _('enable debugging output')),
90 ('', 'debugger', None, _('start debugger')),
90 ('', 'debugger', None, _('start debugger')),
91 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
91 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
92 _('ENCODE')),
92 _('ENCODE')),
93 ('', 'encodingmode', encoding.encodingmode,
93 ('', 'encodingmode', encoding.encodingmode,
94 _('set the charset encoding mode'), _('MODE')),
94 _('set the charset encoding mode'), _('MODE')),
95 ('', 'traceback', None, _('always print a traceback on exception')),
95 ('', 'traceback', None, _('always print a traceback on exception')),
96 ('', 'time', None, _('time how long the command takes')),
96 ('', 'time', None, _('time how long the command takes')),
97 ('', 'profile', None, _('print command execution profile')),
97 ('', 'profile', None, _('print command execution profile')),
98 ('', 'version', None, _('output version information and exit')),
98 ('', 'version', None, _('output version information and exit')),
99 ('h', 'help', None, _('display help and exit')),
99 ('h', 'help', None, _('display help and exit')),
100 ('', 'hidden', False, _('consider hidden changesets')),
100 ('', 'hidden', False, _('consider hidden changesets')),
101 ('', 'pager', 'auto',
101 ('', 'pager', 'auto',
102 _("when to paginate (boolean, always, auto, or never)"), _('TYPE')),
102 _("when to paginate (boolean, always, auto, or never)"), _('TYPE')),
103 ]
103 ]
104
104
105 # options which must be pre-parsed before loading configs and extensions
106 # TODO: perhaps --debugger should be included
107 earlyoptflags = ("--cwd", "-R", "--repository", "--repo", "--config")
108
105 dryrunopts = cmdutil.dryrunopts
109 dryrunopts = cmdutil.dryrunopts
106 remoteopts = cmdutil.remoteopts
110 remoteopts = cmdutil.remoteopts
107 walkopts = cmdutil.walkopts
111 walkopts = cmdutil.walkopts
108 commitopts = cmdutil.commitopts
112 commitopts = cmdutil.commitopts
109 commitopts2 = cmdutil.commitopts2
113 commitopts2 = cmdutil.commitopts2
110 formatteropts = cmdutil.formatteropts
114 formatteropts = cmdutil.formatteropts
111 templateopts = cmdutil.templateopts
115 templateopts = cmdutil.templateopts
112 logopts = cmdutil.logopts
116 logopts = cmdutil.logopts
113 diffopts = cmdutil.diffopts
117 diffopts = cmdutil.diffopts
114 diffwsopts = cmdutil.diffwsopts
118 diffwsopts = cmdutil.diffwsopts
115 diffopts2 = cmdutil.diffopts2
119 diffopts2 = cmdutil.diffopts2
116 mergetoolopts = cmdutil.mergetoolopts
120 mergetoolopts = cmdutil.mergetoolopts
117 similarityopts = cmdutil.similarityopts
121 similarityopts = cmdutil.similarityopts
118 subrepoopts = cmdutil.subrepoopts
122 subrepoopts = cmdutil.subrepoopts
119 debugrevlogopts = cmdutil.debugrevlogopts
123 debugrevlogopts = cmdutil.debugrevlogopts
120
124
121 # Commands start here, listed alphabetically
125 # Commands start here, listed alphabetically
122
126
123 @command('^add',
127 @command('^add',
124 walkopts + subrepoopts + dryrunopts,
128 walkopts + subrepoopts + dryrunopts,
125 _('[OPTION]... [FILE]...'),
129 _('[OPTION]... [FILE]...'),
126 inferrepo=True)
130 inferrepo=True)
127 def add(ui, repo, *pats, **opts):
131 def add(ui, repo, *pats, **opts):
128 """add the specified files on the next commit
132 """add the specified files on the next commit
129
133
130 Schedule files to be version controlled and added to the
134 Schedule files to be version controlled and added to the
131 repository.
135 repository.
132
136
133 The files will be added to the repository at the next commit. To
137 The files will be added to the repository at the next commit. To
134 undo an add before that, see :hg:`forget`.
138 undo an add before that, see :hg:`forget`.
135
139
136 If no names are given, add all files to the repository (except
140 If no names are given, add all files to the repository (except
137 files matching ``.hgignore``).
141 files matching ``.hgignore``).
138
142
139 .. container:: verbose
143 .. container:: verbose
140
144
141 Examples:
145 Examples:
142
146
143 - New (unknown) files are added
147 - New (unknown) files are added
144 automatically by :hg:`add`::
148 automatically by :hg:`add`::
145
149
146 $ ls
150 $ ls
147 foo.c
151 foo.c
148 $ hg status
152 $ hg status
149 ? foo.c
153 ? foo.c
150 $ hg add
154 $ hg add
151 adding foo.c
155 adding foo.c
152 $ hg status
156 $ hg status
153 A foo.c
157 A foo.c
154
158
155 - Specific files to be added can be specified::
159 - Specific files to be added can be specified::
156
160
157 $ ls
161 $ ls
158 bar.c foo.c
162 bar.c foo.c
159 $ hg status
163 $ hg status
160 ? bar.c
164 ? bar.c
161 ? foo.c
165 ? foo.c
162 $ hg add bar.c
166 $ hg add bar.c
163 $ hg status
167 $ hg status
164 A bar.c
168 A bar.c
165 ? foo.c
169 ? foo.c
166
170
167 Returns 0 if all files are successfully added.
171 Returns 0 if all files are successfully added.
168 """
172 """
169
173
170 m = scmutil.match(repo[None], pats, pycompat.byteskwargs(opts))
174 m = scmutil.match(repo[None], pats, pycompat.byteskwargs(opts))
171 rejected = cmdutil.add(ui, repo, m, "", False, **opts)
175 rejected = cmdutil.add(ui, repo, m, "", False, **opts)
172 return rejected and 1 or 0
176 return rejected and 1 or 0
173
177
174 @command('addremove',
178 @command('addremove',
175 similarityopts + subrepoopts + walkopts + dryrunopts,
179 similarityopts + subrepoopts + walkopts + dryrunopts,
176 _('[OPTION]... [FILE]...'),
180 _('[OPTION]... [FILE]...'),
177 inferrepo=True)
181 inferrepo=True)
178 def addremove(ui, repo, *pats, **opts):
182 def addremove(ui, repo, *pats, **opts):
179 """add all new files, delete all missing files
183 """add all new files, delete all missing files
180
184
181 Add all new files and remove all missing files from the
185 Add all new files and remove all missing files from the
182 repository.
186 repository.
183
187
184 Unless names are given, new files are ignored if they match any of
188 Unless names are given, new files are ignored if they match any of
185 the patterns in ``.hgignore``. As with add, these changes take
189 the patterns in ``.hgignore``. As with add, these changes take
186 effect at the next commit.
190 effect at the next commit.
187
191
188 Use the -s/--similarity option to detect renamed files. This
192 Use the -s/--similarity option to detect renamed files. This
189 option takes a percentage between 0 (disabled) and 100 (files must
193 option takes a percentage between 0 (disabled) and 100 (files must
190 be identical) as its parameter. With a parameter greater than 0,
194 be identical) as its parameter. With a parameter greater than 0,
191 this compares every removed file with every added file and records
195 this compares every removed file with every added file and records
192 those similar enough as renames. Detecting renamed files this way
196 those similar enough as renames. Detecting renamed files this way
193 can be expensive. After using this option, :hg:`status -C` can be
197 can be expensive. After using this option, :hg:`status -C` can be
194 used to check which files were identified as moved or renamed. If
198 used to check which files were identified as moved or renamed. If
195 not specified, -s/--similarity defaults to 100 and only renames of
199 not specified, -s/--similarity defaults to 100 and only renames of
196 identical files are detected.
200 identical files are detected.
197
201
198 .. container:: verbose
202 .. container:: verbose
199
203
200 Examples:
204 Examples:
201
205
202 - A number of files (bar.c and foo.c) are new,
206 - A number of files (bar.c and foo.c) are new,
203 while foobar.c has been removed (without using :hg:`remove`)
207 while foobar.c has been removed (without using :hg:`remove`)
204 from the repository::
208 from the repository::
205
209
206 $ ls
210 $ ls
207 bar.c foo.c
211 bar.c foo.c
208 $ hg status
212 $ hg status
209 ! foobar.c
213 ! foobar.c
210 ? bar.c
214 ? bar.c
211 ? foo.c
215 ? foo.c
212 $ hg addremove
216 $ hg addremove
213 adding bar.c
217 adding bar.c
214 adding foo.c
218 adding foo.c
215 removing foobar.c
219 removing foobar.c
216 $ hg status
220 $ hg status
217 A bar.c
221 A bar.c
218 A foo.c
222 A foo.c
219 R foobar.c
223 R foobar.c
220
224
221 - A file foobar.c was moved to foo.c without using :hg:`rename`.
225 - A file foobar.c was moved to foo.c without using :hg:`rename`.
222 Afterwards, it was edited slightly::
226 Afterwards, it was edited slightly::
223
227
224 $ ls
228 $ ls
225 foo.c
229 foo.c
226 $ hg status
230 $ hg status
227 ! foobar.c
231 ! foobar.c
228 ? foo.c
232 ? foo.c
229 $ hg addremove --similarity 90
233 $ hg addremove --similarity 90
230 removing foobar.c
234 removing foobar.c
231 adding foo.c
235 adding foo.c
232 recording removal of foobar.c as rename to foo.c (94% similar)
236 recording removal of foobar.c as rename to foo.c (94% similar)
233 $ hg status -C
237 $ hg status -C
234 A foo.c
238 A foo.c
235 foobar.c
239 foobar.c
236 R foobar.c
240 R foobar.c
237
241
238 Returns 0 if all files are successfully added.
242 Returns 0 if all files are successfully added.
239 """
243 """
240 opts = pycompat.byteskwargs(opts)
244 opts = pycompat.byteskwargs(opts)
241 try:
245 try:
242 sim = float(opts.get('similarity') or 100)
246 sim = float(opts.get('similarity') or 100)
243 except ValueError:
247 except ValueError:
244 raise error.Abort(_('similarity must be a number'))
248 raise error.Abort(_('similarity must be a number'))
245 if sim < 0 or sim > 100:
249 if sim < 0 or sim > 100:
246 raise error.Abort(_('similarity must be between 0 and 100'))
250 raise error.Abort(_('similarity must be between 0 and 100'))
247 matcher = scmutil.match(repo[None], pats, opts)
251 matcher = scmutil.match(repo[None], pats, opts)
248 return scmutil.addremove(repo, matcher, "", opts, similarity=sim / 100.0)
252 return scmutil.addremove(repo, matcher, "", opts, similarity=sim / 100.0)
249
253
250 @command('^annotate|blame',
254 @command('^annotate|blame',
251 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
255 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
252 ('', 'follow', None,
256 ('', 'follow', None,
253 _('follow copies/renames and list the filename (DEPRECATED)')),
257 _('follow copies/renames and list the filename (DEPRECATED)')),
254 ('', 'no-follow', None, _("don't follow copies and renames")),
258 ('', 'no-follow', None, _("don't follow copies and renames")),
255 ('a', 'text', None, _('treat all files as text')),
259 ('a', 'text', None, _('treat all files as text')),
256 ('u', 'user', None, _('list the author (long with -v)')),
260 ('u', 'user', None, _('list the author (long with -v)')),
257 ('f', 'file', None, _('list the filename')),
261 ('f', 'file', None, _('list the filename')),
258 ('d', 'date', None, _('list the date (short with -q)')),
262 ('d', 'date', None, _('list the date (short with -q)')),
259 ('n', 'number', None, _('list the revision number (default)')),
263 ('n', 'number', None, _('list the revision number (default)')),
260 ('c', 'changeset', None, _('list the changeset')),
264 ('c', 'changeset', None, _('list the changeset')),
261 ('l', 'line-number', None, _('show line number at the first appearance')),
265 ('l', 'line-number', None, _('show line number at the first appearance')),
262 ('', 'skip', [], _('revision to not display (EXPERIMENTAL)'), _('REV')),
266 ('', 'skip', [], _('revision to not display (EXPERIMENTAL)'), _('REV')),
263 ] + diffwsopts + walkopts + formatteropts,
267 ] + diffwsopts + walkopts + formatteropts,
264 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
268 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
265 inferrepo=True)
269 inferrepo=True)
266 def annotate(ui, repo, *pats, **opts):
270 def annotate(ui, repo, *pats, **opts):
267 """show changeset information by line for each file
271 """show changeset information by line for each file
268
272
269 List changes in files, showing the revision id responsible for
273 List changes in files, showing the revision id responsible for
270 each line.
274 each line.
271
275
272 This command is useful for discovering when a change was made and
276 This command is useful for discovering when a change was made and
273 by whom.
277 by whom.
274
278
275 If you include --file, --user, or --date, the revision number is
279 If you include --file, --user, or --date, the revision number is
276 suppressed unless you also include --number.
280 suppressed unless you also include --number.
277
281
278 Without the -a/--text option, annotate will avoid processing files
282 Without the -a/--text option, annotate will avoid processing files
279 it detects as binary. With -a, annotate will annotate the file
283 it detects as binary. With -a, annotate will annotate the file
280 anyway, although the results will probably be neither useful
284 anyway, although the results will probably be neither useful
281 nor desirable.
285 nor desirable.
282
286
283 Returns 0 on success.
287 Returns 0 on success.
284 """
288 """
285 opts = pycompat.byteskwargs(opts)
289 opts = pycompat.byteskwargs(opts)
286 if not pats:
290 if not pats:
287 raise error.Abort(_('at least one filename or pattern is required'))
291 raise error.Abort(_('at least one filename or pattern is required'))
288
292
289 if opts.get('follow'):
293 if opts.get('follow'):
290 # --follow is deprecated and now just an alias for -f/--file
294 # --follow is deprecated and now just an alias for -f/--file
291 # to mimic the behavior of Mercurial before version 1.5
295 # to mimic the behavior of Mercurial before version 1.5
292 opts['file'] = True
296 opts['file'] = True
293
297
294 ctx = scmutil.revsingle(repo, opts.get('rev'))
298 ctx = scmutil.revsingle(repo, opts.get('rev'))
295
299
296 rootfm = ui.formatter('annotate', opts)
300 rootfm = ui.formatter('annotate', opts)
297 if ui.quiet:
301 if ui.quiet:
298 datefunc = util.shortdate
302 datefunc = util.shortdate
299 else:
303 else:
300 datefunc = util.datestr
304 datefunc = util.datestr
301 if ctx.rev() is None:
305 if ctx.rev() is None:
302 def hexfn(node):
306 def hexfn(node):
303 if node is None:
307 if node is None:
304 return None
308 return None
305 else:
309 else:
306 return rootfm.hexfunc(node)
310 return rootfm.hexfunc(node)
307 if opts.get('changeset'):
311 if opts.get('changeset'):
308 # omit "+" suffix which is appended to node hex
312 # omit "+" suffix which is appended to node hex
309 def formatrev(rev):
313 def formatrev(rev):
310 if rev is None:
314 if rev is None:
311 return '%d' % ctx.p1().rev()
315 return '%d' % ctx.p1().rev()
312 else:
316 else:
313 return '%d' % rev
317 return '%d' % rev
314 else:
318 else:
315 def formatrev(rev):
319 def formatrev(rev):
316 if rev is None:
320 if rev is None:
317 return '%d+' % ctx.p1().rev()
321 return '%d+' % ctx.p1().rev()
318 else:
322 else:
319 return '%d ' % rev
323 return '%d ' % rev
320 def formathex(hex):
324 def formathex(hex):
321 if hex is None:
325 if hex is None:
322 return '%s+' % rootfm.hexfunc(ctx.p1().node())
326 return '%s+' % rootfm.hexfunc(ctx.p1().node())
323 else:
327 else:
324 return '%s ' % hex
328 return '%s ' % hex
325 else:
329 else:
326 hexfn = rootfm.hexfunc
330 hexfn = rootfm.hexfunc
327 formatrev = formathex = pycompat.bytestr
331 formatrev = formathex = pycompat.bytestr
328
332
329 opmap = [('user', ' ', lambda x: x.fctx.user(), ui.shortuser),
333 opmap = [('user', ' ', lambda x: x.fctx.user(), ui.shortuser),
330 ('number', ' ', lambda x: x.fctx.rev(), formatrev),
334 ('number', ' ', lambda x: x.fctx.rev(), formatrev),
331 ('changeset', ' ', lambda x: hexfn(x.fctx.node()), formathex),
335 ('changeset', ' ', lambda x: hexfn(x.fctx.node()), formathex),
332 ('date', ' ', lambda x: x.fctx.date(), util.cachefunc(datefunc)),
336 ('date', ' ', lambda x: x.fctx.date(), util.cachefunc(datefunc)),
333 ('file', ' ', lambda x: x.fctx.path(), str),
337 ('file', ' ', lambda x: x.fctx.path(), str),
334 ('line_number', ':', lambda x: x.lineno, str),
338 ('line_number', ':', lambda x: x.lineno, str),
335 ]
339 ]
336 fieldnamemap = {'number': 'rev', 'changeset': 'node'}
340 fieldnamemap = {'number': 'rev', 'changeset': 'node'}
337
341
338 if (not opts.get('user') and not opts.get('changeset')
342 if (not opts.get('user') and not opts.get('changeset')
339 and not opts.get('date') and not opts.get('file')):
343 and not opts.get('date') and not opts.get('file')):
340 opts['number'] = True
344 opts['number'] = True
341
345
342 linenumber = opts.get('line_number') is not None
346 linenumber = opts.get('line_number') is not None
343 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
347 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
344 raise error.Abort(_('at least one of -n/-c is required for -l'))
348 raise error.Abort(_('at least one of -n/-c is required for -l'))
345
349
346 ui.pager('annotate')
350 ui.pager('annotate')
347
351
348 if rootfm.isplain():
352 if rootfm.isplain():
349 def makefunc(get, fmt):
353 def makefunc(get, fmt):
350 return lambda x: fmt(get(x))
354 return lambda x: fmt(get(x))
351 else:
355 else:
352 def makefunc(get, fmt):
356 def makefunc(get, fmt):
353 return get
357 return get
354 funcmap = [(makefunc(get, fmt), sep) for op, sep, get, fmt in opmap
358 funcmap = [(makefunc(get, fmt), sep) for op, sep, get, fmt in opmap
355 if opts.get(op)]
359 if opts.get(op)]
356 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
360 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
357 fields = ' '.join(fieldnamemap.get(op, op) for op, sep, get, fmt in opmap
361 fields = ' '.join(fieldnamemap.get(op, op) for op, sep, get, fmt in opmap
358 if opts.get(op))
362 if opts.get(op))
359
363
360 def bad(x, y):
364 def bad(x, y):
361 raise error.Abort("%s: %s" % (x, y))
365 raise error.Abort("%s: %s" % (x, y))
362
366
363 m = scmutil.match(ctx, pats, opts, badfn=bad)
367 m = scmutil.match(ctx, pats, opts, badfn=bad)
364
368
365 follow = not opts.get('no_follow')
369 follow = not opts.get('no_follow')
366 diffopts = patch.difffeatureopts(ui, opts, section='annotate',
370 diffopts = patch.difffeatureopts(ui, opts, section='annotate',
367 whitespace=True)
371 whitespace=True)
368 skiprevs = opts.get('skip')
372 skiprevs = opts.get('skip')
369 if skiprevs:
373 if skiprevs:
370 skiprevs = scmutil.revrange(repo, skiprevs)
374 skiprevs = scmutil.revrange(repo, skiprevs)
371
375
372 for abs in ctx.walk(m):
376 for abs in ctx.walk(m):
373 fctx = ctx[abs]
377 fctx = ctx[abs]
374 rootfm.startitem()
378 rootfm.startitem()
375 rootfm.data(abspath=abs, path=m.rel(abs))
379 rootfm.data(abspath=abs, path=m.rel(abs))
376 if not opts.get('text') and fctx.isbinary():
380 if not opts.get('text') and fctx.isbinary():
377 rootfm.plain(_("%s: binary file\n")
381 rootfm.plain(_("%s: binary file\n")
378 % ((pats and m.rel(abs)) or abs))
382 % ((pats and m.rel(abs)) or abs))
379 continue
383 continue
380
384
381 fm = rootfm.nested('lines')
385 fm = rootfm.nested('lines')
382 lines = fctx.annotate(follow=follow, linenumber=linenumber,
386 lines = fctx.annotate(follow=follow, linenumber=linenumber,
383 skiprevs=skiprevs, diffopts=diffopts)
387 skiprevs=skiprevs, diffopts=diffopts)
384 if not lines:
388 if not lines:
385 fm.end()
389 fm.end()
386 continue
390 continue
387 formats = []
391 formats = []
388 pieces = []
392 pieces = []
389
393
390 for f, sep in funcmap:
394 for f, sep in funcmap:
391 l = [f(n) for n, dummy in lines]
395 l = [f(n) for n, dummy in lines]
392 if fm.isplain():
396 if fm.isplain():
393 sizes = [encoding.colwidth(x) for x in l]
397 sizes = [encoding.colwidth(x) for x in l]
394 ml = max(sizes)
398 ml = max(sizes)
395 formats.append([sep + ' ' * (ml - w) + '%s' for w in sizes])
399 formats.append([sep + ' ' * (ml - w) + '%s' for w in sizes])
396 else:
400 else:
397 formats.append(['%s' for x in l])
401 formats.append(['%s' for x in l])
398 pieces.append(l)
402 pieces.append(l)
399
403
400 for f, p, l in zip(zip(*formats), zip(*pieces), lines):
404 for f, p, l in zip(zip(*formats), zip(*pieces), lines):
401 fm.startitem()
405 fm.startitem()
402 fm.write(fields, "".join(f), *p)
406 fm.write(fields, "".join(f), *p)
403 if l[0].skip:
407 if l[0].skip:
404 fmt = "* %s"
408 fmt = "* %s"
405 else:
409 else:
406 fmt = ": %s"
410 fmt = ": %s"
407 fm.write('line', fmt, l[1])
411 fm.write('line', fmt, l[1])
408
412
409 if not lines[-1][1].endswith('\n'):
413 if not lines[-1][1].endswith('\n'):
410 fm.plain('\n')
414 fm.plain('\n')
411 fm.end()
415 fm.end()
412
416
413 rootfm.end()
417 rootfm.end()
414
418
415 @command('archive',
419 @command('archive',
416 [('', 'no-decode', None, _('do not pass files through decoders')),
420 [('', 'no-decode', None, _('do not pass files through decoders')),
417 ('p', 'prefix', '', _('directory prefix for files in archive'),
421 ('p', 'prefix', '', _('directory prefix for files in archive'),
418 _('PREFIX')),
422 _('PREFIX')),
419 ('r', 'rev', '', _('revision to distribute'), _('REV')),
423 ('r', 'rev', '', _('revision to distribute'), _('REV')),
420 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
424 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
421 ] + subrepoopts + walkopts,
425 ] + subrepoopts + walkopts,
422 _('[OPTION]... DEST'))
426 _('[OPTION]... DEST'))
423 def archive(ui, repo, dest, **opts):
427 def archive(ui, repo, dest, **opts):
424 '''create an unversioned archive of a repository revision
428 '''create an unversioned archive of a repository revision
425
429
426 By default, the revision used is the parent of the working
430 By default, the revision used is the parent of the working
427 directory; use -r/--rev to specify a different revision.
431 directory; use -r/--rev to specify a different revision.
428
432
429 The archive type is automatically detected based on file
433 The archive type is automatically detected based on file
430 extension (to override, use -t/--type).
434 extension (to override, use -t/--type).
431
435
432 .. container:: verbose
436 .. container:: verbose
433
437
434 Examples:
438 Examples:
435
439
436 - create a zip file containing the 1.0 release::
440 - create a zip file containing the 1.0 release::
437
441
438 hg archive -r 1.0 project-1.0.zip
442 hg archive -r 1.0 project-1.0.zip
439
443
440 - create a tarball excluding .hg files::
444 - create a tarball excluding .hg files::
441
445
442 hg archive project.tar.gz -X ".hg*"
446 hg archive project.tar.gz -X ".hg*"
443
447
444 Valid types are:
448 Valid types are:
445
449
446 :``files``: a directory full of files (default)
450 :``files``: a directory full of files (default)
447 :``tar``: tar archive, uncompressed
451 :``tar``: tar archive, uncompressed
448 :``tbz2``: tar archive, compressed using bzip2
452 :``tbz2``: tar archive, compressed using bzip2
449 :``tgz``: tar archive, compressed using gzip
453 :``tgz``: tar archive, compressed using gzip
450 :``uzip``: zip archive, uncompressed
454 :``uzip``: zip archive, uncompressed
451 :``zip``: zip archive, compressed using deflate
455 :``zip``: zip archive, compressed using deflate
452
456
453 The exact name of the destination archive or directory is given
457 The exact name of the destination archive or directory is given
454 using a format string; see :hg:`help export` for details.
458 using a format string; see :hg:`help export` for details.
455
459
456 Each member added to an archive file has a directory prefix
460 Each member added to an archive file has a directory prefix
457 prepended. Use -p/--prefix to specify a format string for the
461 prepended. Use -p/--prefix to specify a format string for the
458 prefix. The default is the basename of the archive, with suffixes
462 prefix. The default is the basename of the archive, with suffixes
459 removed.
463 removed.
460
464
461 Returns 0 on success.
465 Returns 0 on success.
462 '''
466 '''
463
467
464 opts = pycompat.byteskwargs(opts)
468 opts = pycompat.byteskwargs(opts)
465 ctx = scmutil.revsingle(repo, opts.get('rev'))
469 ctx = scmutil.revsingle(repo, opts.get('rev'))
466 if not ctx:
470 if not ctx:
467 raise error.Abort(_('no working directory: please specify a revision'))
471 raise error.Abort(_('no working directory: please specify a revision'))
468 node = ctx.node()
472 node = ctx.node()
469 dest = cmdutil.makefilename(repo, dest, node)
473 dest = cmdutil.makefilename(repo, dest, node)
470 if os.path.realpath(dest) == repo.root:
474 if os.path.realpath(dest) == repo.root:
471 raise error.Abort(_('repository root cannot be destination'))
475 raise error.Abort(_('repository root cannot be destination'))
472
476
473 kind = opts.get('type') or archival.guesskind(dest) or 'files'
477 kind = opts.get('type') or archival.guesskind(dest) or 'files'
474 prefix = opts.get('prefix')
478 prefix = opts.get('prefix')
475
479
476 if dest == '-':
480 if dest == '-':
477 if kind == 'files':
481 if kind == 'files':
478 raise error.Abort(_('cannot archive plain files to stdout'))
482 raise error.Abort(_('cannot archive plain files to stdout'))
479 dest = cmdutil.makefileobj(repo, dest)
483 dest = cmdutil.makefileobj(repo, dest)
480 if not prefix:
484 if not prefix:
481 prefix = os.path.basename(repo.root) + '-%h'
485 prefix = os.path.basename(repo.root) + '-%h'
482
486
483 prefix = cmdutil.makefilename(repo, prefix, node)
487 prefix = cmdutil.makefilename(repo, prefix, node)
484 match = scmutil.match(ctx, [], opts)
488 match = scmutil.match(ctx, [], opts)
485 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
489 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
486 match, prefix, subrepos=opts.get('subrepos'))
490 match, prefix, subrepos=opts.get('subrepos'))
487
491
488 @command('backout',
492 @command('backout',
489 [('', 'merge', None, _('merge with old dirstate parent after backout')),
493 [('', 'merge', None, _('merge with old dirstate parent after backout')),
490 ('', 'commit', None,
494 ('', 'commit', None,
491 _('commit if no conflicts were encountered (DEPRECATED)')),
495 _('commit if no conflicts were encountered (DEPRECATED)')),
492 ('', 'no-commit', None, _('do not commit')),
496 ('', 'no-commit', None, _('do not commit')),
493 ('', 'parent', '',
497 ('', 'parent', '',
494 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
498 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
495 ('r', 'rev', '', _('revision to backout'), _('REV')),
499 ('r', 'rev', '', _('revision to backout'), _('REV')),
496 ('e', 'edit', False, _('invoke editor on commit messages')),
500 ('e', 'edit', False, _('invoke editor on commit messages')),
497 ] + mergetoolopts + walkopts + commitopts + commitopts2,
501 ] + mergetoolopts + walkopts + commitopts + commitopts2,
498 _('[OPTION]... [-r] REV'))
502 _('[OPTION]... [-r] REV'))
499 def backout(ui, repo, node=None, rev=None, **opts):
503 def backout(ui, repo, node=None, rev=None, **opts):
500 '''reverse effect of earlier changeset
504 '''reverse effect of earlier changeset
501
505
502 Prepare a new changeset with the effect of REV undone in the
506 Prepare a new changeset with the effect of REV undone in the
503 current working directory. If no conflicts were encountered,
507 current working directory. If no conflicts were encountered,
504 it will be committed immediately.
508 it will be committed immediately.
505
509
506 If REV is the parent of the working directory, then this new changeset
510 If REV is the parent of the working directory, then this new changeset
507 is committed automatically (unless --no-commit is specified).
511 is committed automatically (unless --no-commit is specified).
508
512
509 .. note::
513 .. note::
510
514
511 :hg:`backout` cannot be used to fix either an unwanted or
515 :hg:`backout` cannot be used to fix either an unwanted or
512 incorrect merge.
516 incorrect merge.
513
517
514 .. container:: verbose
518 .. container:: verbose
515
519
516 Examples:
520 Examples:
517
521
518 - Reverse the effect of the parent of the working directory.
522 - Reverse the effect of the parent of the working directory.
519 This backout will be committed immediately::
523 This backout will be committed immediately::
520
524
521 hg backout -r .
525 hg backout -r .
522
526
523 - Reverse the effect of previous bad revision 23::
527 - Reverse the effect of previous bad revision 23::
524
528
525 hg backout -r 23
529 hg backout -r 23
526
530
527 - Reverse the effect of previous bad revision 23 and
531 - Reverse the effect of previous bad revision 23 and
528 leave changes uncommitted::
532 leave changes uncommitted::
529
533
530 hg backout -r 23 --no-commit
534 hg backout -r 23 --no-commit
531 hg commit -m "Backout revision 23"
535 hg commit -m "Backout revision 23"
532
536
533 By default, the pending changeset will have one parent,
537 By default, the pending changeset will have one parent,
534 maintaining a linear history. With --merge, the pending
538 maintaining a linear history. With --merge, the pending
535 changeset will instead have two parents: the old parent of the
539 changeset will instead have two parents: the old parent of the
536 working directory and a new child of REV that simply undoes REV.
540 working directory and a new child of REV that simply undoes REV.
537
541
538 Before version 1.7, the behavior without --merge was equivalent
542 Before version 1.7, the behavior without --merge was equivalent
539 to specifying --merge followed by :hg:`update --clean .` to
543 to specifying --merge followed by :hg:`update --clean .` to
540 cancel the merge and leave the child of REV as a head to be
544 cancel the merge and leave the child of REV as a head to be
541 merged separately.
545 merged separately.
542
546
543 See :hg:`help dates` for a list of formats valid for -d/--date.
547 See :hg:`help dates` for a list of formats valid for -d/--date.
544
548
545 See :hg:`help revert` for a way to restore files to the state
549 See :hg:`help revert` for a way to restore files to the state
546 of another revision.
550 of another revision.
547
551
548 Returns 0 on success, 1 if nothing to backout or there are unresolved
552 Returns 0 on success, 1 if nothing to backout or there are unresolved
549 files.
553 files.
550 '''
554 '''
551 wlock = lock = None
555 wlock = lock = None
552 try:
556 try:
553 wlock = repo.wlock()
557 wlock = repo.wlock()
554 lock = repo.lock()
558 lock = repo.lock()
555 return _dobackout(ui, repo, node, rev, **opts)
559 return _dobackout(ui, repo, node, rev, **opts)
556 finally:
560 finally:
557 release(lock, wlock)
561 release(lock, wlock)
558
562
559 def _dobackout(ui, repo, node=None, rev=None, **opts):
563 def _dobackout(ui, repo, node=None, rev=None, **opts):
560 opts = pycompat.byteskwargs(opts)
564 opts = pycompat.byteskwargs(opts)
561 if opts.get('commit') and opts.get('no_commit'):
565 if opts.get('commit') and opts.get('no_commit'):
562 raise error.Abort(_("cannot use --commit with --no-commit"))
566 raise error.Abort(_("cannot use --commit with --no-commit"))
563 if opts.get('merge') and opts.get('no_commit'):
567 if opts.get('merge') and opts.get('no_commit'):
564 raise error.Abort(_("cannot use --merge with --no-commit"))
568 raise error.Abort(_("cannot use --merge with --no-commit"))
565
569
566 if rev and node:
570 if rev and node:
567 raise error.Abort(_("please specify just one revision"))
571 raise error.Abort(_("please specify just one revision"))
568
572
569 if not rev:
573 if not rev:
570 rev = node
574 rev = node
571
575
572 if not rev:
576 if not rev:
573 raise error.Abort(_("please specify a revision to backout"))
577 raise error.Abort(_("please specify a revision to backout"))
574
578
575 date = opts.get('date')
579 date = opts.get('date')
576 if date:
580 if date:
577 opts['date'] = util.parsedate(date)
581 opts['date'] = util.parsedate(date)
578
582
579 cmdutil.checkunfinished(repo)
583 cmdutil.checkunfinished(repo)
580 cmdutil.bailifchanged(repo)
584 cmdutil.bailifchanged(repo)
581 node = scmutil.revsingle(repo, rev).node()
585 node = scmutil.revsingle(repo, rev).node()
582
586
583 op1, op2 = repo.dirstate.parents()
587 op1, op2 = repo.dirstate.parents()
584 if not repo.changelog.isancestor(node, op1):
588 if not repo.changelog.isancestor(node, op1):
585 raise error.Abort(_('cannot backout change that is not an ancestor'))
589 raise error.Abort(_('cannot backout change that is not an ancestor'))
586
590
587 p1, p2 = repo.changelog.parents(node)
591 p1, p2 = repo.changelog.parents(node)
588 if p1 == nullid:
592 if p1 == nullid:
589 raise error.Abort(_('cannot backout a change with no parents'))
593 raise error.Abort(_('cannot backout a change with no parents'))
590 if p2 != nullid:
594 if p2 != nullid:
591 if not opts.get('parent'):
595 if not opts.get('parent'):
592 raise error.Abort(_('cannot backout a merge changeset'))
596 raise error.Abort(_('cannot backout a merge changeset'))
593 p = repo.lookup(opts['parent'])
597 p = repo.lookup(opts['parent'])
594 if p not in (p1, p2):
598 if p not in (p1, p2):
595 raise error.Abort(_('%s is not a parent of %s') %
599 raise error.Abort(_('%s is not a parent of %s') %
596 (short(p), short(node)))
600 (short(p), short(node)))
597 parent = p
601 parent = p
598 else:
602 else:
599 if opts.get('parent'):
603 if opts.get('parent'):
600 raise error.Abort(_('cannot use --parent on non-merge changeset'))
604 raise error.Abort(_('cannot use --parent on non-merge changeset'))
601 parent = p1
605 parent = p1
602
606
603 # the backout should appear on the same branch
607 # the backout should appear on the same branch
604 branch = repo.dirstate.branch()
608 branch = repo.dirstate.branch()
605 bheads = repo.branchheads(branch)
609 bheads = repo.branchheads(branch)
606 rctx = scmutil.revsingle(repo, hex(parent))
610 rctx = scmutil.revsingle(repo, hex(parent))
607 if not opts.get('merge') and op1 != node:
611 if not opts.get('merge') and op1 != node:
608 dsguard = dirstateguard.dirstateguard(repo, 'backout')
612 dsguard = dirstateguard.dirstateguard(repo, 'backout')
609 try:
613 try:
610 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
614 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
611 'backout')
615 'backout')
612 stats = mergemod.update(repo, parent, True, True, node, False)
616 stats = mergemod.update(repo, parent, True, True, node, False)
613 repo.setparents(op1, op2)
617 repo.setparents(op1, op2)
614 dsguard.close()
618 dsguard.close()
615 hg._showstats(repo, stats)
619 hg._showstats(repo, stats)
616 if stats[3]:
620 if stats[3]:
617 repo.ui.status(_("use 'hg resolve' to retry unresolved "
621 repo.ui.status(_("use 'hg resolve' to retry unresolved "
618 "file merges\n"))
622 "file merges\n"))
619 return 1
623 return 1
620 finally:
624 finally:
621 ui.setconfig('ui', 'forcemerge', '', '')
625 ui.setconfig('ui', 'forcemerge', '', '')
622 lockmod.release(dsguard)
626 lockmod.release(dsguard)
623 else:
627 else:
624 hg.clean(repo, node, show_stats=False)
628 hg.clean(repo, node, show_stats=False)
625 repo.dirstate.setbranch(branch)
629 repo.dirstate.setbranch(branch)
626 cmdutil.revert(ui, repo, rctx, repo.dirstate.parents())
630 cmdutil.revert(ui, repo, rctx, repo.dirstate.parents())
627
631
628 if opts.get('no_commit'):
632 if opts.get('no_commit'):
629 msg = _("changeset %s backed out, "
633 msg = _("changeset %s backed out, "
630 "don't forget to commit.\n")
634 "don't forget to commit.\n")
631 ui.status(msg % short(node))
635 ui.status(msg % short(node))
632 return 0
636 return 0
633
637
634 def commitfunc(ui, repo, message, match, opts):
638 def commitfunc(ui, repo, message, match, opts):
635 editform = 'backout'
639 editform = 'backout'
636 e = cmdutil.getcommiteditor(editform=editform,
640 e = cmdutil.getcommiteditor(editform=editform,
637 **pycompat.strkwargs(opts))
641 **pycompat.strkwargs(opts))
638 if not message:
642 if not message:
639 # we don't translate commit messages
643 # we don't translate commit messages
640 message = "Backed out changeset %s" % short(node)
644 message = "Backed out changeset %s" % short(node)
641 e = cmdutil.getcommiteditor(edit=True, editform=editform)
645 e = cmdutil.getcommiteditor(edit=True, editform=editform)
642 return repo.commit(message, opts.get('user'), opts.get('date'),
646 return repo.commit(message, opts.get('user'), opts.get('date'),
643 match, editor=e)
647 match, editor=e)
644 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
648 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
645 if not newnode:
649 if not newnode:
646 ui.status(_("nothing changed\n"))
650 ui.status(_("nothing changed\n"))
647 return 1
651 return 1
648 cmdutil.commitstatus(repo, newnode, branch, bheads)
652 cmdutil.commitstatus(repo, newnode, branch, bheads)
649
653
650 def nice(node):
654 def nice(node):
651 return '%d:%s' % (repo.changelog.rev(node), short(node))
655 return '%d:%s' % (repo.changelog.rev(node), short(node))
652 ui.status(_('changeset %s backs out changeset %s\n') %
656 ui.status(_('changeset %s backs out changeset %s\n') %
653 (nice(repo.changelog.tip()), nice(node)))
657 (nice(repo.changelog.tip()), nice(node)))
654 if opts.get('merge') and op1 != node:
658 if opts.get('merge') and op1 != node:
655 hg.clean(repo, op1, show_stats=False)
659 hg.clean(repo, op1, show_stats=False)
656 ui.status(_('merging with changeset %s\n')
660 ui.status(_('merging with changeset %s\n')
657 % nice(repo.changelog.tip()))
661 % nice(repo.changelog.tip()))
658 try:
662 try:
659 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
663 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
660 'backout')
664 'backout')
661 return hg.merge(repo, hex(repo.changelog.tip()))
665 return hg.merge(repo, hex(repo.changelog.tip()))
662 finally:
666 finally:
663 ui.setconfig('ui', 'forcemerge', '', '')
667 ui.setconfig('ui', 'forcemerge', '', '')
664 return 0
668 return 0
665
669
666 @command('bisect',
670 @command('bisect',
667 [('r', 'reset', False, _('reset bisect state')),
671 [('r', 'reset', False, _('reset bisect state')),
668 ('g', 'good', False, _('mark changeset good')),
672 ('g', 'good', False, _('mark changeset good')),
669 ('b', 'bad', False, _('mark changeset bad')),
673 ('b', 'bad', False, _('mark changeset bad')),
670 ('s', 'skip', False, _('skip testing changeset')),
674 ('s', 'skip', False, _('skip testing changeset')),
671 ('e', 'extend', False, _('extend the bisect range')),
675 ('e', 'extend', False, _('extend the bisect range')),
672 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
676 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
673 ('U', 'noupdate', False, _('do not update to target'))],
677 ('U', 'noupdate', False, _('do not update to target'))],
674 _("[-gbsr] [-U] [-c CMD] [REV]"))
678 _("[-gbsr] [-U] [-c CMD] [REV]"))
675 def bisect(ui, repo, rev=None, extra=None, command=None,
679 def bisect(ui, repo, rev=None, extra=None, command=None,
676 reset=None, good=None, bad=None, skip=None, extend=None,
680 reset=None, good=None, bad=None, skip=None, extend=None,
677 noupdate=None):
681 noupdate=None):
678 """subdivision search of changesets
682 """subdivision search of changesets
679
683
680 This command helps to find changesets which introduce problems. To
684 This command helps to find changesets which introduce problems. To
681 use, mark the earliest changeset you know exhibits the problem as
685 use, mark the earliest changeset you know exhibits the problem as
682 bad, then mark the latest changeset which is free from the problem
686 bad, then mark the latest changeset which is free from the problem
683 as good. Bisect will update your working directory to a revision
687 as good. Bisect will update your working directory to a revision
684 for testing (unless the -U/--noupdate option is specified). Once
688 for testing (unless the -U/--noupdate option is specified). Once
685 you have performed tests, mark the working directory as good or
689 you have performed tests, mark the working directory as good or
686 bad, and bisect will either update to another candidate changeset
690 bad, and bisect will either update to another candidate changeset
687 or announce that it has found the bad revision.
691 or announce that it has found the bad revision.
688
692
689 As a shortcut, you can also use the revision argument to mark a
693 As a shortcut, you can also use the revision argument to mark a
690 revision as good or bad without checking it out first.
694 revision as good or bad without checking it out first.
691
695
692 If you supply a command, it will be used for automatic bisection.
696 If you supply a command, it will be used for automatic bisection.
693 The environment variable HG_NODE will contain the ID of the
697 The environment variable HG_NODE will contain the ID of the
694 changeset being tested. The exit status of the command will be
698 changeset being tested. The exit status of the command will be
695 used to mark revisions as good or bad: status 0 means good, 125
699 used to mark revisions as good or bad: status 0 means good, 125
696 means to skip the revision, 127 (command not found) will abort the
700 means to skip the revision, 127 (command not found) will abort the
697 bisection, and any other non-zero exit status means the revision
701 bisection, and any other non-zero exit status means the revision
698 is bad.
702 is bad.
699
703
700 .. container:: verbose
704 .. container:: verbose
701
705
702 Some examples:
706 Some examples:
703
707
704 - start a bisection with known bad revision 34, and good revision 12::
708 - start a bisection with known bad revision 34, and good revision 12::
705
709
706 hg bisect --bad 34
710 hg bisect --bad 34
707 hg bisect --good 12
711 hg bisect --good 12
708
712
709 - advance the current bisection by marking current revision as good or
713 - advance the current bisection by marking current revision as good or
710 bad::
714 bad::
711
715
712 hg bisect --good
716 hg bisect --good
713 hg bisect --bad
717 hg bisect --bad
714
718
715 - mark the current revision, or a known revision, to be skipped (e.g. if
719 - mark the current revision, or a known revision, to be skipped (e.g. if
716 that revision is not usable because of another issue)::
720 that revision is not usable because of another issue)::
717
721
718 hg bisect --skip
722 hg bisect --skip
719 hg bisect --skip 23
723 hg bisect --skip 23
720
724
721 - skip all revisions that do not touch directories ``foo`` or ``bar``::
725 - skip all revisions that do not touch directories ``foo`` or ``bar``::
722
726
723 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
727 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
724
728
725 - forget the current bisection::
729 - forget the current bisection::
726
730
727 hg bisect --reset
731 hg bisect --reset
728
732
729 - use 'make && make tests' to automatically find the first broken
733 - use 'make && make tests' to automatically find the first broken
730 revision::
734 revision::
731
735
732 hg bisect --reset
736 hg bisect --reset
733 hg bisect --bad 34
737 hg bisect --bad 34
734 hg bisect --good 12
738 hg bisect --good 12
735 hg bisect --command "make && make tests"
739 hg bisect --command "make && make tests"
736
740
737 - see all changesets whose states are already known in the current
741 - see all changesets whose states are already known in the current
738 bisection::
742 bisection::
739
743
740 hg log -r "bisect(pruned)"
744 hg log -r "bisect(pruned)"
741
745
742 - see the changeset currently being bisected (especially useful
746 - see the changeset currently being bisected (especially useful
743 if running with -U/--noupdate)::
747 if running with -U/--noupdate)::
744
748
745 hg log -r "bisect(current)"
749 hg log -r "bisect(current)"
746
750
747 - see all changesets that took part in the current bisection::
751 - see all changesets that took part in the current bisection::
748
752
749 hg log -r "bisect(range)"
753 hg log -r "bisect(range)"
750
754
751 - you can even get a nice graph::
755 - you can even get a nice graph::
752
756
753 hg log --graph -r "bisect(range)"
757 hg log --graph -r "bisect(range)"
754
758
755 See :hg:`help revisions.bisect` for more about the `bisect()` predicate.
759 See :hg:`help revisions.bisect` for more about the `bisect()` predicate.
756
760
757 Returns 0 on success.
761 Returns 0 on success.
758 """
762 """
759 # backward compatibility
763 # backward compatibility
760 if rev in "good bad reset init".split():
764 if rev in "good bad reset init".split():
761 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
765 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
762 cmd, rev, extra = rev, extra, None
766 cmd, rev, extra = rev, extra, None
763 if cmd == "good":
767 if cmd == "good":
764 good = True
768 good = True
765 elif cmd == "bad":
769 elif cmd == "bad":
766 bad = True
770 bad = True
767 else:
771 else:
768 reset = True
772 reset = True
769 elif extra:
773 elif extra:
770 raise error.Abort(_('incompatible arguments'))
774 raise error.Abort(_('incompatible arguments'))
771
775
772 incompatibles = {
776 incompatibles = {
773 '--bad': bad,
777 '--bad': bad,
774 '--command': bool(command),
778 '--command': bool(command),
775 '--extend': extend,
779 '--extend': extend,
776 '--good': good,
780 '--good': good,
777 '--reset': reset,
781 '--reset': reset,
778 '--skip': skip,
782 '--skip': skip,
779 }
783 }
780
784
781 enabled = [x for x in incompatibles if incompatibles[x]]
785 enabled = [x for x in incompatibles if incompatibles[x]]
782
786
783 if len(enabled) > 1:
787 if len(enabled) > 1:
784 raise error.Abort(_('%s and %s are incompatible') %
788 raise error.Abort(_('%s and %s are incompatible') %
785 tuple(sorted(enabled)[0:2]))
789 tuple(sorted(enabled)[0:2]))
786
790
787 if reset:
791 if reset:
788 hbisect.resetstate(repo)
792 hbisect.resetstate(repo)
789 return
793 return
790
794
791 state = hbisect.load_state(repo)
795 state = hbisect.load_state(repo)
792
796
793 # update state
797 # update state
794 if good or bad or skip:
798 if good or bad or skip:
795 if rev:
799 if rev:
796 nodes = [repo.lookup(i) for i in scmutil.revrange(repo, [rev])]
800 nodes = [repo.lookup(i) for i in scmutil.revrange(repo, [rev])]
797 else:
801 else:
798 nodes = [repo.lookup('.')]
802 nodes = [repo.lookup('.')]
799 if good:
803 if good:
800 state['good'] += nodes
804 state['good'] += nodes
801 elif bad:
805 elif bad:
802 state['bad'] += nodes
806 state['bad'] += nodes
803 elif skip:
807 elif skip:
804 state['skip'] += nodes
808 state['skip'] += nodes
805 hbisect.save_state(repo, state)
809 hbisect.save_state(repo, state)
806 if not (state['good'] and state['bad']):
810 if not (state['good'] and state['bad']):
807 return
811 return
808
812
809 def mayupdate(repo, node, show_stats=True):
813 def mayupdate(repo, node, show_stats=True):
810 """common used update sequence"""
814 """common used update sequence"""
811 if noupdate:
815 if noupdate:
812 return
816 return
813 cmdutil.checkunfinished(repo)
817 cmdutil.checkunfinished(repo)
814 cmdutil.bailifchanged(repo)
818 cmdutil.bailifchanged(repo)
815 return hg.clean(repo, node, show_stats=show_stats)
819 return hg.clean(repo, node, show_stats=show_stats)
816
820
817 displayer = cmdutil.show_changeset(ui, repo, {})
821 displayer = cmdutil.show_changeset(ui, repo, {})
818
822
819 if command:
823 if command:
820 changesets = 1
824 changesets = 1
821 if noupdate:
825 if noupdate:
822 try:
826 try:
823 node = state['current'][0]
827 node = state['current'][0]
824 except LookupError:
828 except LookupError:
825 raise error.Abort(_('current bisect revision is unknown - '
829 raise error.Abort(_('current bisect revision is unknown - '
826 'start a new bisect to fix'))
830 'start a new bisect to fix'))
827 else:
831 else:
828 node, p2 = repo.dirstate.parents()
832 node, p2 = repo.dirstate.parents()
829 if p2 != nullid:
833 if p2 != nullid:
830 raise error.Abort(_('current bisect revision is a merge'))
834 raise error.Abort(_('current bisect revision is a merge'))
831 if rev:
835 if rev:
832 node = repo[scmutil.revsingle(repo, rev, node)].node()
836 node = repo[scmutil.revsingle(repo, rev, node)].node()
833 try:
837 try:
834 while changesets:
838 while changesets:
835 # update state
839 # update state
836 state['current'] = [node]
840 state['current'] = [node]
837 hbisect.save_state(repo, state)
841 hbisect.save_state(repo, state)
838 status = ui.system(command, environ={'HG_NODE': hex(node)},
842 status = ui.system(command, environ={'HG_NODE': hex(node)},
839 blockedtag='bisect_check')
843 blockedtag='bisect_check')
840 if status == 125:
844 if status == 125:
841 transition = "skip"
845 transition = "skip"
842 elif status == 0:
846 elif status == 0:
843 transition = "good"
847 transition = "good"
844 # status < 0 means process was killed
848 # status < 0 means process was killed
845 elif status == 127:
849 elif status == 127:
846 raise error.Abort(_("failed to execute %s") % command)
850 raise error.Abort(_("failed to execute %s") % command)
847 elif status < 0:
851 elif status < 0:
848 raise error.Abort(_("%s killed") % command)
852 raise error.Abort(_("%s killed") % command)
849 else:
853 else:
850 transition = "bad"
854 transition = "bad"
851 state[transition].append(node)
855 state[transition].append(node)
852 ctx = repo[node]
856 ctx = repo[node]
853 ui.status(_('changeset %d:%s: %s\n') % (ctx, ctx, transition))
857 ui.status(_('changeset %d:%s: %s\n') % (ctx, ctx, transition))
854 hbisect.checkstate(state)
858 hbisect.checkstate(state)
855 # bisect
859 # bisect
856 nodes, changesets, bgood = hbisect.bisect(repo.changelog, state)
860 nodes, changesets, bgood = hbisect.bisect(repo.changelog, state)
857 # update to next check
861 # update to next check
858 node = nodes[0]
862 node = nodes[0]
859 mayupdate(repo, node, show_stats=False)
863 mayupdate(repo, node, show_stats=False)
860 finally:
864 finally:
861 state['current'] = [node]
865 state['current'] = [node]
862 hbisect.save_state(repo, state)
866 hbisect.save_state(repo, state)
863 hbisect.printresult(ui, repo, state, displayer, nodes, bgood)
867 hbisect.printresult(ui, repo, state, displayer, nodes, bgood)
864 return
868 return
865
869
866 hbisect.checkstate(state)
870 hbisect.checkstate(state)
867
871
868 # actually bisect
872 # actually bisect
869 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
873 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
870 if extend:
874 if extend:
871 if not changesets:
875 if not changesets:
872 extendnode = hbisect.extendrange(repo, state, nodes, good)
876 extendnode = hbisect.extendrange(repo, state, nodes, good)
873 if extendnode is not None:
877 if extendnode is not None:
874 ui.write(_("Extending search to changeset %d:%s\n")
878 ui.write(_("Extending search to changeset %d:%s\n")
875 % (extendnode.rev(), extendnode))
879 % (extendnode.rev(), extendnode))
876 state['current'] = [extendnode.node()]
880 state['current'] = [extendnode.node()]
877 hbisect.save_state(repo, state)
881 hbisect.save_state(repo, state)
878 return mayupdate(repo, extendnode.node())
882 return mayupdate(repo, extendnode.node())
879 raise error.Abort(_("nothing to extend"))
883 raise error.Abort(_("nothing to extend"))
880
884
881 if changesets == 0:
885 if changesets == 0:
882 hbisect.printresult(ui, repo, state, displayer, nodes, good)
886 hbisect.printresult(ui, repo, state, displayer, nodes, good)
883 else:
887 else:
884 assert len(nodes) == 1 # only a single node can be tested next
888 assert len(nodes) == 1 # only a single node can be tested next
885 node = nodes[0]
889 node = nodes[0]
886 # compute the approximate number of remaining tests
890 # compute the approximate number of remaining tests
887 tests, size = 0, 2
891 tests, size = 0, 2
888 while size <= changesets:
892 while size <= changesets:
889 tests, size = tests + 1, size * 2
893 tests, size = tests + 1, size * 2
890 rev = repo.changelog.rev(node)
894 rev = repo.changelog.rev(node)
891 ui.write(_("Testing changeset %d:%s "
895 ui.write(_("Testing changeset %d:%s "
892 "(%d changesets remaining, ~%d tests)\n")
896 "(%d changesets remaining, ~%d tests)\n")
893 % (rev, short(node), changesets, tests))
897 % (rev, short(node), changesets, tests))
894 state['current'] = [node]
898 state['current'] = [node]
895 hbisect.save_state(repo, state)
899 hbisect.save_state(repo, state)
896 return mayupdate(repo, node)
900 return mayupdate(repo, node)
897
901
898 @command('bookmarks|bookmark',
902 @command('bookmarks|bookmark',
899 [('f', 'force', False, _('force')),
903 [('f', 'force', False, _('force')),
900 ('r', 'rev', '', _('revision for bookmark action'), _('REV')),
904 ('r', 'rev', '', _('revision for bookmark action'), _('REV')),
901 ('d', 'delete', False, _('delete a given bookmark')),
905 ('d', 'delete', False, _('delete a given bookmark')),
902 ('m', 'rename', '', _('rename a given bookmark'), _('OLD')),
906 ('m', 'rename', '', _('rename a given bookmark'), _('OLD')),
903 ('i', 'inactive', False, _('mark a bookmark inactive')),
907 ('i', 'inactive', False, _('mark a bookmark inactive')),
904 ] + formatteropts,
908 ] + formatteropts,
905 _('hg bookmarks [OPTIONS]... [NAME]...'))
909 _('hg bookmarks [OPTIONS]... [NAME]...'))
906 def bookmark(ui, repo, *names, **opts):
910 def bookmark(ui, repo, *names, **opts):
907 '''create a new bookmark or list existing bookmarks
911 '''create a new bookmark or list existing bookmarks
908
912
909 Bookmarks are labels on changesets to help track lines of development.
913 Bookmarks are labels on changesets to help track lines of development.
910 Bookmarks are unversioned and can be moved, renamed and deleted.
914 Bookmarks are unversioned and can be moved, renamed and deleted.
911 Deleting or moving a bookmark has no effect on the associated changesets.
915 Deleting or moving a bookmark has no effect on the associated changesets.
912
916
913 Creating or updating to a bookmark causes it to be marked as 'active'.
917 Creating or updating to a bookmark causes it to be marked as 'active'.
914 The active bookmark is indicated with a '*'.
918 The active bookmark is indicated with a '*'.
915 When a commit is made, the active bookmark will advance to the new commit.
919 When a commit is made, the active bookmark will advance to the new commit.
916 A plain :hg:`update` will also advance an active bookmark, if possible.
920 A plain :hg:`update` will also advance an active bookmark, if possible.
917 Updating away from a bookmark will cause it to be deactivated.
921 Updating away from a bookmark will cause it to be deactivated.
918
922
919 Bookmarks can be pushed and pulled between repositories (see
923 Bookmarks can be pushed and pulled between repositories (see
920 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
924 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
921 diverged, a new 'divergent bookmark' of the form 'name@path' will
925 diverged, a new 'divergent bookmark' of the form 'name@path' will
922 be created. Using :hg:`merge` will resolve the divergence.
926 be created. Using :hg:`merge` will resolve the divergence.
923
927
924 Specifying bookmark as '.' to -m or -d options is equivalent to specifying
928 Specifying bookmark as '.' to -m or -d options is equivalent to specifying
925 the active bookmark's name.
929 the active bookmark's name.
926
930
927 A bookmark named '@' has the special property that :hg:`clone` will
931 A bookmark named '@' has the special property that :hg:`clone` will
928 check it out by default if it exists.
932 check it out by default if it exists.
929
933
930 .. container:: verbose
934 .. container:: verbose
931
935
932 Examples:
936 Examples:
933
937
934 - create an active bookmark for a new line of development::
938 - create an active bookmark for a new line of development::
935
939
936 hg book new-feature
940 hg book new-feature
937
941
938 - create an inactive bookmark as a place marker::
942 - create an inactive bookmark as a place marker::
939
943
940 hg book -i reviewed
944 hg book -i reviewed
941
945
942 - create an inactive bookmark on another changeset::
946 - create an inactive bookmark on another changeset::
943
947
944 hg book -r .^ tested
948 hg book -r .^ tested
945
949
946 - rename bookmark turkey to dinner::
950 - rename bookmark turkey to dinner::
947
951
948 hg book -m turkey dinner
952 hg book -m turkey dinner
949
953
950 - move the '@' bookmark from another branch::
954 - move the '@' bookmark from another branch::
951
955
952 hg book -f @
956 hg book -f @
953 '''
957 '''
954 force = opts.get(r'force')
958 force = opts.get(r'force')
955 rev = opts.get(r'rev')
959 rev = opts.get(r'rev')
956 delete = opts.get(r'delete')
960 delete = opts.get(r'delete')
957 rename = opts.get(r'rename')
961 rename = opts.get(r'rename')
958 inactive = opts.get(r'inactive')
962 inactive = opts.get(r'inactive')
959
963
960 if delete and rename:
964 if delete and rename:
961 raise error.Abort(_("--delete and --rename are incompatible"))
965 raise error.Abort(_("--delete and --rename are incompatible"))
962 if delete and rev:
966 if delete and rev:
963 raise error.Abort(_("--rev is incompatible with --delete"))
967 raise error.Abort(_("--rev is incompatible with --delete"))
964 if rename and rev:
968 if rename and rev:
965 raise error.Abort(_("--rev is incompatible with --rename"))
969 raise error.Abort(_("--rev is incompatible with --rename"))
966 if not names and (delete or rev):
970 if not names and (delete or rev):
967 raise error.Abort(_("bookmark name required"))
971 raise error.Abort(_("bookmark name required"))
968
972
969 if delete or rename or names or inactive:
973 if delete or rename or names or inactive:
970 with repo.wlock(), repo.lock(), repo.transaction('bookmark') as tr:
974 with repo.wlock(), repo.lock(), repo.transaction('bookmark') as tr:
971 if delete:
975 if delete:
972 names = pycompat.maplist(repo._bookmarks.expandname, names)
976 names = pycompat.maplist(repo._bookmarks.expandname, names)
973 bookmarks.delete(repo, tr, names)
977 bookmarks.delete(repo, tr, names)
974 elif rename:
978 elif rename:
975 if not names:
979 if not names:
976 raise error.Abort(_("new bookmark name required"))
980 raise error.Abort(_("new bookmark name required"))
977 elif len(names) > 1:
981 elif len(names) > 1:
978 raise error.Abort(_("only one new bookmark name allowed"))
982 raise error.Abort(_("only one new bookmark name allowed"))
979 rename = repo._bookmarks.expandname(rename)
983 rename = repo._bookmarks.expandname(rename)
980 bookmarks.rename(repo, tr, rename, names[0], force, inactive)
984 bookmarks.rename(repo, tr, rename, names[0], force, inactive)
981 elif names:
985 elif names:
982 bookmarks.addbookmarks(repo, tr, names, rev, force, inactive)
986 bookmarks.addbookmarks(repo, tr, names, rev, force, inactive)
983 elif inactive:
987 elif inactive:
984 if len(repo._bookmarks) == 0:
988 if len(repo._bookmarks) == 0:
985 ui.status(_("no bookmarks set\n"))
989 ui.status(_("no bookmarks set\n"))
986 elif not repo._activebookmark:
990 elif not repo._activebookmark:
987 ui.status(_("no active bookmark\n"))
991 ui.status(_("no active bookmark\n"))
988 else:
992 else:
989 bookmarks.deactivate(repo)
993 bookmarks.deactivate(repo)
990 else: # show bookmarks
994 else: # show bookmarks
991 bookmarks.printbookmarks(ui, repo, **opts)
995 bookmarks.printbookmarks(ui, repo, **opts)
992
996
993 @command('branch',
997 @command('branch',
994 [('f', 'force', None,
998 [('f', 'force', None,
995 _('set branch name even if it shadows an existing branch')),
999 _('set branch name even if it shadows an existing branch')),
996 ('C', 'clean', None, _('reset branch name to parent branch name'))],
1000 ('C', 'clean', None, _('reset branch name to parent branch name'))],
997 _('[-fC] [NAME]'))
1001 _('[-fC] [NAME]'))
998 def branch(ui, repo, label=None, **opts):
1002 def branch(ui, repo, label=None, **opts):
999 """set or show the current branch name
1003 """set or show the current branch name
1000
1004
1001 .. note::
1005 .. note::
1002
1006
1003 Branch names are permanent and global. Use :hg:`bookmark` to create a
1007 Branch names are permanent and global. Use :hg:`bookmark` to create a
1004 light-weight bookmark instead. See :hg:`help glossary` for more
1008 light-weight bookmark instead. See :hg:`help glossary` for more
1005 information about named branches and bookmarks.
1009 information about named branches and bookmarks.
1006
1010
1007 With no argument, show the current branch name. With one argument,
1011 With no argument, show the current branch name. With one argument,
1008 set the working directory branch name (the branch will not exist
1012 set the working directory branch name (the branch will not exist
1009 in the repository until the next commit). Standard practice
1013 in the repository until the next commit). Standard practice
1010 recommends that primary development take place on the 'default'
1014 recommends that primary development take place on the 'default'
1011 branch.
1015 branch.
1012
1016
1013 Unless -f/--force is specified, branch will not let you set a
1017 Unless -f/--force is specified, branch will not let you set a
1014 branch name that already exists.
1018 branch name that already exists.
1015
1019
1016 Use -C/--clean to reset the working directory branch to that of
1020 Use -C/--clean to reset the working directory branch to that of
1017 the parent of the working directory, negating a previous branch
1021 the parent of the working directory, negating a previous branch
1018 change.
1022 change.
1019
1023
1020 Use the command :hg:`update` to switch to an existing branch. Use
1024 Use the command :hg:`update` to switch to an existing branch. Use
1021 :hg:`commit --close-branch` to mark this branch head as closed.
1025 :hg:`commit --close-branch` to mark this branch head as closed.
1022 When all heads of a branch are closed, the branch will be
1026 When all heads of a branch are closed, the branch will be
1023 considered closed.
1027 considered closed.
1024
1028
1025 Returns 0 on success.
1029 Returns 0 on success.
1026 """
1030 """
1027 opts = pycompat.byteskwargs(opts)
1031 opts = pycompat.byteskwargs(opts)
1028 if label:
1032 if label:
1029 label = label.strip()
1033 label = label.strip()
1030
1034
1031 if not opts.get('clean') and not label:
1035 if not opts.get('clean') and not label:
1032 ui.write("%s\n" % repo.dirstate.branch())
1036 ui.write("%s\n" % repo.dirstate.branch())
1033 return
1037 return
1034
1038
1035 with repo.wlock():
1039 with repo.wlock():
1036 if opts.get('clean'):
1040 if opts.get('clean'):
1037 label = repo[None].p1().branch()
1041 label = repo[None].p1().branch()
1038 repo.dirstate.setbranch(label)
1042 repo.dirstate.setbranch(label)
1039 ui.status(_('reset working directory to branch %s\n') % label)
1043 ui.status(_('reset working directory to branch %s\n') % label)
1040 elif label:
1044 elif label:
1041 if not opts.get('force') and label in repo.branchmap():
1045 if not opts.get('force') and label in repo.branchmap():
1042 if label not in [p.branch() for p in repo[None].parents()]:
1046 if label not in [p.branch() for p in repo[None].parents()]:
1043 raise error.Abort(_('a branch of the same name already'
1047 raise error.Abort(_('a branch of the same name already'
1044 ' exists'),
1048 ' exists'),
1045 # i18n: "it" refers to an existing branch
1049 # i18n: "it" refers to an existing branch
1046 hint=_("use 'hg update' to switch to it"))
1050 hint=_("use 'hg update' to switch to it"))
1047 scmutil.checknewlabel(repo, label, 'branch')
1051 scmutil.checknewlabel(repo, label, 'branch')
1048 repo.dirstate.setbranch(label)
1052 repo.dirstate.setbranch(label)
1049 ui.status(_('marked working directory as branch %s\n') % label)
1053 ui.status(_('marked working directory as branch %s\n') % label)
1050
1054
1051 # find any open named branches aside from default
1055 # find any open named branches aside from default
1052 others = [n for n, h, t, c in repo.branchmap().iterbranches()
1056 others = [n for n, h, t, c in repo.branchmap().iterbranches()
1053 if n != "default" and not c]
1057 if n != "default" and not c]
1054 if not others:
1058 if not others:
1055 ui.status(_('(branches are permanent and global, '
1059 ui.status(_('(branches are permanent and global, '
1056 'did you want a bookmark?)\n'))
1060 'did you want a bookmark?)\n'))
1057
1061
1058 @command('branches',
1062 @command('branches',
1059 [('a', 'active', False,
1063 [('a', 'active', False,
1060 _('show only branches that have unmerged heads (DEPRECATED)')),
1064 _('show only branches that have unmerged heads (DEPRECATED)')),
1061 ('c', 'closed', False, _('show normal and closed branches')),
1065 ('c', 'closed', False, _('show normal and closed branches')),
1062 ] + formatteropts,
1066 ] + formatteropts,
1063 _('[-c]'))
1067 _('[-c]'))
1064 def branches(ui, repo, active=False, closed=False, **opts):
1068 def branches(ui, repo, active=False, closed=False, **opts):
1065 """list repository named branches
1069 """list repository named branches
1066
1070
1067 List the repository's named branches, indicating which ones are
1071 List the repository's named branches, indicating which ones are
1068 inactive. If -c/--closed is specified, also list branches which have
1072 inactive. If -c/--closed is specified, also list branches which have
1069 been marked closed (see :hg:`commit --close-branch`).
1073 been marked closed (see :hg:`commit --close-branch`).
1070
1074
1071 Use the command :hg:`update` to switch to an existing branch.
1075 Use the command :hg:`update` to switch to an existing branch.
1072
1076
1073 Returns 0.
1077 Returns 0.
1074 """
1078 """
1075
1079
1076 opts = pycompat.byteskwargs(opts)
1080 opts = pycompat.byteskwargs(opts)
1077 ui.pager('branches')
1081 ui.pager('branches')
1078 fm = ui.formatter('branches', opts)
1082 fm = ui.formatter('branches', opts)
1079 hexfunc = fm.hexfunc
1083 hexfunc = fm.hexfunc
1080
1084
1081 allheads = set(repo.heads())
1085 allheads = set(repo.heads())
1082 branches = []
1086 branches = []
1083 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1087 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1084 isactive = False
1088 isactive = False
1085 if not isclosed:
1089 if not isclosed:
1086 openheads = set(repo.branchmap().iteropen(heads))
1090 openheads = set(repo.branchmap().iteropen(heads))
1087 isactive = bool(openheads & allheads)
1091 isactive = bool(openheads & allheads)
1088 branches.append((tag, repo[tip], isactive, not isclosed))
1092 branches.append((tag, repo[tip], isactive, not isclosed))
1089 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]),
1093 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]),
1090 reverse=True)
1094 reverse=True)
1091
1095
1092 for tag, ctx, isactive, isopen in branches:
1096 for tag, ctx, isactive, isopen in branches:
1093 if active and not isactive:
1097 if active and not isactive:
1094 continue
1098 continue
1095 if isactive:
1099 if isactive:
1096 label = 'branches.active'
1100 label = 'branches.active'
1097 notice = ''
1101 notice = ''
1098 elif not isopen:
1102 elif not isopen:
1099 if not closed:
1103 if not closed:
1100 continue
1104 continue
1101 label = 'branches.closed'
1105 label = 'branches.closed'
1102 notice = _(' (closed)')
1106 notice = _(' (closed)')
1103 else:
1107 else:
1104 label = 'branches.inactive'
1108 label = 'branches.inactive'
1105 notice = _(' (inactive)')
1109 notice = _(' (inactive)')
1106 current = (tag == repo.dirstate.branch())
1110 current = (tag == repo.dirstate.branch())
1107 if current:
1111 if current:
1108 label = 'branches.current'
1112 label = 'branches.current'
1109
1113
1110 fm.startitem()
1114 fm.startitem()
1111 fm.write('branch', '%s', tag, label=label)
1115 fm.write('branch', '%s', tag, label=label)
1112 rev = ctx.rev()
1116 rev = ctx.rev()
1113 padsize = max(31 - len(str(rev)) - encoding.colwidth(tag), 0)
1117 padsize = max(31 - len(str(rev)) - encoding.colwidth(tag), 0)
1114 fmt = ' ' * padsize + ' %d:%s'
1118 fmt = ' ' * padsize + ' %d:%s'
1115 fm.condwrite(not ui.quiet, 'rev node', fmt, rev, hexfunc(ctx.node()),
1119 fm.condwrite(not ui.quiet, 'rev node', fmt, rev, hexfunc(ctx.node()),
1116 label='log.changeset changeset.%s' % ctx.phasestr())
1120 label='log.changeset changeset.%s' % ctx.phasestr())
1117 fm.context(ctx=ctx)
1121 fm.context(ctx=ctx)
1118 fm.data(active=isactive, closed=not isopen, current=current)
1122 fm.data(active=isactive, closed=not isopen, current=current)
1119 if not ui.quiet:
1123 if not ui.quiet:
1120 fm.plain(notice)
1124 fm.plain(notice)
1121 fm.plain('\n')
1125 fm.plain('\n')
1122 fm.end()
1126 fm.end()
1123
1127
1124 @command('bundle',
1128 @command('bundle',
1125 [('f', 'force', None, _('run even when the destination is unrelated')),
1129 [('f', 'force', None, _('run even when the destination is unrelated')),
1126 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
1130 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
1127 _('REV')),
1131 _('REV')),
1128 ('b', 'branch', [], _('a specific branch you would like to bundle'),
1132 ('b', 'branch', [], _('a specific branch you would like to bundle'),
1129 _('BRANCH')),
1133 _('BRANCH')),
1130 ('', 'base', [],
1134 ('', 'base', [],
1131 _('a base changeset assumed to be available at the destination'),
1135 _('a base changeset assumed to be available at the destination'),
1132 _('REV')),
1136 _('REV')),
1133 ('a', 'all', None, _('bundle all changesets in the repository')),
1137 ('a', 'all', None, _('bundle all changesets in the repository')),
1134 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
1138 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
1135 ] + remoteopts,
1139 ] + remoteopts,
1136 _('[-f] [-t BUNDLESPEC] [-a] [-r REV]... [--base REV]... FILE [DEST]'))
1140 _('[-f] [-t BUNDLESPEC] [-a] [-r REV]... [--base REV]... FILE [DEST]'))
1137 def bundle(ui, repo, fname, dest=None, **opts):
1141 def bundle(ui, repo, fname, dest=None, **opts):
1138 """create a bundle file
1142 """create a bundle file
1139
1143
1140 Generate a bundle file containing data to be added to a repository.
1144 Generate a bundle file containing data to be added to a repository.
1141
1145
1142 To create a bundle containing all changesets, use -a/--all
1146 To create a bundle containing all changesets, use -a/--all
1143 (or --base null). Otherwise, hg assumes the destination will have
1147 (or --base null). Otherwise, hg assumes the destination will have
1144 all the nodes you specify with --base parameters. Otherwise, hg
1148 all the nodes you specify with --base parameters. Otherwise, hg
1145 will assume the repository has all the nodes in destination, or
1149 will assume the repository has all the nodes in destination, or
1146 default-push/default if no destination is specified.
1150 default-push/default if no destination is specified.
1147
1151
1148 You can change bundle format with the -t/--type option. See
1152 You can change bundle format with the -t/--type option. See
1149 :hg:`help bundlespec` for documentation on this format. By default,
1153 :hg:`help bundlespec` for documentation on this format. By default,
1150 the most appropriate format is used and compression defaults to
1154 the most appropriate format is used and compression defaults to
1151 bzip2.
1155 bzip2.
1152
1156
1153 The bundle file can then be transferred using conventional means
1157 The bundle file can then be transferred using conventional means
1154 and applied to another repository with the unbundle or pull
1158 and applied to another repository with the unbundle or pull
1155 command. This is useful when direct push and pull are not
1159 command. This is useful when direct push and pull are not
1156 available or when exporting an entire repository is undesirable.
1160 available or when exporting an entire repository is undesirable.
1157
1161
1158 Applying bundles preserves all changeset contents including
1162 Applying bundles preserves all changeset contents including
1159 permissions, copy/rename information, and revision history.
1163 permissions, copy/rename information, and revision history.
1160
1164
1161 Returns 0 on success, 1 if no changes found.
1165 Returns 0 on success, 1 if no changes found.
1162 """
1166 """
1163 opts = pycompat.byteskwargs(opts)
1167 opts = pycompat.byteskwargs(opts)
1164 revs = None
1168 revs = None
1165 if 'rev' in opts:
1169 if 'rev' in opts:
1166 revstrings = opts['rev']
1170 revstrings = opts['rev']
1167 revs = scmutil.revrange(repo, revstrings)
1171 revs = scmutil.revrange(repo, revstrings)
1168 if revstrings and not revs:
1172 if revstrings and not revs:
1169 raise error.Abort(_('no commits to bundle'))
1173 raise error.Abort(_('no commits to bundle'))
1170
1174
1171 bundletype = opts.get('type', 'bzip2').lower()
1175 bundletype = opts.get('type', 'bzip2').lower()
1172 try:
1176 try:
1173 bcompression, cgversion, params = exchange.parsebundlespec(
1177 bcompression, cgversion, params = exchange.parsebundlespec(
1174 repo, bundletype, strict=False)
1178 repo, bundletype, strict=False)
1175 except error.UnsupportedBundleSpecification as e:
1179 except error.UnsupportedBundleSpecification as e:
1176 raise error.Abort(str(e),
1180 raise error.Abort(str(e),
1177 hint=_("see 'hg help bundlespec' for supported "
1181 hint=_("see 'hg help bundlespec' for supported "
1178 "values for --type"))
1182 "values for --type"))
1179
1183
1180 # Packed bundles are a pseudo bundle format for now.
1184 # Packed bundles are a pseudo bundle format for now.
1181 if cgversion == 's1':
1185 if cgversion == 's1':
1182 raise error.Abort(_('packed bundles cannot be produced by "hg bundle"'),
1186 raise error.Abort(_('packed bundles cannot be produced by "hg bundle"'),
1183 hint=_("use 'hg debugcreatestreamclonebundle'"))
1187 hint=_("use 'hg debugcreatestreamclonebundle'"))
1184
1188
1185 if opts.get('all'):
1189 if opts.get('all'):
1186 if dest:
1190 if dest:
1187 raise error.Abort(_("--all is incompatible with specifying "
1191 raise error.Abort(_("--all is incompatible with specifying "
1188 "a destination"))
1192 "a destination"))
1189 if opts.get('base'):
1193 if opts.get('base'):
1190 ui.warn(_("ignoring --base because --all was specified\n"))
1194 ui.warn(_("ignoring --base because --all was specified\n"))
1191 base = ['null']
1195 base = ['null']
1192 else:
1196 else:
1193 base = scmutil.revrange(repo, opts.get('base'))
1197 base = scmutil.revrange(repo, opts.get('base'))
1194 if cgversion not in changegroup.supportedoutgoingversions(repo):
1198 if cgversion not in changegroup.supportedoutgoingversions(repo):
1195 raise error.Abort(_("repository does not support bundle version %s") %
1199 raise error.Abort(_("repository does not support bundle version %s") %
1196 cgversion)
1200 cgversion)
1197
1201
1198 if base:
1202 if base:
1199 if dest:
1203 if dest:
1200 raise error.Abort(_("--base is incompatible with specifying "
1204 raise error.Abort(_("--base is incompatible with specifying "
1201 "a destination"))
1205 "a destination"))
1202 common = [repo.lookup(rev) for rev in base]
1206 common = [repo.lookup(rev) for rev in base]
1203 heads = revs and map(repo.lookup, revs) or None
1207 heads = revs and map(repo.lookup, revs) or None
1204 outgoing = discovery.outgoing(repo, common, heads)
1208 outgoing = discovery.outgoing(repo, common, heads)
1205 else:
1209 else:
1206 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1210 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1207 dest, branches = hg.parseurl(dest, opts.get('branch'))
1211 dest, branches = hg.parseurl(dest, opts.get('branch'))
1208 other = hg.peer(repo, opts, dest)
1212 other = hg.peer(repo, opts, dest)
1209 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1213 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1210 heads = revs and map(repo.lookup, revs) or revs
1214 heads = revs and map(repo.lookup, revs) or revs
1211 outgoing = discovery.findcommonoutgoing(repo, other,
1215 outgoing = discovery.findcommonoutgoing(repo, other,
1212 onlyheads=heads,
1216 onlyheads=heads,
1213 force=opts.get('force'),
1217 force=opts.get('force'),
1214 portable=True)
1218 portable=True)
1215
1219
1216 if not outgoing.missing:
1220 if not outgoing.missing:
1217 scmutil.nochangesfound(ui, repo, not base and outgoing.excluded)
1221 scmutil.nochangesfound(ui, repo, not base and outgoing.excluded)
1218 return 1
1222 return 1
1219
1223
1220 if cgversion == '01': #bundle1
1224 if cgversion == '01': #bundle1
1221 if bcompression is None:
1225 if bcompression is None:
1222 bcompression = 'UN'
1226 bcompression = 'UN'
1223 bversion = 'HG10' + bcompression
1227 bversion = 'HG10' + bcompression
1224 bcompression = None
1228 bcompression = None
1225 elif cgversion in ('02', '03'):
1229 elif cgversion in ('02', '03'):
1226 bversion = 'HG20'
1230 bversion = 'HG20'
1227 else:
1231 else:
1228 raise error.ProgrammingError(
1232 raise error.ProgrammingError(
1229 'bundle: unexpected changegroup version %s' % cgversion)
1233 'bundle: unexpected changegroup version %s' % cgversion)
1230
1234
1231 # TODO compression options should be derived from bundlespec parsing.
1235 # TODO compression options should be derived from bundlespec parsing.
1232 # This is a temporary hack to allow adjusting bundle compression
1236 # This is a temporary hack to allow adjusting bundle compression
1233 # level without a) formalizing the bundlespec changes to declare it
1237 # level without a) formalizing the bundlespec changes to declare it
1234 # b) introducing a command flag.
1238 # b) introducing a command flag.
1235 compopts = {}
1239 compopts = {}
1236 complevel = ui.configint('experimental', 'bundlecomplevel')
1240 complevel = ui.configint('experimental', 'bundlecomplevel')
1237 if complevel is not None:
1241 if complevel is not None:
1238 compopts['level'] = complevel
1242 compopts['level'] = complevel
1239
1243
1240
1244
1241 contentopts = {'cg.version': cgversion}
1245 contentopts = {'cg.version': cgversion}
1242 if repo.ui.configbool('experimental', 'evolution.bundle-obsmarker'):
1246 if repo.ui.configbool('experimental', 'evolution.bundle-obsmarker'):
1243 contentopts['obsolescence'] = True
1247 contentopts['obsolescence'] = True
1244 if repo.ui.configbool('experimental', 'bundle-phases'):
1248 if repo.ui.configbool('experimental', 'bundle-phases'):
1245 contentopts['phases'] = True
1249 contentopts['phases'] = True
1246 bundle2.writenewbundle(ui, repo, 'bundle', fname, bversion, outgoing,
1250 bundle2.writenewbundle(ui, repo, 'bundle', fname, bversion, outgoing,
1247 contentopts, compression=bcompression,
1251 contentopts, compression=bcompression,
1248 compopts=compopts)
1252 compopts=compopts)
1249
1253
1250 @command('cat',
1254 @command('cat',
1251 [('o', 'output', '',
1255 [('o', 'output', '',
1252 _('print output to file with formatted name'), _('FORMAT')),
1256 _('print output to file with formatted name'), _('FORMAT')),
1253 ('r', 'rev', '', _('print the given revision'), _('REV')),
1257 ('r', 'rev', '', _('print the given revision'), _('REV')),
1254 ('', 'decode', None, _('apply any matching decode filter')),
1258 ('', 'decode', None, _('apply any matching decode filter')),
1255 ] + walkopts + formatteropts,
1259 ] + walkopts + formatteropts,
1256 _('[OPTION]... FILE...'),
1260 _('[OPTION]... FILE...'),
1257 inferrepo=True)
1261 inferrepo=True)
1258 def cat(ui, repo, file1, *pats, **opts):
1262 def cat(ui, repo, file1, *pats, **opts):
1259 """output the current or given revision of files
1263 """output the current or given revision of files
1260
1264
1261 Print the specified files as they were at the given revision. If
1265 Print the specified files as they were at the given revision. If
1262 no revision is given, the parent of the working directory is used.
1266 no revision is given, the parent of the working directory is used.
1263
1267
1264 Output may be to a file, in which case the name of the file is
1268 Output may be to a file, in which case the name of the file is
1265 given using a format string. The formatting rules as follows:
1269 given using a format string. The formatting rules as follows:
1266
1270
1267 :``%%``: literal "%" character
1271 :``%%``: literal "%" character
1268 :``%s``: basename of file being printed
1272 :``%s``: basename of file being printed
1269 :``%d``: dirname of file being printed, or '.' if in repository root
1273 :``%d``: dirname of file being printed, or '.' if in repository root
1270 :``%p``: root-relative path name of file being printed
1274 :``%p``: root-relative path name of file being printed
1271 :``%H``: changeset hash (40 hexadecimal digits)
1275 :``%H``: changeset hash (40 hexadecimal digits)
1272 :``%R``: changeset revision number
1276 :``%R``: changeset revision number
1273 :``%h``: short-form changeset hash (12 hexadecimal digits)
1277 :``%h``: short-form changeset hash (12 hexadecimal digits)
1274 :``%r``: zero-padded changeset revision number
1278 :``%r``: zero-padded changeset revision number
1275 :``%b``: basename of the exporting repository
1279 :``%b``: basename of the exporting repository
1276
1280
1277 Returns 0 on success.
1281 Returns 0 on success.
1278 """
1282 """
1279 ctx = scmutil.revsingle(repo, opts.get('rev'))
1283 ctx = scmutil.revsingle(repo, opts.get('rev'))
1280 m = scmutil.match(ctx, (file1,) + pats, opts)
1284 m = scmutil.match(ctx, (file1,) + pats, opts)
1281 fntemplate = opts.pop('output', '')
1285 fntemplate = opts.pop('output', '')
1282 if cmdutil.isstdiofilename(fntemplate):
1286 if cmdutil.isstdiofilename(fntemplate):
1283 fntemplate = ''
1287 fntemplate = ''
1284
1288
1285 if fntemplate:
1289 if fntemplate:
1286 fm = formatter.nullformatter(ui, 'cat')
1290 fm = formatter.nullformatter(ui, 'cat')
1287 else:
1291 else:
1288 ui.pager('cat')
1292 ui.pager('cat')
1289 fm = ui.formatter('cat', opts)
1293 fm = ui.formatter('cat', opts)
1290 with fm:
1294 with fm:
1291 return cmdutil.cat(ui, repo, ctx, m, fm, fntemplate, '', **opts)
1295 return cmdutil.cat(ui, repo, ctx, m, fm, fntemplate, '', **opts)
1292
1296
1293 @command('^clone',
1297 @command('^clone',
1294 [('U', 'noupdate', None, _('the clone will include an empty working '
1298 [('U', 'noupdate', None, _('the clone will include an empty working '
1295 'directory (only a repository)')),
1299 'directory (only a repository)')),
1296 ('u', 'updaterev', '', _('revision, tag, or branch to check out'),
1300 ('u', 'updaterev', '', _('revision, tag, or branch to check out'),
1297 _('REV')),
1301 _('REV')),
1298 ('r', 'rev', [], _('include the specified changeset'), _('REV')),
1302 ('r', 'rev', [], _('include the specified changeset'), _('REV')),
1299 ('b', 'branch', [], _('clone only the specified branch'), _('BRANCH')),
1303 ('b', 'branch', [], _('clone only the specified branch'), _('BRANCH')),
1300 ('', 'pull', None, _('use pull protocol to copy metadata')),
1304 ('', 'pull', None, _('use pull protocol to copy metadata')),
1301 ('', 'uncompressed', None,
1305 ('', 'uncompressed', None,
1302 _('an alias to --stream (DEPRECATED)')),
1306 _('an alias to --stream (DEPRECATED)')),
1303 ('', 'stream', None,
1307 ('', 'stream', None,
1304 _('clone with minimal data processing')),
1308 _('clone with minimal data processing')),
1305 ] + remoteopts,
1309 ] + remoteopts,
1306 _('[OPTION]... SOURCE [DEST]'),
1310 _('[OPTION]... SOURCE [DEST]'),
1307 norepo=True)
1311 norepo=True)
1308 def clone(ui, source, dest=None, **opts):
1312 def clone(ui, source, dest=None, **opts):
1309 """make a copy of an existing repository
1313 """make a copy of an existing repository
1310
1314
1311 Create a copy of an existing repository in a new directory.
1315 Create a copy of an existing repository in a new directory.
1312
1316
1313 If no destination directory name is specified, it defaults to the
1317 If no destination directory name is specified, it defaults to the
1314 basename of the source.
1318 basename of the source.
1315
1319
1316 The location of the source is added to the new repository's
1320 The location of the source is added to the new repository's
1317 ``.hg/hgrc`` file, as the default to be used for future pulls.
1321 ``.hg/hgrc`` file, as the default to be used for future pulls.
1318
1322
1319 Only local paths and ``ssh://`` URLs are supported as
1323 Only local paths and ``ssh://`` URLs are supported as
1320 destinations. For ``ssh://`` destinations, no working directory or
1324 destinations. For ``ssh://`` destinations, no working directory or
1321 ``.hg/hgrc`` will be created on the remote side.
1325 ``.hg/hgrc`` will be created on the remote side.
1322
1326
1323 If the source repository has a bookmark called '@' set, that
1327 If the source repository has a bookmark called '@' set, that
1324 revision will be checked out in the new repository by default.
1328 revision will be checked out in the new repository by default.
1325
1329
1326 To check out a particular version, use -u/--update, or
1330 To check out a particular version, use -u/--update, or
1327 -U/--noupdate to create a clone with no working directory.
1331 -U/--noupdate to create a clone with no working directory.
1328
1332
1329 To pull only a subset of changesets, specify one or more revisions
1333 To pull only a subset of changesets, specify one or more revisions
1330 identifiers with -r/--rev or branches with -b/--branch. The
1334 identifiers with -r/--rev or branches with -b/--branch. The
1331 resulting clone will contain only the specified changesets and
1335 resulting clone will contain only the specified changesets and
1332 their ancestors. These options (or 'clone src#rev dest') imply
1336 their ancestors. These options (or 'clone src#rev dest') imply
1333 --pull, even for local source repositories.
1337 --pull, even for local source repositories.
1334
1338
1335 In normal clone mode, the remote normalizes repository data into a common
1339 In normal clone mode, the remote normalizes repository data into a common
1336 exchange format and the receiving end translates this data into its local
1340 exchange format and the receiving end translates this data into its local
1337 storage format. --stream activates a different clone mode that essentially
1341 storage format. --stream activates a different clone mode that essentially
1338 copies repository files from the remote with minimal data processing. This
1342 copies repository files from the remote with minimal data processing. This
1339 significantly reduces the CPU cost of a clone both remotely and locally.
1343 significantly reduces the CPU cost of a clone both remotely and locally.
1340 However, it often increases the transferred data size by 30-40%. This can
1344 However, it often increases the transferred data size by 30-40%. This can
1341 result in substantially faster clones where I/O throughput is plentiful,
1345 result in substantially faster clones where I/O throughput is plentiful,
1342 especially for larger repositories. A side-effect of --stream clones is
1346 especially for larger repositories. A side-effect of --stream clones is
1343 that storage settings and requirements on the remote are applied locally:
1347 that storage settings and requirements on the remote are applied locally:
1344 a modern client may inherit legacy or inefficient storage used by the
1348 a modern client may inherit legacy or inefficient storage used by the
1345 remote or a legacy Mercurial client may not be able to clone from a
1349 remote or a legacy Mercurial client may not be able to clone from a
1346 modern Mercurial remote.
1350 modern Mercurial remote.
1347
1351
1348 .. note::
1352 .. note::
1349
1353
1350 Specifying a tag will include the tagged changeset but not the
1354 Specifying a tag will include the tagged changeset but not the
1351 changeset containing the tag.
1355 changeset containing the tag.
1352
1356
1353 .. container:: verbose
1357 .. container:: verbose
1354
1358
1355 For efficiency, hardlinks are used for cloning whenever the
1359 For efficiency, hardlinks are used for cloning whenever the
1356 source and destination are on the same filesystem (note this
1360 source and destination are on the same filesystem (note this
1357 applies only to the repository data, not to the working
1361 applies only to the repository data, not to the working
1358 directory). Some filesystems, such as AFS, implement hardlinking
1362 directory). Some filesystems, such as AFS, implement hardlinking
1359 incorrectly, but do not report errors. In these cases, use the
1363 incorrectly, but do not report errors. In these cases, use the
1360 --pull option to avoid hardlinking.
1364 --pull option to avoid hardlinking.
1361
1365
1362 Mercurial will update the working directory to the first applicable
1366 Mercurial will update the working directory to the first applicable
1363 revision from this list:
1367 revision from this list:
1364
1368
1365 a) null if -U or the source repository has no changesets
1369 a) null if -U or the source repository has no changesets
1366 b) if -u . and the source repository is local, the first parent of
1370 b) if -u . and the source repository is local, the first parent of
1367 the source repository's working directory
1371 the source repository's working directory
1368 c) the changeset specified with -u (if a branch name, this means the
1372 c) the changeset specified with -u (if a branch name, this means the
1369 latest head of that branch)
1373 latest head of that branch)
1370 d) the changeset specified with -r
1374 d) the changeset specified with -r
1371 e) the tipmost head specified with -b
1375 e) the tipmost head specified with -b
1372 f) the tipmost head specified with the url#branch source syntax
1376 f) the tipmost head specified with the url#branch source syntax
1373 g) the revision marked with the '@' bookmark, if present
1377 g) the revision marked with the '@' bookmark, if present
1374 h) the tipmost head of the default branch
1378 h) the tipmost head of the default branch
1375 i) tip
1379 i) tip
1376
1380
1377 When cloning from servers that support it, Mercurial may fetch
1381 When cloning from servers that support it, Mercurial may fetch
1378 pre-generated data from a server-advertised URL. When this is done,
1382 pre-generated data from a server-advertised URL. When this is done,
1379 hooks operating on incoming changesets and changegroups may fire twice,
1383 hooks operating on incoming changesets and changegroups may fire twice,
1380 once for the bundle fetched from the URL and another for any additional
1384 once for the bundle fetched from the URL and another for any additional
1381 data not fetched from this URL. In addition, if an error occurs, the
1385 data not fetched from this URL. In addition, if an error occurs, the
1382 repository may be rolled back to a partial clone. This behavior may
1386 repository may be rolled back to a partial clone. This behavior may
1383 change in future releases. See :hg:`help -e clonebundles` for more.
1387 change in future releases. See :hg:`help -e clonebundles` for more.
1384
1388
1385 Examples:
1389 Examples:
1386
1390
1387 - clone a remote repository to a new directory named hg/::
1391 - clone a remote repository to a new directory named hg/::
1388
1392
1389 hg clone https://www.mercurial-scm.org/repo/hg/
1393 hg clone https://www.mercurial-scm.org/repo/hg/
1390
1394
1391 - create a lightweight local clone::
1395 - create a lightweight local clone::
1392
1396
1393 hg clone project/ project-feature/
1397 hg clone project/ project-feature/
1394
1398
1395 - clone from an absolute path on an ssh server (note double-slash)::
1399 - clone from an absolute path on an ssh server (note double-slash)::
1396
1400
1397 hg clone ssh://user@server//home/projects/alpha/
1401 hg clone ssh://user@server//home/projects/alpha/
1398
1402
1399 - do a streaming clone while checking out a specified version::
1403 - do a streaming clone while checking out a specified version::
1400
1404
1401 hg clone --stream http://server/repo -u 1.5
1405 hg clone --stream http://server/repo -u 1.5
1402
1406
1403 - create a repository without changesets after a particular revision::
1407 - create a repository without changesets after a particular revision::
1404
1408
1405 hg clone -r 04e544 experimental/ good/
1409 hg clone -r 04e544 experimental/ good/
1406
1410
1407 - clone (and track) a particular named branch::
1411 - clone (and track) a particular named branch::
1408
1412
1409 hg clone https://www.mercurial-scm.org/repo/hg/#stable
1413 hg clone https://www.mercurial-scm.org/repo/hg/#stable
1410
1414
1411 See :hg:`help urls` for details on specifying URLs.
1415 See :hg:`help urls` for details on specifying URLs.
1412
1416
1413 Returns 0 on success.
1417 Returns 0 on success.
1414 """
1418 """
1415 opts = pycompat.byteskwargs(opts)
1419 opts = pycompat.byteskwargs(opts)
1416 if opts.get('noupdate') and opts.get('updaterev'):
1420 if opts.get('noupdate') and opts.get('updaterev'):
1417 raise error.Abort(_("cannot specify both --noupdate and --updaterev"))
1421 raise error.Abort(_("cannot specify both --noupdate and --updaterev"))
1418
1422
1419 r = hg.clone(ui, opts, source, dest,
1423 r = hg.clone(ui, opts, source, dest,
1420 pull=opts.get('pull'),
1424 pull=opts.get('pull'),
1421 stream=opts.get('stream') or opts.get('uncompressed'),
1425 stream=opts.get('stream') or opts.get('uncompressed'),
1422 rev=opts.get('rev'),
1426 rev=opts.get('rev'),
1423 update=opts.get('updaterev') or not opts.get('noupdate'),
1427 update=opts.get('updaterev') or not opts.get('noupdate'),
1424 branch=opts.get('branch'),
1428 branch=opts.get('branch'),
1425 shareopts=opts.get('shareopts'))
1429 shareopts=opts.get('shareopts'))
1426
1430
1427 return r is None
1431 return r is None
1428
1432
1429 @command('^commit|ci',
1433 @command('^commit|ci',
1430 [('A', 'addremove', None,
1434 [('A', 'addremove', None,
1431 _('mark new/missing files as added/removed before committing')),
1435 _('mark new/missing files as added/removed before committing')),
1432 ('', 'close-branch', None,
1436 ('', 'close-branch', None,
1433 _('mark a branch head as closed')),
1437 _('mark a branch head as closed')),
1434 ('', 'amend', None, _('amend the parent of the working directory')),
1438 ('', 'amend', None, _('amend the parent of the working directory')),
1435 ('s', 'secret', None, _('use the secret phase for committing')),
1439 ('s', 'secret', None, _('use the secret phase for committing')),
1436 ('e', 'edit', None, _('invoke editor on commit messages')),
1440 ('e', 'edit', None, _('invoke editor on commit messages')),
1437 ('i', 'interactive', None, _('use interactive mode')),
1441 ('i', 'interactive', None, _('use interactive mode')),
1438 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1442 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1439 _('[OPTION]... [FILE]...'),
1443 _('[OPTION]... [FILE]...'),
1440 inferrepo=True)
1444 inferrepo=True)
1441 def commit(ui, repo, *pats, **opts):
1445 def commit(ui, repo, *pats, **opts):
1442 """commit the specified files or all outstanding changes
1446 """commit the specified files or all outstanding changes
1443
1447
1444 Commit changes to the given files into the repository. Unlike a
1448 Commit changes to the given files into the repository. Unlike a
1445 centralized SCM, this operation is a local operation. See
1449 centralized SCM, this operation is a local operation. See
1446 :hg:`push` for a way to actively distribute your changes.
1450 :hg:`push` for a way to actively distribute your changes.
1447
1451
1448 If a list of files is omitted, all changes reported by :hg:`status`
1452 If a list of files is omitted, all changes reported by :hg:`status`
1449 will be committed.
1453 will be committed.
1450
1454
1451 If you are committing the result of a merge, do not provide any
1455 If you are committing the result of a merge, do not provide any
1452 filenames or -I/-X filters.
1456 filenames or -I/-X filters.
1453
1457
1454 If no commit message is specified, Mercurial starts your
1458 If no commit message is specified, Mercurial starts your
1455 configured editor where you can enter a message. In case your
1459 configured editor where you can enter a message. In case your
1456 commit fails, you will find a backup of your message in
1460 commit fails, you will find a backup of your message in
1457 ``.hg/last-message.txt``.
1461 ``.hg/last-message.txt``.
1458
1462
1459 The --close-branch flag can be used to mark the current branch
1463 The --close-branch flag can be used to mark the current branch
1460 head closed. When all heads of a branch are closed, the branch
1464 head closed. When all heads of a branch are closed, the branch
1461 will be considered closed and no longer listed.
1465 will be considered closed and no longer listed.
1462
1466
1463 The --amend flag can be used to amend the parent of the
1467 The --amend flag can be used to amend the parent of the
1464 working directory with a new commit that contains the changes
1468 working directory with a new commit that contains the changes
1465 in the parent in addition to those currently reported by :hg:`status`,
1469 in the parent in addition to those currently reported by :hg:`status`,
1466 if there are any. The old commit is stored in a backup bundle in
1470 if there are any. The old commit is stored in a backup bundle in
1467 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1471 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1468 on how to restore it).
1472 on how to restore it).
1469
1473
1470 Message, user and date are taken from the amended commit unless
1474 Message, user and date are taken from the amended commit unless
1471 specified. When a message isn't specified on the command line,
1475 specified. When a message isn't specified on the command line,
1472 the editor will open with the message of the amended commit.
1476 the editor will open with the message of the amended commit.
1473
1477
1474 It is not possible to amend public changesets (see :hg:`help phases`)
1478 It is not possible to amend public changesets (see :hg:`help phases`)
1475 or changesets that have children.
1479 or changesets that have children.
1476
1480
1477 See :hg:`help dates` for a list of formats valid for -d/--date.
1481 See :hg:`help dates` for a list of formats valid for -d/--date.
1478
1482
1479 Returns 0 on success, 1 if nothing changed.
1483 Returns 0 on success, 1 if nothing changed.
1480
1484
1481 .. container:: verbose
1485 .. container:: verbose
1482
1486
1483 Examples:
1487 Examples:
1484
1488
1485 - commit all files ending in .py::
1489 - commit all files ending in .py::
1486
1490
1487 hg commit --include "set:**.py"
1491 hg commit --include "set:**.py"
1488
1492
1489 - commit all non-binary files::
1493 - commit all non-binary files::
1490
1494
1491 hg commit --exclude "set:binary()"
1495 hg commit --exclude "set:binary()"
1492
1496
1493 - amend the current commit and set the date to now::
1497 - amend the current commit and set the date to now::
1494
1498
1495 hg commit --amend --date now
1499 hg commit --amend --date now
1496 """
1500 """
1497 wlock = lock = None
1501 wlock = lock = None
1498 try:
1502 try:
1499 wlock = repo.wlock()
1503 wlock = repo.wlock()
1500 lock = repo.lock()
1504 lock = repo.lock()
1501 return _docommit(ui, repo, *pats, **opts)
1505 return _docommit(ui, repo, *pats, **opts)
1502 finally:
1506 finally:
1503 release(lock, wlock)
1507 release(lock, wlock)
1504
1508
1505 def _docommit(ui, repo, *pats, **opts):
1509 def _docommit(ui, repo, *pats, **opts):
1506 if opts.get(r'interactive'):
1510 if opts.get(r'interactive'):
1507 opts.pop(r'interactive')
1511 opts.pop(r'interactive')
1508 ret = cmdutil.dorecord(ui, repo, commit, None, False,
1512 ret = cmdutil.dorecord(ui, repo, commit, None, False,
1509 cmdutil.recordfilter, *pats,
1513 cmdutil.recordfilter, *pats,
1510 **opts)
1514 **opts)
1511 # ret can be 0 (no changes to record) or the value returned by
1515 # ret can be 0 (no changes to record) or the value returned by
1512 # commit(), 1 if nothing changed or None on success.
1516 # commit(), 1 if nothing changed or None on success.
1513 return 1 if ret == 0 else ret
1517 return 1 if ret == 0 else ret
1514
1518
1515 opts = pycompat.byteskwargs(opts)
1519 opts = pycompat.byteskwargs(opts)
1516 if opts.get('subrepos'):
1520 if opts.get('subrepos'):
1517 if opts.get('amend'):
1521 if opts.get('amend'):
1518 raise error.Abort(_('cannot amend with --subrepos'))
1522 raise error.Abort(_('cannot amend with --subrepos'))
1519 # Let --subrepos on the command line override config setting.
1523 # Let --subrepos on the command line override config setting.
1520 ui.setconfig('ui', 'commitsubrepos', True, 'commit')
1524 ui.setconfig('ui', 'commitsubrepos', True, 'commit')
1521
1525
1522 cmdutil.checkunfinished(repo, commit=True)
1526 cmdutil.checkunfinished(repo, commit=True)
1523
1527
1524 branch = repo[None].branch()
1528 branch = repo[None].branch()
1525 bheads = repo.branchheads(branch)
1529 bheads = repo.branchheads(branch)
1526
1530
1527 extra = {}
1531 extra = {}
1528 if opts.get('close_branch'):
1532 if opts.get('close_branch'):
1529 extra['close'] = 1
1533 extra['close'] = 1
1530
1534
1531 if not bheads:
1535 if not bheads:
1532 raise error.Abort(_('can only close branch heads'))
1536 raise error.Abort(_('can only close branch heads'))
1533 elif opts.get('amend'):
1537 elif opts.get('amend'):
1534 if repo[None].parents()[0].p1().branch() != branch and \
1538 if repo[None].parents()[0].p1().branch() != branch and \
1535 repo[None].parents()[0].p2().branch() != branch:
1539 repo[None].parents()[0].p2().branch() != branch:
1536 raise error.Abort(_('can only close branch heads'))
1540 raise error.Abort(_('can only close branch heads'))
1537
1541
1538 if opts.get('amend'):
1542 if opts.get('amend'):
1539 if ui.configbool('ui', 'commitsubrepos'):
1543 if ui.configbool('ui', 'commitsubrepos'):
1540 raise error.Abort(_('cannot amend with ui.commitsubrepos enabled'))
1544 raise error.Abort(_('cannot amend with ui.commitsubrepos enabled'))
1541
1545
1542 old = repo['.']
1546 old = repo['.']
1543 if not old.mutable():
1547 if not old.mutable():
1544 raise error.Abort(_('cannot amend public changesets'))
1548 raise error.Abort(_('cannot amend public changesets'))
1545 if len(repo[None].parents()) > 1:
1549 if len(repo[None].parents()) > 1:
1546 raise error.Abort(_('cannot amend while merging'))
1550 raise error.Abort(_('cannot amend while merging'))
1547 allowunstable = obsolete.isenabled(repo, obsolete.allowunstableopt)
1551 allowunstable = obsolete.isenabled(repo, obsolete.allowunstableopt)
1548 if not allowunstable and old.children():
1552 if not allowunstable and old.children():
1549 raise error.Abort(_('cannot amend changeset with children'))
1553 raise error.Abort(_('cannot amend changeset with children'))
1550
1554
1551 # Currently histedit gets confused if an amend happens while histedit
1555 # Currently histedit gets confused if an amend happens while histedit
1552 # is in progress. Since we have a checkunfinished command, we are
1556 # is in progress. Since we have a checkunfinished command, we are
1553 # temporarily honoring it.
1557 # temporarily honoring it.
1554 #
1558 #
1555 # Note: eventually this guard will be removed. Please do not expect
1559 # Note: eventually this guard will be removed. Please do not expect
1556 # this behavior to remain.
1560 # this behavior to remain.
1557 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
1561 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
1558 cmdutil.checkunfinished(repo)
1562 cmdutil.checkunfinished(repo)
1559
1563
1560 node = cmdutil.amend(ui, repo, old, extra, pats, opts)
1564 node = cmdutil.amend(ui, repo, old, extra, pats, opts)
1561 if node == old.node():
1565 if node == old.node():
1562 ui.status(_("nothing changed\n"))
1566 ui.status(_("nothing changed\n"))
1563 return 1
1567 return 1
1564 else:
1568 else:
1565 def commitfunc(ui, repo, message, match, opts):
1569 def commitfunc(ui, repo, message, match, opts):
1566 overrides = {}
1570 overrides = {}
1567 if opts.get('secret'):
1571 if opts.get('secret'):
1568 overrides[('phases', 'new-commit')] = 'secret'
1572 overrides[('phases', 'new-commit')] = 'secret'
1569
1573
1570 baseui = repo.baseui
1574 baseui = repo.baseui
1571 with baseui.configoverride(overrides, 'commit'):
1575 with baseui.configoverride(overrides, 'commit'):
1572 with ui.configoverride(overrides, 'commit'):
1576 with ui.configoverride(overrides, 'commit'):
1573 editform = cmdutil.mergeeditform(repo[None],
1577 editform = cmdutil.mergeeditform(repo[None],
1574 'commit.normal')
1578 'commit.normal')
1575 editor = cmdutil.getcommiteditor(
1579 editor = cmdutil.getcommiteditor(
1576 editform=editform, **pycompat.strkwargs(opts))
1580 editform=editform, **pycompat.strkwargs(opts))
1577 return repo.commit(message,
1581 return repo.commit(message,
1578 opts.get('user'),
1582 opts.get('user'),
1579 opts.get('date'),
1583 opts.get('date'),
1580 match,
1584 match,
1581 editor=editor,
1585 editor=editor,
1582 extra=extra)
1586 extra=extra)
1583
1587
1584 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1588 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1585
1589
1586 if not node:
1590 if not node:
1587 stat = cmdutil.postcommitstatus(repo, pats, opts)
1591 stat = cmdutil.postcommitstatus(repo, pats, opts)
1588 if stat[3]:
1592 if stat[3]:
1589 ui.status(_("nothing changed (%d missing files, see "
1593 ui.status(_("nothing changed (%d missing files, see "
1590 "'hg status')\n") % len(stat[3]))
1594 "'hg status')\n") % len(stat[3]))
1591 else:
1595 else:
1592 ui.status(_("nothing changed\n"))
1596 ui.status(_("nothing changed\n"))
1593 return 1
1597 return 1
1594
1598
1595 cmdutil.commitstatus(repo, node, branch, bheads, opts)
1599 cmdutil.commitstatus(repo, node, branch, bheads, opts)
1596
1600
1597 @command('config|showconfig|debugconfig',
1601 @command('config|showconfig|debugconfig',
1598 [('u', 'untrusted', None, _('show untrusted configuration options')),
1602 [('u', 'untrusted', None, _('show untrusted configuration options')),
1599 ('e', 'edit', None, _('edit user config')),
1603 ('e', 'edit', None, _('edit user config')),
1600 ('l', 'local', None, _('edit repository config')),
1604 ('l', 'local', None, _('edit repository config')),
1601 ('g', 'global', None, _('edit global config'))] + formatteropts,
1605 ('g', 'global', None, _('edit global config'))] + formatteropts,
1602 _('[-u] [NAME]...'),
1606 _('[-u] [NAME]...'),
1603 optionalrepo=True)
1607 optionalrepo=True)
1604 def config(ui, repo, *values, **opts):
1608 def config(ui, repo, *values, **opts):
1605 """show combined config settings from all hgrc files
1609 """show combined config settings from all hgrc files
1606
1610
1607 With no arguments, print names and values of all config items.
1611 With no arguments, print names and values of all config items.
1608
1612
1609 With one argument of the form section.name, print just the value
1613 With one argument of the form section.name, print just the value
1610 of that config item.
1614 of that config item.
1611
1615
1612 With multiple arguments, print names and values of all config
1616 With multiple arguments, print names and values of all config
1613 items with matching section names.
1617 items with matching section names.
1614
1618
1615 With --edit, start an editor on the user-level config file. With
1619 With --edit, start an editor on the user-level config file. With
1616 --global, edit the system-wide config file. With --local, edit the
1620 --global, edit the system-wide config file. With --local, edit the
1617 repository-level config file.
1621 repository-level config file.
1618
1622
1619 With --debug, the source (filename and line number) is printed
1623 With --debug, the source (filename and line number) is printed
1620 for each config item.
1624 for each config item.
1621
1625
1622 See :hg:`help config` for more information about config files.
1626 See :hg:`help config` for more information about config files.
1623
1627
1624 Returns 0 on success, 1 if NAME does not exist.
1628 Returns 0 on success, 1 if NAME does not exist.
1625
1629
1626 """
1630 """
1627
1631
1628 opts = pycompat.byteskwargs(opts)
1632 opts = pycompat.byteskwargs(opts)
1629 if opts.get('edit') or opts.get('local') or opts.get('global'):
1633 if opts.get('edit') or opts.get('local') or opts.get('global'):
1630 if opts.get('local') and opts.get('global'):
1634 if opts.get('local') and opts.get('global'):
1631 raise error.Abort(_("can't use --local and --global together"))
1635 raise error.Abort(_("can't use --local and --global together"))
1632
1636
1633 if opts.get('local'):
1637 if opts.get('local'):
1634 if not repo:
1638 if not repo:
1635 raise error.Abort(_("can't use --local outside a repository"))
1639 raise error.Abort(_("can't use --local outside a repository"))
1636 paths = [repo.vfs.join('hgrc')]
1640 paths = [repo.vfs.join('hgrc')]
1637 elif opts.get('global'):
1641 elif opts.get('global'):
1638 paths = rcutil.systemrcpath()
1642 paths = rcutil.systemrcpath()
1639 else:
1643 else:
1640 paths = rcutil.userrcpath()
1644 paths = rcutil.userrcpath()
1641
1645
1642 for f in paths:
1646 for f in paths:
1643 if os.path.exists(f):
1647 if os.path.exists(f):
1644 break
1648 break
1645 else:
1649 else:
1646 if opts.get('global'):
1650 if opts.get('global'):
1647 samplehgrc = uimod.samplehgrcs['global']
1651 samplehgrc = uimod.samplehgrcs['global']
1648 elif opts.get('local'):
1652 elif opts.get('local'):
1649 samplehgrc = uimod.samplehgrcs['local']
1653 samplehgrc = uimod.samplehgrcs['local']
1650 else:
1654 else:
1651 samplehgrc = uimod.samplehgrcs['user']
1655 samplehgrc = uimod.samplehgrcs['user']
1652
1656
1653 f = paths[0]
1657 f = paths[0]
1654 fp = open(f, "wb")
1658 fp = open(f, "wb")
1655 fp.write(util.tonativeeol(samplehgrc))
1659 fp.write(util.tonativeeol(samplehgrc))
1656 fp.close()
1660 fp.close()
1657
1661
1658 editor = ui.geteditor()
1662 editor = ui.geteditor()
1659 ui.system("%s \"%s\"" % (editor, f),
1663 ui.system("%s \"%s\"" % (editor, f),
1660 onerr=error.Abort, errprefix=_("edit failed"),
1664 onerr=error.Abort, errprefix=_("edit failed"),
1661 blockedtag='config_edit')
1665 blockedtag='config_edit')
1662 return
1666 return
1663 ui.pager('config')
1667 ui.pager('config')
1664 fm = ui.formatter('config', opts)
1668 fm = ui.formatter('config', opts)
1665 for t, f in rcutil.rccomponents():
1669 for t, f in rcutil.rccomponents():
1666 if t == 'path':
1670 if t == 'path':
1667 ui.debug('read config from: %s\n' % f)
1671 ui.debug('read config from: %s\n' % f)
1668 elif t == 'items':
1672 elif t == 'items':
1669 for section, name, value, source in f:
1673 for section, name, value, source in f:
1670 ui.debug('set config by: %s\n' % source)
1674 ui.debug('set config by: %s\n' % source)
1671 else:
1675 else:
1672 raise error.ProgrammingError('unknown rctype: %s' % t)
1676 raise error.ProgrammingError('unknown rctype: %s' % t)
1673 untrusted = bool(opts.get('untrusted'))
1677 untrusted = bool(opts.get('untrusted'))
1674 if values:
1678 if values:
1675 sections = [v for v in values if '.' not in v]
1679 sections = [v for v in values if '.' not in v]
1676 items = [v for v in values if '.' in v]
1680 items = [v for v in values if '.' in v]
1677 if len(items) > 1 or items and sections:
1681 if len(items) > 1 or items and sections:
1678 raise error.Abort(_('only one config item permitted'))
1682 raise error.Abort(_('only one config item permitted'))
1679 matched = False
1683 matched = False
1680 for section, name, value in ui.walkconfig(untrusted=untrusted):
1684 for section, name, value in ui.walkconfig(untrusted=untrusted):
1681 source = ui.configsource(section, name, untrusted)
1685 source = ui.configsource(section, name, untrusted)
1682 value = pycompat.bytestr(value)
1686 value = pycompat.bytestr(value)
1683 if fm.isplain():
1687 if fm.isplain():
1684 source = source or 'none'
1688 source = source or 'none'
1685 value = value.replace('\n', '\\n')
1689 value = value.replace('\n', '\\n')
1686 entryname = section + '.' + name
1690 entryname = section + '.' + name
1687 if values:
1691 if values:
1688 for v in values:
1692 for v in values:
1689 if v == section:
1693 if v == section:
1690 fm.startitem()
1694 fm.startitem()
1691 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1695 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1692 fm.write('name value', '%s=%s\n', entryname, value)
1696 fm.write('name value', '%s=%s\n', entryname, value)
1693 matched = True
1697 matched = True
1694 elif v == entryname:
1698 elif v == entryname:
1695 fm.startitem()
1699 fm.startitem()
1696 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1700 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1697 fm.write('value', '%s\n', value)
1701 fm.write('value', '%s\n', value)
1698 fm.data(name=entryname)
1702 fm.data(name=entryname)
1699 matched = True
1703 matched = True
1700 else:
1704 else:
1701 fm.startitem()
1705 fm.startitem()
1702 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1706 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1703 fm.write('name value', '%s=%s\n', entryname, value)
1707 fm.write('name value', '%s=%s\n', entryname, value)
1704 matched = True
1708 matched = True
1705 fm.end()
1709 fm.end()
1706 if matched:
1710 if matched:
1707 return 0
1711 return 0
1708 return 1
1712 return 1
1709
1713
1710 @command('copy|cp',
1714 @command('copy|cp',
1711 [('A', 'after', None, _('record a copy that has already occurred')),
1715 [('A', 'after', None, _('record a copy that has already occurred')),
1712 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1716 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1713 ] + walkopts + dryrunopts,
1717 ] + walkopts + dryrunopts,
1714 _('[OPTION]... [SOURCE]... DEST'))
1718 _('[OPTION]... [SOURCE]... DEST'))
1715 def copy(ui, repo, *pats, **opts):
1719 def copy(ui, repo, *pats, **opts):
1716 """mark files as copied for the next commit
1720 """mark files as copied for the next commit
1717
1721
1718 Mark dest as having copies of source files. If dest is a
1722 Mark dest as having copies of source files. If dest is a
1719 directory, copies are put in that directory. If dest is a file,
1723 directory, copies are put in that directory. If dest is a file,
1720 the source must be a single file.
1724 the source must be a single file.
1721
1725
1722 By default, this command copies the contents of files as they
1726 By default, this command copies the contents of files as they
1723 exist in the working directory. If invoked with -A/--after, the
1727 exist in the working directory. If invoked with -A/--after, the
1724 operation is recorded, but no copying is performed.
1728 operation is recorded, but no copying is performed.
1725
1729
1726 This command takes effect with the next commit. To undo a copy
1730 This command takes effect with the next commit. To undo a copy
1727 before that, see :hg:`revert`.
1731 before that, see :hg:`revert`.
1728
1732
1729 Returns 0 on success, 1 if errors are encountered.
1733 Returns 0 on success, 1 if errors are encountered.
1730 """
1734 """
1731 opts = pycompat.byteskwargs(opts)
1735 opts = pycompat.byteskwargs(opts)
1732 with repo.wlock(False):
1736 with repo.wlock(False):
1733 return cmdutil.copy(ui, repo, pats, opts)
1737 return cmdutil.copy(ui, repo, pats, opts)
1734
1738
1735 @command('debugcommands', [], _('[COMMAND]'), norepo=True)
1739 @command('debugcommands', [], _('[COMMAND]'), norepo=True)
1736 def debugcommands(ui, cmd='', *args):
1740 def debugcommands(ui, cmd='', *args):
1737 """list all available commands and options"""
1741 """list all available commands and options"""
1738 for cmd, vals in sorted(table.iteritems()):
1742 for cmd, vals in sorted(table.iteritems()):
1739 cmd = cmd.split('|')[0].strip('^')
1743 cmd = cmd.split('|')[0].strip('^')
1740 opts = ', '.join([i[1] for i in vals[1]])
1744 opts = ', '.join([i[1] for i in vals[1]])
1741 ui.write('%s: %s\n' % (cmd, opts))
1745 ui.write('%s: %s\n' % (cmd, opts))
1742
1746
1743 @command('debugcomplete',
1747 @command('debugcomplete',
1744 [('o', 'options', None, _('show the command options'))],
1748 [('o', 'options', None, _('show the command options'))],
1745 _('[-o] CMD'),
1749 _('[-o] CMD'),
1746 norepo=True)
1750 norepo=True)
1747 def debugcomplete(ui, cmd='', **opts):
1751 def debugcomplete(ui, cmd='', **opts):
1748 """returns the completion list associated with the given command"""
1752 """returns the completion list associated with the given command"""
1749
1753
1750 if opts.get('options'):
1754 if opts.get('options'):
1751 options = []
1755 options = []
1752 otables = [globalopts]
1756 otables = [globalopts]
1753 if cmd:
1757 if cmd:
1754 aliases, entry = cmdutil.findcmd(cmd, table, False)
1758 aliases, entry = cmdutil.findcmd(cmd, table, False)
1755 otables.append(entry[1])
1759 otables.append(entry[1])
1756 for t in otables:
1760 for t in otables:
1757 for o in t:
1761 for o in t:
1758 if "(DEPRECATED)" in o[3]:
1762 if "(DEPRECATED)" in o[3]:
1759 continue
1763 continue
1760 if o[0]:
1764 if o[0]:
1761 options.append('-%s' % o[0])
1765 options.append('-%s' % o[0])
1762 options.append('--%s' % o[1])
1766 options.append('--%s' % o[1])
1763 ui.write("%s\n" % "\n".join(options))
1767 ui.write("%s\n" % "\n".join(options))
1764 return
1768 return
1765
1769
1766 cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
1770 cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
1767 if ui.verbose:
1771 if ui.verbose:
1768 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1772 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1769 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1773 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1770
1774
1771 @command('^diff',
1775 @command('^diff',
1772 [('r', 'rev', [], _('revision'), _('REV')),
1776 [('r', 'rev', [], _('revision'), _('REV')),
1773 ('c', 'change', '', _('change made by revision'), _('REV'))
1777 ('c', 'change', '', _('change made by revision'), _('REV'))
1774 ] + diffopts + diffopts2 + walkopts + subrepoopts,
1778 ] + diffopts + diffopts2 + walkopts + subrepoopts,
1775 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
1779 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
1776 inferrepo=True)
1780 inferrepo=True)
1777 def diff(ui, repo, *pats, **opts):
1781 def diff(ui, repo, *pats, **opts):
1778 """diff repository (or selected files)
1782 """diff repository (or selected files)
1779
1783
1780 Show differences between revisions for the specified files.
1784 Show differences between revisions for the specified files.
1781
1785
1782 Differences between files are shown using the unified diff format.
1786 Differences between files are shown using the unified diff format.
1783
1787
1784 .. note::
1788 .. note::
1785
1789
1786 :hg:`diff` may generate unexpected results for merges, as it will
1790 :hg:`diff` may generate unexpected results for merges, as it will
1787 default to comparing against the working directory's first
1791 default to comparing against the working directory's first
1788 parent changeset if no revisions are specified.
1792 parent changeset if no revisions are specified.
1789
1793
1790 When two revision arguments are given, then changes are shown
1794 When two revision arguments are given, then changes are shown
1791 between those revisions. If only one revision is specified then
1795 between those revisions. If only one revision is specified then
1792 that revision is compared to the working directory, and, when no
1796 that revision is compared to the working directory, and, when no
1793 revisions are specified, the working directory files are compared
1797 revisions are specified, the working directory files are compared
1794 to its first parent.
1798 to its first parent.
1795
1799
1796 Alternatively you can specify -c/--change with a revision to see
1800 Alternatively you can specify -c/--change with a revision to see
1797 the changes in that changeset relative to its first parent.
1801 the changes in that changeset relative to its first parent.
1798
1802
1799 Without the -a/--text option, diff will avoid generating diffs of
1803 Without the -a/--text option, diff will avoid generating diffs of
1800 files it detects as binary. With -a, diff will generate a diff
1804 files it detects as binary. With -a, diff will generate a diff
1801 anyway, probably with undesirable results.
1805 anyway, probably with undesirable results.
1802
1806
1803 Use the -g/--git option to generate diffs in the git extended diff
1807 Use the -g/--git option to generate diffs in the git extended diff
1804 format. For more information, read :hg:`help diffs`.
1808 format. For more information, read :hg:`help diffs`.
1805
1809
1806 .. container:: verbose
1810 .. container:: verbose
1807
1811
1808 Examples:
1812 Examples:
1809
1813
1810 - compare a file in the current working directory to its parent::
1814 - compare a file in the current working directory to its parent::
1811
1815
1812 hg diff foo.c
1816 hg diff foo.c
1813
1817
1814 - compare two historical versions of a directory, with rename info::
1818 - compare two historical versions of a directory, with rename info::
1815
1819
1816 hg diff --git -r 1.0:1.2 lib/
1820 hg diff --git -r 1.0:1.2 lib/
1817
1821
1818 - get change stats relative to the last change on some date::
1822 - get change stats relative to the last change on some date::
1819
1823
1820 hg diff --stat -r "date('may 2')"
1824 hg diff --stat -r "date('may 2')"
1821
1825
1822 - diff all newly-added files that contain a keyword::
1826 - diff all newly-added files that contain a keyword::
1823
1827
1824 hg diff "set:added() and grep(GNU)"
1828 hg diff "set:added() and grep(GNU)"
1825
1829
1826 - compare a revision and its parents::
1830 - compare a revision and its parents::
1827
1831
1828 hg diff -c 9353 # compare against first parent
1832 hg diff -c 9353 # compare against first parent
1829 hg diff -r 9353^:9353 # same using revset syntax
1833 hg diff -r 9353^:9353 # same using revset syntax
1830 hg diff -r 9353^2:9353 # compare against the second parent
1834 hg diff -r 9353^2:9353 # compare against the second parent
1831
1835
1832 Returns 0 on success.
1836 Returns 0 on success.
1833 """
1837 """
1834
1838
1835 opts = pycompat.byteskwargs(opts)
1839 opts = pycompat.byteskwargs(opts)
1836 revs = opts.get('rev')
1840 revs = opts.get('rev')
1837 change = opts.get('change')
1841 change = opts.get('change')
1838 stat = opts.get('stat')
1842 stat = opts.get('stat')
1839 reverse = opts.get('reverse')
1843 reverse = opts.get('reverse')
1840
1844
1841 if revs and change:
1845 if revs and change:
1842 msg = _('cannot specify --rev and --change at the same time')
1846 msg = _('cannot specify --rev and --change at the same time')
1843 raise error.Abort(msg)
1847 raise error.Abort(msg)
1844 elif change:
1848 elif change:
1845 node2 = scmutil.revsingle(repo, change, None).node()
1849 node2 = scmutil.revsingle(repo, change, None).node()
1846 node1 = repo[node2].p1().node()
1850 node1 = repo[node2].p1().node()
1847 else:
1851 else:
1848 node1, node2 = scmutil.revpair(repo, revs)
1852 node1, node2 = scmutil.revpair(repo, revs)
1849
1853
1850 if reverse:
1854 if reverse:
1851 node1, node2 = node2, node1
1855 node1, node2 = node2, node1
1852
1856
1853 diffopts = patch.diffallopts(ui, opts)
1857 diffopts = patch.diffallopts(ui, opts)
1854 m = scmutil.match(repo[node2], pats, opts)
1858 m = scmutil.match(repo[node2], pats, opts)
1855 ui.pager('diff')
1859 ui.pager('diff')
1856 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
1860 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
1857 listsubrepos=opts.get('subrepos'),
1861 listsubrepos=opts.get('subrepos'),
1858 root=opts.get('root'))
1862 root=opts.get('root'))
1859
1863
1860 @command('^export',
1864 @command('^export',
1861 [('o', 'output', '',
1865 [('o', 'output', '',
1862 _('print output to file with formatted name'), _('FORMAT')),
1866 _('print output to file with formatted name'), _('FORMAT')),
1863 ('', 'switch-parent', None, _('diff against the second parent')),
1867 ('', 'switch-parent', None, _('diff against the second parent')),
1864 ('r', 'rev', [], _('revisions to export'), _('REV')),
1868 ('r', 'rev', [], _('revisions to export'), _('REV')),
1865 ] + diffopts,
1869 ] + diffopts,
1866 _('[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'))
1870 _('[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'))
1867 def export(ui, repo, *changesets, **opts):
1871 def export(ui, repo, *changesets, **opts):
1868 """dump the header and diffs for one or more changesets
1872 """dump the header and diffs for one or more changesets
1869
1873
1870 Print the changeset header and diffs for one or more revisions.
1874 Print the changeset header and diffs for one or more revisions.
1871 If no revision is given, the parent of the working directory is used.
1875 If no revision is given, the parent of the working directory is used.
1872
1876
1873 The information shown in the changeset header is: author, date,
1877 The information shown in the changeset header is: author, date,
1874 branch name (if non-default), changeset hash, parent(s) and commit
1878 branch name (if non-default), changeset hash, parent(s) and commit
1875 comment.
1879 comment.
1876
1880
1877 .. note::
1881 .. note::
1878
1882
1879 :hg:`export` may generate unexpected diff output for merge
1883 :hg:`export` may generate unexpected diff output for merge
1880 changesets, as it will compare the merge changeset against its
1884 changesets, as it will compare the merge changeset against its
1881 first parent only.
1885 first parent only.
1882
1886
1883 Output may be to a file, in which case the name of the file is
1887 Output may be to a file, in which case the name of the file is
1884 given using a format string. The formatting rules are as follows:
1888 given using a format string. The formatting rules are as follows:
1885
1889
1886 :``%%``: literal "%" character
1890 :``%%``: literal "%" character
1887 :``%H``: changeset hash (40 hexadecimal digits)
1891 :``%H``: changeset hash (40 hexadecimal digits)
1888 :``%N``: number of patches being generated
1892 :``%N``: number of patches being generated
1889 :``%R``: changeset revision number
1893 :``%R``: changeset revision number
1890 :``%b``: basename of the exporting repository
1894 :``%b``: basename of the exporting repository
1891 :``%h``: short-form changeset hash (12 hexadecimal digits)
1895 :``%h``: short-form changeset hash (12 hexadecimal digits)
1892 :``%m``: first line of the commit message (only alphanumeric characters)
1896 :``%m``: first line of the commit message (only alphanumeric characters)
1893 :``%n``: zero-padded sequence number, starting at 1
1897 :``%n``: zero-padded sequence number, starting at 1
1894 :``%r``: zero-padded changeset revision number
1898 :``%r``: zero-padded changeset revision number
1895
1899
1896 Without the -a/--text option, export will avoid generating diffs
1900 Without the -a/--text option, export will avoid generating diffs
1897 of files it detects as binary. With -a, export will generate a
1901 of files it detects as binary. With -a, export will generate a
1898 diff anyway, probably with undesirable results.
1902 diff anyway, probably with undesirable results.
1899
1903
1900 Use the -g/--git option to generate diffs in the git extended diff
1904 Use the -g/--git option to generate diffs in the git extended diff
1901 format. See :hg:`help diffs` for more information.
1905 format. See :hg:`help diffs` for more information.
1902
1906
1903 With the --switch-parent option, the diff will be against the
1907 With the --switch-parent option, the diff will be against the
1904 second parent. It can be useful to review a merge.
1908 second parent. It can be useful to review a merge.
1905
1909
1906 .. container:: verbose
1910 .. container:: verbose
1907
1911
1908 Examples:
1912 Examples:
1909
1913
1910 - use export and import to transplant a bugfix to the current
1914 - use export and import to transplant a bugfix to the current
1911 branch::
1915 branch::
1912
1916
1913 hg export -r 9353 | hg import -
1917 hg export -r 9353 | hg import -
1914
1918
1915 - export all the changesets between two revisions to a file with
1919 - export all the changesets between two revisions to a file with
1916 rename information::
1920 rename information::
1917
1921
1918 hg export --git -r 123:150 > changes.txt
1922 hg export --git -r 123:150 > changes.txt
1919
1923
1920 - split outgoing changes into a series of patches with
1924 - split outgoing changes into a series of patches with
1921 descriptive names::
1925 descriptive names::
1922
1926
1923 hg export -r "outgoing()" -o "%n-%m.patch"
1927 hg export -r "outgoing()" -o "%n-%m.patch"
1924
1928
1925 Returns 0 on success.
1929 Returns 0 on success.
1926 """
1930 """
1927 opts = pycompat.byteskwargs(opts)
1931 opts = pycompat.byteskwargs(opts)
1928 changesets += tuple(opts.get('rev', []))
1932 changesets += tuple(opts.get('rev', []))
1929 if not changesets:
1933 if not changesets:
1930 changesets = ['.']
1934 changesets = ['.']
1931 revs = scmutil.revrange(repo, changesets)
1935 revs = scmutil.revrange(repo, changesets)
1932 if not revs:
1936 if not revs:
1933 raise error.Abort(_("export requires at least one changeset"))
1937 raise error.Abort(_("export requires at least one changeset"))
1934 if len(revs) > 1:
1938 if len(revs) > 1:
1935 ui.note(_('exporting patches:\n'))
1939 ui.note(_('exporting patches:\n'))
1936 else:
1940 else:
1937 ui.note(_('exporting patch:\n'))
1941 ui.note(_('exporting patch:\n'))
1938 ui.pager('export')
1942 ui.pager('export')
1939 cmdutil.export(repo, revs, fntemplate=opts.get('output'),
1943 cmdutil.export(repo, revs, fntemplate=opts.get('output'),
1940 switch_parent=opts.get('switch_parent'),
1944 switch_parent=opts.get('switch_parent'),
1941 opts=patch.diffallopts(ui, opts))
1945 opts=patch.diffallopts(ui, opts))
1942
1946
1943 @command('files',
1947 @command('files',
1944 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
1948 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
1945 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
1949 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
1946 ] + walkopts + formatteropts + subrepoopts,
1950 ] + walkopts + formatteropts + subrepoopts,
1947 _('[OPTION]... [FILE]...'))
1951 _('[OPTION]... [FILE]...'))
1948 def files(ui, repo, *pats, **opts):
1952 def files(ui, repo, *pats, **opts):
1949 """list tracked files
1953 """list tracked files
1950
1954
1951 Print files under Mercurial control in the working directory or
1955 Print files under Mercurial control in the working directory or
1952 specified revision for given files (excluding removed files).
1956 specified revision for given files (excluding removed files).
1953 Files can be specified as filenames or filesets.
1957 Files can be specified as filenames or filesets.
1954
1958
1955 If no files are given to match, this command prints the names
1959 If no files are given to match, this command prints the names
1956 of all files under Mercurial control.
1960 of all files under Mercurial control.
1957
1961
1958 .. container:: verbose
1962 .. container:: verbose
1959
1963
1960 Examples:
1964 Examples:
1961
1965
1962 - list all files under the current directory::
1966 - list all files under the current directory::
1963
1967
1964 hg files .
1968 hg files .
1965
1969
1966 - shows sizes and flags for current revision::
1970 - shows sizes and flags for current revision::
1967
1971
1968 hg files -vr .
1972 hg files -vr .
1969
1973
1970 - list all files named README::
1974 - list all files named README::
1971
1975
1972 hg files -I "**/README"
1976 hg files -I "**/README"
1973
1977
1974 - list all binary files::
1978 - list all binary files::
1975
1979
1976 hg files "set:binary()"
1980 hg files "set:binary()"
1977
1981
1978 - find files containing a regular expression::
1982 - find files containing a regular expression::
1979
1983
1980 hg files "set:grep('bob')"
1984 hg files "set:grep('bob')"
1981
1985
1982 - search tracked file contents with xargs and grep::
1986 - search tracked file contents with xargs and grep::
1983
1987
1984 hg files -0 | xargs -0 grep foo
1988 hg files -0 | xargs -0 grep foo
1985
1989
1986 See :hg:`help patterns` and :hg:`help filesets` for more information
1990 See :hg:`help patterns` and :hg:`help filesets` for more information
1987 on specifying file patterns.
1991 on specifying file patterns.
1988
1992
1989 Returns 0 if a match is found, 1 otherwise.
1993 Returns 0 if a match is found, 1 otherwise.
1990
1994
1991 """
1995 """
1992
1996
1993 opts = pycompat.byteskwargs(opts)
1997 opts = pycompat.byteskwargs(opts)
1994 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
1998 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
1995
1999
1996 end = '\n'
2000 end = '\n'
1997 if opts.get('print0'):
2001 if opts.get('print0'):
1998 end = '\0'
2002 end = '\0'
1999 fmt = '%s' + end
2003 fmt = '%s' + end
2000
2004
2001 m = scmutil.match(ctx, pats, opts)
2005 m = scmutil.match(ctx, pats, opts)
2002 ui.pager('files')
2006 ui.pager('files')
2003 with ui.formatter('files', opts) as fm:
2007 with ui.formatter('files', opts) as fm:
2004 return cmdutil.files(ui, ctx, m, fm, fmt, opts.get('subrepos'))
2008 return cmdutil.files(ui, ctx, m, fm, fmt, opts.get('subrepos'))
2005
2009
2006 @command('^forget', walkopts, _('[OPTION]... FILE...'), inferrepo=True)
2010 @command('^forget', walkopts, _('[OPTION]... FILE...'), inferrepo=True)
2007 def forget(ui, repo, *pats, **opts):
2011 def forget(ui, repo, *pats, **opts):
2008 """forget the specified files on the next commit
2012 """forget the specified files on the next commit
2009
2013
2010 Mark the specified files so they will no longer be tracked
2014 Mark the specified files so they will no longer be tracked
2011 after the next commit.
2015 after the next commit.
2012
2016
2013 This only removes files from the current branch, not from the
2017 This only removes files from the current branch, not from the
2014 entire project history, and it does not delete them from the
2018 entire project history, and it does not delete them from the
2015 working directory.
2019 working directory.
2016
2020
2017 To delete the file from the working directory, see :hg:`remove`.
2021 To delete the file from the working directory, see :hg:`remove`.
2018
2022
2019 To undo a forget before the next commit, see :hg:`add`.
2023 To undo a forget before the next commit, see :hg:`add`.
2020
2024
2021 .. container:: verbose
2025 .. container:: verbose
2022
2026
2023 Examples:
2027 Examples:
2024
2028
2025 - forget newly-added binary files::
2029 - forget newly-added binary files::
2026
2030
2027 hg forget "set:added() and binary()"
2031 hg forget "set:added() and binary()"
2028
2032
2029 - forget files that would be excluded by .hgignore::
2033 - forget files that would be excluded by .hgignore::
2030
2034
2031 hg forget "set:hgignore()"
2035 hg forget "set:hgignore()"
2032
2036
2033 Returns 0 on success.
2037 Returns 0 on success.
2034 """
2038 """
2035
2039
2036 opts = pycompat.byteskwargs(opts)
2040 opts = pycompat.byteskwargs(opts)
2037 if not pats:
2041 if not pats:
2038 raise error.Abort(_('no files specified'))
2042 raise error.Abort(_('no files specified'))
2039
2043
2040 m = scmutil.match(repo[None], pats, opts)
2044 m = scmutil.match(repo[None], pats, opts)
2041 rejected = cmdutil.forget(ui, repo, m, prefix="", explicitonly=False)[0]
2045 rejected = cmdutil.forget(ui, repo, m, prefix="", explicitonly=False)[0]
2042 return rejected and 1 or 0
2046 return rejected and 1 or 0
2043
2047
2044 @command(
2048 @command(
2045 'graft',
2049 'graft',
2046 [('r', 'rev', [], _('revisions to graft'), _('REV')),
2050 [('r', 'rev', [], _('revisions to graft'), _('REV')),
2047 ('c', 'continue', False, _('resume interrupted graft')),
2051 ('c', 'continue', False, _('resume interrupted graft')),
2048 ('e', 'edit', False, _('invoke editor on commit messages')),
2052 ('e', 'edit', False, _('invoke editor on commit messages')),
2049 ('', 'log', None, _('append graft info to log message')),
2053 ('', 'log', None, _('append graft info to log message')),
2050 ('f', 'force', False, _('force graft')),
2054 ('f', 'force', False, _('force graft')),
2051 ('D', 'currentdate', False,
2055 ('D', 'currentdate', False,
2052 _('record the current date as commit date')),
2056 _('record the current date as commit date')),
2053 ('U', 'currentuser', False,
2057 ('U', 'currentuser', False,
2054 _('record the current user as committer'), _('DATE'))]
2058 _('record the current user as committer'), _('DATE'))]
2055 + commitopts2 + mergetoolopts + dryrunopts,
2059 + commitopts2 + mergetoolopts + dryrunopts,
2056 _('[OPTION]... [-r REV]... REV...'))
2060 _('[OPTION]... [-r REV]... REV...'))
2057 def graft(ui, repo, *revs, **opts):
2061 def graft(ui, repo, *revs, **opts):
2058 '''copy changes from other branches onto the current branch
2062 '''copy changes from other branches onto the current branch
2059
2063
2060 This command uses Mercurial's merge logic to copy individual
2064 This command uses Mercurial's merge logic to copy individual
2061 changes from other branches without merging branches in the
2065 changes from other branches without merging branches in the
2062 history graph. This is sometimes known as 'backporting' or
2066 history graph. This is sometimes known as 'backporting' or
2063 'cherry-picking'. By default, graft will copy user, date, and
2067 'cherry-picking'. By default, graft will copy user, date, and
2064 description from the source changesets.
2068 description from the source changesets.
2065
2069
2066 Changesets that are ancestors of the current revision, that have
2070 Changesets that are ancestors of the current revision, that have
2067 already been grafted, or that are merges will be skipped.
2071 already been grafted, or that are merges will be skipped.
2068
2072
2069 If --log is specified, log messages will have a comment appended
2073 If --log is specified, log messages will have a comment appended
2070 of the form::
2074 of the form::
2071
2075
2072 (grafted from CHANGESETHASH)
2076 (grafted from CHANGESETHASH)
2073
2077
2074 If --force is specified, revisions will be grafted even if they
2078 If --force is specified, revisions will be grafted even if they
2075 are already ancestors of, or have been grafted to, the destination.
2079 are already ancestors of, or have been grafted to, the destination.
2076 This is useful when the revisions have since been backed out.
2080 This is useful when the revisions have since been backed out.
2077
2081
2078 If a graft merge results in conflicts, the graft process is
2082 If a graft merge results in conflicts, the graft process is
2079 interrupted so that the current merge can be manually resolved.
2083 interrupted so that the current merge can be manually resolved.
2080 Once all conflicts are addressed, the graft process can be
2084 Once all conflicts are addressed, the graft process can be
2081 continued with the -c/--continue option.
2085 continued with the -c/--continue option.
2082
2086
2083 .. note::
2087 .. note::
2084
2088
2085 The -c/--continue option does not reapply earlier options, except
2089 The -c/--continue option does not reapply earlier options, except
2086 for --force.
2090 for --force.
2087
2091
2088 .. container:: verbose
2092 .. container:: verbose
2089
2093
2090 Examples:
2094 Examples:
2091
2095
2092 - copy a single change to the stable branch and edit its description::
2096 - copy a single change to the stable branch and edit its description::
2093
2097
2094 hg update stable
2098 hg update stable
2095 hg graft --edit 9393
2099 hg graft --edit 9393
2096
2100
2097 - graft a range of changesets with one exception, updating dates::
2101 - graft a range of changesets with one exception, updating dates::
2098
2102
2099 hg graft -D "2085::2093 and not 2091"
2103 hg graft -D "2085::2093 and not 2091"
2100
2104
2101 - continue a graft after resolving conflicts::
2105 - continue a graft after resolving conflicts::
2102
2106
2103 hg graft -c
2107 hg graft -c
2104
2108
2105 - show the source of a grafted changeset::
2109 - show the source of a grafted changeset::
2106
2110
2107 hg log --debug -r .
2111 hg log --debug -r .
2108
2112
2109 - show revisions sorted by date::
2113 - show revisions sorted by date::
2110
2114
2111 hg log -r "sort(all(), date)"
2115 hg log -r "sort(all(), date)"
2112
2116
2113 See :hg:`help revisions` for more about specifying revisions.
2117 See :hg:`help revisions` for more about specifying revisions.
2114
2118
2115 Returns 0 on successful completion.
2119 Returns 0 on successful completion.
2116 '''
2120 '''
2117 with repo.wlock():
2121 with repo.wlock():
2118 return _dograft(ui, repo, *revs, **opts)
2122 return _dograft(ui, repo, *revs, **opts)
2119
2123
2120 def _dograft(ui, repo, *revs, **opts):
2124 def _dograft(ui, repo, *revs, **opts):
2121 opts = pycompat.byteskwargs(opts)
2125 opts = pycompat.byteskwargs(opts)
2122 if revs and opts.get('rev'):
2126 if revs and opts.get('rev'):
2123 ui.warn(_('warning: inconsistent use of --rev might give unexpected '
2127 ui.warn(_('warning: inconsistent use of --rev might give unexpected '
2124 'revision ordering!\n'))
2128 'revision ordering!\n'))
2125
2129
2126 revs = list(revs)
2130 revs = list(revs)
2127 revs.extend(opts.get('rev'))
2131 revs.extend(opts.get('rev'))
2128
2132
2129 if not opts.get('user') and opts.get('currentuser'):
2133 if not opts.get('user') and opts.get('currentuser'):
2130 opts['user'] = ui.username()
2134 opts['user'] = ui.username()
2131 if not opts.get('date') and opts.get('currentdate'):
2135 if not opts.get('date') and opts.get('currentdate'):
2132 opts['date'] = "%d %d" % util.makedate()
2136 opts['date'] = "%d %d" % util.makedate()
2133
2137
2134 editor = cmdutil.getcommiteditor(editform='graft',
2138 editor = cmdutil.getcommiteditor(editform='graft',
2135 **pycompat.strkwargs(opts))
2139 **pycompat.strkwargs(opts))
2136
2140
2137 cont = False
2141 cont = False
2138 if opts.get('continue'):
2142 if opts.get('continue'):
2139 cont = True
2143 cont = True
2140 if revs:
2144 if revs:
2141 raise error.Abort(_("can't specify --continue and revisions"))
2145 raise error.Abort(_("can't specify --continue and revisions"))
2142 # read in unfinished revisions
2146 # read in unfinished revisions
2143 try:
2147 try:
2144 nodes = repo.vfs.read('graftstate').splitlines()
2148 nodes = repo.vfs.read('graftstate').splitlines()
2145 revs = [repo[node].rev() for node in nodes]
2149 revs = [repo[node].rev() for node in nodes]
2146 except IOError as inst:
2150 except IOError as inst:
2147 if inst.errno != errno.ENOENT:
2151 if inst.errno != errno.ENOENT:
2148 raise
2152 raise
2149 cmdutil.wrongtooltocontinue(repo, _('graft'))
2153 cmdutil.wrongtooltocontinue(repo, _('graft'))
2150 else:
2154 else:
2151 cmdutil.checkunfinished(repo)
2155 cmdutil.checkunfinished(repo)
2152 cmdutil.bailifchanged(repo)
2156 cmdutil.bailifchanged(repo)
2153 if not revs:
2157 if not revs:
2154 raise error.Abort(_('no revisions specified'))
2158 raise error.Abort(_('no revisions specified'))
2155 revs = scmutil.revrange(repo, revs)
2159 revs = scmutil.revrange(repo, revs)
2156
2160
2157 skipped = set()
2161 skipped = set()
2158 # check for merges
2162 # check for merges
2159 for rev in repo.revs('%ld and merge()', revs):
2163 for rev in repo.revs('%ld and merge()', revs):
2160 ui.warn(_('skipping ungraftable merge revision %d\n') % rev)
2164 ui.warn(_('skipping ungraftable merge revision %d\n') % rev)
2161 skipped.add(rev)
2165 skipped.add(rev)
2162 revs = [r for r in revs if r not in skipped]
2166 revs = [r for r in revs if r not in skipped]
2163 if not revs:
2167 if not revs:
2164 return -1
2168 return -1
2165
2169
2166 # Don't check in the --continue case, in effect retaining --force across
2170 # Don't check in the --continue case, in effect retaining --force across
2167 # --continues. That's because without --force, any revisions we decided to
2171 # --continues. That's because without --force, any revisions we decided to
2168 # skip would have been filtered out here, so they wouldn't have made their
2172 # skip would have been filtered out here, so they wouldn't have made their
2169 # way to the graftstate. With --force, any revisions we would have otherwise
2173 # way to the graftstate. With --force, any revisions we would have otherwise
2170 # skipped would not have been filtered out, and if they hadn't been applied
2174 # skipped would not have been filtered out, and if they hadn't been applied
2171 # already, they'd have been in the graftstate.
2175 # already, they'd have been in the graftstate.
2172 if not (cont or opts.get('force')):
2176 if not (cont or opts.get('force')):
2173 # check for ancestors of dest branch
2177 # check for ancestors of dest branch
2174 crev = repo['.'].rev()
2178 crev = repo['.'].rev()
2175 ancestors = repo.changelog.ancestors([crev], inclusive=True)
2179 ancestors = repo.changelog.ancestors([crev], inclusive=True)
2176 # XXX make this lazy in the future
2180 # XXX make this lazy in the future
2177 # don't mutate while iterating, create a copy
2181 # don't mutate while iterating, create a copy
2178 for rev in list(revs):
2182 for rev in list(revs):
2179 if rev in ancestors:
2183 if rev in ancestors:
2180 ui.warn(_('skipping ancestor revision %d:%s\n') %
2184 ui.warn(_('skipping ancestor revision %d:%s\n') %
2181 (rev, repo[rev]))
2185 (rev, repo[rev]))
2182 # XXX remove on list is slow
2186 # XXX remove on list is slow
2183 revs.remove(rev)
2187 revs.remove(rev)
2184 if not revs:
2188 if not revs:
2185 return -1
2189 return -1
2186
2190
2187 # analyze revs for earlier grafts
2191 # analyze revs for earlier grafts
2188 ids = {}
2192 ids = {}
2189 for ctx in repo.set("%ld", revs):
2193 for ctx in repo.set("%ld", revs):
2190 ids[ctx.hex()] = ctx.rev()
2194 ids[ctx.hex()] = ctx.rev()
2191 n = ctx.extra().get('source')
2195 n = ctx.extra().get('source')
2192 if n:
2196 if n:
2193 ids[n] = ctx.rev()
2197 ids[n] = ctx.rev()
2194
2198
2195 # check ancestors for earlier grafts
2199 # check ancestors for earlier grafts
2196 ui.debug('scanning for duplicate grafts\n')
2200 ui.debug('scanning for duplicate grafts\n')
2197
2201
2198 # The only changesets we can be sure doesn't contain grafts of any
2202 # The only changesets we can be sure doesn't contain grafts of any
2199 # revs, are the ones that are common ancestors of *all* revs:
2203 # revs, are the ones that are common ancestors of *all* revs:
2200 for rev in repo.revs('only(%d,ancestor(%ld))', crev, revs):
2204 for rev in repo.revs('only(%d,ancestor(%ld))', crev, revs):
2201 ctx = repo[rev]
2205 ctx = repo[rev]
2202 n = ctx.extra().get('source')
2206 n = ctx.extra().get('source')
2203 if n in ids:
2207 if n in ids:
2204 try:
2208 try:
2205 r = repo[n].rev()
2209 r = repo[n].rev()
2206 except error.RepoLookupError:
2210 except error.RepoLookupError:
2207 r = None
2211 r = None
2208 if r in revs:
2212 if r in revs:
2209 ui.warn(_('skipping revision %d:%s '
2213 ui.warn(_('skipping revision %d:%s '
2210 '(already grafted to %d:%s)\n')
2214 '(already grafted to %d:%s)\n')
2211 % (r, repo[r], rev, ctx))
2215 % (r, repo[r], rev, ctx))
2212 revs.remove(r)
2216 revs.remove(r)
2213 elif ids[n] in revs:
2217 elif ids[n] in revs:
2214 if r is None:
2218 if r is None:
2215 ui.warn(_('skipping already grafted revision %d:%s '
2219 ui.warn(_('skipping already grafted revision %d:%s '
2216 '(%d:%s also has unknown origin %s)\n')
2220 '(%d:%s also has unknown origin %s)\n')
2217 % (ids[n], repo[ids[n]], rev, ctx, n[:12]))
2221 % (ids[n], repo[ids[n]], rev, ctx, n[:12]))
2218 else:
2222 else:
2219 ui.warn(_('skipping already grafted revision %d:%s '
2223 ui.warn(_('skipping already grafted revision %d:%s '
2220 '(%d:%s also has origin %d:%s)\n')
2224 '(%d:%s also has origin %d:%s)\n')
2221 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12]))
2225 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12]))
2222 revs.remove(ids[n])
2226 revs.remove(ids[n])
2223 elif ctx.hex() in ids:
2227 elif ctx.hex() in ids:
2224 r = ids[ctx.hex()]
2228 r = ids[ctx.hex()]
2225 ui.warn(_('skipping already grafted revision %d:%s '
2229 ui.warn(_('skipping already grafted revision %d:%s '
2226 '(was grafted from %d:%s)\n') %
2230 '(was grafted from %d:%s)\n') %
2227 (r, repo[r], rev, ctx))
2231 (r, repo[r], rev, ctx))
2228 revs.remove(r)
2232 revs.remove(r)
2229 if not revs:
2233 if not revs:
2230 return -1
2234 return -1
2231
2235
2232 for pos, ctx in enumerate(repo.set("%ld", revs)):
2236 for pos, ctx in enumerate(repo.set("%ld", revs)):
2233 desc = '%d:%s "%s"' % (ctx.rev(), ctx,
2237 desc = '%d:%s "%s"' % (ctx.rev(), ctx,
2234 ctx.description().split('\n', 1)[0])
2238 ctx.description().split('\n', 1)[0])
2235 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
2239 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
2236 if names:
2240 if names:
2237 desc += ' (%s)' % ' '.join(names)
2241 desc += ' (%s)' % ' '.join(names)
2238 ui.status(_('grafting %s\n') % desc)
2242 ui.status(_('grafting %s\n') % desc)
2239 if opts.get('dry_run'):
2243 if opts.get('dry_run'):
2240 continue
2244 continue
2241
2245
2242 source = ctx.extra().get('source')
2246 source = ctx.extra().get('source')
2243 extra = {}
2247 extra = {}
2244 if source:
2248 if source:
2245 extra['source'] = source
2249 extra['source'] = source
2246 extra['intermediate-source'] = ctx.hex()
2250 extra['intermediate-source'] = ctx.hex()
2247 else:
2251 else:
2248 extra['source'] = ctx.hex()
2252 extra['source'] = ctx.hex()
2249 user = ctx.user()
2253 user = ctx.user()
2250 if opts.get('user'):
2254 if opts.get('user'):
2251 user = opts['user']
2255 user = opts['user']
2252 date = ctx.date()
2256 date = ctx.date()
2253 if opts.get('date'):
2257 if opts.get('date'):
2254 date = opts['date']
2258 date = opts['date']
2255 message = ctx.description()
2259 message = ctx.description()
2256 if opts.get('log'):
2260 if opts.get('log'):
2257 message += '\n(grafted from %s)' % ctx.hex()
2261 message += '\n(grafted from %s)' % ctx.hex()
2258
2262
2259 # we don't merge the first commit when continuing
2263 # we don't merge the first commit when continuing
2260 if not cont:
2264 if not cont:
2261 # perform the graft merge with p1(rev) as 'ancestor'
2265 # perform the graft merge with p1(rev) as 'ancestor'
2262 try:
2266 try:
2263 # ui.forcemerge is an internal variable, do not document
2267 # ui.forcemerge is an internal variable, do not document
2264 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
2268 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
2265 'graft')
2269 'graft')
2266 stats = mergemod.graft(repo, ctx, ctx.p1(),
2270 stats = mergemod.graft(repo, ctx, ctx.p1(),
2267 ['local', 'graft'])
2271 ['local', 'graft'])
2268 finally:
2272 finally:
2269 repo.ui.setconfig('ui', 'forcemerge', '', 'graft')
2273 repo.ui.setconfig('ui', 'forcemerge', '', 'graft')
2270 # report any conflicts
2274 # report any conflicts
2271 if stats and stats[3] > 0:
2275 if stats and stats[3] > 0:
2272 # write out state for --continue
2276 # write out state for --continue
2273 nodelines = [repo[rev].hex() + "\n" for rev in revs[pos:]]
2277 nodelines = [repo[rev].hex() + "\n" for rev in revs[pos:]]
2274 repo.vfs.write('graftstate', ''.join(nodelines))
2278 repo.vfs.write('graftstate', ''.join(nodelines))
2275 extra = ''
2279 extra = ''
2276 if opts.get('user'):
2280 if opts.get('user'):
2277 extra += ' --user %s' % util.shellquote(opts['user'])
2281 extra += ' --user %s' % util.shellquote(opts['user'])
2278 if opts.get('date'):
2282 if opts.get('date'):
2279 extra += ' --date %s' % util.shellquote(opts['date'])
2283 extra += ' --date %s' % util.shellquote(opts['date'])
2280 if opts.get('log'):
2284 if opts.get('log'):
2281 extra += ' --log'
2285 extra += ' --log'
2282 hint=_("use 'hg resolve' and 'hg graft --continue%s'") % extra
2286 hint=_("use 'hg resolve' and 'hg graft --continue%s'") % extra
2283 raise error.Abort(
2287 raise error.Abort(
2284 _("unresolved conflicts, can't continue"),
2288 _("unresolved conflicts, can't continue"),
2285 hint=hint)
2289 hint=hint)
2286 else:
2290 else:
2287 cont = False
2291 cont = False
2288
2292
2289 # commit
2293 # commit
2290 node = repo.commit(text=message, user=user,
2294 node = repo.commit(text=message, user=user,
2291 date=date, extra=extra, editor=editor)
2295 date=date, extra=extra, editor=editor)
2292 if node is None:
2296 if node is None:
2293 ui.warn(
2297 ui.warn(
2294 _('note: graft of %d:%s created no changes to commit\n') %
2298 _('note: graft of %d:%s created no changes to commit\n') %
2295 (ctx.rev(), ctx))
2299 (ctx.rev(), ctx))
2296
2300
2297 # remove state when we complete successfully
2301 # remove state when we complete successfully
2298 if not opts.get('dry_run'):
2302 if not opts.get('dry_run'):
2299 repo.vfs.unlinkpath('graftstate', ignoremissing=True)
2303 repo.vfs.unlinkpath('graftstate', ignoremissing=True)
2300
2304
2301 return 0
2305 return 0
2302
2306
2303 @command('grep',
2307 @command('grep',
2304 [('0', 'print0', None, _('end fields with NUL')),
2308 [('0', 'print0', None, _('end fields with NUL')),
2305 ('', 'all', None, _('print all revisions that match')),
2309 ('', 'all', None, _('print all revisions that match')),
2306 ('a', 'text', None, _('treat all files as text')),
2310 ('a', 'text', None, _('treat all files as text')),
2307 ('f', 'follow', None,
2311 ('f', 'follow', None,
2308 _('follow changeset history,'
2312 _('follow changeset history,'
2309 ' or file history across copies and renames')),
2313 ' or file history across copies and renames')),
2310 ('i', 'ignore-case', None, _('ignore case when matching')),
2314 ('i', 'ignore-case', None, _('ignore case when matching')),
2311 ('l', 'files-with-matches', None,
2315 ('l', 'files-with-matches', None,
2312 _('print only filenames and revisions that match')),
2316 _('print only filenames and revisions that match')),
2313 ('n', 'line-number', None, _('print matching line numbers')),
2317 ('n', 'line-number', None, _('print matching line numbers')),
2314 ('r', 'rev', [],
2318 ('r', 'rev', [],
2315 _('only search files changed within revision range'), _('REV')),
2319 _('only search files changed within revision range'), _('REV')),
2316 ('u', 'user', None, _('list the author (long with -v)')),
2320 ('u', 'user', None, _('list the author (long with -v)')),
2317 ('d', 'date', None, _('list the date (short with -q)')),
2321 ('d', 'date', None, _('list the date (short with -q)')),
2318 ] + formatteropts + walkopts,
2322 ] + formatteropts + walkopts,
2319 _('[OPTION]... PATTERN [FILE]...'),
2323 _('[OPTION]... PATTERN [FILE]...'),
2320 inferrepo=True)
2324 inferrepo=True)
2321 def grep(ui, repo, pattern, *pats, **opts):
2325 def grep(ui, repo, pattern, *pats, **opts):
2322 """search revision history for a pattern in specified files
2326 """search revision history for a pattern in specified files
2323
2327
2324 Search revision history for a regular expression in the specified
2328 Search revision history for a regular expression in the specified
2325 files or the entire project.
2329 files or the entire project.
2326
2330
2327 By default, grep prints the most recent revision number for each
2331 By default, grep prints the most recent revision number for each
2328 file in which it finds a match. To get it to print every revision
2332 file in which it finds a match. To get it to print every revision
2329 that contains a change in match status ("-" for a match that becomes
2333 that contains a change in match status ("-" for a match that becomes
2330 a non-match, or "+" for a non-match that becomes a match), use the
2334 a non-match, or "+" for a non-match that becomes a match), use the
2331 --all flag.
2335 --all flag.
2332
2336
2333 PATTERN can be any Python (roughly Perl-compatible) regular
2337 PATTERN can be any Python (roughly Perl-compatible) regular
2334 expression.
2338 expression.
2335
2339
2336 If no FILEs are specified (and -f/--follow isn't set), all files in
2340 If no FILEs are specified (and -f/--follow isn't set), all files in
2337 the repository are searched, including those that don't exist in the
2341 the repository are searched, including those that don't exist in the
2338 current branch or have been deleted in a prior changeset.
2342 current branch or have been deleted in a prior changeset.
2339
2343
2340 Returns 0 if a match is found, 1 otherwise.
2344 Returns 0 if a match is found, 1 otherwise.
2341 """
2345 """
2342 opts = pycompat.byteskwargs(opts)
2346 opts = pycompat.byteskwargs(opts)
2343 reflags = re.M
2347 reflags = re.M
2344 if opts.get('ignore_case'):
2348 if opts.get('ignore_case'):
2345 reflags |= re.I
2349 reflags |= re.I
2346 try:
2350 try:
2347 regexp = util.re.compile(pattern, reflags)
2351 regexp = util.re.compile(pattern, reflags)
2348 except re.error as inst:
2352 except re.error as inst:
2349 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
2353 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
2350 return 1
2354 return 1
2351 sep, eol = ':', '\n'
2355 sep, eol = ':', '\n'
2352 if opts.get('print0'):
2356 if opts.get('print0'):
2353 sep = eol = '\0'
2357 sep = eol = '\0'
2354
2358
2355 getfile = util.lrucachefunc(repo.file)
2359 getfile = util.lrucachefunc(repo.file)
2356
2360
2357 def matchlines(body):
2361 def matchlines(body):
2358 begin = 0
2362 begin = 0
2359 linenum = 0
2363 linenum = 0
2360 while begin < len(body):
2364 while begin < len(body):
2361 match = regexp.search(body, begin)
2365 match = regexp.search(body, begin)
2362 if not match:
2366 if not match:
2363 break
2367 break
2364 mstart, mend = match.span()
2368 mstart, mend = match.span()
2365 linenum += body.count('\n', begin, mstart) + 1
2369 linenum += body.count('\n', begin, mstart) + 1
2366 lstart = body.rfind('\n', begin, mstart) + 1 or begin
2370 lstart = body.rfind('\n', begin, mstart) + 1 or begin
2367 begin = body.find('\n', mend) + 1 or len(body) + 1
2371 begin = body.find('\n', mend) + 1 or len(body) + 1
2368 lend = begin - 1
2372 lend = begin - 1
2369 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
2373 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
2370
2374
2371 class linestate(object):
2375 class linestate(object):
2372 def __init__(self, line, linenum, colstart, colend):
2376 def __init__(self, line, linenum, colstart, colend):
2373 self.line = line
2377 self.line = line
2374 self.linenum = linenum
2378 self.linenum = linenum
2375 self.colstart = colstart
2379 self.colstart = colstart
2376 self.colend = colend
2380 self.colend = colend
2377
2381
2378 def __hash__(self):
2382 def __hash__(self):
2379 return hash((self.linenum, self.line))
2383 return hash((self.linenum, self.line))
2380
2384
2381 def __eq__(self, other):
2385 def __eq__(self, other):
2382 return self.line == other.line
2386 return self.line == other.line
2383
2387
2384 def findpos(self):
2388 def findpos(self):
2385 """Iterate all (start, end) indices of matches"""
2389 """Iterate all (start, end) indices of matches"""
2386 yield self.colstart, self.colend
2390 yield self.colstart, self.colend
2387 p = self.colend
2391 p = self.colend
2388 while p < len(self.line):
2392 while p < len(self.line):
2389 m = regexp.search(self.line, p)
2393 m = regexp.search(self.line, p)
2390 if not m:
2394 if not m:
2391 break
2395 break
2392 yield m.span()
2396 yield m.span()
2393 p = m.end()
2397 p = m.end()
2394
2398
2395 matches = {}
2399 matches = {}
2396 copies = {}
2400 copies = {}
2397 def grepbody(fn, rev, body):
2401 def grepbody(fn, rev, body):
2398 matches[rev].setdefault(fn, [])
2402 matches[rev].setdefault(fn, [])
2399 m = matches[rev][fn]
2403 m = matches[rev][fn]
2400 for lnum, cstart, cend, line in matchlines(body):
2404 for lnum, cstart, cend, line in matchlines(body):
2401 s = linestate(line, lnum, cstart, cend)
2405 s = linestate(line, lnum, cstart, cend)
2402 m.append(s)
2406 m.append(s)
2403
2407
2404 def difflinestates(a, b):
2408 def difflinestates(a, b):
2405 sm = difflib.SequenceMatcher(None, a, b)
2409 sm = difflib.SequenceMatcher(None, a, b)
2406 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
2410 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
2407 if tag == 'insert':
2411 if tag == 'insert':
2408 for i in xrange(blo, bhi):
2412 for i in xrange(blo, bhi):
2409 yield ('+', b[i])
2413 yield ('+', b[i])
2410 elif tag == 'delete':
2414 elif tag == 'delete':
2411 for i in xrange(alo, ahi):
2415 for i in xrange(alo, ahi):
2412 yield ('-', a[i])
2416 yield ('-', a[i])
2413 elif tag == 'replace':
2417 elif tag == 'replace':
2414 for i in xrange(alo, ahi):
2418 for i in xrange(alo, ahi):
2415 yield ('-', a[i])
2419 yield ('-', a[i])
2416 for i in xrange(blo, bhi):
2420 for i in xrange(blo, bhi):
2417 yield ('+', b[i])
2421 yield ('+', b[i])
2418
2422
2419 def display(fm, fn, ctx, pstates, states):
2423 def display(fm, fn, ctx, pstates, states):
2420 rev = ctx.rev()
2424 rev = ctx.rev()
2421 if fm.isplain():
2425 if fm.isplain():
2422 formatuser = ui.shortuser
2426 formatuser = ui.shortuser
2423 else:
2427 else:
2424 formatuser = str
2428 formatuser = str
2425 if ui.quiet:
2429 if ui.quiet:
2426 datefmt = '%Y-%m-%d'
2430 datefmt = '%Y-%m-%d'
2427 else:
2431 else:
2428 datefmt = '%a %b %d %H:%M:%S %Y %1%2'
2432 datefmt = '%a %b %d %H:%M:%S %Y %1%2'
2429 found = False
2433 found = False
2430 @util.cachefunc
2434 @util.cachefunc
2431 def binary():
2435 def binary():
2432 flog = getfile(fn)
2436 flog = getfile(fn)
2433 return util.binary(flog.read(ctx.filenode(fn)))
2437 return util.binary(flog.read(ctx.filenode(fn)))
2434
2438
2435 fieldnamemap = {'filename': 'file', 'linenumber': 'line_number'}
2439 fieldnamemap = {'filename': 'file', 'linenumber': 'line_number'}
2436 if opts.get('all'):
2440 if opts.get('all'):
2437 iter = difflinestates(pstates, states)
2441 iter = difflinestates(pstates, states)
2438 else:
2442 else:
2439 iter = [('', l) for l in states]
2443 iter = [('', l) for l in states]
2440 for change, l in iter:
2444 for change, l in iter:
2441 fm.startitem()
2445 fm.startitem()
2442 fm.data(node=fm.hexfunc(ctx.node()))
2446 fm.data(node=fm.hexfunc(ctx.node()))
2443 cols = [
2447 cols = [
2444 ('filename', fn, True),
2448 ('filename', fn, True),
2445 ('rev', rev, True),
2449 ('rev', rev, True),
2446 ('linenumber', l.linenum, opts.get('line_number')),
2450 ('linenumber', l.linenum, opts.get('line_number')),
2447 ]
2451 ]
2448 if opts.get('all'):
2452 if opts.get('all'):
2449 cols.append(('change', change, True))
2453 cols.append(('change', change, True))
2450 cols.extend([
2454 cols.extend([
2451 ('user', formatuser(ctx.user()), opts.get('user')),
2455 ('user', formatuser(ctx.user()), opts.get('user')),
2452 ('date', fm.formatdate(ctx.date(), datefmt), opts.get('date')),
2456 ('date', fm.formatdate(ctx.date(), datefmt), opts.get('date')),
2453 ])
2457 ])
2454 lastcol = next(name for name, data, cond in reversed(cols) if cond)
2458 lastcol = next(name for name, data, cond in reversed(cols) if cond)
2455 for name, data, cond in cols:
2459 for name, data, cond in cols:
2456 field = fieldnamemap.get(name, name)
2460 field = fieldnamemap.get(name, name)
2457 fm.condwrite(cond, field, '%s', data, label='grep.%s' % name)
2461 fm.condwrite(cond, field, '%s', data, label='grep.%s' % name)
2458 if cond and name != lastcol:
2462 if cond and name != lastcol:
2459 fm.plain(sep, label='grep.sep')
2463 fm.plain(sep, label='grep.sep')
2460 if not opts.get('files_with_matches'):
2464 if not opts.get('files_with_matches'):
2461 fm.plain(sep, label='grep.sep')
2465 fm.plain(sep, label='grep.sep')
2462 if not opts.get('text') and binary():
2466 if not opts.get('text') and binary():
2463 fm.plain(_(" Binary file matches"))
2467 fm.plain(_(" Binary file matches"))
2464 else:
2468 else:
2465 displaymatches(fm.nested('texts'), l)
2469 displaymatches(fm.nested('texts'), l)
2466 fm.plain(eol)
2470 fm.plain(eol)
2467 found = True
2471 found = True
2468 if opts.get('files_with_matches'):
2472 if opts.get('files_with_matches'):
2469 break
2473 break
2470 return found
2474 return found
2471
2475
2472 def displaymatches(fm, l):
2476 def displaymatches(fm, l):
2473 p = 0
2477 p = 0
2474 for s, e in l.findpos():
2478 for s, e in l.findpos():
2475 if p < s:
2479 if p < s:
2476 fm.startitem()
2480 fm.startitem()
2477 fm.write('text', '%s', l.line[p:s])
2481 fm.write('text', '%s', l.line[p:s])
2478 fm.data(matched=False)
2482 fm.data(matched=False)
2479 fm.startitem()
2483 fm.startitem()
2480 fm.write('text', '%s', l.line[s:e], label='grep.match')
2484 fm.write('text', '%s', l.line[s:e], label='grep.match')
2481 fm.data(matched=True)
2485 fm.data(matched=True)
2482 p = e
2486 p = e
2483 if p < len(l.line):
2487 if p < len(l.line):
2484 fm.startitem()
2488 fm.startitem()
2485 fm.write('text', '%s', l.line[p:])
2489 fm.write('text', '%s', l.line[p:])
2486 fm.data(matched=False)
2490 fm.data(matched=False)
2487 fm.end()
2491 fm.end()
2488
2492
2489 skip = {}
2493 skip = {}
2490 revfiles = {}
2494 revfiles = {}
2491 match = scmutil.match(repo[None], pats, opts)
2495 match = scmutil.match(repo[None], pats, opts)
2492 found = False
2496 found = False
2493 follow = opts.get('follow')
2497 follow = opts.get('follow')
2494
2498
2495 def prep(ctx, fns):
2499 def prep(ctx, fns):
2496 rev = ctx.rev()
2500 rev = ctx.rev()
2497 pctx = ctx.p1()
2501 pctx = ctx.p1()
2498 parent = pctx.rev()
2502 parent = pctx.rev()
2499 matches.setdefault(rev, {})
2503 matches.setdefault(rev, {})
2500 matches.setdefault(parent, {})
2504 matches.setdefault(parent, {})
2501 files = revfiles.setdefault(rev, [])
2505 files = revfiles.setdefault(rev, [])
2502 for fn in fns:
2506 for fn in fns:
2503 flog = getfile(fn)
2507 flog = getfile(fn)
2504 try:
2508 try:
2505 fnode = ctx.filenode(fn)
2509 fnode = ctx.filenode(fn)
2506 except error.LookupError:
2510 except error.LookupError:
2507 continue
2511 continue
2508
2512
2509 copied = flog.renamed(fnode)
2513 copied = flog.renamed(fnode)
2510 copy = follow and copied and copied[0]
2514 copy = follow and copied and copied[0]
2511 if copy:
2515 if copy:
2512 copies.setdefault(rev, {})[fn] = copy
2516 copies.setdefault(rev, {})[fn] = copy
2513 if fn in skip:
2517 if fn in skip:
2514 if copy:
2518 if copy:
2515 skip[copy] = True
2519 skip[copy] = True
2516 continue
2520 continue
2517 files.append(fn)
2521 files.append(fn)
2518
2522
2519 if fn not in matches[rev]:
2523 if fn not in matches[rev]:
2520 grepbody(fn, rev, flog.read(fnode))
2524 grepbody(fn, rev, flog.read(fnode))
2521
2525
2522 pfn = copy or fn
2526 pfn = copy or fn
2523 if pfn not in matches[parent]:
2527 if pfn not in matches[parent]:
2524 try:
2528 try:
2525 fnode = pctx.filenode(pfn)
2529 fnode = pctx.filenode(pfn)
2526 grepbody(pfn, parent, flog.read(fnode))
2530 grepbody(pfn, parent, flog.read(fnode))
2527 except error.LookupError:
2531 except error.LookupError:
2528 pass
2532 pass
2529
2533
2530 ui.pager('grep')
2534 ui.pager('grep')
2531 fm = ui.formatter('grep', opts)
2535 fm = ui.formatter('grep', opts)
2532 for ctx in cmdutil.walkchangerevs(repo, match, opts, prep):
2536 for ctx in cmdutil.walkchangerevs(repo, match, opts, prep):
2533 rev = ctx.rev()
2537 rev = ctx.rev()
2534 parent = ctx.p1().rev()
2538 parent = ctx.p1().rev()
2535 for fn in sorted(revfiles.get(rev, [])):
2539 for fn in sorted(revfiles.get(rev, [])):
2536 states = matches[rev][fn]
2540 states = matches[rev][fn]
2537 copy = copies.get(rev, {}).get(fn)
2541 copy = copies.get(rev, {}).get(fn)
2538 if fn in skip:
2542 if fn in skip:
2539 if copy:
2543 if copy:
2540 skip[copy] = True
2544 skip[copy] = True
2541 continue
2545 continue
2542 pstates = matches.get(parent, {}).get(copy or fn, [])
2546 pstates = matches.get(parent, {}).get(copy or fn, [])
2543 if pstates or states:
2547 if pstates or states:
2544 r = display(fm, fn, ctx, pstates, states)
2548 r = display(fm, fn, ctx, pstates, states)
2545 found = found or r
2549 found = found or r
2546 if r and not opts.get('all'):
2550 if r and not opts.get('all'):
2547 skip[fn] = True
2551 skip[fn] = True
2548 if copy:
2552 if copy:
2549 skip[copy] = True
2553 skip[copy] = True
2550 del matches[rev]
2554 del matches[rev]
2551 del revfiles[rev]
2555 del revfiles[rev]
2552 fm.end()
2556 fm.end()
2553
2557
2554 return not found
2558 return not found
2555
2559
2556 @command('heads',
2560 @command('heads',
2557 [('r', 'rev', '',
2561 [('r', 'rev', '',
2558 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
2562 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
2559 ('t', 'topo', False, _('show topological heads only')),
2563 ('t', 'topo', False, _('show topological heads only')),
2560 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
2564 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
2561 ('c', 'closed', False, _('show normal and closed branch heads')),
2565 ('c', 'closed', False, _('show normal and closed branch heads')),
2562 ] + templateopts,
2566 ] + templateopts,
2563 _('[-ct] [-r STARTREV] [REV]...'))
2567 _('[-ct] [-r STARTREV] [REV]...'))
2564 def heads(ui, repo, *branchrevs, **opts):
2568 def heads(ui, repo, *branchrevs, **opts):
2565 """show branch heads
2569 """show branch heads
2566
2570
2567 With no arguments, show all open branch heads in the repository.
2571 With no arguments, show all open branch heads in the repository.
2568 Branch heads are changesets that have no descendants on the
2572 Branch heads are changesets that have no descendants on the
2569 same branch. They are where development generally takes place and
2573 same branch. They are where development generally takes place and
2570 are the usual targets for update and merge operations.
2574 are the usual targets for update and merge operations.
2571
2575
2572 If one or more REVs are given, only open branch heads on the
2576 If one or more REVs are given, only open branch heads on the
2573 branches associated with the specified changesets are shown. This
2577 branches associated with the specified changesets are shown. This
2574 means that you can use :hg:`heads .` to see the heads on the
2578 means that you can use :hg:`heads .` to see the heads on the
2575 currently checked-out branch.
2579 currently checked-out branch.
2576
2580
2577 If -c/--closed is specified, also show branch heads marked closed
2581 If -c/--closed is specified, also show branch heads marked closed
2578 (see :hg:`commit --close-branch`).
2582 (see :hg:`commit --close-branch`).
2579
2583
2580 If STARTREV is specified, only those heads that are descendants of
2584 If STARTREV is specified, only those heads that are descendants of
2581 STARTREV will be displayed.
2585 STARTREV will be displayed.
2582
2586
2583 If -t/--topo is specified, named branch mechanics will be ignored and only
2587 If -t/--topo is specified, named branch mechanics will be ignored and only
2584 topological heads (changesets with no children) will be shown.
2588 topological heads (changesets with no children) will be shown.
2585
2589
2586 Returns 0 if matching heads are found, 1 if not.
2590 Returns 0 if matching heads are found, 1 if not.
2587 """
2591 """
2588
2592
2589 opts = pycompat.byteskwargs(opts)
2593 opts = pycompat.byteskwargs(opts)
2590 start = None
2594 start = None
2591 if 'rev' in opts:
2595 if 'rev' in opts:
2592 start = scmutil.revsingle(repo, opts['rev'], None).node()
2596 start = scmutil.revsingle(repo, opts['rev'], None).node()
2593
2597
2594 if opts.get('topo'):
2598 if opts.get('topo'):
2595 heads = [repo[h] for h in repo.heads(start)]
2599 heads = [repo[h] for h in repo.heads(start)]
2596 else:
2600 else:
2597 heads = []
2601 heads = []
2598 for branch in repo.branchmap():
2602 for branch in repo.branchmap():
2599 heads += repo.branchheads(branch, start, opts.get('closed'))
2603 heads += repo.branchheads(branch, start, opts.get('closed'))
2600 heads = [repo[h] for h in heads]
2604 heads = [repo[h] for h in heads]
2601
2605
2602 if branchrevs:
2606 if branchrevs:
2603 branches = set(repo[br].branch() for br in branchrevs)
2607 branches = set(repo[br].branch() for br in branchrevs)
2604 heads = [h for h in heads if h.branch() in branches]
2608 heads = [h for h in heads if h.branch() in branches]
2605
2609
2606 if opts.get('active') and branchrevs:
2610 if opts.get('active') and branchrevs:
2607 dagheads = repo.heads(start)
2611 dagheads = repo.heads(start)
2608 heads = [h for h in heads if h.node() in dagheads]
2612 heads = [h for h in heads if h.node() in dagheads]
2609
2613
2610 if branchrevs:
2614 if branchrevs:
2611 haveheads = set(h.branch() for h in heads)
2615 haveheads = set(h.branch() for h in heads)
2612 if branches - haveheads:
2616 if branches - haveheads:
2613 headless = ', '.join(b for b in branches - haveheads)
2617 headless = ', '.join(b for b in branches - haveheads)
2614 msg = _('no open branch heads found on branches %s')
2618 msg = _('no open branch heads found on branches %s')
2615 if opts.get('rev'):
2619 if opts.get('rev'):
2616 msg += _(' (started at %s)') % opts['rev']
2620 msg += _(' (started at %s)') % opts['rev']
2617 ui.warn((msg + '\n') % headless)
2621 ui.warn((msg + '\n') % headless)
2618
2622
2619 if not heads:
2623 if not heads:
2620 return 1
2624 return 1
2621
2625
2622 ui.pager('heads')
2626 ui.pager('heads')
2623 heads = sorted(heads, key=lambda x: -x.rev())
2627 heads = sorted(heads, key=lambda x: -x.rev())
2624 displayer = cmdutil.show_changeset(ui, repo, opts)
2628 displayer = cmdutil.show_changeset(ui, repo, opts)
2625 for ctx in heads:
2629 for ctx in heads:
2626 displayer.show(ctx)
2630 displayer.show(ctx)
2627 displayer.close()
2631 displayer.close()
2628
2632
2629 @command('help',
2633 @command('help',
2630 [('e', 'extension', None, _('show only help for extensions')),
2634 [('e', 'extension', None, _('show only help for extensions')),
2631 ('c', 'command', None, _('show only help for commands')),
2635 ('c', 'command', None, _('show only help for commands')),
2632 ('k', 'keyword', None, _('show topics matching keyword')),
2636 ('k', 'keyword', None, _('show topics matching keyword')),
2633 ('s', 'system', [], _('show help for specific platform(s)')),
2637 ('s', 'system', [], _('show help for specific platform(s)')),
2634 ],
2638 ],
2635 _('[-ecks] [TOPIC]'),
2639 _('[-ecks] [TOPIC]'),
2636 norepo=True)
2640 norepo=True)
2637 def help_(ui, name=None, **opts):
2641 def help_(ui, name=None, **opts):
2638 """show help for a given topic or a help overview
2642 """show help for a given topic or a help overview
2639
2643
2640 With no arguments, print a list of commands with short help messages.
2644 With no arguments, print a list of commands with short help messages.
2641
2645
2642 Given a topic, extension, or command name, print help for that
2646 Given a topic, extension, or command name, print help for that
2643 topic.
2647 topic.
2644
2648
2645 Returns 0 if successful.
2649 Returns 0 if successful.
2646 """
2650 """
2647
2651
2648 keep = opts.get(r'system') or []
2652 keep = opts.get(r'system') or []
2649 if len(keep) == 0:
2653 if len(keep) == 0:
2650 if pycompat.sysplatform.startswith('win'):
2654 if pycompat.sysplatform.startswith('win'):
2651 keep.append('windows')
2655 keep.append('windows')
2652 elif pycompat.sysplatform == 'OpenVMS':
2656 elif pycompat.sysplatform == 'OpenVMS':
2653 keep.append('vms')
2657 keep.append('vms')
2654 elif pycompat.sysplatform == 'plan9':
2658 elif pycompat.sysplatform == 'plan9':
2655 keep.append('plan9')
2659 keep.append('plan9')
2656 else:
2660 else:
2657 keep.append('unix')
2661 keep.append('unix')
2658 keep.append(pycompat.sysplatform.lower())
2662 keep.append(pycompat.sysplatform.lower())
2659 if ui.verbose:
2663 if ui.verbose:
2660 keep.append('verbose')
2664 keep.append('verbose')
2661
2665
2662 commands = sys.modules[__name__]
2666 commands = sys.modules[__name__]
2663 formatted = help.formattedhelp(ui, commands, name, keep=keep, **opts)
2667 formatted = help.formattedhelp(ui, commands, name, keep=keep, **opts)
2664 ui.pager('help')
2668 ui.pager('help')
2665 ui.write(formatted)
2669 ui.write(formatted)
2666
2670
2667
2671
2668 @command('identify|id',
2672 @command('identify|id',
2669 [('r', 'rev', '',
2673 [('r', 'rev', '',
2670 _('identify the specified revision'), _('REV')),
2674 _('identify the specified revision'), _('REV')),
2671 ('n', 'num', None, _('show local revision number')),
2675 ('n', 'num', None, _('show local revision number')),
2672 ('i', 'id', None, _('show global revision id')),
2676 ('i', 'id', None, _('show global revision id')),
2673 ('b', 'branch', None, _('show branch')),
2677 ('b', 'branch', None, _('show branch')),
2674 ('t', 'tags', None, _('show tags')),
2678 ('t', 'tags', None, _('show tags')),
2675 ('B', 'bookmarks', None, _('show bookmarks')),
2679 ('B', 'bookmarks', None, _('show bookmarks')),
2676 ] + remoteopts + formatteropts,
2680 ] + remoteopts + formatteropts,
2677 _('[-nibtB] [-r REV] [SOURCE]'),
2681 _('[-nibtB] [-r REV] [SOURCE]'),
2678 optionalrepo=True)
2682 optionalrepo=True)
2679 def identify(ui, repo, source=None, rev=None,
2683 def identify(ui, repo, source=None, rev=None,
2680 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
2684 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
2681 """identify the working directory or specified revision
2685 """identify the working directory or specified revision
2682
2686
2683 Print a summary identifying the repository state at REV using one or
2687 Print a summary identifying the repository state at REV using one or
2684 two parent hash identifiers, followed by a "+" if the working
2688 two parent hash identifiers, followed by a "+" if the working
2685 directory has uncommitted changes, the branch name (if not default),
2689 directory has uncommitted changes, the branch name (if not default),
2686 a list of tags, and a list of bookmarks.
2690 a list of tags, and a list of bookmarks.
2687
2691
2688 When REV is not given, print a summary of the current state of the
2692 When REV is not given, print a summary of the current state of the
2689 repository.
2693 repository.
2690
2694
2691 Specifying a path to a repository root or Mercurial bundle will
2695 Specifying a path to a repository root or Mercurial bundle will
2692 cause lookup to operate on that repository/bundle.
2696 cause lookup to operate on that repository/bundle.
2693
2697
2694 .. container:: verbose
2698 .. container:: verbose
2695
2699
2696 Examples:
2700 Examples:
2697
2701
2698 - generate a build identifier for the working directory::
2702 - generate a build identifier for the working directory::
2699
2703
2700 hg id --id > build-id.dat
2704 hg id --id > build-id.dat
2701
2705
2702 - find the revision corresponding to a tag::
2706 - find the revision corresponding to a tag::
2703
2707
2704 hg id -n -r 1.3
2708 hg id -n -r 1.3
2705
2709
2706 - check the most recent revision of a remote repository::
2710 - check the most recent revision of a remote repository::
2707
2711
2708 hg id -r tip https://www.mercurial-scm.org/repo/hg/
2712 hg id -r tip https://www.mercurial-scm.org/repo/hg/
2709
2713
2710 See :hg:`log` for generating more information about specific revisions,
2714 See :hg:`log` for generating more information about specific revisions,
2711 including full hash identifiers.
2715 including full hash identifiers.
2712
2716
2713 Returns 0 if successful.
2717 Returns 0 if successful.
2714 """
2718 """
2715
2719
2716 opts = pycompat.byteskwargs(opts)
2720 opts = pycompat.byteskwargs(opts)
2717 if not repo and not source:
2721 if not repo and not source:
2718 raise error.Abort(_("there is no Mercurial repository here "
2722 raise error.Abort(_("there is no Mercurial repository here "
2719 "(.hg not found)"))
2723 "(.hg not found)"))
2720
2724
2721 if ui.debugflag:
2725 if ui.debugflag:
2722 hexfunc = hex
2726 hexfunc = hex
2723 else:
2727 else:
2724 hexfunc = short
2728 hexfunc = short
2725 default = not (num or id or branch or tags or bookmarks)
2729 default = not (num or id or branch or tags or bookmarks)
2726 output = []
2730 output = []
2727 revs = []
2731 revs = []
2728
2732
2729 if source:
2733 if source:
2730 source, branches = hg.parseurl(ui.expandpath(source))
2734 source, branches = hg.parseurl(ui.expandpath(source))
2731 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
2735 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
2732 repo = peer.local()
2736 repo = peer.local()
2733 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
2737 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
2734
2738
2735 fm = ui.formatter('identify', opts)
2739 fm = ui.formatter('identify', opts)
2736 fm.startitem()
2740 fm.startitem()
2737
2741
2738 if not repo:
2742 if not repo:
2739 if num or branch or tags:
2743 if num or branch or tags:
2740 raise error.Abort(
2744 raise error.Abort(
2741 _("can't query remote revision number, branch, or tags"))
2745 _("can't query remote revision number, branch, or tags"))
2742 if not rev and revs:
2746 if not rev and revs:
2743 rev = revs[0]
2747 rev = revs[0]
2744 if not rev:
2748 if not rev:
2745 rev = "tip"
2749 rev = "tip"
2746
2750
2747 remoterev = peer.lookup(rev)
2751 remoterev = peer.lookup(rev)
2748 hexrev = hexfunc(remoterev)
2752 hexrev = hexfunc(remoterev)
2749 if default or id:
2753 if default or id:
2750 output = [hexrev]
2754 output = [hexrev]
2751 fm.data(id=hexrev)
2755 fm.data(id=hexrev)
2752
2756
2753 def getbms():
2757 def getbms():
2754 bms = []
2758 bms = []
2755
2759
2756 if 'bookmarks' in peer.listkeys('namespaces'):
2760 if 'bookmarks' in peer.listkeys('namespaces'):
2757 hexremoterev = hex(remoterev)
2761 hexremoterev = hex(remoterev)
2758 bms = [bm for bm, bmr in peer.listkeys('bookmarks').iteritems()
2762 bms = [bm for bm, bmr in peer.listkeys('bookmarks').iteritems()
2759 if bmr == hexremoterev]
2763 if bmr == hexremoterev]
2760
2764
2761 return sorted(bms)
2765 return sorted(bms)
2762
2766
2763 bms = getbms()
2767 bms = getbms()
2764 if bookmarks:
2768 if bookmarks:
2765 output.extend(bms)
2769 output.extend(bms)
2766 elif default and not ui.quiet:
2770 elif default and not ui.quiet:
2767 # multiple bookmarks for a single parent separated by '/'
2771 # multiple bookmarks for a single parent separated by '/'
2768 bm = '/'.join(bms)
2772 bm = '/'.join(bms)
2769 if bm:
2773 if bm:
2770 output.append(bm)
2774 output.append(bm)
2771
2775
2772 fm.data(node=hex(remoterev))
2776 fm.data(node=hex(remoterev))
2773 fm.data(bookmarks=fm.formatlist(bms, name='bookmark'))
2777 fm.data(bookmarks=fm.formatlist(bms, name='bookmark'))
2774 else:
2778 else:
2775 ctx = scmutil.revsingle(repo, rev, None)
2779 ctx = scmutil.revsingle(repo, rev, None)
2776
2780
2777 if ctx.rev() is None:
2781 if ctx.rev() is None:
2778 ctx = repo[None]
2782 ctx = repo[None]
2779 parents = ctx.parents()
2783 parents = ctx.parents()
2780 taglist = []
2784 taglist = []
2781 for p in parents:
2785 for p in parents:
2782 taglist.extend(p.tags())
2786 taglist.extend(p.tags())
2783
2787
2784 dirty = ""
2788 dirty = ""
2785 if ctx.dirty(missing=True, merge=False, branch=False):
2789 if ctx.dirty(missing=True, merge=False, branch=False):
2786 dirty = '+'
2790 dirty = '+'
2787 fm.data(dirty=dirty)
2791 fm.data(dirty=dirty)
2788
2792
2789 hexoutput = [hexfunc(p.node()) for p in parents]
2793 hexoutput = [hexfunc(p.node()) for p in parents]
2790 if default or id:
2794 if default or id:
2791 output = ["%s%s" % ('+'.join(hexoutput), dirty)]
2795 output = ["%s%s" % ('+'.join(hexoutput), dirty)]
2792 fm.data(id="%s%s" % ('+'.join(hexoutput), dirty))
2796 fm.data(id="%s%s" % ('+'.join(hexoutput), dirty))
2793
2797
2794 if num:
2798 if num:
2795 numoutput = ["%d" % p.rev() for p in parents]
2799 numoutput = ["%d" % p.rev() for p in parents]
2796 output.append("%s%s" % ('+'.join(numoutput), dirty))
2800 output.append("%s%s" % ('+'.join(numoutput), dirty))
2797
2801
2798 fn = fm.nested('parents')
2802 fn = fm.nested('parents')
2799 for p in parents:
2803 for p in parents:
2800 fn.startitem()
2804 fn.startitem()
2801 fn.data(rev=p.rev())
2805 fn.data(rev=p.rev())
2802 fn.data(node=p.hex())
2806 fn.data(node=p.hex())
2803 fn.context(ctx=p)
2807 fn.context(ctx=p)
2804 fn.end()
2808 fn.end()
2805 else:
2809 else:
2806 hexoutput = hexfunc(ctx.node())
2810 hexoutput = hexfunc(ctx.node())
2807 if default or id:
2811 if default or id:
2808 output = [hexoutput]
2812 output = [hexoutput]
2809 fm.data(id=hexoutput)
2813 fm.data(id=hexoutput)
2810
2814
2811 if num:
2815 if num:
2812 output.append(pycompat.bytestr(ctx.rev()))
2816 output.append(pycompat.bytestr(ctx.rev()))
2813 taglist = ctx.tags()
2817 taglist = ctx.tags()
2814
2818
2815 if default and not ui.quiet:
2819 if default and not ui.quiet:
2816 b = ctx.branch()
2820 b = ctx.branch()
2817 if b != 'default':
2821 if b != 'default':
2818 output.append("(%s)" % b)
2822 output.append("(%s)" % b)
2819
2823
2820 # multiple tags for a single parent separated by '/'
2824 # multiple tags for a single parent separated by '/'
2821 t = '/'.join(taglist)
2825 t = '/'.join(taglist)
2822 if t:
2826 if t:
2823 output.append(t)
2827 output.append(t)
2824
2828
2825 # multiple bookmarks for a single parent separated by '/'
2829 # multiple bookmarks for a single parent separated by '/'
2826 bm = '/'.join(ctx.bookmarks())
2830 bm = '/'.join(ctx.bookmarks())
2827 if bm:
2831 if bm:
2828 output.append(bm)
2832 output.append(bm)
2829 else:
2833 else:
2830 if branch:
2834 if branch:
2831 output.append(ctx.branch())
2835 output.append(ctx.branch())
2832
2836
2833 if tags:
2837 if tags:
2834 output.extend(taglist)
2838 output.extend(taglist)
2835
2839
2836 if bookmarks:
2840 if bookmarks:
2837 output.extend(ctx.bookmarks())
2841 output.extend(ctx.bookmarks())
2838
2842
2839 fm.data(node=ctx.hex())
2843 fm.data(node=ctx.hex())
2840 fm.data(branch=ctx.branch())
2844 fm.data(branch=ctx.branch())
2841 fm.data(tags=fm.formatlist(taglist, name='tag', sep=':'))
2845 fm.data(tags=fm.formatlist(taglist, name='tag', sep=':'))
2842 fm.data(bookmarks=fm.formatlist(ctx.bookmarks(), name='bookmark'))
2846 fm.data(bookmarks=fm.formatlist(ctx.bookmarks(), name='bookmark'))
2843 fm.context(ctx=ctx)
2847 fm.context(ctx=ctx)
2844
2848
2845 fm.plain("%s\n" % ' '.join(output))
2849 fm.plain("%s\n" % ' '.join(output))
2846 fm.end()
2850 fm.end()
2847
2851
2848 @command('import|patch',
2852 @command('import|patch',
2849 [('p', 'strip', 1,
2853 [('p', 'strip', 1,
2850 _('directory strip option for patch. This has the same '
2854 _('directory strip option for patch. This has the same '
2851 'meaning as the corresponding patch option'), _('NUM')),
2855 'meaning as the corresponding patch option'), _('NUM')),
2852 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
2856 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
2853 ('e', 'edit', False, _('invoke editor on commit messages')),
2857 ('e', 'edit', False, _('invoke editor on commit messages')),
2854 ('f', 'force', None,
2858 ('f', 'force', None,
2855 _('skip check for outstanding uncommitted changes (DEPRECATED)')),
2859 _('skip check for outstanding uncommitted changes (DEPRECATED)')),
2856 ('', 'no-commit', None,
2860 ('', 'no-commit', None,
2857 _("don't commit, just update the working directory")),
2861 _("don't commit, just update the working directory")),
2858 ('', 'bypass', None,
2862 ('', 'bypass', None,
2859 _("apply patch without touching the working directory")),
2863 _("apply patch without touching the working directory")),
2860 ('', 'partial', None,
2864 ('', 'partial', None,
2861 _('commit even if some hunks fail')),
2865 _('commit even if some hunks fail')),
2862 ('', 'exact', None,
2866 ('', 'exact', None,
2863 _('abort if patch would apply lossily')),
2867 _('abort if patch would apply lossily')),
2864 ('', 'prefix', '',
2868 ('', 'prefix', '',
2865 _('apply patch to subdirectory'), _('DIR')),
2869 _('apply patch to subdirectory'), _('DIR')),
2866 ('', 'import-branch', None,
2870 ('', 'import-branch', None,
2867 _('use any branch information in patch (implied by --exact)'))] +
2871 _('use any branch information in patch (implied by --exact)'))] +
2868 commitopts + commitopts2 + similarityopts,
2872 commitopts + commitopts2 + similarityopts,
2869 _('[OPTION]... PATCH...'))
2873 _('[OPTION]... PATCH...'))
2870 def import_(ui, repo, patch1=None, *patches, **opts):
2874 def import_(ui, repo, patch1=None, *patches, **opts):
2871 """import an ordered set of patches
2875 """import an ordered set of patches
2872
2876
2873 Import a list of patches and commit them individually (unless
2877 Import a list of patches and commit them individually (unless
2874 --no-commit is specified).
2878 --no-commit is specified).
2875
2879
2876 To read a patch from standard input (stdin), use "-" as the patch
2880 To read a patch from standard input (stdin), use "-" as the patch
2877 name. If a URL is specified, the patch will be downloaded from
2881 name. If a URL is specified, the patch will be downloaded from
2878 there.
2882 there.
2879
2883
2880 Import first applies changes to the working directory (unless
2884 Import first applies changes to the working directory (unless
2881 --bypass is specified), import will abort if there are outstanding
2885 --bypass is specified), import will abort if there are outstanding
2882 changes.
2886 changes.
2883
2887
2884 Use --bypass to apply and commit patches directly to the
2888 Use --bypass to apply and commit patches directly to the
2885 repository, without affecting the working directory. Without
2889 repository, without affecting the working directory. Without
2886 --exact, patches will be applied on top of the working directory
2890 --exact, patches will be applied on top of the working directory
2887 parent revision.
2891 parent revision.
2888
2892
2889 You can import a patch straight from a mail message. Even patches
2893 You can import a patch straight from a mail message. Even patches
2890 as attachments work (to use the body part, it must have type
2894 as attachments work (to use the body part, it must have type
2891 text/plain or text/x-patch). From and Subject headers of email
2895 text/plain or text/x-patch). From and Subject headers of email
2892 message are used as default committer and commit message. All
2896 message are used as default committer and commit message. All
2893 text/plain body parts before first diff are added to the commit
2897 text/plain body parts before first diff are added to the commit
2894 message.
2898 message.
2895
2899
2896 If the imported patch was generated by :hg:`export`, user and
2900 If the imported patch was generated by :hg:`export`, user and
2897 description from patch override values from message headers and
2901 description from patch override values from message headers and
2898 body. Values given on command line with -m/--message and -u/--user
2902 body. Values given on command line with -m/--message and -u/--user
2899 override these.
2903 override these.
2900
2904
2901 If --exact is specified, import will set the working directory to
2905 If --exact is specified, import will set the working directory to
2902 the parent of each patch before applying it, and will abort if the
2906 the parent of each patch before applying it, and will abort if the
2903 resulting changeset has a different ID than the one recorded in
2907 resulting changeset has a different ID than the one recorded in
2904 the patch. This will guard against various ways that portable
2908 the patch. This will guard against various ways that portable
2905 patch formats and mail systems might fail to transfer Mercurial
2909 patch formats and mail systems might fail to transfer Mercurial
2906 data or metadata. See :hg:`bundle` for lossless transmission.
2910 data or metadata. See :hg:`bundle` for lossless transmission.
2907
2911
2908 Use --partial to ensure a changeset will be created from the patch
2912 Use --partial to ensure a changeset will be created from the patch
2909 even if some hunks fail to apply. Hunks that fail to apply will be
2913 even if some hunks fail to apply. Hunks that fail to apply will be
2910 written to a <target-file>.rej file. Conflicts can then be resolved
2914 written to a <target-file>.rej file. Conflicts can then be resolved
2911 by hand before :hg:`commit --amend` is run to update the created
2915 by hand before :hg:`commit --amend` is run to update the created
2912 changeset. This flag exists to let people import patches that
2916 changeset. This flag exists to let people import patches that
2913 partially apply without losing the associated metadata (author,
2917 partially apply without losing the associated metadata (author,
2914 date, description, ...).
2918 date, description, ...).
2915
2919
2916 .. note::
2920 .. note::
2917
2921
2918 When no hunks apply cleanly, :hg:`import --partial` will create
2922 When no hunks apply cleanly, :hg:`import --partial` will create
2919 an empty changeset, importing only the patch metadata.
2923 an empty changeset, importing only the patch metadata.
2920
2924
2921 With -s/--similarity, hg will attempt to discover renames and
2925 With -s/--similarity, hg will attempt to discover renames and
2922 copies in the patch in the same way as :hg:`addremove`.
2926 copies in the patch in the same way as :hg:`addremove`.
2923
2927
2924 It is possible to use external patch programs to perform the patch
2928 It is possible to use external patch programs to perform the patch
2925 by setting the ``ui.patch`` configuration option. For the default
2929 by setting the ``ui.patch`` configuration option. For the default
2926 internal tool, the fuzz can also be configured via ``patch.fuzz``.
2930 internal tool, the fuzz can also be configured via ``patch.fuzz``.
2927 See :hg:`help config` for more information about configuration
2931 See :hg:`help config` for more information about configuration
2928 files and how to use these options.
2932 files and how to use these options.
2929
2933
2930 See :hg:`help dates` for a list of formats valid for -d/--date.
2934 See :hg:`help dates` for a list of formats valid for -d/--date.
2931
2935
2932 .. container:: verbose
2936 .. container:: verbose
2933
2937
2934 Examples:
2938 Examples:
2935
2939
2936 - import a traditional patch from a website and detect renames::
2940 - import a traditional patch from a website and detect renames::
2937
2941
2938 hg import -s 80 http://example.com/bugfix.patch
2942 hg import -s 80 http://example.com/bugfix.patch
2939
2943
2940 - import a changeset from an hgweb server::
2944 - import a changeset from an hgweb server::
2941
2945
2942 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
2946 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
2943
2947
2944 - import all the patches in an Unix-style mbox::
2948 - import all the patches in an Unix-style mbox::
2945
2949
2946 hg import incoming-patches.mbox
2950 hg import incoming-patches.mbox
2947
2951
2948 - import patches from stdin::
2952 - import patches from stdin::
2949
2953
2950 hg import -
2954 hg import -
2951
2955
2952 - attempt to exactly restore an exported changeset (not always
2956 - attempt to exactly restore an exported changeset (not always
2953 possible)::
2957 possible)::
2954
2958
2955 hg import --exact proposed-fix.patch
2959 hg import --exact proposed-fix.patch
2956
2960
2957 - use an external tool to apply a patch which is too fuzzy for
2961 - use an external tool to apply a patch which is too fuzzy for
2958 the default internal tool.
2962 the default internal tool.
2959
2963
2960 hg import --config ui.patch="patch --merge" fuzzy.patch
2964 hg import --config ui.patch="patch --merge" fuzzy.patch
2961
2965
2962 - change the default fuzzing from 2 to a less strict 7
2966 - change the default fuzzing from 2 to a less strict 7
2963
2967
2964 hg import --config ui.fuzz=7 fuzz.patch
2968 hg import --config ui.fuzz=7 fuzz.patch
2965
2969
2966 Returns 0 on success, 1 on partial success (see --partial).
2970 Returns 0 on success, 1 on partial success (see --partial).
2967 """
2971 """
2968
2972
2969 opts = pycompat.byteskwargs(opts)
2973 opts = pycompat.byteskwargs(opts)
2970 if not patch1:
2974 if not patch1:
2971 raise error.Abort(_('need at least one patch to import'))
2975 raise error.Abort(_('need at least one patch to import'))
2972
2976
2973 patches = (patch1,) + patches
2977 patches = (patch1,) + patches
2974
2978
2975 date = opts.get('date')
2979 date = opts.get('date')
2976 if date:
2980 if date:
2977 opts['date'] = util.parsedate(date)
2981 opts['date'] = util.parsedate(date)
2978
2982
2979 exact = opts.get('exact')
2983 exact = opts.get('exact')
2980 update = not opts.get('bypass')
2984 update = not opts.get('bypass')
2981 if not update and opts.get('no_commit'):
2985 if not update and opts.get('no_commit'):
2982 raise error.Abort(_('cannot use --no-commit with --bypass'))
2986 raise error.Abort(_('cannot use --no-commit with --bypass'))
2983 try:
2987 try:
2984 sim = float(opts.get('similarity') or 0)
2988 sim = float(opts.get('similarity') or 0)
2985 except ValueError:
2989 except ValueError:
2986 raise error.Abort(_('similarity must be a number'))
2990 raise error.Abort(_('similarity must be a number'))
2987 if sim < 0 or sim > 100:
2991 if sim < 0 or sim > 100:
2988 raise error.Abort(_('similarity must be between 0 and 100'))
2992 raise error.Abort(_('similarity must be between 0 and 100'))
2989 if sim and not update:
2993 if sim and not update:
2990 raise error.Abort(_('cannot use --similarity with --bypass'))
2994 raise error.Abort(_('cannot use --similarity with --bypass'))
2991 if exact:
2995 if exact:
2992 if opts.get('edit'):
2996 if opts.get('edit'):
2993 raise error.Abort(_('cannot use --exact with --edit'))
2997 raise error.Abort(_('cannot use --exact with --edit'))
2994 if opts.get('prefix'):
2998 if opts.get('prefix'):
2995 raise error.Abort(_('cannot use --exact with --prefix'))
2999 raise error.Abort(_('cannot use --exact with --prefix'))
2996
3000
2997 base = opts["base"]
3001 base = opts["base"]
2998 wlock = dsguard = lock = tr = None
3002 wlock = dsguard = lock = tr = None
2999 msgs = []
3003 msgs = []
3000 ret = 0
3004 ret = 0
3001
3005
3002
3006
3003 try:
3007 try:
3004 wlock = repo.wlock()
3008 wlock = repo.wlock()
3005
3009
3006 if update:
3010 if update:
3007 cmdutil.checkunfinished(repo)
3011 cmdutil.checkunfinished(repo)
3008 if (exact or not opts.get('force')):
3012 if (exact or not opts.get('force')):
3009 cmdutil.bailifchanged(repo)
3013 cmdutil.bailifchanged(repo)
3010
3014
3011 if not opts.get('no_commit'):
3015 if not opts.get('no_commit'):
3012 lock = repo.lock()
3016 lock = repo.lock()
3013 tr = repo.transaction('import')
3017 tr = repo.transaction('import')
3014 else:
3018 else:
3015 dsguard = dirstateguard.dirstateguard(repo, 'import')
3019 dsguard = dirstateguard.dirstateguard(repo, 'import')
3016 parents = repo[None].parents()
3020 parents = repo[None].parents()
3017 for patchurl in patches:
3021 for patchurl in patches:
3018 if patchurl == '-':
3022 if patchurl == '-':
3019 ui.status(_('applying patch from stdin\n'))
3023 ui.status(_('applying patch from stdin\n'))
3020 patchfile = ui.fin
3024 patchfile = ui.fin
3021 patchurl = 'stdin' # for error message
3025 patchurl = 'stdin' # for error message
3022 else:
3026 else:
3023 patchurl = os.path.join(base, patchurl)
3027 patchurl = os.path.join(base, patchurl)
3024 ui.status(_('applying %s\n') % patchurl)
3028 ui.status(_('applying %s\n') % patchurl)
3025 patchfile = hg.openpath(ui, patchurl)
3029 patchfile = hg.openpath(ui, patchurl)
3026
3030
3027 haspatch = False
3031 haspatch = False
3028 for hunk in patch.split(patchfile):
3032 for hunk in patch.split(patchfile):
3029 (msg, node, rej) = cmdutil.tryimportone(ui, repo, hunk,
3033 (msg, node, rej) = cmdutil.tryimportone(ui, repo, hunk,
3030 parents, opts,
3034 parents, opts,
3031 msgs, hg.clean)
3035 msgs, hg.clean)
3032 if msg:
3036 if msg:
3033 haspatch = True
3037 haspatch = True
3034 ui.note(msg + '\n')
3038 ui.note(msg + '\n')
3035 if update or exact:
3039 if update or exact:
3036 parents = repo[None].parents()
3040 parents = repo[None].parents()
3037 else:
3041 else:
3038 parents = [repo[node]]
3042 parents = [repo[node]]
3039 if rej:
3043 if rej:
3040 ui.write_err(_("patch applied partially\n"))
3044 ui.write_err(_("patch applied partially\n"))
3041 ui.write_err(_("(fix the .rej files and run "
3045 ui.write_err(_("(fix the .rej files and run "
3042 "`hg commit --amend`)\n"))
3046 "`hg commit --amend`)\n"))
3043 ret = 1
3047 ret = 1
3044 break
3048 break
3045
3049
3046 if not haspatch:
3050 if not haspatch:
3047 raise error.Abort(_('%s: no diffs found') % patchurl)
3051 raise error.Abort(_('%s: no diffs found') % patchurl)
3048
3052
3049 if tr:
3053 if tr:
3050 tr.close()
3054 tr.close()
3051 if msgs:
3055 if msgs:
3052 repo.savecommitmessage('\n* * *\n'.join(msgs))
3056 repo.savecommitmessage('\n* * *\n'.join(msgs))
3053 if dsguard:
3057 if dsguard:
3054 dsguard.close()
3058 dsguard.close()
3055 return ret
3059 return ret
3056 finally:
3060 finally:
3057 if tr:
3061 if tr:
3058 tr.release()
3062 tr.release()
3059 release(lock, dsguard, wlock)
3063 release(lock, dsguard, wlock)
3060
3064
3061 @command('incoming|in',
3065 @command('incoming|in',
3062 [('f', 'force', None,
3066 [('f', 'force', None,
3063 _('run even if remote repository is unrelated')),
3067 _('run even if remote repository is unrelated')),
3064 ('n', 'newest-first', None, _('show newest record first')),
3068 ('n', 'newest-first', None, _('show newest record first')),
3065 ('', 'bundle', '',
3069 ('', 'bundle', '',
3066 _('file to store the bundles into'), _('FILE')),
3070 _('file to store the bundles into'), _('FILE')),
3067 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3071 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3068 ('B', 'bookmarks', False, _("compare bookmarks")),
3072 ('B', 'bookmarks', False, _("compare bookmarks")),
3069 ('b', 'branch', [],
3073 ('b', 'branch', [],
3070 _('a specific branch you would like to pull'), _('BRANCH')),
3074 _('a specific branch you would like to pull'), _('BRANCH')),
3071 ] + logopts + remoteopts + subrepoopts,
3075 ] + logopts + remoteopts + subrepoopts,
3072 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
3076 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
3073 def incoming(ui, repo, source="default", **opts):
3077 def incoming(ui, repo, source="default", **opts):
3074 """show new changesets found in source
3078 """show new changesets found in source
3075
3079
3076 Show new changesets found in the specified path/URL or the default
3080 Show new changesets found in the specified path/URL or the default
3077 pull location. These are the changesets that would have been pulled
3081 pull location. These are the changesets that would have been pulled
3078 by :hg:`pull` at the time you issued this command.
3082 by :hg:`pull` at the time you issued this command.
3079
3083
3080 See pull for valid source format details.
3084 See pull for valid source format details.
3081
3085
3082 .. container:: verbose
3086 .. container:: verbose
3083
3087
3084 With -B/--bookmarks, the result of bookmark comparison between
3088 With -B/--bookmarks, the result of bookmark comparison between
3085 local and remote repositories is displayed. With -v/--verbose,
3089 local and remote repositories is displayed. With -v/--verbose,
3086 status is also displayed for each bookmark like below::
3090 status is also displayed for each bookmark like below::
3087
3091
3088 BM1 01234567890a added
3092 BM1 01234567890a added
3089 BM2 1234567890ab advanced
3093 BM2 1234567890ab advanced
3090 BM3 234567890abc diverged
3094 BM3 234567890abc diverged
3091 BM4 34567890abcd changed
3095 BM4 34567890abcd changed
3092
3096
3093 The action taken locally when pulling depends on the
3097 The action taken locally when pulling depends on the
3094 status of each bookmark:
3098 status of each bookmark:
3095
3099
3096 :``added``: pull will create it
3100 :``added``: pull will create it
3097 :``advanced``: pull will update it
3101 :``advanced``: pull will update it
3098 :``diverged``: pull will create a divergent bookmark
3102 :``diverged``: pull will create a divergent bookmark
3099 :``changed``: result depends on remote changesets
3103 :``changed``: result depends on remote changesets
3100
3104
3101 From the point of view of pulling behavior, bookmark
3105 From the point of view of pulling behavior, bookmark
3102 existing only in the remote repository are treated as ``added``,
3106 existing only in the remote repository are treated as ``added``,
3103 even if it is in fact locally deleted.
3107 even if it is in fact locally deleted.
3104
3108
3105 .. container:: verbose
3109 .. container:: verbose
3106
3110
3107 For remote repository, using --bundle avoids downloading the
3111 For remote repository, using --bundle avoids downloading the
3108 changesets twice if the incoming is followed by a pull.
3112 changesets twice if the incoming is followed by a pull.
3109
3113
3110 Examples:
3114 Examples:
3111
3115
3112 - show incoming changes with patches and full description::
3116 - show incoming changes with patches and full description::
3113
3117
3114 hg incoming -vp
3118 hg incoming -vp
3115
3119
3116 - show incoming changes excluding merges, store a bundle::
3120 - show incoming changes excluding merges, store a bundle::
3117
3121
3118 hg in -vpM --bundle incoming.hg
3122 hg in -vpM --bundle incoming.hg
3119 hg pull incoming.hg
3123 hg pull incoming.hg
3120
3124
3121 - briefly list changes inside a bundle::
3125 - briefly list changes inside a bundle::
3122
3126
3123 hg in changes.hg -T "{desc|firstline}\\n"
3127 hg in changes.hg -T "{desc|firstline}\\n"
3124
3128
3125 Returns 0 if there are incoming changes, 1 otherwise.
3129 Returns 0 if there are incoming changes, 1 otherwise.
3126 """
3130 """
3127 opts = pycompat.byteskwargs(opts)
3131 opts = pycompat.byteskwargs(opts)
3128 if opts.get('graph'):
3132 if opts.get('graph'):
3129 cmdutil.checkunsupportedgraphflags([], opts)
3133 cmdutil.checkunsupportedgraphflags([], opts)
3130 def display(other, chlist, displayer):
3134 def display(other, chlist, displayer):
3131 revdag = cmdutil.graphrevs(other, chlist, opts)
3135 revdag = cmdutil.graphrevs(other, chlist, opts)
3132 cmdutil.displaygraph(ui, repo, revdag, displayer,
3136 cmdutil.displaygraph(ui, repo, revdag, displayer,
3133 graphmod.asciiedges)
3137 graphmod.asciiedges)
3134
3138
3135 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
3139 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
3136 return 0
3140 return 0
3137
3141
3138 if opts.get('bundle') and opts.get('subrepos'):
3142 if opts.get('bundle') and opts.get('subrepos'):
3139 raise error.Abort(_('cannot combine --bundle and --subrepos'))
3143 raise error.Abort(_('cannot combine --bundle and --subrepos'))
3140
3144
3141 if opts.get('bookmarks'):
3145 if opts.get('bookmarks'):
3142 source, branches = hg.parseurl(ui.expandpath(source),
3146 source, branches = hg.parseurl(ui.expandpath(source),
3143 opts.get('branch'))
3147 opts.get('branch'))
3144 other = hg.peer(repo, opts, source)
3148 other = hg.peer(repo, opts, source)
3145 if 'bookmarks' not in other.listkeys('namespaces'):
3149 if 'bookmarks' not in other.listkeys('namespaces'):
3146 ui.warn(_("remote doesn't support bookmarks\n"))
3150 ui.warn(_("remote doesn't support bookmarks\n"))
3147 return 0
3151 return 0
3148 ui.pager('incoming')
3152 ui.pager('incoming')
3149 ui.status(_('comparing with %s\n') % util.hidepassword(source))
3153 ui.status(_('comparing with %s\n') % util.hidepassword(source))
3150 return bookmarks.incoming(ui, repo, other)
3154 return bookmarks.incoming(ui, repo, other)
3151
3155
3152 repo._subtoppath = ui.expandpath(source)
3156 repo._subtoppath = ui.expandpath(source)
3153 try:
3157 try:
3154 return hg.incoming(ui, repo, source, opts)
3158 return hg.incoming(ui, repo, source, opts)
3155 finally:
3159 finally:
3156 del repo._subtoppath
3160 del repo._subtoppath
3157
3161
3158
3162
3159 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'),
3163 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'),
3160 norepo=True)
3164 norepo=True)
3161 def init(ui, dest=".", **opts):
3165 def init(ui, dest=".", **opts):
3162 """create a new repository in the given directory
3166 """create a new repository in the given directory
3163
3167
3164 Initialize a new repository in the given directory. If the given
3168 Initialize a new repository in the given directory. If the given
3165 directory does not exist, it will be created.
3169 directory does not exist, it will be created.
3166
3170
3167 If no directory is given, the current directory is used.
3171 If no directory is given, the current directory is used.
3168
3172
3169 It is possible to specify an ``ssh://`` URL as the destination.
3173 It is possible to specify an ``ssh://`` URL as the destination.
3170 See :hg:`help urls` for more information.
3174 See :hg:`help urls` for more information.
3171
3175
3172 Returns 0 on success.
3176 Returns 0 on success.
3173 """
3177 """
3174 opts = pycompat.byteskwargs(opts)
3178 opts = pycompat.byteskwargs(opts)
3175 hg.peer(ui, opts, ui.expandpath(dest), create=True)
3179 hg.peer(ui, opts, ui.expandpath(dest), create=True)
3176
3180
3177 @command('locate',
3181 @command('locate',
3178 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3182 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3179 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3183 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3180 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
3184 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
3181 ] + walkopts,
3185 ] + walkopts,
3182 _('[OPTION]... [PATTERN]...'))
3186 _('[OPTION]... [PATTERN]...'))
3183 def locate(ui, repo, *pats, **opts):
3187 def locate(ui, repo, *pats, **opts):
3184 """locate files matching specific patterns (DEPRECATED)
3188 """locate files matching specific patterns (DEPRECATED)
3185
3189
3186 Print files under Mercurial control in the working directory whose
3190 Print files under Mercurial control in the working directory whose
3187 names match the given patterns.
3191 names match the given patterns.
3188
3192
3189 By default, this command searches all directories in the working
3193 By default, this command searches all directories in the working
3190 directory. To search just the current directory and its
3194 directory. To search just the current directory and its
3191 subdirectories, use "--include .".
3195 subdirectories, use "--include .".
3192
3196
3193 If no patterns are given to match, this command prints the names
3197 If no patterns are given to match, this command prints the names
3194 of all files under Mercurial control in the working directory.
3198 of all files under Mercurial control in the working directory.
3195
3199
3196 If you want to feed the output of this command into the "xargs"
3200 If you want to feed the output of this command into the "xargs"
3197 command, use the -0 option to both this command and "xargs". This
3201 command, use the -0 option to both this command and "xargs". This
3198 will avoid the problem of "xargs" treating single filenames that
3202 will avoid the problem of "xargs" treating single filenames that
3199 contain whitespace as multiple filenames.
3203 contain whitespace as multiple filenames.
3200
3204
3201 See :hg:`help files` for a more versatile command.
3205 See :hg:`help files` for a more versatile command.
3202
3206
3203 Returns 0 if a match is found, 1 otherwise.
3207 Returns 0 if a match is found, 1 otherwise.
3204 """
3208 """
3205 opts = pycompat.byteskwargs(opts)
3209 opts = pycompat.byteskwargs(opts)
3206 if opts.get('print0'):
3210 if opts.get('print0'):
3207 end = '\0'
3211 end = '\0'
3208 else:
3212 else:
3209 end = '\n'
3213 end = '\n'
3210 rev = scmutil.revsingle(repo, opts.get('rev'), None).node()
3214 rev = scmutil.revsingle(repo, opts.get('rev'), None).node()
3211
3215
3212 ret = 1
3216 ret = 1
3213 ctx = repo[rev]
3217 ctx = repo[rev]
3214 m = scmutil.match(ctx, pats, opts, default='relglob',
3218 m = scmutil.match(ctx, pats, opts, default='relglob',
3215 badfn=lambda x, y: False)
3219 badfn=lambda x, y: False)
3216
3220
3217 ui.pager('locate')
3221 ui.pager('locate')
3218 for abs in ctx.matches(m):
3222 for abs in ctx.matches(m):
3219 if opts.get('fullpath'):
3223 if opts.get('fullpath'):
3220 ui.write(repo.wjoin(abs), end)
3224 ui.write(repo.wjoin(abs), end)
3221 else:
3225 else:
3222 ui.write(((pats and m.rel(abs)) or abs), end)
3226 ui.write(((pats and m.rel(abs)) or abs), end)
3223 ret = 0
3227 ret = 0
3224
3228
3225 return ret
3229 return ret
3226
3230
3227 @command('^log|history',
3231 @command('^log|history',
3228 [('f', 'follow', None,
3232 [('f', 'follow', None,
3229 _('follow changeset history, or file history across copies and renames')),
3233 _('follow changeset history, or file history across copies and renames')),
3230 ('', 'follow-first', None,
3234 ('', 'follow-first', None,
3231 _('only follow the first parent of merge changesets (DEPRECATED)')),
3235 _('only follow the first parent of merge changesets (DEPRECATED)')),
3232 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
3236 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
3233 ('C', 'copies', None, _('show copied files')),
3237 ('C', 'copies', None, _('show copied files')),
3234 ('k', 'keyword', [],
3238 ('k', 'keyword', [],
3235 _('do case-insensitive search for a given text'), _('TEXT')),
3239 _('do case-insensitive search for a given text'), _('TEXT')),
3236 ('r', 'rev', [], _('show the specified revision or revset'), _('REV')),
3240 ('r', 'rev', [], _('show the specified revision or revset'), _('REV')),
3237 ('L', 'line-range', [],
3241 ('L', 'line-range', [],
3238 _('follow line range of specified file (EXPERIMENTAL)'),
3242 _('follow line range of specified file (EXPERIMENTAL)'),
3239 _('FILE,RANGE')),
3243 _('FILE,RANGE')),
3240 ('', 'removed', None, _('include revisions where files were removed')),
3244 ('', 'removed', None, _('include revisions where files were removed')),
3241 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
3245 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
3242 ('u', 'user', [], _('revisions committed by user'), _('USER')),
3246 ('u', 'user', [], _('revisions committed by user'), _('USER')),
3243 ('', 'only-branch', [],
3247 ('', 'only-branch', [],
3244 _('show only changesets within the given named branch (DEPRECATED)'),
3248 _('show only changesets within the given named branch (DEPRECATED)'),
3245 _('BRANCH')),
3249 _('BRANCH')),
3246 ('b', 'branch', [],
3250 ('b', 'branch', [],
3247 _('show changesets within the given named branch'), _('BRANCH')),
3251 _('show changesets within the given named branch'), _('BRANCH')),
3248 ('P', 'prune', [],
3252 ('P', 'prune', [],
3249 _('do not display revision or any of its ancestors'), _('REV')),
3253 _('do not display revision or any of its ancestors'), _('REV')),
3250 ] + logopts + walkopts,
3254 ] + logopts + walkopts,
3251 _('[OPTION]... [FILE]'),
3255 _('[OPTION]... [FILE]'),
3252 inferrepo=True)
3256 inferrepo=True)
3253 def log(ui, repo, *pats, **opts):
3257 def log(ui, repo, *pats, **opts):
3254 """show revision history of entire repository or files
3258 """show revision history of entire repository or files
3255
3259
3256 Print the revision history of the specified files or the entire
3260 Print the revision history of the specified files or the entire
3257 project.
3261 project.
3258
3262
3259 If no revision range is specified, the default is ``tip:0`` unless
3263 If no revision range is specified, the default is ``tip:0`` unless
3260 --follow is set, in which case the working directory parent is
3264 --follow is set, in which case the working directory parent is
3261 used as the starting revision.
3265 used as the starting revision.
3262
3266
3263 File history is shown without following rename or copy history of
3267 File history is shown without following rename or copy history of
3264 files. Use -f/--follow with a filename to follow history across
3268 files. Use -f/--follow with a filename to follow history across
3265 renames and copies. --follow without a filename will only show
3269 renames and copies. --follow without a filename will only show
3266 ancestors or descendants of the starting revision.
3270 ancestors or descendants of the starting revision.
3267
3271
3268 By default this command prints revision number and changeset id,
3272 By default this command prints revision number and changeset id,
3269 tags, non-trivial parents, user, date and time, and a summary for
3273 tags, non-trivial parents, user, date and time, and a summary for
3270 each commit. When the -v/--verbose switch is used, the list of
3274 each commit. When the -v/--verbose switch is used, the list of
3271 changed files and full commit message are shown.
3275 changed files and full commit message are shown.
3272
3276
3273 With --graph the revisions are shown as an ASCII art DAG with the most
3277 With --graph the revisions are shown as an ASCII art DAG with the most
3274 recent changeset at the top.
3278 recent changeset at the top.
3275 'o' is a changeset, '@' is a working directory parent, 'x' is obsolete,
3279 'o' is a changeset, '@' is a working directory parent, 'x' is obsolete,
3276 and '+' represents a fork where the changeset from the lines below is a
3280 and '+' represents a fork where the changeset from the lines below is a
3277 parent of the 'o' merge on the same line.
3281 parent of the 'o' merge on the same line.
3278 Paths in the DAG are represented with '|', '/' and so forth. ':' in place
3282 Paths in the DAG are represented with '|', '/' and so forth. ':' in place
3279 of a '|' indicates one or more revisions in a path are omitted.
3283 of a '|' indicates one or more revisions in a path are omitted.
3280
3284
3281 .. container:: verbose
3285 .. container:: verbose
3282
3286
3283 Use -L/--line-range FILE,M:N options to follow the history of lines
3287 Use -L/--line-range FILE,M:N options to follow the history of lines
3284 from M to N in FILE. With -p/--patch only diff hunks affecting
3288 from M to N in FILE. With -p/--patch only diff hunks affecting
3285 specified line range will be shown. This option requires --follow;
3289 specified line range will be shown. This option requires --follow;
3286 it can be specified multiple times. Currently, this option is not
3290 it can be specified multiple times. Currently, this option is not
3287 compatible with --graph. This option is experimental.
3291 compatible with --graph. This option is experimental.
3288
3292
3289 .. note::
3293 .. note::
3290
3294
3291 :hg:`log --patch` may generate unexpected diff output for merge
3295 :hg:`log --patch` may generate unexpected diff output for merge
3292 changesets, as it will only compare the merge changeset against
3296 changesets, as it will only compare the merge changeset against
3293 its first parent. Also, only files different from BOTH parents
3297 its first parent. Also, only files different from BOTH parents
3294 will appear in files:.
3298 will appear in files:.
3295
3299
3296 .. note::
3300 .. note::
3297
3301
3298 For performance reasons, :hg:`log FILE` may omit duplicate changes
3302 For performance reasons, :hg:`log FILE` may omit duplicate changes
3299 made on branches and will not show removals or mode changes. To
3303 made on branches and will not show removals or mode changes. To
3300 see all such changes, use the --removed switch.
3304 see all such changes, use the --removed switch.
3301
3305
3302 .. container:: verbose
3306 .. container:: verbose
3303
3307
3304 .. note::
3308 .. note::
3305
3309
3306 The history resulting from -L/--line-range options depends on diff
3310 The history resulting from -L/--line-range options depends on diff
3307 options; for instance if white-spaces are ignored, respective changes
3311 options; for instance if white-spaces are ignored, respective changes
3308 with only white-spaces in specified line range will not be listed.
3312 with only white-spaces in specified line range will not be listed.
3309
3313
3310 .. container:: verbose
3314 .. container:: verbose
3311
3315
3312 Some examples:
3316 Some examples:
3313
3317
3314 - changesets with full descriptions and file lists::
3318 - changesets with full descriptions and file lists::
3315
3319
3316 hg log -v
3320 hg log -v
3317
3321
3318 - changesets ancestral to the working directory::
3322 - changesets ancestral to the working directory::
3319
3323
3320 hg log -f
3324 hg log -f
3321
3325
3322 - last 10 commits on the current branch::
3326 - last 10 commits on the current branch::
3323
3327
3324 hg log -l 10 -b .
3328 hg log -l 10 -b .
3325
3329
3326 - changesets showing all modifications of a file, including removals::
3330 - changesets showing all modifications of a file, including removals::
3327
3331
3328 hg log --removed file.c
3332 hg log --removed file.c
3329
3333
3330 - all changesets that touch a directory, with diffs, excluding merges::
3334 - all changesets that touch a directory, with diffs, excluding merges::
3331
3335
3332 hg log -Mp lib/
3336 hg log -Mp lib/
3333
3337
3334 - all revision numbers that match a keyword::
3338 - all revision numbers that match a keyword::
3335
3339
3336 hg log -k bug --template "{rev}\\n"
3340 hg log -k bug --template "{rev}\\n"
3337
3341
3338 - the full hash identifier of the working directory parent::
3342 - the full hash identifier of the working directory parent::
3339
3343
3340 hg log -r . --template "{node}\\n"
3344 hg log -r . --template "{node}\\n"
3341
3345
3342 - list available log templates::
3346 - list available log templates::
3343
3347
3344 hg log -T list
3348 hg log -T list
3345
3349
3346 - check if a given changeset is included in a tagged release::
3350 - check if a given changeset is included in a tagged release::
3347
3351
3348 hg log -r "a21ccf and ancestor(1.9)"
3352 hg log -r "a21ccf and ancestor(1.9)"
3349
3353
3350 - find all changesets by some user in a date range::
3354 - find all changesets by some user in a date range::
3351
3355
3352 hg log -k alice -d "may 2008 to jul 2008"
3356 hg log -k alice -d "may 2008 to jul 2008"
3353
3357
3354 - summary of all changesets after the last tag::
3358 - summary of all changesets after the last tag::
3355
3359
3356 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
3360 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
3357
3361
3358 - changesets touching lines 13 to 23 for file.c::
3362 - changesets touching lines 13 to 23 for file.c::
3359
3363
3360 hg log -L file.c,13:23
3364 hg log -L file.c,13:23
3361
3365
3362 - changesets touching lines 13 to 23 for file.c and lines 2 to 6 of
3366 - changesets touching lines 13 to 23 for file.c and lines 2 to 6 of
3363 main.c with patch::
3367 main.c with patch::
3364
3368
3365 hg log -L file.c,13:23 -L main.c,2:6 -p
3369 hg log -L file.c,13:23 -L main.c,2:6 -p
3366
3370
3367 See :hg:`help dates` for a list of formats valid for -d/--date.
3371 See :hg:`help dates` for a list of formats valid for -d/--date.
3368
3372
3369 See :hg:`help revisions` for more about specifying and ordering
3373 See :hg:`help revisions` for more about specifying and ordering
3370 revisions.
3374 revisions.
3371
3375
3372 See :hg:`help templates` for more about pre-packaged styles and
3376 See :hg:`help templates` for more about pre-packaged styles and
3373 specifying custom templates. The default template used by the log
3377 specifying custom templates. The default template used by the log
3374 command can be customized via the ``ui.logtemplate`` configuration
3378 command can be customized via the ``ui.logtemplate`` configuration
3375 setting.
3379 setting.
3376
3380
3377 Returns 0 on success.
3381 Returns 0 on success.
3378
3382
3379 """
3383 """
3380 opts = pycompat.byteskwargs(opts)
3384 opts = pycompat.byteskwargs(opts)
3381 linerange = opts.get('line_range')
3385 linerange = opts.get('line_range')
3382
3386
3383 if linerange and not opts.get('follow'):
3387 if linerange and not opts.get('follow'):
3384 raise error.Abort(_('--line-range requires --follow'))
3388 raise error.Abort(_('--line-range requires --follow'))
3385
3389
3386 if linerange and pats:
3390 if linerange and pats:
3387 raise error.Abort(
3391 raise error.Abort(
3388 _('FILE arguments are not compatible with --line-range option')
3392 _('FILE arguments are not compatible with --line-range option')
3389 )
3393 )
3390
3394
3391 if opts.get('follow') and opts.get('rev'):
3395 if opts.get('follow') and opts.get('rev'):
3392 opts['rev'] = [revsetlang.formatspec('reverse(::%lr)', opts.get('rev'))]
3396 opts['rev'] = [revsetlang.formatspec('reverse(::%lr)', opts.get('rev'))]
3393 del opts['follow']
3397 del opts['follow']
3394
3398
3395 if opts.get('graph'):
3399 if opts.get('graph'):
3396 if linerange:
3400 if linerange:
3397 raise error.Abort(_('graph not supported with line range patterns'))
3401 raise error.Abort(_('graph not supported with line range patterns'))
3398 return cmdutil.graphlog(ui, repo, pats, opts)
3402 return cmdutil.graphlog(ui, repo, pats, opts)
3399
3403
3400 revs, expr, filematcher = cmdutil.getlogrevs(repo, pats, opts)
3404 revs, expr, filematcher = cmdutil.getlogrevs(repo, pats, opts)
3401 hunksfilter = None
3405 hunksfilter = None
3402
3406
3403 if linerange:
3407 if linerange:
3404 revs, lrfilematcher, hunksfilter = cmdutil.getloglinerangerevs(
3408 revs, lrfilematcher, hunksfilter = cmdutil.getloglinerangerevs(
3405 repo, revs, opts)
3409 repo, revs, opts)
3406
3410
3407 if filematcher is not None and lrfilematcher is not None:
3411 if filematcher is not None and lrfilematcher is not None:
3408 basefilematcher = filematcher
3412 basefilematcher = filematcher
3409
3413
3410 def filematcher(rev):
3414 def filematcher(rev):
3411 files = (basefilematcher(rev).files()
3415 files = (basefilematcher(rev).files()
3412 + lrfilematcher(rev).files())
3416 + lrfilematcher(rev).files())
3413 return scmutil.matchfiles(repo, files)
3417 return scmutil.matchfiles(repo, files)
3414
3418
3415 elif filematcher is None:
3419 elif filematcher is None:
3416 filematcher = lrfilematcher
3420 filematcher = lrfilematcher
3417
3421
3418 limit = cmdutil.loglimit(opts)
3422 limit = cmdutil.loglimit(opts)
3419 count = 0
3423 count = 0
3420
3424
3421 getrenamed = None
3425 getrenamed = None
3422 if opts.get('copies'):
3426 if opts.get('copies'):
3423 endrev = None
3427 endrev = None
3424 if opts.get('rev'):
3428 if opts.get('rev'):
3425 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
3429 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
3426 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
3430 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
3427
3431
3428 ui.pager('log')
3432 ui.pager('log')
3429 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
3433 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
3430 for rev in revs:
3434 for rev in revs:
3431 if count == limit:
3435 if count == limit:
3432 break
3436 break
3433 ctx = repo[rev]
3437 ctx = repo[rev]
3434 copies = None
3438 copies = None
3435 if getrenamed is not None and rev:
3439 if getrenamed is not None and rev:
3436 copies = []
3440 copies = []
3437 for fn in ctx.files():
3441 for fn in ctx.files():
3438 rename = getrenamed(fn, rev)
3442 rename = getrenamed(fn, rev)
3439 if rename:
3443 if rename:
3440 copies.append((fn, rename[0]))
3444 copies.append((fn, rename[0]))
3441 if filematcher:
3445 if filematcher:
3442 revmatchfn = filematcher(ctx.rev())
3446 revmatchfn = filematcher(ctx.rev())
3443 else:
3447 else:
3444 revmatchfn = None
3448 revmatchfn = None
3445 if hunksfilter:
3449 if hunksfilter:
3446 revhunksfilter = hunksfilter(rev)
3450 revhunksfilter = hunksfilter(rev)
3447 else:
3451 else:
3448 revhunksfilter = None
3452 revhunksfilter = None
3449 displayer.show(ctx, copies=copies, matchfn=revmatchfn,
3453 displayer.show(ctx, copies=copies, matchfn=revmatchfn,
3450 hunksfilterfn=revhunksfilter)
3454 hunksfilterfn=revhunksfilter)
3451 if displayer.flush(ctx):
3455 if displayer.flush(ctx):
3452 count += 1
3456 count += 1
3453
3457
3454 displayer.close()
3458 displayer.close()
3455
3459
3456 @command('manifest',
3460 @command('manifest',
3457 [('r', 'rev', '', _('revision to display'), _('REV')),
3461 [('r', 'rev', '', _('revision to display'), _('REV')),
3458 ('', 'all', False, _("list files from all revisions"))]
3462 ('', 'all', False, _("list files from all revisions"))]
3459 + formatteropts,
3463 + formatteropts,
3460 _('[-r REV]'))
3464 _('[-r REV]'))
3461 def manifest(ui, repo, node=None, rev=None, **opts):
3465 def manifest(ui, repo, node=None, rev=None, **opts):
3462 """output the current or given revision of the project manifest
3466 """output the current or given revision of the project manifest
3463
3467
3464 Print a list of version controlled files for the given revision.
3468 Print a list of version controlled files for the given revision.
3465 If no revision is given, the first parent of the working directory
3469 If no revision is given, the first parent of the working directory
3466 is used, or the null revision if no revision is checked out.
3470 is used, or the null revision if no revision is checked out.
3467
3471
3468 With -v, print file permissions, symlink and executable bits.
3472 With -v, print file permissions, symlink and executable bits.
3469 With --debug, print file revision hashes.
3473 With --debug, print file revision hashes.
3470
3474
3471 If option --all is specified, the list of all files from all revisions
3475 If option --all is specified, the list of all files from all revisions
3472 is printed. This includes deleted and renamed files.
3476 is printed. This includes deleted and renamed files.
3473
3477
3474 Returns 0 on success.
3478 Returns 0 on success.
3475 """
3479 """
3476 opts = pycompat.byteskwargs(opts)
3480 opts = pycompat.byteskwargs(opts)
3477 fm = ui.formatter('manifest', opts)
3481 fm = ui.formatter('manifest', opts)
3478
3482
3479 if opts.get('all'):
3483 if opts.get('all'):
3480 if rev or node:
3484 if rev or node:
3481 raise error.Abort(_("can't specify a revision with --all"))
3485 raise error.Abort(_("can't specify a revision with --all"))
3482
3486
3483 res = []
3487 res = []
3484 prefix = "data/"
3488 prefix = "data/"
3485 suffix = ".i"
3489 suffix = ".i"
3486 plen = len(prefix)
3490 plen = len(prefix)
3487 slen = len(suffix)
3491 slen = len(suffix)
3488 with repo.lock():
3492 with repo.lock():
3489 for fn, b, size in repo.store.datafiles():
3493 for fn, b, size in repo.store.datafiles():
3490 if size != 0 and fn[-slen:] == suffix and fn[:plen] == prefix:
3494 if size != 0 and fn[-slen:] == suffix and fn[:plen] == prefix:
3491 res.append(fn[plen:-slen])
3495 res.append(fn[plen:-slen])
3492 ui.pager('manifest')
3496 ui.pager('manifest')
3493 for f in res:
3497 for f in res:
3494 fm.startitem()
3498 fm.startitem()
3495 fm.write("path", '%s\n', f)
3499 fm.write("path", '%s\n', f)
3496 fm.end()
3500 fm.end()
3497 return
3501 return
3498
3502
3499 if rev and node:
3503 if rev and node:
3500 raise error.Abort(_("please specify just one revision"))
3504 raise error.Abort(_("please specify just one revision"))
3501
3505
3502 if not node:
3506 if not node:
3503 node = rev
3507 node = rev
3504
3508
3505 char = {'l': '@', 'x': '*', '': ''}
3509 char = {'l': '@', 'x': '*', '': ''}
3506 mode = {'l': '644', 'x': '755', '': '644'}
3510 mode = {'l': '644', 'x': '755', '': '644'}
3507 ctx = scmutil.revsingle(repo, node)
3511 ctx = scmutil.revsingle(repo, node)
3508 mf = ctx.manifest()
3512 mf = ctx.manifest()
3509 ui.pager('manifest')
3513 ui.pager('manifest')
3510 for f in ctx:
3514 for f in ctx:
3511 fm.startitem()
3515 fm.startitem()
3512 fl = ctx[f].flags()
3516 fl = ctx[f].flags()
3513 fm.condwrite(ui.debugflag, 'hash', '%s ', hex(mf[f]))
3517 fm.condwrite(ui.debugflag, 'hash', '%s ', hex(mf[f]))
3514 fm.condwrite(ui.verbose, 'mode type', '%s %1s ', mode[fl], char[fl])
3518 fm.condwrite(ui.verbose, 'mode type', '%s %1s ', mode[fl], char[fl])
3515 fm.write('path', '%s\n', f)
3519 fm.write('path', '%s\n', f)
3516 fm.end()
3520 fm.end()
3517
3521
3518 @command('^merge',
3522 @command('^merge',
3519 [('f', 'force', None,
3523 [('f', 'force', None,
3520 _('force a merge including outstanding changes (DEPRECATED)')),
3524 _('force a merge including outstanding changes (DEPRECATED)')),
3521 ('r', 'rev', '', _('revision to merge'), _('REV')),
3525 ('r', 'rev', '', _('revision to merge'), _('REV')),
3522 ('P', 'preview', None,
3526 ('P', 'preview', None,
3523 _('review revisions to merge (no merge is performed)'))
3527 _('review revisions to merge (no merge is performed)'))
3524 ] + mergetoolopts,
3528 ] + mergetoolopts,
3525 _('[-P] [[-r] REV]'))
3529 _('[-P] [[-r] REV]'))
3526 def merge(ui, repo, node=None, **opts):
3530 def merge(ui, repo, node=None, **opts):
3527 """merge another revision into working directory
3531 """merge another revision into working directory
3528
3532
3529 The current working directory is updated with all changes made in
3533 The current working directory is updated with all changes made in
3530 the requested revision since the last common predecessor revision.
3534 the requested revision since the last common predecessor revision.
3531
3535
3532 Files that changed between either parent are marked as changed for
3536 Files that changed between either parent are marked as changed for
3533 the next commit and a commit must be performed before any further
3537 the next commit and a commit must be performed before any further
3534 updates to the repository are allowed. The next commit will have
3538 updates to the repository are allowed. The next commit will have
3535 two parents.
3539 two parents.
3536
3540
3537 ``--tool`` can be used to specify the merge tool used for file
3541 ``--tool`` can be used to specify the merge tool used for file
3538 merges. It overrides the HGMERGE environment variable and your
3542 merges. It overrides the HGMERGE environment variable and your
3539 configuration files. See :hg:`help merge-tools` for options.
3543 configuration files. See :hg:`help merge-tools` for options.
3540
3544
3541 If no revision is specified, the working directory's parent is a
3545 If no revision is specified, the working directory's parent is a
3542 head revision, and the current branch contains exactly one other
3546 head revision, and the current branch contains exactly one other
3543 head, the other head is merged with by default. Otherwise, an
3547 head, the other head is merged with by default. Otherwise, an
3544 explicit revision with which to merge with must be provided.
3548 explicit revision with which to merge with must be provided.
3545
3549
3546 See :hg:`help resolve` for information on handling file conflicts.
3550 See :hg:`help resolve` for information on handling file conflicts.
3547
3551
3548 To undo an uncommitted merge, use :hg:`update --clean .` which
3552 To undo an uncommitted merge, use :hg:`update --clean .` which
3549 will check out a clean copy of the original merge parent, losing
3553 will check out a clean copy of the original merge parent, losing
3550 all changes.
3554 all changes.
3551
3555
3552 Returns 0 on success, 1 if there are unresolved files.
3556 Returns 0 on success, 1 if there are unresolved files.
3553 """
3557 """
3554
3558
3555 opts = pycompat.byteskwargs(opts)
3559 opts = pycompat.byteskwargs(opts)
3556 if opts.get('rev') and node:
3560 if opts.get('rev') and node:
3557 raise error.Abort(_("please specify just one revision"))
3561 raise error.Abort(_("please specify just one revision"))
3558 if not node:
3562 if not node:
3559 node = opts.get('rev')
3563 node = opts.get('rev')
3560
3564
3561 if node:
3565 if node:
3562 node = scmutil.revsingle(repo, node).node()
3566 node = scmutil.revsingle(repo, node).node()
3563
3567
3564 if not node:
3568 if not node:
3565 node = repo[destutil.destmerge(repo)].node()
3569 node = repo[destutil.destmerge(repo)].node()
3566
3570
3567 if opts.get('preview'):
3571 if opts.get('preview'):
3568 # find nodes that are ancestors of p2 but not of p1
3572 # find nodes that are ancestors of p2 but not of p1
3569 p1 = repo.lookup('.')
3573 p1 = repo.lookup('.')
3570 p2 = repo.lookup(node)
3574 p2 = repo.lookup(node)
3571 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
3575 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
3572
3576
3573 displayer = cmdutil.show_changeset(ui, repo, opts)
3577 displayer = cmdutil.show_changeset(ui, repo, opts)
3574 for node in nodes:
3578 for node in nodes:
3575 displayer.show(repo[node])
3579 displayer.show(repo[node])
3576 displayer.close()
3580 displayer.close()
3577 return 0
3581 return 0
3578
3582
3579 try:
3583 try:
3580 # ui.forcemerge is an internal variable, do not document
3584 # ui.forcemerge is an internal variable, do not document
3581 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''), 'merge')
3585 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''), 'merge')
3582 force = opts.get('force')
3586 force = opts.get('force')
3583 labels = ['working copy', 'merge rev']
3587 labels = ['working copy', 'merge rev']
3584 return hg.merge(repo, node, force=force, mergeforce=force,
3588 return hg.merge(repo, node, force=force, mergeforce=force,
3585 labels=labels)
3589 labels=labels)
3586 finally:
3590 finally:
3587 ui.setconfig('ui', 'forcemerge', '', 'merge')
3591 ui.setconfig('ui', 'forcemerge', '', 'merge')
3588
3592
3589 @command('outgoing|out',
3593 @command('outgoing|out',
3590 [('f', 'force', None, _('run even when the destination is unrelated')),
3594 [('f', 'force', None, _('run even when the destination is unrelated')),
3591 ('r', 'rev', [],
3595 ('r', 'rev', [],
3592 _('a changeset intended to be included in the destination'), _('REV')),
3596 _('a changeset intended to be included in the destination'), _('REV')),
3593 ('n', 'newest-first', None, _('show newest record first')),
3597 ('n', 'newest-first', None, _('show newest record first')),
3594 ('B', 'bookmarks', False, _('compare bookmarks')),
3598 ('B', 'bookmarks', False, _('compare bookmarks')),
3595 ('b', 'branch', [], _('a specific branch you would like to push'),
3599 ('b', 'branch', [], _('a specific branch you would like to push'),
3596 _('BRANCH')),
3600 _('BRANCH')),
3597 ] + logopts + remoteopts + subrepoopts,
3601 ] + logopts + remoteopts + subrepoopts,
3598 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
3602 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
3599 def outgoing(ui, repo, dest=None, **opts):
3603 def outgoing(ui, repo, dest=None, **opts):
3600 """show changesets not found in the destination
3604 """show changesets not found in the destination
3601
3605
3602 Show changesets not found in the specified destination repository
3606 Show changesets not found in the specified destination repository
3603 or the default push location. These are the changesets that would
3607 or the default push location. These are the changesets that would
3604 be pushed if a push was requested.
3608 be pushed if a push was requested.
3605
3609
3606 See pull for details of valid destination formats.
3610 See pull for details of valid destination formats.
3607
3611
3608 .. container:: verbose
3612 .. container:: verbose
3609
3613
3610 With -B/--bookmarks, the result of bookmark comparison between
3614 With -B/--bookmarks, the result of bookmark comparison between
3611 local and remote repositories is displayed. With -v/--verbose,
3615 local and remote repositories is displayed. With -v/--verbose,
3612 status is also displayed for each bookmark like below::
3616 status is also displayed for each bookmark like below::
3613
3617
3614 BM1 01234567890a added
3618 BM1 01234567890a added
3615 BM2 deleted
3619 BM2 deleted
3616 BM3 234567890abc advanced
3620 BM3 234567890abc advanced
3617 BM4 34567890abcd diverged
3621 BM4 34567890abcd diverged
3618 BM5 4567890abcde changed
3622 BM5 4567890abcde changed
3619
3623
3620 The action taken when pushing depends on the
3624 The action taken when pushing depends on the
3621 status of each bookmark:
3625 status of each bookmark:
3622
3626
3623 :``added``: push with ``-B`` will create it
3627 :``added``: push with ``-B`` will create it
3624 :``deleted``: push with ``-B`` will delete it
3628 :``deleted``: push with ``-B`` will delete it
3625 :``advanced``: push will update it
3629 :``advanced``: push will update it
3626 :``diverged``: push with ``-B`` will update it
3630 :``diverged``: push with ``-B`` will update it
3627 :``changed``: push with ``-B`` will update it
3631 :``changed``: push with ``-B`` will update it
3628
3632
3629 From the point of view of pushing behavior, bookmarks
3633 From the point of view of pushing behavior, bookmarks
3630 existing only in the remote repository are treated as
3634 existing only in the remote repository are treated as
3631 ``deleted``, even if it is in fact added remotely.
3635 ``deleted``, even if it is in fact added remotely.
3632
3636
3633 Returns 0 if there are outgoing changes, 1 otherwise.
3637 Returns 0 if there are outgoing changes, 1 otherwise.
3634 """
3638 """
3635 opts = pycompat.byteskwargs(opts)
3639 opts = pycompat.byteskwargs(opts)
3636 if opts.get('graph'):
3640 if opts.get('graph'):
3637 cmdutil.checkunsupportedgraphflags([], opts)
3641 cmdutil.checkunsupportedgraphflags([], opts)
3638 o, other = hg._outgoing(ui, repo, dest, opts)
3642 o, other = hg._outgoing(ui, repo, dest, opts)
3639 if not o:
3643 if not o:
3640 cmdutil.outgoinghooks(ui, repo, other, opts, o)
3644 cmdutil.outgoinghooks(ui, repo, other, opts, o)
3641 return
3645 return
3642
3646
3643 revdag = cmdutil.graphrevs(repo, o, opts)
3647 revdag = cmdutil.graphrevs(repo, o, opts)
3644 ui.pager('outgoing')
3648 ui.pager('outgoing')
3645 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
3649 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
3646 cmdutil.displaygraph(ui, repo, revdag, displayer, graphmod.asciiedges)
3650 cmdutil.displaygraph(ui, repo, revdag, displayer, graphmod.asciiedges)
3647 cmdutil.outgoinghooks(ui, repo, other, opts, o)
3651 cmdutil.outgoinghooks(ui, repo, other, opts, o)
3648 return 0
3652 return 0
3649
3653
3650 if opts.get('bookmarks'):
3654 if opts.get('bookmarks'):
3651 dest = ui.expandpath(dest or 'default-push', dest or 'default')
3655 dest = ui.expandpath(dest or 'default-push', dest or 'default')
3652 dest, branches = hg.parseurl(dest, opts.get('branch'))
3656 dest, branches = hg.parseurl(dest, opts.get('branch'))
3653 other = hg.peer(repo, opts, dest)
3657 other = hg.peer(repo, opts, dest)
3654 if 'bookmarks' not in other.listkeys('namespaces'):
3658 if 'bookmarks' not in other.listkeys('namespaces'):
3655 ui.warn(_("remote doesn't support bookmarks\n"))
3659 ui.warn(_("remote doesn't support bookmarks\n"))
3656 return 0
3660 return 0
3657 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
3661 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
3658 ui.pager('outgoing')
3662 ui.pager('outgoing')
3659 return bookmarks.outgoing(ui, repo, other)
3663 return bookmarks.outgoing(ui, repo, other)
3660
3664
3661 repo._subtoppath = ui.expandpath(dest or 'default-push', dest or 'default')
3665 repo._subtoppath = ui.expandpath(dest or 'default-push', dest or 'default')
3662 try:
3666 try:
3663 return hg.outgoing(ui, repo, dest, opts)
3667 return hg.outgoing(ui, repo, dest, opts)
3664 finally:
3668 finally:
3665 del repo._subtoppath
3669 del repo._subtoppath
3666
3670
3667 @command('parents',
3671 @command('parents',
3668 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
3672 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
3669 ] + templateopts,
3673 ] + templateopts,
3670 _('[-r REV] [FILE]'),
3674 _('[-r REV] [FILE]'),
3671 inferrepo=True)
3675 inferrepo=True)
3672 def parents(ui, repo, file_=None, **opts):
3676 def parents(ui, repo, file_=None, **opts):
3673 """show the parents of the working directory or revision (DEPRECATED)
3677 """show the parents of the working directory or revision (DEPRECATED)
3674
3678
3675 Print the working directory's parent revisions. If a revision is
3679 Print the working directory's parent revisions. If a revision is
3676 given via -r/--rev, the parent of that revision will be printed.
3680 given via -r/--rev, the parent of that revision will be printed.
3677 If a file argument is given, the revision in which the file was
3681 If a file argument is given, the revision in which the file was
3678 last changed (before the working directory revision or the
3682 last changed (before the working directory revision or the
3679 argument to --rev if given) is printed.
3683 argument to --rev if given) is printed.
3680
3684
3681 This command is equivalent to::
3685 This command is equivalent to::
3682
3686
3683 hg log -r "p1()+p2()" or
3687 hg log -r "p1()+p2()" or
3684 hg log -r "p1(REV)+p2(REV)" or
3688 hg log -r "p1(REV)+p2(REV)" or
3685 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
3689 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
3686 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
3690 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
3687
3691
3688 See :hg:`summary` and :hg:`help revsets` for related information.
3692 See :hg:`summary` and :hg:`help revsets` for related information.
3689
3693
3690 Returns 0 on success.
3694 Returns 0 on success.
3691 """
3695 """
3692
3696
3693 opts = pycompat.byteskwargs(opts)
3697 opts = pycompat.byteskwargs(opts)
3694 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
3698 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
3695
3699
3696 if file_:
3700 if file_:
3697 m = scmutil.match(ctx, (file_,), opts)
3701 m = scmutil.match(ctx, (file_,), opts)
3698 if m.anypats() or len(m.files()) != 1:
3702 if m.anypats() or len(m.files()) != 1:
3699 raise error.Abort(_('can only specify an explicit filename'))
3703 raise error.Abort(_('can only specify an explicit filename'))
3700 file_ = m.files()[0]
3704 file_ = m.files()[0]
3701 filenodes = []
3705 filenodes = []
3702 for cp in ctx.parents():
3706 for cp in ctx.parents():
3703 if not cp:
3707 if not cp:
3704 continue
3708 continue
3705 try:
3709 try:
3706 filenodes.append(cp.filenode(file_))
3710 filenodes.append(cp.filenode(file_))
3707 except error.LookupError:
3711 except error.LookupError:
3708 pass
3712 pass
3709 if not filenodes:
3713 if not filenodes:
3710 raise error.Abort(_("'%s' not found in manifest!") % file_)
3714 raise error.Abort(_("'%s' not found in manifest!") % file_)
3711 p = []
3715 p = []
3712 for fn in filenodes:
3716 for fn in filenodes:
3713 fctx = repo.filectx(file_, fileid=fn)
3717 fctx = repo.filectx(file_, fileid=fn)
3714 p.append(fctx.node())
3718 p.append(fctx.node())
3715 else:
3719 else:
3716 p = [cp.node() for cp in ctx.parents()]
3720 p = [cp.node() for cp in ctx.parents()]
3717
3721
3718 displayer = cmdutil.show_changeset(ui, repo, opts)
3722 displayer = cmdutil.show_changeset(ui, repo, opts)
3719 for n in p:
3723 for n in p:
3720 if n != nullid:
3724 if n != nullid:
3721 displayer.show(repo[n])
3725 displayer.show(repo[n])
3722 displayer.close()
3726 displayer.close()
3723
3727
3724 @command('paths', formatteropts, _('[NAME]'), optionalrepo=True)
3728 @command('paths', formatteropts, _('[NAME]'), optionalrepo=True)
3725 def paths(ui, repo, search=None, **opts):
3729 def paths(ui, repo, search=None, **opts):
3726 """show aliases for remote repositories
3730 """show aliases for remote repositories
3727
3731
3728 Show definition of symbolic path name NAME. If no name is given,
3732 Show definition of symbolic path name NAME. If no name is given,
3729 show definition of all available names.
3733 show definition of all available names.
3730
3734
3731 Option -q/--quiet suppresses all output when searching for NAME
3735 Option -q/--quiet suppresses all output when searching for NAME
3732 and shows only the path names when listing all definitions.
3736 and shows only the path names when listing all definitions.
3733
3737
3734 Path names are defined in the [paths] section of your
3738 Path names are defined in the [paths] section of your
3735 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
3739 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
3736 repository, ``.hg/hgrc`` is used, too.
3740 repository, ``.hg/hgrc`` is used, too.
3737
3741
3738 The path names ``default`` and ``default-push`` have a special
3742 The path names ``default`` and ``default-push`` have a special
3739 meaning. When performing a push or pull operation, they are used
3743 meaning. When performing a push or pull operation, they are used
3740 as fallbacks if no location is specified on the command-line.
3744 as fallbacks if no location is specified on the command-line.
3741 When ``default-push`` is set, it will be used for push and
3745 When ``default-push`` is set, it will be used for push and
3742 ``default`` will be used for pull; otherwise ``default`` is used
3746 ``default`` will be used for pull; otherwise ``default`` is used
3743 as the fallback for both. When cloning a repository, the clone
3747 as the fallback for both. When cloning a repository, the clone
3744 source is written as ``default`` in ``.hg/hgrc``.
3748 source is written as ``default`` in ``.hg/hgrc``.
3745
3749
3746 .. note::
3750 .. note::
3747
3751
3748 ``default`` and ``default-push`` apply to all inbound (e.g.
3752 ``default`` and ``default-push`` apply to all inbound (e.g.
3749 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
3753 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
3750 and :hg:`bundle`) operations.
3754 and :hg:`bundle`) operations.
3751
3755
3752 See :hg:`help urls` for more information.
3756 See :hg:`help urls` for more information.
3753
3757
3754 Returns 0 on success.
3758 Returns 0 on success.
3755 """
3759 """
3756
3760
3757 opts = pycompat.byteskwargs(opts)
3761 opts = pycompat.byteskwargs(opts)
3758 ui.pager('paths')
3762 ui.pager('paths')
3759 if search:
3763 if search:
3760 pathitems = [(name, path) for name, path in ui.paths.iteritems()
3764 pathitems = [(name, path) for name, path in ui.paths.iteritems()
3761 if name == search]
3765 if name == search]
3762 else:
3766 else:
3763 pathitems = sorted(ui.paths.iteritems())
3767 pathitems = sorted(ui.paths.iteritems())
3764
3768
3765 fm = ui.formatter('paths', opts)
3769 fm = ui.formatter('paths', opts)
3766 if fm.isplain():
3770 if fm.isplain():
3767 hidepassword = util.hidepassword
3771 hidepassword = util.hidepassword
3768 else:
3772 else:
3769 hidepassword = str
3773 hidepassword = str
3770 if ui.quiet:
3774 if ui.quiet:
3771 namefmt = '%s\n'
3775 namefmt = '%s\n'
3772 else:
3776 else:
3773 namefmt = '%s = '
3777 namefmt = '%s = '
3774 showsubopts = not search and not ui.quiet
3778 showsubopts = not search and not ui.quiet
3775
3779
3776 for name, path in pathitems:
3780 for name, path in pathitems:
3777 fm.startitem()
3781 fm.startitem()
3778 fm.condwrite(not search, 'name', namefmt, name)
3782 fm.condwrite(not search, 'name', namefmt, name)
3779 fm.condwrite(not ui.quiet, 'url', '%s\n', hidepassword(path.rawloc))
3783 fm.condwrite(not ui.quiet, 'url', '%s\n', hidepassword(path.rawloc))
3780 for subopt, value in sorted(path.suboptions.items()):
3784 for subopt, value in sorted(path.suboptions.items()):
3781 assert subopt not in ('name', 'url')
3785 assert subopt not in ('name', 'url')
3782 if showsubopts:
3786 if showsubopts:
3783 fm.plain('%s:%s = ' % (name, subopt))
3787 fm.plain('%s:%s = ' % (name, subopt))
3784 fm.condwrite(showsubopts, subopt, '%s\n', value)
3788 fm.condwrite(showsubopts, subopt, '%s\n', value)
3785
3789
3786 fm.end()
3790 fm.end()
3787
3791
3788 if search and not pathitems:
3792 if search and not pathitems:
3789 if not ui.quiet:
3793 if not ui.quiet:
3790 ui.warn(_("not found!\n"))
3794 ui.warn(_("not found!\n"))
3791 return 1
3795 return 1
3792 else:
3796 else:
3793 return 0
3797 return 0
3794
3798
3795 @command('phase',
3799 @command('phase',
3796 [('p', 'public', False, _('set changeset phase to public')),
3800 [('p', 'public', False, _('set changeset phase to public')),
3797 ('d', 'draft', False, _('set changeset phase to draft')),
3801 ('d', 'draft', False, _('set changeset phase to draft')),
3798 ('s', 'secret', False, _('set changeset phase to secret')),
3802 ('s', 'secret', False, _('set changeset phase to secret')),
3799 ('f', 'force', False, _('allow to move boundary backward')),
3803 ('f', 'force', False, _('allow to move boundary backward')),
3800 ('r', 'rev', [], _('target revision'), _('REV')),
3804 ('r', 'rev', [], _('target revision'), _('REV')),
3801 ],
3805 ],
3802 _('[-p|-d|-s] [-f] [-r] [REV...]'))
3806 _('[-p|-d|-s] [-f] [-r] [REV...]'))
3803 def phase(ui, repo, *revs, **opts):
3807 def phase(ui, repo, *revs, **opts):
3804 """set or show the current phase name
3808 """set or show the current phase name
3805
3809
3806 With no argument, show the phase name of the current revision(s).
3810 With no argument, show the phase name of the current revision(s).
3807
3811
3808 With one of -p/--public, -d/--draft or -s/--secret, change the
3812 With one of -p/--public, -d/--draft or -s/--secret, change the
3809 phase value of the specified revisions.
3813 phase value of the specified revisions.
3810
3814
3811 Unless -f/--force is specified, :hg:`phase` won't move changesets from a
3815 Unless -f/--force is specified, :hg:`phase` won't move changesets from a
3812 lower phase to a higher phase. Phases are ordered as follows::
3816 lower phase to a higher phase. Phases are ordered as follows::
3813
3817
3814 public < draft < secret
3818 public < draft < secret
3815
3819
3816 Returns 0 on success, 1 if some phases could not be changed.
3820 Returns 0 on success, 1 if some phases could not be changed.
3817
3821
3818 (For more information about the phases concept, see :hg:`help phases`.)
3822 (For more information about the phases concept, see :hg:`help phases`.)
3819 """
3823 """
3820 opts = pycompat.byteskwargs(opts)
3824 opts = pycompat.byteskwargs(opts)
3821 # search for a unique phase argument
3825 # search for a unique phase argument
3822 targetphase = None
3826 targetphase = None
3823 for idx, name in enumerate(phases.phasenames):
3827 for idx, name in enumerate(phases.phasenames):
3824 if opts[name]:
3828 if opts[name]:
3825 if targetphase is not None:
3829 if targetphase is not None:
3826 raise error.Abort(_('only one phase can be specified'))
3830 raise error.Abort(_('only one phase can be specified'))
3827 targetphase = idx
3831 targetphase = idx
3828
3832
3829 # look for specified revision
3833 # look for specified revision
3830 revs = list(revs)
3834 revs = list(revs)
3831 revs.extend(opts['rev'])
3835 revs.extend(opts['rev'])
3832 if not revs:
3836 if not revs:
3833 # display both parents as the second parent phase can influence
3837 # display both parents as the second parent phase can influence
3834 # the phase of a merge commit
3838 # the phase of a merge commit
3835 revs = [c.rev() for c in repo[None].parents()]
3839 revs = [c.rev() for c in repo[None].parents()]
3836
3840
3837 revs = scmutil.revrange(repo, revs)
3841 revs = scmutil.revrange(repo, revs)
3838
3842
3839 lock = None
3843 lock = None
3840 ret = 0
3844 ret = 0
3841 if targetphase is None:
3845 if targetphase is None:
3842 # display
3846 # display
3843 for r in revs:
3847 for r in revs:
3844 ctx = repo[r]
3848 ctx = repo[r]
3845 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
3849 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
3846 else:
3850 else:
3847 tr = None
3851 tr = None
3848 lock = repo.lock()
3852 lock = repo.lock()
3849 try:
3853 try:
3850 tr = repo.transaction("phase")
3854 tr = repo.transaction("phase")
3851 # set phase
3855 # set phase
3852 if not revs:
3856 if not revs:
3853 raise error.Abort(_('empty revision set'))
3857 raise error.Abort(_('empty revision set'))
3854 nodes = [repo[r].node() for r in revs]
3858 nodes = [repo[r].node() for r in revs]
3855 # moving revision from public to draft may hide them
3859 # moving revision from public to draft may hide them
3856 # We have to check result on an unfiltered repository
3860 # We have to check result on an unfiltered repository
3857 unfi = repo.unfiltered()
3861 unfi = repo.unfiltered()
3858 getphase = unfi._phasecache.phase
3862 getphase = unfi._phasecache.phase
3859 olddata = [getphase(unfi, r) for r in unfi]
3863 olddata = [getphase(unfi, r) for r in unfi]
3860 phases.advanceboundary(repo, tr, targetphase, nodes)
3864 phases.advanceboundary(repo, tr, targetphase, nodes)
3861 if opts['force']:
3865 if opts['force']:
3862 phases.retractboundary(repo, tr, targetphase, nodes)
3866 phases.retractboundary(repo, tr, targetphase, nodes)
3863 tr.close()
3867 tr.close()
3864 finally:
3868 finally:
3865 if tr is not None:
3869 if tr is not None:
3866 tr.release()
3870 tr.release()
3867 lock.release()
3871 lock.release()
3868 getphase = unfi._phasecache.phase
3872 getphase = unfi._phasecache.phase
3869 newdata = [getphase(unfi, r) for r in unfi]
3873 newdata = [getphase(unfi, r) for r in unfi]
3870 changes = sum(newdata[r] != olddata[r] for r in unfi)
3874 changes = sum(newdata[r] != olddata[r] for r in unfi)
3871 cl = unfi.changelog
3875 cl = unfi.changelog
3872 rejected = [n for n in nodes
3876 rejected = [n for n in nodes
3873 if newdata[cl.rev(n)] < targetphase]
3877 if newdata[cl.rev(n)] < targetphase]
3874 if rejected:
3878 if rejected:
3875 ui.warn(_('cannot move %i changesets to a higher '
3879 ui.warn(_('cannot move %i changesets to a higher '
3876 'phase, use --force\n') % len(rejected))
3880 'phase, use --force\n') % len(rejected))
3877 ret = 1
3881 ret = 1
3878 if changes:
3882 if changes:
3879 msg = _('phase changed for %i changesets\n') % changes
3883 msg = _('phase changed for %i changesets\n') % changes
3880 if ret:
3884 if ret:
3881 ui.status(msg)
3885 ui.status(msg)
3882 else:
3886 else:
3883 ui.note(msg)
3887 ui.note(msg)
3884 else:
3888 else:
3885 ui.warn(_('no phases changed\n'))
3889 ui.warn(_('no phases changed\n'))
3886 return ret
3890 return ret
3887
3891
3888 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
3892 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
3889 """Run after a changegroup has been added via pull/unbundle
3893 """Run after a changegroup has been added via pull/unbundle
3890
3894
3891 This takes arguments below:
3895 This takes arguments below:
3892
3896
3893 :modheads: change of heads by pull/unbundle
3897 :modheads: change of heads by pull/unbundle
3894 :optupdate: updating working directory is needed or not
3898 :optupdate: updating working directory is needed or not
3895 :checkout: update destination revision (or None to default destination)
3899 :checkout: update destination revision (or None to default destination)
3896 :brev: a name, which might be a bookmark to be activated after updating
3900 :brev: a name, which might be a bookmark to be activated after updating
3897 """
3901 """
3898 if modheads == 0:
3902 if modheads == 0:
3899 return
3903 return
3900 if optupdate:
3904 if optupdate:
3901 try:
3905 try:
3902 return hg.updatetotally(ui, repo, checkout, brev)
3906 return hg.updatetotally(ui, repo, checkout, brev)
3903 except error.UpdateAbort as inst:
3907 except error.UpdateAbort as inst:
3904 msg = _("not updating: %s") % str(inst)
3908 msg = _("not updating: %s") % str(inst)
3905 hint = inst.hint
3909 hint = inst.hint
3906 raise error.UpdateAbort(msg, hint=hint)
3910 raise error.UpdateAbort(msg, hint=hint)
3907 if modheads > 1:
3911 if modheads > 1:
3908 currentbranchheads = len(repo.branchheads())
3912 currentbranchheads = len(repo.branchheads())
3909 if currentbranchheads == modheads:
3913 if currentbranchheads == modheads:
3910 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
3914 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
3911 elif currentbranchheads > 1:
3915 elif currentbranchheads > 1:
3912 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to "
3916 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to "
3913 "merge)\n"))
3917 "merge)\n"))
3914 else:
3918 else:
3915 ui.status(_("(run 'hg heads' to see heads)\n"))
3919 ui.status(_("(run 'hg heads' to see heads)\n"))
3916 elif not ui.configbool('commands', 'update.requiredest'):
3920 elif not ui.configbool('commands', 'update.requiredest'):
3917 ui.status(_("(run 'hg update' to get a working copy)\n"))
3921 ui.status(_("(run 'hg update' to get a working copy)\n"))
3918
3922
3919 @command('^pull',
3923 @command('^pull',
3920 [('u', 'update', None,
3924 [('u', 'update', None,
3921 _('update to new branch head if changesets were pulled')),
3925 _('update to new branch head if changesets were pulled')),
3922 ('f', 'force', None, _('run even when remote repository is unrelated')),
3926 ('f', 'force', None, _('run even when remote repository is unrelated')),
3923 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3927 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3924 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
3928 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
3925 ('b', 'branch', [], _('a specific branch you would like to pull'),
3929 ('b', 'branch', [], _('a specific branch you would like to pull'),
3926 _('BRANCH')),
3930 _('BRANCH')),
3927 ] + remoteopts,
3931 ] + remoteopts,
3928 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
3932 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
3929 def pull(ui, repo, source="default", **opts):
3933 def pull(ui, repo, source="default", **opts):
3930 """pull changes from the specified source
3934 """pull changes from the specified source
3931
3935
3932 Pull changes from a remote repository to a local one.
3936 Pull changes from a remote repository to a local one.
3933
3937
3934 This finds all changes from the repository at the specified path
3938 This finds all changes from the repository at the specified path
3935 or URL and adds them to a local repository (the current one unless
3939 or URL and adds them to a local repository (the current one unless
3936 -R is specified). By default, this does not update the copy of the
3940 -R is specified). By default, this does not update the copy of the
3937 project in the working directory.
3941 project in the working directory.
3938
3942
3939 Use :hg:`incoming` if you want to see what would have been added
3943 Use :hg:`incoming` if you want to see what would have been added
3940 by a pull at the time you issued this command. If you then decide
3944 by a pull at the time you issued this command. If you then decide
3941 to add those changes to the repository, you should use :hg:`pull
3945 to add those changes to the repository, you should use :hg:`pull
3942 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
3946 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
3943
3947
3944 If SOURCE is omitted, the 'default' path will be used.
3948 If SOURCE is omitted, the 'default' path will be used.
3945 See :hg:`help urls` for more information.
3949 See :hg:`help urls` for more information.
3946
3950
3947 Specifying bookmark as ``.`` is equivalent to specifying the active
3951 Specifying bookmark as ``.`` is equivalent to specifying the active
3948 bookmark's name.
3952 bookmark's name.
3949
3953
3950 Returns 0 on success, 1 if an update had unresolved files.
3954 Returns 0 on success, 1 if an update had unresolved files.
3951 """
3955 """
3952
3956
3953 opts = pycompat.byteskwargs(opts)
3957 opts = pycompat.byteskwargs(opts)
3954 if ui.configbool('commands', 'update.requiredest') and opts.get('update'):
3958 if ui.configbool('commands', 'update.requiredest') and opts.get('update'):
3955 msg = _('update destination required by configuration')
3959 msg = _('update destination required by configuration')
3956 hint = _('use hg pull followed by hg update DEST')
3960 hint = _('use hg pull followed by hg update DEST')
3957 raise error.Abort(msg, hint=hint)
3961 raise error.Abort(msg, hint=hint)
3958
3962
3959 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
3963 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
3960 ui.status(_('pulling from %s\n') % util.hidepassword(source))
3964 ui.status(_('pulling from %s\n') % util.hidepassword(source))
3961 other = hg.peer(repo, opts, source)
3965 other = hg.peer(repo, opts, source)
3962 try:
3966 try:
3963 revs, checkout = hg.addbranchrevs(repo, other, branches,
3967 revs, checkout = hg.addbranchrevs(repo, other, branches,
3964 opts.get('rev'))
3968 opts.get('rev'))
3965
3969
3966
3970
3967 pullopargs = {}
3971 pullopargs = {}
3968 if opts.get('bookmark'):
3972 if opts.get('bookmark'):
3969 if not revs:
3973 if not revs:
3970 revs = []
3974 revs = []
3971 # The list of bookmark used here is not the one used to actually
3975 # The list of bookmark used here is not the one used to actually
3972 # update the bookmark name. This can result in the revision pulled
3976 # update the bookmark name. This can result in the revision pulled
3973 # not ending up with the name of the bookmark because of a race
3977 # not ending up with the name of the bookmark because of a race
3974 # condition on the server. (See issue 4689 for details)
3978 # condition on the server. (See issue 4689 for details)
3975 remotebookmarks = other.listkeys('bookmarks')
3979 remotebookmarks = other.listkeys('bookmarks')
3976 pullopargs['remotebookmarks'] = remotebookmarks
3980 pullopargs['remotebookmarks'] = remotebookmarks
3977 for b in opts['bookmark']:
3981 for b in opts['bookmark']:
3978 b = repo._bookmarks.expandname(b)
3982 b = repo._bookmarks.expandname(b)
3979 if b not in remotebookmarks:
3983 if b not in remotebookmarks:
3980 raise error.Abort(_('remote bookmark %s not found!') % b)
3984 raise error.Abort(_('remote bookmark %s not found!') % b)
3981 revs.append(remotebookmarks[b])
3985 revs.append(remotebookmarks[b])
3982
3986
3983 if revs:
3987 if revs:
3984 try:
3988 try:
3985 # When 'rev' is a bookmark name, we cannot guarantee that it
3989 # When 'rev' is a bookmark name, we cannot guarantee that it
3986 # will be updated with that name because of a race condition
3990 # will be updated with that name because of a race condition
3987 # server side. (See issue 4689 for details)
3991 # server side. (See issue 4689 for details)
3988 oldrevs = revs
3992 oldrevs = revs
3989 revs = [] # actually, nodes
3993 revs = [] # actually, nodes
3990 for r in oldrevs:
3994 for r in oldrevs:
3991 node = other.lookup(r)
3995 node = other.lookup(r)
3992 revs.append(node)
3996 revs.append(node)
3993 if r == checkout:
3997 if r == checkout:
3994 checkout = node
3998 checkout = node
3995 except error.CapabilityError:
3999 except error.CapabilityError:
3996 err = _("other repository doesn't support revision lookup, "
4000 err = _("other repository doesn't support revision lookup, "
3997 "so a rev cannot be specified.")
4001 "so a rev cannot be specified.")
3998 raise error.Abort(err)
4002 raise error.Abort(err)
3999
4003
4000 pullopargs.update(opts.get('opargs', {}))
4004 pullopargs.update(opts.get('opargs', {}))
4001 modheads = exchange.pull(repo, other, heads=revs,
4005 modheads = exchange.pull(repo, other, heads=revs,
4002 force=opts.get('force'),
4006 force=opts.get('force'),
4003 bookmarks=opts.get('bookmark', ()),
4007 bookmarks=opts.get('bookmark', ()),
4004 opargs=pullopargs).cgresult
4008 opargs=pullopargs).cgresult
4005
4009
4006 # brev is a name, which might be a bookmark to be activated at
4010 # brev is a name, which might be a bookmark to be activated at
4007 # the end of the update. In other words, it is an explicit
4011 # the end of the update. In other words, it is an explicit
4008 # destination of the update
4012 # destination of the update
4009 brev = None
4013 brev = None
4010
4014
4011 if checkout:
4015 if checkout:
4012 checkout = str(repo.changelog.rev(checkout))
4016 checkout = str(repo.changelog.rev(checkout))
4013
4017
4014 # order below depends on implementation of
4018 # order below depends on implementation of
4015 # hg.addbranchrevs(). opts['bookmark'] is ignored,
4019 # hg.addbranchrevs(). opts['bookmark'] is ignored,
4016 # because 'checkout' is determined without it.
4020 # because 'checkout' is determined without it.
4017 if opts.get('rev'):
4021 if opts.get('rev'):
4018 brev = opts['rev'][0]
4022 brev = opts['rev'][0]
4019 elif opts.get('branch'):
4023 elif opts.get('branch'):
4020 brev = opts['branch'][0]
4024 brev = opts['branch'][0]
4021 else:
4025 else:
4022 brev = branches[0]
4026 brev = branches[0]
4023 repo._subtoppath = source
4027 repo._subtoppath = source
4024 try:
4028 try:
4025 ret = postincoming(ui, repo, modheads, opts.get('update'),
4029 ret = postincoming(ui, repo, modheads, opts.get('update'),
4026 checkout, brev)
4030 checkout, brev)
4027
4031
4028 finally:
4032 finally:
4029 del repo._subtoppath
4033 del repo._subtoppath
4030
4034
4031 finally:
4035 finally:
4032 other.close()
4036 other.close()
4033 return ret
4037 return ret
4034
4038
4035 @command('^push',
4039 @command('^push',
4036 [('f', 'force', None, _('force push')),
4040 [('f', 'force', None, _('force push')),
4037 ('r', 'rev', [],
4041 ('r', 'rev', [],
4038 _('a changeset intended to be included in the destination'),
4042 _('a changeset intended to be included in the destination'),
4039 _('REV')),
4043 _('REV')),
4040 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
4044 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
4041 ('b', 'branch', [],
4045 ('b', 'branch', [],
4042 _('a specific branch you would like to push'), _('BRANCH')),
4046 _('a specific branch you would like to push'), _('BRANCH')),
4043 ('', 'new-branch', False, _('allow pushing a new branch')),
4047 ('', 'new-branch', False, _('allow pushing a new branch')),
4044 ('', 'pushvars', [], _('variables that can be sent to server (ADVANCED)')),
4048 ('', 'pushvars', [], _('variables that can be sent to server (ADVANCED)')),
4045 ] + remoteopts,
4049 ] + remoteopts,
4046 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
4050 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
4047 def push(ui, repo, dest=None, **opts):
4051 def push(ui, repo, dest=None, **opts):
4048 """push changes to the specified destination
4052 """push changes to the specified destination
4049
4053
4050 Push changesets from the local repository to the specified
4054 Push changesets from the local repository to the specified
4051 destination.
4055 destination.
4052
4056
4053 This operation is symmetrical to pull: it is identical to a pull
4057 This operation is symmetrical to pull: it is identical to a pull
4054 in the destination repository from the current one.
4058 in the destination repository from the current one.
4055
4059
4056 By default, push will not allow creation of new heads at the
4060 By default, push will not allow creation of new heads at the
4057 destination, since multiple heads would make it unclear which head
4061 destination, since multiple heads would make it unclear which head
4058 to use. In this situation, it is recommended to pull and merge
4062 to use. In this situation, it is recommended to pull and merge
4059 before pushing.
4063 before pushing.
4060
4064
4061 Use --new-branch if you want to allow push to create a new named
4065 Use --new-branch if you want to allow push to create a new named
4062 branch that is not present at the destination. This allows you to
4066 branch that is not present at the destination. This allows you to
4063 only create a new branch without forcing other changes.
4067 only create a new branch without forcing other changes.
4064
4068
4065 .. note::
4069 .. note::
4066
4070
4067 Extra care should be taken with the -f/--force option,
4071 Extra care should be taken with the -f/--force option,
4068 which will push all new heads on all branches, an action which will
4072 which will push all new heads on all branches, an action which will
4069 almost always cause confusion for collaborators.
4073 almost always cause confusion for collaborators.
4070
4074
4071 If -r/--rev is used, the specified revision and all its ancestors
4075 If -r/--rev is used, the specified revision and all its ancestors
4072 will be pushed to the remote repository.
4076 will be pushed to the remote repository.
4073
4077
4074 If -B/--bookmark is used, the specified bookmarked revision, its
4078 If -B/--bookmark is used, the specified bookmarked revision, its
4075 ancestors, and the bookmark will be pushed to the remote
4079 ancestors, and the bookmark will be pushed to the remote
4076 repository. Specifying ``.`` is equivalent to specifying the active
4080 repository. Specifying ``.`` is equivalent to specifying the active
4077 bookmark's name.
4081 bookmark's name.
4078
4082
4079 Please see :hg:`help urls` for important details about ``ssh://``
4083 Please see :hg:`help urls` for important details about ``ssh://``
4080 URLs. If DESTINATION is omitted, a default path will be used.
4084 URLs. If DESTINATION is omitted, a default path will be used.
4081
4085
4082 .. container:: verbose
4086 .. container:: verbose
4083
4087
4084 The --pushvars option sends strings to the server that become
4088 The --pushvars option sends strings to the server that become
4085 environment variables prepended with ``HG_USERVAR_``. For example,
4089 environment variables prepended with ``HG_USERVAR_``. For example,
4086 ``--pushvars ENABLE_FEATURE=true``, provides the server side hooks with
4090 ``--pushvars ENABLE_FEATURE=true``, provides the server side hooks with
4087 ``HG_USERVAR_ENABLE_FEATURE=true`` as part of their environment.
4091 ``HG_USERVAR_ENABLE_FEATURE=true`` as part of their environment.
4088
4092
4089 pushvars can provide for user-overridable hooks as well as set debug
4093 pushvars can provide for user-overridable hooks as well as set debug
4090 levels. One example is having a hook that blocks commits containing
4094 levels. One example is having a hook that blocks commits containing
4091 conflict markers, but enables the user to override the hook if the file
4095 conflict markers, but enables the user to override the hook if the file
4092 is using conflict markers for testing purposes or the file format has
4096 is using conflict markers for testing purposes or the file format has
4093 strings that look like conflict markers.
4097 strings that look like conflict markers.
4094
4098
4095 By default, servers will ignore `--pushvars`. To enable it add the
4099 By default, servers will ignore `--pushvars`. To enable it add the
4096 following to your configuration file::
4100 following to your configuration file::
4097
4101
4098 [push]
4102 [push]
4099 pushvars.server = true
4103 pushvars.server = true
4100
4104
4101 Returns 0 if push was successful, 1 if nothing to push.
4105 Returns 0 if push was successful, 1 if nothing to push.
4102 """
4106 """
4103
4107
4104 opts = pycompat.byteskwargs(opts)
4108 opts = pycompat.byteskwargs(opts)
4105 if opts.get('bookmark'):
4109 if opts.get('bookmark'):
4106 ui.setconfig('bookmarks', 'pushing', opts['bookmark'], 'push')
4110 ui.setconfig('bookmarks', 'pushing', opts['bookmark'], 'push')
4107 for b in opts['bookmark']:
4111 for b in opts['bookmark']:
4108 # translate -B options to -r so changesets get pushed
4112 # translate -B options to -r so changesets get pushed
4109 b = repo._bookmarks.expandname(b)
4113 b = repo._bookmarks.expandname(b)
4110 if b in repo._bookmarks:
4114 if b in repo._bookmarks:
4111 opts.setdefault('rev', []).append(b)
4115 opts.setdefault('rev', []).append(b)
4112 else:
4116 else:
4113 # if we try to push a deleted bookmark, translate it to null
4117 # if we try to push a deleted bookmark, translate it to null
4114 # this lets simultaneous -r, -b options continue working
4118 # this lets simultaneous -r, -b options continue working
4115 opts.setdefault('rev', []).append("null")
4119 opts.setdefault('rev', []).append("null")
4116
4120
4117 path = ui.paths.getpath(dest, default=('default-push', 'default'))
4121 path = ui.paths.getpath(dest, default=('default-push', 'default'))
4118 if not path:
4122 if not path:
4119 raise error.Abort(_('default repository not configured!'),
4123 raise error.Abort(_('default repository not configured!'),
4120 hint=_("see 'hg help config.paths'"))
4124 hint=_("see 'hg help config.paths'"))
4121 dest = path.pushloc or path.loc
4125 dest = path.pushloc or path.loc
4122 branches = (path.branch, opts.get('branch') or [])
4126 branches = (path.branch, opts.get('branch') or [])
4123 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
4127 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
4124 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
4128 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
4125 other = hg.peer(repo, opts, dest)
4129 other = hg.peer(repo, opts, dest)
4126
4130
4127 if revs:
4131 if revs:
4128 revs = [repo.lookup(r) for r in scmutil.revrange(repo, revs)]
4132 revs = [repo.lookup(r) for r in scmutil.revrange(repo, revs)]
4129 if not revs:
4133 if not revs:
4130 raise error.Abort(_("specified revisions evaluate to an empty set"),
4134 raise error.Abort(_("specified revisions evaluate to an empty set"),
4131 hint=_("use different revision arguments"))
4135 hint=_("use different revision arguments"))
4132 elif path.pushrev:
4136 elif path.pushrev:
4133 # It doesn't make any sense to specify ancestor revisions. So limit
4137 # It doesn't make any sense to specify ancestor revisions. So limit
4134 # to DAG heads to make discovery simpler.
4138 # to DAG heads to make discovery simpler.
4135 expr = revsetlang.formatspec('heads(%r)', path.pushrev)
4139 expr = revsetlang.formatspec('heads(%r)', path.pushrev)
4136 revs = scmutil.revrange(repo, [expr])
4140 revs = scmutil.revrange(repo, [expr])
4137 revs = [repo[rev].node() for rev in revs]
4141 revs = [repo[rev].node() for rev in revs]
4138 if not revs:
4142 if not revs:
4139 raise error.Abort(_('default push revset for path evaluates to an '
4143 raise error.Abort(_('default push revset for path evaluates to an '
4140 'empty set'))
4144 'empty set'))
4141
4145
4142 repo._subtoppath = dest
4146 repo._subtoppath = dest
4143 try:
4147 try:
4144 # push subrepos depth-first for coherent ordering
4148 # push subrepos depth-first for coherent ordering
4145 c = repo['']
4149 c = repo['']
4146 subs = c.substate # only repos that are committed
4150 subs = c.substate # only repos that are committed
4147 for s in sorted(subs):
4151 for s in sorted(subs):
4148 result = c.sub(s).push(opts)
4152 result = c.sub(s).push(opts)
4149 if result == 0:
4153 if result == 0:
4150 return not result
4154 return not result
4151 finally:
4155 finally:
4152 del repo._subtoppath
4156 del repo._subtoppath
4153
4157
4154 opargs = dict(opts.get('opargs', {})) # copy opargs since we may mutate it
4158 opargs = dict(opts.get('opargs', {})) # copy opargs since we may mutate it
4155 opargs.setdefault('pushvars', []).extend(opts.get('pushvars', []))
4159 opargs.setdefault('pushvars', []).extend(opts.get('pushvars', []))
4156
4160
4157 pushop = exchange.push(repo, other, opts.get('force'), revs=revs,
4161 pushop = exchange.push(repo, other, opts.get('force'), revs=revs,
4158 newbranch=opts.get('new_branch'),
4162 newbranch=opts.get('new_branch'),
4159 bookmarks=opts.get('bookmark', ()),
4163 bookmarks=opts.get('bookmark', ()),
4160 opargs=opargs)
4164 opargs=opargs)
4161
4165
4162 result = not pushop.cgresult
4166 result = not pushop.cgresult
4163
4167
4164 if pushop.bkresult is not None:
4168 if pushop.bkresult is not None:
4165 if pushop.bkresult == 2:
4169 if pushop.bkresult == 2:
4166 result = 2
4170 result = 2
4167 elif not result and pushop.bkresult:
4171 elif not result and pushop.bkresult:
4168 result = 2
4172 result = 2
4169
4173
4170 return result
4174 return result
4171
4175
4172 @command('recover', [])
4176 @command('recover', [])
4173 def recover(ui, repo):
4177 def recover(ui, repo):
4174 """roll back an interrupted transaction
4178 """roll back an interrupted transaction
4175
4179
4176 Recover from an interrupted commit or pull.
4180 Recover from an interrupted commit or pull.
4177
4181
4178 This command tries to fix the repository status after an
4182 This command tries to fix the repository status after an
4179 interrupted operation. It should only be necessary when Mercurial
4183 interrupted operation. It should only be necessary when Mercurial
4180 suggests it.
4184 suggests it.
4181
4185
4182 Returns 0 if successful, 1 if nothing to recover or verify fails.
4186 Returns 0 if successful, 1 if nothing to recover or verify fails.
4183 """
4187 """
4184 if repo.recover():
4188 if repo.recover():
4185 return hg.verify(repo)
4189 return hg.verify(repo)
4186 return 1
4190 return 1
4187
4191
4188 @command('^remove|rm',
4192 @command('^remove|rm',
4189 [('A', 'after', None, _('record delete for missing files')),
4193 [('A', 'after', None, _('record delete for missing files')),
4190 ('f', 'force', None,
4194 ('f', 'force', None,
4191 _('forget added files, delete modified files')),
4195 _('forget added files, delete modified files')),
4192 ] + subrepoopts + walkopts,
4196 ] + subrepoopts + walkopts,
4193 _('[OPTION]... FILE...'),
4197 _('[OPTION]... FILE...'),
4194 inferrepo=True)
4198 inferrepo=True)
4195 def remove(ui, repo, *pats, **opts):
4199 def remove(ui, repo, *pats, **opts):
4196 """remove the specified files on the next commit
4200 """remove the specified files on the next commit
4197
4201
4198 Schedule the indicated files for removal from the current branch.
4202 Schedule the indicated files for removal from the current branch.
4199
4203
4200 This command schedules the files to be removed at the next commit.
4204 This command schedules the files to be removed at the next commit.
4201 To undo a remove before that, see :hg:`revert`. To undo added
4205 To undo a remove before that, see :hg:`revert`. To undo added
4202 files, see :hg:`forget`.
4206 files, see :hg:`forget`.
4203
4207
4204 .. container:: verbose
4208 .. container:: verbose
4205
4209
4206 -A/--after can be used to remove only files that have already
4210 -A/--after can be used to remove only files that have already
4207 been deleted, -f/--force can be used to force deletion, and -Af
4211 been deleted, -f/--force can be used to force deletion, and -Af
4208 can be used to remove files from the next revision without
4212 can be used to remove files from the next revision without
4209 deleting them from the working directory.
4213 deleting them from the working directory.
4210
4214
4211 The following table details the behavior of remove for different
4215 The following table details the behavior of remove for different
4212 file states (columns) and option combinations (rows). The file
4216 file states (columns) and option combinations (rows). The file
4213 states are Added [A], Clean [C], Modified [M] and Missing [!]
4217 states are Added [A], Clean [C], Modified [M] and Missing [!]
4214 (as reported by :hg:`status`). The actions are Warn, Remove
4218 (as reported by :hg:`status`). The actions are Warn, Remove
4215 (from branch) and Delete (from disk):
4219 (from branch) and Delete (from disk):
4216
4220
4217 ========= == == == ==
4221 ========= == == == ==
4218 opt/state A C M !
4222 opt/state A C M !
4219 ========= == == == ==
4223 ========= == == == ==
4220 none W RD W R
4224 none W RD W R
4221 -f R RD RD R
4225 -f R RD RD R
4222 -A W W W R
4226 -A W W W R
4223 -Af R R R R
4227 -Af R R R R
4224 ========= == == == ==
4228 ========= == == == ==
4225
4229
4226 .. note::
4230 .. note::
4227
4231
4228 :hg:`remove` never deletes files in Added [A] state from the
4232 :hg:`remove` never deletes files in Added [A] state from the
4229 working directory, not even if ``--force`` is specified.
4233 working directory, not even if ``--force`` is specified.
4230
4234
4231 Returns 0 on success, 1 if any warnings encountered.
4235 Returns 0 on success, 1 if any warnings encountered.
4232 """
4236 """
4233
4237
4234 opts = pycompat.byteskwargs(opts)
4238 opts = pycompat.byteskwargs(opts)
4235 after, force = opts.get('after'), opts.get('force')
4239 after, force = opts.get('after'), opts.get('force')
4236 if not pats and not after:
4240 if not pats and not after:
4237 raise error.Abort(_('no files specified'))
4241 raise error.Abort(_('no files specified'))
4238
4242
4239 m = scmutil.match(repo[None], pats, opts)
4243 m = scmutil.match(repo[None], pats, opts)
4240 subrepos = opts.get('subrepos')
4244 subrepos = opts.get('subrepos')
4241 return cmdutil.remove(ui, repo, m, "", after, force, subrepos)
4245 return cmdutil.remove(ui, repo, m, "", after, force, subrepos)
4242
4246
4243 @command('rename|move|mv',
4247 @command('rename|move|mv',
4244 [('A', 'after', None, _('record a rename that has already occurred')),
4248 [('A', 'after', None, _('record a rename that has already occurred')),
4245 ('f', 'force', None, _('forcibly copy over an existing managed file')),
4249 ('f', 'force', None, _('forcibly copy over an existing managed file')),
4246 ] + walkopts + dryrunopts,
4250 ] + walkopts + dryrunopts,
4247 _('[OPTION]... SOURCE... DEST'))
4251 _('[OPTION]... SOURCE... DEST'))
4248 def rename(ui, repo, *pats, **opts):
4252 def rename(ui, repo, *pats, **opts):
4249 """rename files; equivalent of copy + remove
4253 """rename files; equivalent of copy + remove
4250
4254
4251 Mark dest as copies of sources; mark sources for deletion. If dest
4255 Mark dest as copies of sources; mark sources for deletion. If dest
4252 is a directory, copies are put in that directory. If dest is a
4256 is a directory, copies are put in that directory. If dest is a
4253 file, there can only be one source.
4257 file, there can only be one source.
4254
4258
4255 By default, this command copies the contents of files as they
4259 By default, this command copies the contents of files as they
4256 exist in the working directory. If invoked with -A/--after, the
4260 exist in the working directory. If invoked with -A/--after, the
4257 operation is recorded, but no copying is performed.
4261 operation is recorded, but no copying is performed.
4258
4262
4259 This command takes effect at the next commit. To undo a rename
4263 This command takes effect at the next commit. To undo a rename
4260 before that, see :hg:`revert`.
4264 before that, see :hg:`revert`.
4261
4265
4262 Returns 0 on success, 1 if errors are encountered.
4266 Returns 0 on success, 1 if errors are encountered.
4263 """
4267 """
4264 opts = pycompat.byteskwargs(opts)
4268 opts = pycompat.byteskwargs(opts)
4265 with repo.wlock(False):
4269 with repo.wlock(False):
4266 return cmdutil.copy(ui, repo, pats, opts, rename=True)
4270 return cmdutil.copy(ui, repo, pats, opts, rename=True)
4267
4271
4268 @command('resolve',
4272 @command('resolve',
4269 [('a', 'all', None, _('select all unresolved files')),
4273 [('a', 'all', None, _('select all unresolved files')),
4270 ('l', 'list', None, _('list state of files needing merge')),
4274 ('l', 'list', None, _('list state of files needing merge')),
4271 ('m', 'mark', None, _('mark files as resolved')),
4275 ('m', 'mark', None, _('mark files as resolved')),
4272 ('u', 'unmark', None, _('mark files as unresolved')),
4276 ('u', 'unmark', None, _('mark files as unresolved')),
4273 ('n', 'no-status', None, _('hide status prefix'))]
4277 ('n', 'no-status', None, _('hide status prefix'))]
4274 + mergetoolopts + walkopts + formatteropts,
4278 + mergetoolopts + walkopts + formatteropts,
4275 _('[OPTION]... [FILE]...'),
4279 _('[OPTION]... [FILE]...'),
4276 inferrepo=True)
4280 inferrepo=True)
4277 def resolve(ui, repo, *pats, **opts):
4281 def resolve(ui, repo, *pats, **opts):
4278 """redo merges or set/view the merge status of files
4282 """redo merges or set/view the merge status of files
4279
4283
4280 Merges with unresolved conflicts are often the result of
4284 Merges with unresolved conflicts are often the result of
4281 non-interactive merging using the ``internal:merge`` configuration
4285 non-interactive merging using the ``internal:merge`` configuration
4282 setting, or a command-line merge tool like ``diff3``. The resolve
4286 setting, or a command-line merge tool like ``diff3``. The resolve
4283 command is used to manage the files involved in a merge, after
4287 command is used to manage the files involved in a merge, after
4284 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
4288 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
4285 working directory must have two parents). See :hg:`help
4289 working directory must have two parents). See :hg:`help
4286 merge-tools` for information on configuring merge tools.
4290 merge-tools` for information on configuring merge tools.
4287
4291
4288 The resolve command can be used in the following ways:
4292 The resolve command can be used in the following ways:
4289
4293
4290 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
4294 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
4291 files, discarding any previous merge attempts. Re-merging is not
4295 files, discarding any previous merge attempts. Re-merging is not
4292 performed for files already marked as resolved. Use ``--all/-a``
4296 performed for files already marked as resolved. Use ``--all/-a``
4293 to select all unresolved files. ``--tool`` can be used to specify
4297 to select all unresolved files. ``--tool`` can be used to specify
4294 the merge tool used for the given files. It overrides the HGMERGE
4298 the merge tool used for the given files. It overrides the HGMERGE
4295 environment variable and your configuration files. Previous file
4299 environment variable and your configuration files. Previous file
4296 contents are saved with a ``.orig`` suffix.
4300 contents are saved with a ``.orig`` suffix.
4297
4301
4298 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
4302 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
4299 (e.g. after having manually fixed-up the files). The default is
4303 (e.g. after having manually fixed-up the files). The default is
4300 to mark all unresolved files.
4304 to mark all unresolved files.
4301
4305
4302 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
4306 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
4303 default is to mark all resolved files.
4307 default is to mark all resolved files.
4304
4308
4305 - :hg:`resolve -l`: list files which had or still have conflicts.
4309 - :hg:`resolve -l`: list files which had or still have conflicts.
4306 In the printed list, ``U`` = unresolved and ``R`` = resolved.
4310 In the printed list, ``U`` = unresolved and ``R`` = resolved.
4307 You can use ``set:unresolved()`` or ``set:resolved()`` to filter
4311 You can use ``set:unresolved()`` or ``set:resolved()`` to filter
4308 the list. See :hg:`help filesets` for details.
4312 the list. See :hg:`help filesets` for details.
4309
4313
4310 .. note::
4314 .. note::
4311
4315
4312 Mercurial will not let you commit files with unresolved merge
4316 Mercurial will not let you commit files with unresolved merge
4313 conflicts. You must use :hg:`resolve -m ...` before you can
4317 conflicts. You must use :hg:`resolve -m ...` before you can
4314 commit after a conflicting merge.
4318 commit after a conflicting merge.
4315
4319
4316 Returns 0 on success, 1 if any files fail a resolve attempt.
4320 Returns 0 on success, 1 if any files fail a resolve attempt.
4317 """
4321 """
4318
4322
4319 opts = pycompat.byteskwargs(opts)
4323 opts = pycompat.byteskwargs(opts)
4320 flaglist = 'all mark unmark list no_status'.split()
4324 flaglist = 'all mark unmark list no_status'.split()
4321 all, mark, unmark, show, nostatus = \
4325 all, mark, unmark, show, nostatus = \
4322 [opts.get(o) for o in flaglist]
4326 [opts.get(o) for o in flaglist]
4323
4327
4324 if (show and (mark or unmark)) or (mark and unmark):
4328 if (show and (mark or unmark)) or (mark and unmark):
4325 raise error.Abort(_("too many options specified"))
4329 raise error.Abort(_("too many options specified"))
4326 if pats and all:
4330 if pats and all:
4327 raise error.Abort(_("can't specify --all and patterns"))
4331 raise error.Abort(_("can't specify --all and patterns"))
4328 if not (all or pats or show or mark or unmark):
4332 if not (all or pats or show or mark or unmark):
4329 raise error.Abort(_('no files or directories specified'),
4333 raise error.Abort(_('no files or directories specified'),
4330 hint=('use --all to re-merge all unresolved files'))
4334 hint=('use --all to re-merge all unresolved files'))
4331
4335
4332 if show:
4336 if show:
4333 ui.pager('resolve')
4337 ui.pager('resolve')
4334 fm = ui.formatter('resolve', opts)
4338 fm = ui.formatter('resolve', opts)
4335 ms = mergemod.mergestate.read(repo)
4339 ms = mergemod.mergestate.read(repo)
4336 m = scmutil.match(repo[None], pats, opts)
4340 m = scmutil.match(repo[None], pats, opts)
4337
4341
4338 # Labels and keys based on merge state. Unresolved path conflicts show
4342 # Labels and keys based on merge state. Unresolved path conflicts show
4339 # as 'P'. Resolved path conflicts show as 'R', the same as normal
4343 # as 'P'. Resolved path conflicts show as 'R', the same as normal
4340 # resolved conflicts.
4344 # resolved conflicts.
4341 mergestateinfo = {
4345 mergestateinfo = {
4342 'u': ('resolve.unresolved', 'U'),
4346 'u': ('resolve.unresolved', 'U'),
4343 'r': ('resolve.resolved', 'R'),
4347 'r': ('resolve.resolved', 'R'),
4344 'pu': ('resolve.unresolved', 'P'),
4348 'pu': ('resolve.unresolved', 'P'),
4345 'pr': ('resolve.resolved', 'R'),
4349 'pr': ('resolve.resolved', 'R'),
4346 'd': ('resolve.driverresolved', 'D'),
4350 'd': ('resolve.driverresolved', 'D'),
4347 }
4351 }
4348
4352
4349 for f in ms:
4353 for f in ms:
4350 if not m(f):
4354 if not m(f):
4351 continue
4355 continue
4352
4356
4353 label, key = mergestateinfo[ms[f]]
4357 label, key = mergestateinfo[ms[f]]
4354 fm.startitem()
4358 fm.startitem()
4355 fm.condwrite(not nostatus, 'status', '%s ', key, label=label)
4359 fm.condwrite(not nostatus, 'status', '%s ', key, label=label)
4356 fm.write('path', '%s\n', f, label=label)
4360 fm.write('path', '%s\n', f, label=label)
4357 fm.end()
4361 fm.end()
4358 return 0
4362 return 0
4359
4363
4360 with repo.wlock():
4364 with repo.wlock():
4361 ms = mergemod.mergestate.read(repo)
4365 ms = mergemod.mergestate.read(repo)
4362
4366
4363 if not (ms.active() or repo.dirstate.p2() != nullid):
4367 if not (ms.active() or repo.dirstate.p2() != nullid):
4364 raise error.Abort(
4368 raise error.Abort(
4365 _('resolve command not applicable when not merging'))
4369 _('resolve command not applicable when not merging'))
4366
4370
4367 wctx = repo[None]
4371 wctx = repo[None]
4368
4372
4369 if ms.mergedriver and ms.mdstate() == 'u':
4373 if ms.mergedriver and ms.mdstate() == 'u':
4370 proceed = mergemod.driverpreprocess(repo, ms, wctx)
4374 proceed = mergemod.driverpreprocess(repo, ms, wctx)
4371 ms.commit()
4375 ms.commit()
4372 # allow mark and unmark to go through
4376 # allow mark and unmark to go through
4373 if not mark and not unmark and not proceed:
4377 if not mark and not unmark and not proceed:
4374 return 1
4378 return 1
4375
4379
4376 m = scmutil.match(wctx, pats, opts)
4380 m = scmutil.match(wctx, pats, opts)
4377 ret = 0
4381 ret = 0
4378 didwork = False
4382 didwork = False
4379 runconclude = False
4383 runconclude = False
4380
4384
4381 tocomplete = []
4385 tocomplete = []
4382 for f in ms:
4386 for f in ms:
4383 if not m(f):
4387 if not m(f):
4384 continue
4388 continue
4385
4389
4386 didwork = True
4390 didwork = True
4387
4391
4388 # don't let driver-resolved files be marked, and run the conclude
4392 # don't let driver-resolved files be marked, and run the conclude
4389 # step if asked to resolve
4393 # step if asked to resolve
4390 if ms[f] == "d":
4394 if ms[f] == "d":
4391 exact = m.exact(f)
4395 exact = m.exact(f)
4392 if mark:
4396 if mark:
4393 if exact:
4397 if exact:
4394 ui.warn(_('not marking %s as it is driver-resolved\n')
4398 ui.warn(_('not marking %s as it is driver-resolved\n')
4395 % f)
4399 % f)
4396 elif unmark:
4400 elif unmark:
4397 if exact:
4401 if exact:
4398 ui.warn(_('not unmarking %s as it is driver-resolved\n')
4402 ui.warn(_('not unmarking %s as it is driver-resolved\n')
4399 % f)
4403 % f)
4400 else:
4404 else:
4401 runconclude = True
4405 runconclude = True
4402 continue
4406 continue
4403
4407
4404 # path conflicts must be resolved manually
4408 # path conflicts must be resolved manually
4405 if ms[f] in ("pu", "pr"):
4409 if ms[f] in ("pu", "pr"):
4406 if mark:
4410 if mark:
4407 ms.mark(f, "pr")
4411 ms.mark(f, "pr")
4408 elif unmark:
4412 elif unmark:
4409 ms.mark(f, "pu")
4413 ms.mark(f, "pu")
4410 elif ms[f] == "pu":
4414 elif ms[f] == "pu":
4411 ui.warn(_('%s: path conflict must be resolved manually\n')
4415 ui.warn(_('%s: path conflict must be resolved manually\n')
4412 % f)
4416 % f)
4413 continue
4417 continue
4414
4418
4415 if mark:
4419 if mark:
4416 ms.mark(f, "r")
4420 ms.mark(f, "r")
4417 elif unmark:
4421 elif unmark:
4418 ms.mark(f, "u")
4422 ms.mark(f, "u")
4419 else:
4423 else:
4420 # backup pre-resolve (merge uses .orig for its own purposes)
4424 # backup pre-resolve (merge uses .orig for its own purposes)
4421 a = repo.wjoin(f)
4425 a = repo.wjoin(f)
4422 try:
4426 try:
4423 util.copyfile(a, a + ".resolve")
4427 util.copyfile(a, a + ".resolve")
4424 except (IOError, OSError) as inst:
4428 except (IOError, OSError) as inst:
4425 if inst.errno != errno.ENOENT:
4429 if inst.errno != errno.ENOENT:
4426 raise
4430 raise
4427
4431
4428 try:
4432 try:
4429 # preresolve file
4433 # preresolve file
4430 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
4434 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
4431 'resolve')
4435 'resolve')
4432 complete, r = ms.preresolve(f, wctx)
4436 complete, r = ms.preresolve(f, wctx)
4433 if not complete:
4437 if not complete:
4434 tocomplete.append(f)
4438 tocomplete.append(f)
4435 elif r:
4439 elif r:
4436 ret = 1
4440 ret = 1
4437 finally:
4441 finally:
4438 ui.setconfig('ui', 'forcemerge', '', 'resolve')
4442 ui.setconfig('ui', 'forcemerge', '', 'resolve')
4439 ms.commit()
4443 ms.commit()
4440
4444
4441 # replace filemerge's .orig file with our resolve file, but only
4445 # replace filemerge's .orig file with our resolve file, but only
4442 # for merges that are complete
4446 # for merges that are complete
4443 if complete:
4447 if complete:
4444 try:
4448 try:
4445 util.rename(a + ".resolve",
4449 util.rename(a + ".resolve",
4446 scmutil.origpath(ui, repo, a))
4450 scmutil.origpath(ui, repo, a))
4447 except OSError as inst:
4451 except OSError as inst:
4448 if inst.errno != errno.ENOENT:
4452 if inst.errno != errno.ENOENT:
4449 raise
4453 raise
4450
4454
4451 for f in tocomplete:
4455 for f in tocomplete:
4452 try:
4456 try:
4453 # resolve file
4457 # resolve file
4454 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
4458 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
4455 'resolve')
4459 'resolve')
4456 r = ms.resolve(f, wctx)
4460 r = ms.resolve(f, wctx)
4457 if r:
4461 if r:
4458 ret = 1
4462 ret = 1
4459 finally:
4463 finally:
4460 ui.setconfig('ui', 'forcemerge', '', 'resolve')
4464 ui.setconfig('ui', 'forcemerge', '', 'resolve')
4461 ms.commit()
4465 ms.commit()
4462
4466
4463 # replace filemerge's .orig file with our resolve file
4467 # replace filemerge's .orig file with our resolve file
4464 a = repo.wjoin(f)
4468 a = repo.wjoin(f)
4465 try:
4469 try:
4466 util.rename(a + ".resolve", scmutil.origpath(ui, repo, a))
4470 util.rename(a + ".resolve", scmutil.origpath(ui, repo, a))
4467 except OSError as inst:
4471 except OSError as inst:
4468 if inst.errno != errno.ENOENT:
4472 if inst.errno != errno.ENOENT:
4469 raise
4473 raise
4470
4474
4471 ms.commit()
4475 ms.commit()
4472 ms.recordactions()
4476 ms.recordactions()
4473
4477
4474 if not didwork and pats:
4478 if not didwork and pats:
4475 hint = None
4479 hint = None
4476 if not any([p for p in pats if p.find(':') >= 0]):
4480 if not any([p for p in pats if p.find(':') >= 0]):
4477 pats = ['path:%s' % p for p in pats]
4481 pats = ['path:%s' % p for p in pats]
4478 m = scmutil.match(wctx, pats, opts)
4482 m = scmutil.match(wctx, pats, opts)
4479 for f in ms:
4483 for f in ms:
4480 if not m(f):
4484 if not m(f):
4481 continue
4485 continue
4482 flags = ''.join(['-%s ' % o[0] for o in flaglist
4486 flags = ''.join(['-%s ' % o[0] for o in flaglist
4483 if opts.get(o)])
4487 if opts.get(o)])
4484 hint = _("(try: hg resolve %s%s)\n") % (
4488 hint = _("(try: hg resolve %s%s)\n") % (
4485 flags,
4489 flags,
4486 ' '.join(pats))
4490 ' '.join(pats))
4487 break
4491 break
4488 ui.warn(_("arguments do not match paths that need resolving\n"))
4492 ui.warn(_("arguments do not match paths that need resolving\n"))
4489 if hint:
4493 if hint:
4490 ui.warn(hint)
4494 ui.warn(hint)
4491 elif ms.mergedriver and ms.mdstate() != 's':
4495 elif ms.mergedriver and ms.mdstate() != 's':
4492 # run conclude step when either a driver-resolved file is requested
4496 # run conclude step when either a driver-resolved file is requested
4493 # or there are no driver-resolved files
4497 # or there are no driver-resolved files
4494 # we can't use 'ret' to determine whether any files are unresolved
4498 # we can't use 'ret' to determine whether any files are unresolved
4495 # because we might not have tried to resolve some
4499 # because we might not have tried to resolve some
4496 if ((runconclude or not list(ms.driverresolved()))
4500 if ((runconclude or not list(ms.driverresolved()))
4497 and not list(ms.unresolved())):
4501 and not list(ms.unresolved())):
4498 proceed = mergemod.driverconclude(repo, ms, wctx)
4502 proceed = mergemod.driverconclude(repo, ms, wctx)
4499 ms.commit()
4503 ms.commit()
4500 if not proceed:
4504 if not proceed:
4501 return 1
4505 return 1
4502
4506
4503 # Nudge users into finishing an unfinished operation
4507 # Nudge users into finishing an unfinished operation
4504 unresolvedf = list(ms.unresolved())
4508 unresolvedf = list(ms.unresolved())
4505 driverresolvedf = list(ms.driverresolved())
4509 driverresolvedf = list(ms.driverresolved())
4506 if not unresolvedf and not driverresolvedf:
4510 if not unresolvedf and not driverresolvedf:
4507 ui.status(_('(no more unresolved files)\n'))
4511 ui.status(_('(no more unresolved files)\n'))
4508 cmdutil.checkafterresolved(repo)
4512 cmdutil.checkafterresolved(repo)
4509 elif not unresolvedf:
4513 elif not unresolvedf:
4510 ui.status(_('(no more unresolved files -- '
4514 ui.status(_('(no more unresolved files -- '
4511 'run "hg resolve --all" to conclude)\n'))
4515 'run "hg resolve --all" to conclude)\n'))
4512
4516
4513 return ret
4517 return ret
4514
4518
4515 @command('revert',
4519 @command('revert',
4516 [('a', 'all', None, _('revert all changes when no arguments given')),
4520 [('a', 'all', None, _('revert all changes when no arguments given')),
4517 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
4521 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
4518 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
4522 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
4519 ('C', 'no-backup', None, _('do not save backup copies of files')),
4523 ('C', 'no-backup', None, _('do not save backup copies of files')),
4520 ('i', 'interactive', None,
4524 ('i', 'interactive', None,
4521 _('interactively select the changes (EXPERIMENTAL)')),
4525 _('interactively select the changes (EXPERIMENTAL)')),
4522 ] + walkopts + dryrunopts,
4526 ] + walkopts + dryrunopts,
4523 _('[OPTION]... [-r REV] [NAME]...'))
4527 _('[OPTION]... [-r REV] [NAME]...'))
4524 def revert(ui, repo, *pats, **opts):
4528 def revert(ui, repo, *pats, **opts):
4525 """restore files to their checkout state
4529 """restore files to their checkout state
4526
4530
4527 .. note::
4531 .. note::
4528
4532
4529 To check out earlier revisions, you should use :hg:`update REV`.
4533 To check out earlier revisions, you should use :hg:`update REV`.
4530 To cancel an uncommitted merge (and lose your changes),
4534 To cancel an uncommitted merge (and lose your changes),
4531 use :hg:`update --clean .`.
4535 use :hg:`update --clean .`.
4532
4536
4533 With no revision specified, revert the specified files or directories
4537 With no revision specified, revert the specified files or directories
4534 to the contents they had in the parent of the working directory.
4538 to the contents they had in the parent of the working directory.
4535 This restores the contents of files to an unmodified
4539 This restores the contents of files to an unmodified
4536 state and unschedules adds, removes, copies, and renames. If the
4540 state and unschedules adds, removes, copies, and renames. If the
4537 working directory has two parents, you must explicitly specify a
4541 working directory has two parents, you must explicitly specify a
4538 revision.
4542 revision.
4539
4543
4540 Using the -r/--rev or -d/--date options, revert the given files or
4544 Using the -r/--rev or -d/--date options, revert the given files or
4541 directories to their states as of a specific revision. Because
4545 directories to their states as of a specific revision. Because
4542 revert does not change the working directory parents, this will
4546 revert does not change the working directory parents, this will
4543 cause these files to appear modified. This can be helpful to "back
4547 cause these files to appear modified. This can be helpful to "back
4544 out" some or all of an earlier change. See :hg:`backout` for a
4548 out" some or all of an earlier change. See :hg:`backout` for a
4545 related method.
4549 related method.
4546
4550
4547 Modified files are saved with a .orig suffix before reverting.
4551 Modified files are saved with a .orig suffix before reverting.
4548 To disable these backups, use --no-backup. It is possible to store
4552 To disable these backups, use --no-backup. It is possible to store
4549 the backup files in a custom directory relative to the root of the
4553 the backup files in a custom directory relative to the root of the
4550 repository by setting the ``ui.origbackuppath`` configuration
4554 repository by setting the ``ui.origbackuppath`` configuration
4551 option.
4555 option.
4552
4556
4553 See :hg:`help dates` for a list of formats valid for -d/--date.
4557 See :hg:`help dates` for a list of formats valid for -d/--date.
4554
4558
4555 See :hg:`help backout` for a way to reverse the effect of an
4559 See :hg:`help backout` for a way to reverse the effect of an
4556 earlier changeset.
4560 earlier changeset.
4557
4561
4558 Returns 0 on success.
4562 Returns 0 on success.
4559 """
4563 """
4560
4564
4561 if opts.get("date"):
4565 if opts.get("date"):
4562 if opts.get("rev"):
4566 if opts.get("rev"):
4563 raise error.Abort(_("you can't specify a revision and a date"))
4567 raise error.Abort(_("you can't specify a revision and a date"))
4564 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
4568 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
4565
4569
4566 parent, p2 = repo.dirstate.parents()
4570 parent, p2 = repo.dirstate.parents()
4567 if not opts.get('rev') and p2 != nullid:
4571 if not opts.get('rev') and p2 != nullid:
4568 # revert after merge is a trap for new users (issue2915)
4572 # revert after merge is a trap for new users (issue2915)
4569 raise error.Abort(_('uncommitted merge with no revision specified'),
4573 raise error.Abort(_('uncommitted merge with no revision specified'),
4570 hint=_("use 'hg update' or see 'hg help revert'"))
4574 hint=_("use 'hg update' or see 'hg help revert'"))
4571
4575
4572 ctx = scmutil.revsingle(repo, opts.get('rev'))
4576 ctx = scmutil.revsingle(repo, opts.get('rev'))
4573
4577
4574 if (not (pats or opts.get('include') or opts.get('exclude') or
4578 if (not (pats or opts.get('include') or opts.get('exclude') or
4575 opts.get('all') or opts.get('interactive'))):
4579 opts.get('all') or opts.get('interactive'))):
4576 msg = _("no files or directories specified")
4580 msg = _("no files or directories specified")
4577 if p2 != nullid:
4581 if p2 != nullid:
4578 hint = _("uncommitted merge, use --all to discard all changes,"
4582 hint = _("uncommitted merge, use --all to discard all changes,"
4579 " or 'hg update -C .' to abort the merge")
4583 " or 'hg update -C .' to abort the merge")
4580 raise error.Abort(msg, hint=hint)
4584 raise error.Abort(msg, hint=hint)
4581 dirty = any(repo.status())
4585 dirty = any(repo.status())
4582 node = ctx.node()
4586 node = ctx.node()
4583 if node != parent:
4587 if node != parent:
4584 if dirty:
4588 if dirty:
4585 hint = _("uncommitted changes, use --all to discard all"
4589 hint = _("uncommitted changes, use --all to discard all"
4586 " changes, or 'hg update %s' to update") % ctx.rev()
4590 " changes, or 'hg update %s' to update") % ctx.rev()
4587 else:
4591 else:
4588 hint = _("use --all to revert all files,"
4592 hint = _("use --all to revert all files,"
4589 " or 'hg update %s' to update") % ctx.rev()
4593 " or 'hg update %s' to update") % ctx.rev()
4590 elif dirty:
4594 elif dirty:
4591 hint = _("uncommitted changes, use --all to discard all changes")
4595 hint = _("uncommitted changes, use --all to discard all changes")
4592 else:
4596 else:
4593 hint = _("use --all to revert all files")
4597 hint = _("use --all to revert all files")
4594 raise error.Abort(msg, hint=hint)
4598 raise error.Abort(msg, hint=hint)
4595
4599
4596 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats, **opts)
4600 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats, **opts)
4597
4601
4598 @command('rollback', dryrunopts +
4602 @command('rollback', dryrunopts +
4599 [('f', 'force', False, _('ignore safety measures'))])
4603 [('f', 'force', False, _('ignore safety measures'))])
4600 def rollback(ui, repo, **opts):
4604 def rollback(ui, repo, **opts):
4601 """roll back the last transaction (DANGEROUS) (DEPRECATED)
4605 """roll back the last transaction (DANGEROUS) (DEPRECATED)
4602
4606
4603 Please use :hg:`commit --amend` instead of rollback to correct
4607 Please use :hg:`commit --amend` instead of rollback to correct
4604 mistakes in the last commit.
4608 mistakes in the last commit.
4605
4609
4606 This command should be used with care. There is only one level of
4610 This command should be used with care. There is only one level of
4607 rollback, and there is no way to undo a rollback. It will also
4611 rollback, and there is no way to undo a rollback. It will also
4608 restore the dirstate at the time of the last transaction, losing
4612 restore the dirstate at the time of the last transaction, losing
4609 any dirstate changes since that time. This command does not alter
4613 any dirstate changes since that time. This command does not alter
4610 the working directory.
4614 the working directory.
4611
4615
4612 Transactions are used to encapsulate the effects of all commands
4616 Transactions are used to encapsulate the effects of all commands
4613 that create new changesets or propagate existing changesets into a
4617 that create new changesets or propagate existing changesets into a
4614 repository.
4618 repository.
4615
4619
4616 .. container:: verbose
4620 .. container:: verbose
4617
4621
4618 For example, the following commands are transactional, and their
4622 For example, the following commands are transactional, and their
4619 effects can be rolled back:
4623 effects can be rolled back:
4620
4624
4621 - commit
4625 - commit
4622 - import
4626 - import
4623 - pull
4627 - pull
4624 - push (with this repository as the destination)
4628 - push (with this repository as the destination)
4625 - unbundle
4629 - unbundle
4626
4630
4627 To avoid permanent data loss, rollback will refuse to rollback a
4631 To avoid permanent data loss, rollback will refuse to rollback a
4628 commit transaction if it isn't checked out. Use --force to
4632 commit transaction if it isn't checked out. Use --force to
4629 override this protection.
4633 override this protection.
4630
4634
4631 The rollback command can be entirely disabled by setting the
4635 The rollback command can be entirely disabled by setting the
4632 ``ui.rollback`` configuration setting to false. If you're here
4636 ``ui.rollback`` configuration setting to false. If you're here
4633 because you want to use rollback and it's disabled, you can
4637 because you want to use rollback and it's disabled, you can
4634 re-enable the command by setting ``ui.rollback`` to true.
4638 re-enable the command by setting ``ui.rollback`` to true.
4635
4639
4636 This command is not intended for use on public repositories. Once
4640 This command is not intended for use on public repositories. Once
4637 changes are visible for pull by other users, rolling a transaction
4641 changes are visible for pull by other users, rolling a transaction
4638 back locally is ineffective (someone else may already have pulled
4642 back locally is ineffective (someone else may already have pulled
4639 the changes). Furthermore, a race is possible with readers of the
4643 the changes). Furthermore, a race is possible with readers of the
4640 repository; for example an in-progress pull from the repository
4644 repository; for example an in-progress pull from the repository
4641 may fail if a rollback is performed.
4645 may fail if a rollback is performed.
4642
4646
4643 Returns 0 on success, 1 if no rollback data is available.
4647 Returns 0 on success, 1 if no rollback data is available.
4644 """
4648 """
4645 if not ui.configbool('ui', 'rollback'):
4649 if not ui.configbool('ui', 'rollback'):
4646 raise error.Abort(_('rollback is disabled because it is unsafe'),
4650 raise error.Abort(_('rollback is disabled because it is unsafe'),
4647 hint=('see `hg help -v rollback` for information'))
4651 hint=('see `hg help -v rollback` for information'))
4648 return repo.rollback(dryrun=opts.get(r'dry_run'),
4652 return repo.rollback(dryrun=opts.get(r'dry_run'),
4649 force=opts.get(r'force'))
4653 force=opts.get(r'force'))
4650
4654
4651 @command('root', [])
4655 @command('root', [])
4652 def root(ui, repo):
4656 def root(ui, repo):
4653 """print the root (top) of the current working directory
4657 """print the root (top) of the current working directory
4654
4658
4655 Print the root directory of the current repository.
4659 Print the root directory of the current repository.
4656
4660
4657 Returns 0 on success.
4661 Returns 0 on success.
4658 """
4662 """
4659 ui.write(repo.root + "\n")
4663 ui.write(repo.root + "\n")
4660
4664
4661 @command('^serve',
4665 @command('^serve',
4662 [('A', 'accesslog', '', _('name of access log file to write to'),
4666 [('A', 'accesslog', '', _('name of access log file to write to'),
4663 _('FILE')),
4667 _('FILE')),
4664 ('d', 'daemon', None, _('run server in background')),
4668 ('d', 'daemon', None, _('run server in background')),
4665 ('', 'daemon-postexec', [], _('used internally by daemon mode')),
4669 ('', 'daemon-postexec', [], _('used internally by daemon mode')),
4666 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
4670 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
4667 # use string type, then we can check if something was passed
4671 # use string type, then we can check if something was passed
4668 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
4672 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
4669 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
4673 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
4670 _('ADDR')),
4674 _('ADDR')),
4671 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
4675 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
4672 _('PREFIX')),
4676 _('PREFIX')),
4673 ('n', 'name', '',
4677 ('n', 'name', '',
4674 _('name to show in web pages (default: working directory)'), _('NAME')),
4678 _('name to show in web pages (default: working directory)'), _('NAME')),
4675 ('', 'web-conf', '',
4679 ('', 'web-conf', '',
4676 _("name of the hgweb config file (see 'hg help hgweb')"), _('FILE')),
4680 _("name of the hgweb config file (see 'hg help hgweb')"), _('FILE')),
4677 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
4681 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
4678 _('FILE')),
4682 _('FILE')),
4679 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
4683 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
4680 ('', 'stdio', None, _('for remote clients (ADVANCED)')),
4684 ('', 'stdio', None, _('for remote clients (ADVANCED)')),
4681 ('', 'cmdserver', '', _('for remote clients (ADVANCED)'), _('MODE')),
4685 ('', 'cmdserver', '', _('for remote clients (ADVANCED)'), _('MODE')),
4682 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
4686 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
4683 ('', 'style', '', _('template style to use'), _('STYLE')),
4687 ('', 'style', '', _('template style to use'), _('STYLE')),
4684 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
4688 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
4685 ('', 'certificate', '', _('SSL certificate file'), _('FILE'))]
4689 ('', 'certificate', '', _('SSL certificate file'), _('FILE'))]
4686 + subrepoopts,
4690 + subrepoopts,
4687 _('[OPTION]...'),
4691 _('[OPTION]...'),
4688 optionalrepo=True)
4692 optionalrepo=True)
4689 def serve(ui, repo, **opts):
4693 def serve(ui, repo, **opts):
4690 """start stand-alone webserver
4694 """start stand-alone webserver
4691
4695
4692 Start a local HTTP repository browser and pull server. You can use
4696 Start a local HTTP repository browser and pull server. You can use
4693 this for ad-hoc sharing and browsing of repositories. It is
4697 this for ad-hoc sharing and browsing of repositories. It is
4694 recommended to use a real web server to serve a repository for
4698 recommended to use a real web server to serve a repository for
4695 longer periods of time.
4699 longer periods of time.
4696
4700
4697 Please note that the server does not implement access control.
4701 Please note that the server does not implement access control.
4698 This means that, by default, anybody can read from the server and
4702 This means that, by default, anybody can read from the server and
4699 nobody can write to it by default. Set the ``web.allow_push``
4703 nobody can write to it by default. Set the ``web.allow_push``
4700 option to ``*`` to allow everybody to push to the server. You
4704 option to ``*`` to allow everybody to push to the server. You
4701 should use a real web server if you need to authenticate users.
4705 should use a real web server if you need to authenticate users.
4702
4706
4703 By default, the server logs accesses to stdout and errors to
4707 By default, the server logs accesses to stdout and errors to
4704 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
4708 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
4705 files.
4709 files.
4706
4710
4707 To have the server choose a free port number to listen on, specify
4711 To have the server choose a free port number to listen on, specify
4708 a port number of 0; in this case, the server will print the port
4712 a port number of 0; in this case, the server will print the port
4709 number it uses.
4713 number it uses.
4710
4714
4711 Returns 0 on success.
4715 Returns 0 on success.
4712 """
4716 """
4713
4717
4714 opts = pycompat.byteskwargs(opts)
4718 opts = pycompat.byteskwargs(opts)
4715 if opts["stdio"] and opts["cmdserver"]:
4719 if opts["stdio"] and opts["cmdserver"]:
4716 raise error.Abort(_("cannot use --stdio with --cmdserver"))
4720 raise error.Abort(_("cannot use --stdio with --cmdserver"))
4717
4721
4718 if opts["stdio"]:
4722 if opts["stdio"]:
4719 if repo is None:
4723 if repo is None:
4720 raise error.RepoError(_("there is no Mercurial repository here"
4724 raise error.RepoError(_("there is no Mercurial repository here"
4721 " (.hg not found)"))
4725 " (.hg not found)"))
4722 s = sshserver.sshserver(ui, repo)
4726 s = sshserver.sshserver(ui, repo)
4723 s.serve_forever()
4727 s.serve_forever()
4724
4728
4725 service = server.createservice(ui, repo, opts)
4729 service = server.createservice(ui, repo, opts)
4726 return server.runservice(opts, initfn=service.init, runfn=service.run)
4730 return server.runservice(opts, initfn=service.init, runfn=service.run)
4727
4731
4728 @command('^status|st',
4732 @command('^status|st',
4729 [('A', 'all', None, _('show status of all files')),
4733 [('A', 'all', None, _('show status of all files')),
4730 ('m', 'modified', None, _('show only modified files')),
4734 ('m', 'modified', None, _('show only modified files')),
4731 ('a', 'added', None, _('show only added files')),
4735 ('a', 'added', None, _('show only added files')),
4732 ('r', 'removed', None, _('show only removed files')),
4736 ('r', 'removed', None, _('show only removed files')),
4733 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
4737 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
4734 ('c', 'clean', None, _('show only files without changes')),
4738 ('c', 'clean', None, _('show only files without changes')),
4735 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
4739 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
4736 ('i', 'ignored', None, _('show only ignored files')),
4740 ('i', 'ignored', None, _('show only ignored files')),
4737 ('n', 'no-status', None, _('hide status prefix')),
4741 ('n', 'no-status', None, _('hide status prefix')),
4738 ('t', 'terse', '', _('show the terse output (EXPERIMENTAL)')),
4742 ('t', 'terse', '', _('show the terse output (EXPERIMENTAL)')),
4739 ('C', 'copies', None, _('show source of copied files')),
4743 ('C', 'copies', None, _('show source of copied files')),
4740 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
4744 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
4741 ('', 'rev', [], _('show difference from revision'), _('REV')),
4745 ('', 'rev', [], _('show difference from revision'), _('REV')),
4742 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
4746 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
4743 ] + walkopts + subrepoopts + formatteropts,
4747 ] + walkopts + subrepoopts + formatteropts,
4744 _('[OPTION]... [FILE]...'),
4748 _('[OPTION]... [FILE]...'),
4745 inferrepo=True)
4749 inferrepo=True)
4746 def status(ui, repo, *pats, **opts):
4750 def status(ui, repo, *pats, **opts):
4747 """show changed files in the working directory
4751 """show changed files in the working directory
4748
4752
4749 Show status of files in the repository. If names are given, only
4753 Show status of files in the repository. If names are given, only
4750 files that match are shown. Files that are clean or ignored or
4754 files that match are shown. Files that are clean or ignored or
4751 the source of a copy/move operation, are not listed unless
4755 the source of a copy/move operation, are not listed unless
4752 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
4756 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
4753 Unless options described with "show only ..." are given, the
4757 Unless options described with "show only ..." are given, the
4754 options -mardu are used.
4758 options -mardu are used.
4755
4759
4756 Option -q/--quiet hides untracked (unknown and ignored) files
4760 Option -q/--quiet hides untracked (unknown and ignored) files
4757 unless explicitly requested with -u/--unknown or -i/--ignored.
4761 unless explicitly requested with -u/--unknown or -i/--ignored.
4758
4762
4759 .. note::
4763 .. note::
4760
4764
4761 :hg:`status` may appear to disagree with diff if permissions have
4765 :hg:`status` may appear to disagree with diff if permissions have
4762 changed or a merge has occurred. The standard diff format does
4766 changed or a merge has occurred. The standard diff format does
4763 not report permission changes and diff only reports changes
4767 not report permission changes and diff only reports changes
4764 relative to one merge parent.
4768 relative to one merge parent.
4765
4769
4766 If one revision is given, it is used as the base revision.
4770 If one revision is given, it is used as the base revision.
4767 If two revisions are given, the differences between them are
4771 If two revisions are given, the differences between them are
4768 shown. The --change option can also be used as a shortcut to list
4772 shown. The --change option can also be used as a shortcut to list
4769 the changed files of a revision from its first parent.
4773 the changed files of a revision from its first parent.
4770
4774
4771 The codes used to show the status of files are::
4775 The codes used to show the status of files are::
4772
4776
4773 M = modified
4777 M = modified
4774 A = added
4778 A = added
4775 R = removed
4779 R = removed
4776 C = clean
4780 C = clean
4777 ! = missing (deleted by non-hg command, but still tracked)
4781 ! = missing (deleted by non-hg command, but still tracked)
4778 ? = not tracked
4782 ? = not tracked
4779 I = ignored
4783 I = ignored
4780 = origin of the previous file (with --copies)
4784 = origin of the previous file (with --copies)
4781
4785
4782 .. container:: verbose
4786 .. container:: verbose
4783
4787
4784 The -t/--terse option abbreviates the output by showing only the directory
4788 The -t/--terse option abbreviates the output by showing only the directory
4785 name if all the files in it share the same status. The option takes an
4789 name if all the files in it share the same status. The option takes an
4786 argument indicating the statuses to abbreviate: 'm' for 'modified', 'a'
4790 argument indicating the statuses to abbreviate: 'm' for 'modified', 'a'
4787 for 'added', 'r' for 'removed', 'd' for 'deleted', 'u' for 'unknown', 'i'
4791 for 'added', 'r' for 'removed', 'd' for 'deleted', 'u' for 'unknown', 'i'
4788 for 'ignored' and 'c' for clean.
4792 for 'ignored' and 'c' for clean.
4789
4793
4790 It abbreviates only those statuses which are passed. Note that clean and
4794 It abbreviates only those statuses which are passed. Note that clean and
4791 ignored files are not displayed with '--terse ic' unless the -c/--clean
4795 ignored files are not displayed with '--terse ic' unless the -c/--clean
4792 and -i/--ignored options are also used.
4796 and -i/--ignored options are also used.
4793
4797
4794 The -v/--verbose option shows information when the repository is in an
4798 The -v/--verbose option shows information when the repository is in an
4795 unfinished merge, shelve, rebase state etc. You can have this behavior
4799 unfinished merge, shelve, rebase state etc. You can have this behavior
4796 turned on by default by enabling the ``commands.status.verbose`` option.
4800 turned on by default by enabling the ``commands.status.verbose`` option.
4797
4801
4798 You can skip displaying some of these states by setting
4802 You can skip displaying some of these states by setting
4799 ``commands.status.skipstates`` to one or more of: 'bisect', 'graft',
4803 ``commands.status.skipstates`` to one or more of: 'bisect', 'graft',
4800 'histedit', 'merge', 'rebase', or 'unshelve'.
4804 'histedit', 'merge', 'rebase', or 'unshelve'.
4801
4805
4802 Examples:
4806 Examples:
4803
4807
4804 - show changes in the working directory relative to a
4808 - show changes in the working directory relative to a
4805 changeset::
4809 changeset::
4806
4810
4807 hg status --rev 9353
4811 hg status --rev 9353
4808
4812
4809 - show changes in the working directory relative to the
4813 - show changes in the working directory relative to the
4810 current directory (see :hg:`help patterns` for more information)::
4814 current directory (see :hg:`help patterns` for more information)::
4811
4815
4812 hg status re:
4816 hg status re:
4813
4817
4814 - show all changes including copies in an existing changeset::
4818 - show all changes including copies in an existing changeset::
4815
4819
4816 hg status --copies --change 9353
4820 hg status --copies --change 9353
4817
4821
4818 - get a NUL separated list of added files, suitable for xargs::
4822 - get a NUL separated list of added files, suitable for xargs::
4819
4823
4820 hg status -an0
4824 hg status -an0
4821
4825
4822 - show more information about the repository status, abbreviating
4826 - show more information about the repository status, abbreviating
4823 added, removed, modified, deleted, and untracked paths::
4827 added, removed, modified, deleted, and untracked paths::
4824
4828
4825 hg status -v -t mardu
4829 hg status -v -t mardu
4826
4830
4827 Returns 0 on success.
4831 Returns 0 on success.
4828
4832
4829 """
4833 """
4830
4834
4831 opts = pycompat.byteskwargs(opts)
4835 opts = pycompat.byteskwargs(opts)
4832 revs = opts.get('rev')
4836 revs = opts.get('rev')
4833 change = opts.get('change')
4837 change = opts.get('change')
4834 terse = opts.get('terse')
4838 terse = opts.get('terse')
4835
4839
4836 if revs and change:
4840 if revs and change:
4837 msg = _('cannot specify --rev and --change at the same time')
4841 msg = _('cannot specify --rev and --change at the same time')
4838 raise error.Abort(msg)
4842 raise error.Abort(msg)
4839 elif revs and terse:
4843 elif revs and terse:
4840 msg = _('cannot use --terse with --rev')
4844 msg = _('cannot use --terse with --rev')
4841 raise error.Abort(msg)
4845 raise error.Abort(msg)
4842 elif change:
4846 elif change:
4843 node2 = scmutil.revsingle(repo, change, None).node()
4847 node2 = scmutil.revsingle(repo, change, None).node()
4844 node1 = repo[node2].p1().node()
4848 node1 = repo[node2].p1().node()
4845 else:
4849 else:
4846 node1, node2 = scmutil.revpair(repo, revs)
4850 node1, node2 = scmutil.revpair(repo, revs)
4847
4851
4848 if pats or ui.configbool('commands', 'status.relative'):
4852 if pats or ui.configbool('commands', 'status.relative'):
4849 cwd = repo.getcwd()
4853 cwd = repo.getcwd()
4850 else:
4854 else:
4851 cwd = ''
4855 cwd = ''
4852
4856
4853 if opts.get('print0'):
4857 if opts.get('print0'):
4854 end = '\0'
4858 end = '\0'
4855 else:
4859 else:
4856 end = '\n'
4860 end = '\n'
4857 copy = {}
4861 copy = {}
4858 states = 'modified added removed deleted unknown ignored clean'.split()
4862 states = 'modified added removed deleted unknown ignored clean'.split()
4859 show = [k for k in states if opts.get(k)]
4863 show = [k for k in states if opts.get(k)]
4860 if opts.get('all'):
4864 if opts.get('all'):
4861 show += ui.quiet and (states[:4] + ['clean']) or states
4865 show += ui.quiet and (states[:4] + ['clean']) or states
4862
4866
4863 if not show:
4867 if not show:
4864 if ui.quiet:
4868 if ui.quiet:
4865 show = states[:4]
4869 show = states[:4]
4866 else:
4870 else:
4867 show = states[:5]
4871 show = states[:5]
4868
4872
4869 m = scmutil.match(repo[node2], pats, opts)
4873 m = scmutil.match(repo[node2], pats, opts)
4870 if terse:
4874 if terse:
4871 # we need to compute clean and unknown to terse
4875 # we need to compute clean and unknown to terse
4872 stat = repo.status(node1, node2, m,
4876 stat = repo.status(node1, node2, m,
4873 'ignored' in show or 'i' in terse,
4877 'ignored' in show or 'i' in terse,
4874 True, True, opts.get('subrepos'))
4878 True, True, opts.get('subrepos'))
4875
4879
4876 stat = cmdutil.tersedir(stat, terse)
4880 stat = cmdutil.tersedir(stat, terse)
4877 else:
4881 else:
4878 stat = repo.status(node1, node2, m,
4882 stat = repo.status(node1, node2, m,
4879 'ignored' in show, 'clean' in show,
4883 'ignored' in show, 'clean' in show,
4880 'unknown' in show, opts.get('subrepos'))
4884 'unknown' in show, opts.get('subrepos'))
4881
4885
4882 changestates = zip(states, pycompat.iterbytestr('MAR!?IC'), stat)
4886 changestates = zip(states, pycompat.iterbytestr('MAR!?IC'), stat)
4883
4887
4884 if (opts.get('all') or opts.get('copies')
4888 if (opts.get('all') or opts.get('copies')
4885 or ui.configbool('ui', 'statuscopies')) and not opts.get('no_status'):
4889 or ui.configbool('ui', 'statuscopies')) and not opts.get('no_status'):
4886 copy = copies.pathcopies(repo[node1], repo[node2], m)
4890 copy = copies.pathcopies(repo[node1], repo[node2], m)
4887
4891
4888 ui.pager('status')
4892 ui.pager('status')
4889 fm = ui.formatter('status', opts)
4893 fm = ui.formatter('status', opts)
4890 fmt = '%s' + end
4894 fmt = '%s' + end
4891 showchar = not opts.get('no_status')
4895 showchar = not opts.get('no_status')
4892
4896
4893 for state, char, files in changestates:
4897 for state, char, files in changestates:
4894 if state in show:
4898 if state in show:
4895 label = 'status.' + state
4899 label = 'status.' + state
4896 for f in files:
4900 for f in files:
4897 fm.startitem()
4901 fm.startitem()
4898 fm.condwrite(showchar, 'status', '%s ', char, label=label)
4902 fm.condwrite(showchar, 'status', '%s ', char, label=label)
4899 fm.write('path', fmt, repo.pathto(f, cwd), label=label)
4903 fm.write('path', fmt, repo.pathto(f, cwd), label=label)
4900 if f in copy:
4904 if f in copy:
4901 fm.write("copy", ' %s' + end, repo.pathto(copy[f], cwd),
4905 fm.write("copy", ' %s' + end, repo.pathto(copy[f], cwd),
4902 label='status.copied')
4906 label='status.copied')
4903
4907
4904 if ((ui.verbose or ui.configbool('commands', 'status.verbose'))
4908 if ((ui.verbose or ui.configbool('commands', 'status.verbose'))
4905 and not ui.plain()):
4909 and not ui.plain()):
4906 cmdutil.morestatus(repo, fm)
4910 cmdutil.morestatus(repo, fm)
4907 fm.end()
4911 fm.end()
4908
4912
4909 @command('^summary|sum',
4913 @command('^summary|sum',
4910 [('', 'remote', None, _('check for push and pull'))], '[--remote]')
4914 [('', 'remote', None, _('check for push and pull'))], '[--remote]')
4911 def summary(ui, repo, **opts):
4915 def summary(ui, repo, **opts):
4912 """summarize working directory state
4916 """summarize working directory state
4913
4917
4914 This generates a brief summary of the working directory state,
4918 This generates a brief summary of the working directory state,
4915 including parents, branch, commit status, phase and available updates.
4919 including parents, branch, commit status, phase and available updates.
4916
4920
4917 With the --remote option, this will check the default paths for
4921 With the --remote option, this will check the default paths for
4918 incoming and outgoing changes. This can be time-consuming.
4922 incoming and outgoing changes. This can be time-consuming.
4919
4923
4920 Returns 0 on success.
4924 Returns 0 on success.
4921 """
4925 """
4922
4926
4923 opts = pycompat.byteskwargs(opts)
4927 opts = pycompat.byteskwargs(opts)
4924 ui.pager('summary')
4928 ui.pager('summary')
4925 ctx = repo[None]
4929 ctx = repo[None]
4926 parents = ctx.parents()
4930 parents = ctx.parents()
4927 pnode = parents[0].node()
4931 pnode = parents[0].node()
4928 marks = []
4932 marks = []
4929
4933
4930 ms = None
4934 ms = None
4931 try:
4935 try:
4932 ms = mergemod.mergestate.read(repo)
4936 ms = mergemod.mergestate.read(repo)
4933 except error.UnsupportedMergeRecords as e:
4937 except error.UnsupportedMergeRecords as e:
4934 s = ' '.join(e.recordtypes)
4938 s = ' '.join(e.recordtypes)
4935 ui.warn(
4939 ui.warn(
4936 _('warning: merge state has unsupported record types: %s\n') % s)
4940 _('warning: merge state has unsupported record types: %s\n') % s)
4937 unresolved = []
4941 unresolved = []
4938 else:
4942 else:
4939 unresolved = list(ms.unresolved())
4943 unresolved = list(ms.unresolved())
4940
4944
4941 for p in parents:
4945 for p in parents:
4942 # label with log.changeset (instead of log.parent) since this
4946 # label with log.changeset (instead of log.parent) since this
4943 # shows a working directory parent *changeset*:
4947 # shows a working directory parent *changeset*:
4944 # i18n: column positioning for "hg summary"
4948 # i18n: column positioning for "hg summary"
4945 ui.write(_('parent: %d:%s ') % (p.rev(), p),
4949 ui.write(_('parent: %d:%s ') % (p.rev(), p),
4946 label=cmdutil._changesetlabels(p))
4950 label=cmdutil._changesetlabels(p))
4947 ui.write(' '.join(p.tags()), label='log.tag')
4951 ui.write(' '.join(p.tags()), label='log.tag')
4948 if p.bookmarks():
4952 if p.bookmarks():
4949 marks.extend(p.bookmarks())
4953 marks.extend(p.bookmarks())
4950 if p.rev() == -1:
4954 if p.rev() == -1:
4951 if not len(repo):
4955 if not len(repo):
4952 ui.write(_(' (empty repository)'))
4956 ui.write(_(' (empty repository)'))
4953 else:
4957 else:
4954 ui.write(_(' (no revision checked out)'))
4958 ui.write(_(' (no revision checked out)'))
4955 if p.obsolete():
4959 if p.obsolete():
4956 ui.write(_(' (obsolete)'))
4960 ui.write(_(' (obsolete)'))
4957 if p.isunstable():
4961 if p.isunstable():
4958 instabilities = (ui.label(instability, 'trouble.%s' % instability)
4962 instabilities = (ui.label(instability, 'trouble.%s' % instability)
4959 for instability in p.instabilities())
4963 for instability in p.instabilities())
4960 ui.write(' ('
4964 ui.write(' ('
4961 + ', '.join(instabilities)
4965 + ', '.join(instabilities)
4962 + ')')
4966 + ')')
4963 ui.write('\n')
4967 ui.write('\n')
4964 if p.description():
4968 if p.description():
4965 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
4969 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
4966 label='log.summary')
4970 label='log.summary')
4967
4971
4968 branch = ctx.branch()
4972 branch = ctx.branch()
4969 bheads = repo.branchheads(branch)
4973 bheads = repo.branchheads(branch)
4970 # i18n: column positioning for "hg summary"
4974 # i18n: column positioning for "hg summary"
4971 m = _('branch: %s\n') % branch
4975 m = _('branch: %s\n') % branch
4972 if branch != 'default':
4976 if branch != 'default':
4973 ui.write(m, label='log.branch')
4977 ui.write(m, label='log.branch')
4974 else:
4978 else:
4975 ui.status(m, label='log.branch')
4979 ui.status(m, label='log.branch')
4976
4980
4977 if marks:
4981 if marks:
4978 active = repo._activebookmark
4982 active = repo._activebookmark
4979 # i18n: column positioning for "hg summary"
4983 # i18n: column positioning for "hg summary"
4980 ui.write(_('bookmarks:'), label='log.bookmark')
4984 ui.write(_('bookmarks:'), label='log.bookmark')
4981 if active is not None:
4985 if active is not None:
4982 if active in marks:
4986 if active in marks:
4983 ui.write(' *' + active, label=bookmarks.activebookmarklabel)
4987 ui.write(' *' + active, label=bookmarks.activebookmarklabel)
4984 marks.remove(active)
4988 marks.remove(active)
4985 else:
4989 else:
4986 ui.write(' [%s]' % active, label=bookmarks.activebookmarklabel)
4990 ui.write(' [%s]' % active, label=bookmarks.activebookmarklabel)
4987 for m in marks:
4991 for m in marks:
4988 ui.write(' ' + m, label='log.bookmark')
4992 ui.write(' ' + m, label='log.bookmark')
4989 ui.write('\n', label='log.bookmark')
4993 ui.write('\n', label='log.bookmark')
4990
4994
4991 status = repo.status(unknown=True)
4995 status = repo.status(unknown=True)
4992
4996
4993 c = repo.dirstate.copies()
4997 c = repo.dirstate.copies()
4994 copied, renamed = [], []
4998 copied, renamed = [], []
4995 for d, s in c.iteritems():
4999 for d, s in c.iteritems():
4996 if s in status.removed:
5000 if s in status.removed:
4997 status.removed.remove(s)
5001 status.removed.remove(s)
4998 renamed.append(d)
5002 renamed.append(d)
4999 else:
5003 else:
5000 copied.append(d)
5004 copied.append(d)
5001 if d in status.added:
5005 if d in status.added:
5002 status.added.remove(d)
5006 status.added.remove(d)
5003
5007
5004 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
5008 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
5005
5009
5006 labels = [(ui.label(_('%d modified'), 'status.modified'), status.modified),
5010 labels = [(ui.label(_('%d modified'), 'status.modified'), status.modified),
5007 (ui.label(_('%d added'), 'status.added'), status.added),
5011 (ui.label(_('%d added'), 'status.added'), status.added),
5008 (ui.label(_('%d removed'), 'status.removed'), status.removed),
5012 (ui.label(_('%d removed'), 'status.removed'), status.removed),
5009 (ui.label(_('%d renamed'), 'status.copied'), renamed),
5013 (ui.label(_('%d renamed'), 'status.copied'), renamed),
5010 (ui.label(_('%d copied'), 'status.copied'), copied),
5014 (ui.label(_('%d copied'), 'status.copied'), copied),
5011 (ui.label(_('%d deleted'), 'status.deleted'), status.deleted),
5015 (ui.label(_('%d deleted'), 'status.deleted'), status.deleted),
5012 (ui.label(_('%d unknown'), 'status.unknown'), status.unknown),
5016 (ui.label(_('%d unknown'), 'status.unknown'), status.unknown),
5013 (ui.label(_('%d unresolved'), 'resolve.unresolved'), unresolved),
5017 (ui.label(_('%d unresolved'), 'resolve.unresolved'), unresolved),
5014 (ui.label(_('%d subrepos'), 'status.modified'), subs)]
5018 (ui.label(_('%d subrepos'), 'status.modified'), subs)]
5015 t = []
5019 t = []
5016 for l, s in labels:
5020 for l, s in labels:
5017 if s:
5021 if s:
5018 t.append(l % len(s))
5022 t.append(l % len(s))
5019
5023
5020 t = ', '.join(t)
5024 t = ', '.join(t)
5021 cleanworkdir = False
5025 cleanworkdir = False
5022
5026
5023 if repo.vfs.exists('graftstate'):
5027 if repo.vfs.exists('graftstate'):
5024 t += _(' (graft in progress)')
5028 t += _(' (graft in progress)')
5025 if repo.vfs.exists('updatestate'):
5029 if repo.vfs.exists('updatestate'):
5026 t += _(' (interrupted update)')
5030 t += _(' (interrupted update)')
5027 elif len(parents) > 1:
5031 elif len(parents) > 1:
5028 t += _(' (merge)')
5032 t += _(' (merge)')
5029 elif branch != parents[0].branch():
5033 elif branch != parents[0].branch():
5030 t += _(' (new branch)')
5034 t += _(' (new branch)')
5031 elif (parents[0].closesbranch() and
5035 elif (parents[0].closesbranch() and
5032 pnode in repo.branchheads(branch, closed=True)):
5036 pnode in repo.branchheads(branch, closed=True)):
5033 t += _(' (head closed)')
5037 t += _(' (head closed)')
5034 elif not (status.modified or status.added or status.removed or renamed or
5038 elif not (status.modified or status.added or status.removed or renamed or
5035 copied or subs):
5039 copied or subs):
5036 t += _(' (clean)')
5040 t += _(' (clean)')
5037 cleanworkdir = True
5041 cleanworkdir = True
5038 elif pnode not in bheads:
5042 elif pnode not in bheads:
5039 t += _(' (new branch head)')
5043 t += _(' (new branch head)')
5040
5044
5041 if parents:
5045 if parents:
5042 pendingphase = max(p.phase() for p in parents)
5046 pendingphase = max(p.phase() for p in parents)
5043 else:
5047 else:
5044 pendingphase = phases.public
5048 pendingphase = phases.public
5045
5049
5046 if pendingphase > phases.newcommitphase(ui):
5050 if pendingphase > phases.newcommitphase(ui):
5047 t += ' (%s)' % phases.phasenames[pendingphase]
5051 t += ' (%s)' % phases.phasenames[pendingphase]
5048
5052
5049 if cleanworkdir:
5053 if cleanworkdir:
5050 # i18n: column positioning for "hg summary"
5054 # i18n: column positioning for "hg summary"
5051 ui.status(_('commit: %s\n') % t.strip())
5055 ui.status(_('commit: %s\n') % t.strip())
5052 else:
5056 else:
5053 # i18n: column positioning for "hg summary"
5057 # i18n: column positioning for "hg summary"
5054 ui.write(_('commit: %s\n') % t.strip())
5058 ui.write(_('commit: %s\n') % t.strip())
5055
5059
5056 # all ancestors of branch heads - all ancestors of parent = new csets
5060 # all ancestors of branch heads - all ancestors of parent = new csets
5057 new = len(repo.changelog.findmissing([pctx.node() for pctx in parents],
5061 new = len(repo.changelog.findmissing([pctx.node() for pctx in parents],
5058 bheads))
5062 bheads))
5059
5063
5060 if new == 0:
5064 if new == 0:
5061 # i18n: column positioning for "hg summary"
5065 # i18n: column positioning for "hg summary"
5062 ui.status(_('update: (current)\n'))
5066 ui.status(_('update: (current)\n'))
5063 elif pnode not in bheads:
5067 elif pnode not in bheads:
5064 # i18n: column positioning for "hg summary"
5068 # i18n: column positioning for "hg summary"
5065 ui.write(_('update: %d new changesets (update)\n') % new)
5069 ui.write(_('update: %d new changesets (update)\n') % new)
5066 else:
5070 else:
5067 # i18n: column positioning for "hg summary"
5071 # i18n: column positioning for "hg summary"
5068 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
5072 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
5069 (new, len(bheads)))
5073 (new, len(bheads)))
5070
5074
5071 t = []
5075 t = []
5072 draft = len(repo.revs('draft()'))
5076 draft = len(repo.revs('draft()'))
5073 if draft:
5077 if draft:
5074 t.append(_('%d draft') % draft)
5078 t.append(_('%d draft') % draft)
5075 secret = len(repo.revs('secret()'))
5079 secret = len(repo.revs('secret()'))
5076 if secret:
5080 if secret:
5077 t.append(_('%d secret') % secret)
5081 t.append(_('%d secret') % secret)
5078
5082
5079 if draft or secret:
5083 if draft or secret:
5080 ui.status(_('phases: %s\n') % ', '.join(t))
5084 ui.status(_('phases: %s\n') % ', '.join(t))
5081
5085
5082 if obsolete.isenabled(repo, obsolete.createmarkersopt):
5086 if obsolete.isenabled(repo, obsolete.createmarkersopt):
5083 for trouble in ("orphan", "contentdivergent", "phasedivergent"):
5087 for trouble in ("orphan", "contentdivergent", "phasedivergent"):
5084 numtrouble = len(repo.revs(trouble + "()"))
5088 numtrouble = len(repo.revs(trouble + "()"))
5085 # We write all the possibilities to ease translation
5089 # We write all the possibilities to ease translation
5086 troublemsg = {
5090 troublemsg = {
5087 "orphan": _("orphan: %d changesets"),
5091 "orphan": _("orphan: %d changesets"),
5088 "contentdivergent": _("content-divergent: %d changesets"),
5092 "contentdivergent": _("content-divergent: %d changesets"),
5089 "phasedivergent": _("phase-divergent: %d changesets"),
5093 "phasedivergent": _("phase-divergent: %d changesets"),
5090 }
5094 }
5091 if numtrouble > 0:
5095 if numtrouble > 0:
5092 ui.status(troublemsg[trouble] % numtrouble + "\n")
5096 ui.status(troublemsg[trouble] % numtrouble + "\n")
5093
5097
5094 cmdutil.summaryhooks(ui, repo)
5098 cmdutil.summaryhooks(ui, repo)
5095
5099
5096 if opts.get('remote'):
5100 if opts.get('remote'):
5097 needsincoming, needsoutgoing = True, True
5101 needsincoming, needsoutgoing = True, True
5098 else:
5102 else:
5099 needsincoming, needsoutgoing = False, False
5103 needsincoming, needsoutgoing = False, False
5100 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
5104 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
5101 if i:
5105 if i:
5102 needsincoming = True
5106 needsincoming = True
5103 if o:
5107 if o:
5104 needsoutgoing = True
5108 needsoutgoing = True
5105 if not needsincoming and not needsoutgoing:
5109 if not needsincoming and not needsoutgoing:
5106 return
5110 return
5107
5111
5108 def getincoming():
5112 def getincoming():
5109 source, branches = hg.parseurl(ui.expandpath('default'))
5113 source, branches = hg.parseurl(ui.expandpath('default'))
5110 sbranch = branches[0]
5114 sbranch = branches[0]
5111 try:
5115 try:
5112 other = hg.peer(repo, {}, source)
5116 other = hg.peer(repo, {}, source)
5113 except error.RepoError:
5117 except error.RepoError:
5114 if opts.get('remote'):
5118 if opts.get('remote'):
5115 raise
5119 raise
5116 return source, sbranch, None, None, None
5120 return source, sbranch, None, None, None
5117 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
5121 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
5118 if revs:
5122 if revs:
5119 revs = [other.lookup(rev) for rev in revs]
5123 revs = [other.lookup(rev) for rev in revs]
5120 ui.debug('comparing with %s\n' % util.hidepassword(source))
5124 ui.debug('comparing with %s\n' % util.hidepassword(source))
5121 repo.ui.pushbuffer()
5125 repo.ui.pushbuffer()
5122 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
5126 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
5123 repo.ui.popbuffer()
5127 repo.ui.popbuffer()
5124 return source, sbranch, other, commoninc, commoninc[1]
5128 return source, sbranch, other, commoninc, commoninc[1]
5125
5129
5126 if needsincoming:
5130 if needsincoming:
5127 source, sbranch, sother, commoninc, incoming = getincoming()
5131 source, sbranch, sother, commoninc, incoming = getincoming()
5128 else:
5132 else:
5129 source = sbranch = sother = commoninc = incoming = None
5133 source = sbranch = sother = commoninc = incoming = None
5130
5134
5131 def getoutgoing():
5135 def getoutgoing():
5132 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
5136 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
5133 dbranch = branches[0]
5137 dbranch = branches[0]
5134 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
5138 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
5135 if source != dest:
5139 if source != dest:
5136 try:
5140 try:
5137 dother = hg.peer(repo, {}, dest)
5141 dother = hg.peer(repo, {}, dest)
5138 except error.RepoError:
5142 except error.RepoError:
5139 if opts.get('remote'):
5143 if opts.get('remote'):
5140 raise
5144 raise
5141 return dest, dbranch, None, None
5145 return dest, dbranch, None, None
5142 ui.debug('comparing with %s\n' % util.hidepassword(dest))
5146 ui.debug('comparing with %s\n' % util.hidepassword(dest))
5143 elif sother is None:
5147 elif sother is None:
5144 # there is no explicit destination peer, but source one is invalid
5148 # there is no explicit destination peer, but source one is invalid
5145 return dest, dbranch, None, None
5149 return dest, dbranch, None, None
5146 else:
5150 else:
5147 dother = sother
5151 dother = sother
5148 if (source != dest or (sbranch is not None and sbranch != dbranch)):
5152 if (source != dest or (sbranch is not None and sbranch != dbranch)):
5149 common = None
5153 common = None
5150 else:
5154 else:
5151 common = commoninc
5155 common = commoninc
5152 if revs:
5156 if revs:
5153 revs = [repo.lookup(rev) for rev in revs]
5157 revs = [repo.lookup(rev) for rev in revs]
5154 repo.ui.pushbuffer()
5158 repo.ui.pushbuffer()
5155 outgoing = discovery.findcommonoutgoing(repo, dother, onlyheads=revs,
5159 outgoing = discovery.findcommonoutgoing(repo, dother, onlyheads=revs,
5156 commoninc=common)
5160 commoninc=common)
5157 repo.ui.popbuffer()
5161 repo.ui.popbuffer()
5158 return dest, dbranch, dother, outgoing
5162 return dest, dbranch, dother, outgoing
5159
5163
5160 if needsoutgoing:
5164 if needsoutgoing:
5161 dest, dbranch, dother, outgoing = getoutgoing()
5165 dest, dbranch, dother, outgoing = getoutgoing()
5162 else:
5166 else:
5163 dest = dbranch = dother = outgoing = None
5167 dest = dbranch = dother = outgoing = None
5164
5168
5165 if opts.get('remote'):
5169 if opts.get('remote'):
5166 t = []
5170 t = []
5167 if incoming:
5171 if incoming:
5168 t.append(_('1 or more incoming'))
5172 t.append(_('1 or more incoming'))
5169 o = outgoing.missing
5173 o = outgoing.missing
5170 if o:
5174 if o:
5171 t.append(_('%d outgoing') % len(o))
5175 t.append(_('%d outgoing') % len(o))
5172 other = dother or sother
5176 other = dother or sother
5173 if 'bookmarks' in other.listkeys('namespaces'):
5177 if 'bookmarks' in other.listkeys('namespaces'):
5174 counts = bookmarks.summary(repo, other)
5178 counts = bookmarks.summary(repo, other)
5175 if counts[0] > 0:
5179 if counts[0] > 0:
5176 t.append(_('%d incoming bookmarks') % counts[0])
5180 t.append(_('%d incoming bookmarks') % counts[0])
5177 if counts[1] > 0:
5181 if counts[1] > 0:
5178 t.append(_('%d outgoing bookmarks') % counts[1])
5182 t.append(_('%d outgoing bookmarks') % counts[1])
5179
5183
5180 if t:
5184 if t:
5181 # i18n: column positioning for "hg summary"
5185 # i18n: column positioning for "hg summary"
5182 ui.write(_('remote: %s\n') % (', '.join(t)))
5186 ui.write(_('remote: %s\n') % (', '.join(t)))
5183 else:
5187 else:
5184 # i18n: column positioning for "hg summary"
5188 # i18n: column positioning for "hg summary"
5185 ui.status(_('remote: (synced)\n'))
5189 ui.status(_('remote: (synced)\n'))
5186
5190
5187 cmdutil.summaryremotehooks(ui, repo, opts,
5191 cmdutil.summaryremotehooks(ui, repo, opts,
5188 ((source, sbranch, sother, commoninc),
5192 ((source, sbranch, sother, commoninc),
5189 (dest, dbranch, dother, outgoing)))
5193 (dest, dbranch, dother, outgoing)))
5190
5194
5191 @command('tag',
5195 @command('tag',
5192 [('f', 'force', None, _('force tag')),
5196 [('f', 'force', None, _('force tag')),
5193 ('l', 'local', None, _('make the tag local')),
5197 ('l', 'local', None, _('make the tag local')),
5194 ('r', 'rev', '', _('revision to tag'), _('REV')),
5198 ('r', 'rev', '', _('revision to tag'), _('REV')),
5195 ('', 'remove', None, _('remove a tag')),
5199 ('', 'remove', None, _('remove a tag')),
5196 # -l/--local is already there, commitopts cannot be used
5200 # -l/--local is already there, commitopts cannot be used
5197 ('e', 'edit', None, _('invoke editor on commit messages')),
5201 ('e', 'edit', None, _('invoke editor on commit messages')),
5198 ('m', 'message', '', _('use text as commit message'), _('TEXT')),
5202 ('m', 'message', '', _('use text as commit message'), _('TEXT')),
5199 ] + commitopts2,
5203 ] + commitopts2,
5200 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
5204 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
5201 def tag(ui, repo, name1, *names, **opts):
5205 def tag(ui, repo, name1, *names, **opts):
5202 """add one or more tags for the current or given revision
5206 """add one or more tags for the current or given revision
5203
5207
5204 Name a particular revision using <name>.
5208 Name a particular revision using <name>.
5205
5209
5206 Tags are used to name particular revisions of the repository and are
5210 Tags are used to name particular revisions of the repository and are
5207 very useful to compare different revisions, to go back to significant
5211 very useful to compare different revisions, to go back to significant
5208 earlier versions or to mark branch points as releases, etc. Changing
5212 earlier versions or to mark branch points as releases, etc. Changing
5209 an existing tag is normally disallowed; use -f/--force to override.
5213 an existing tag is normally disallowed; use -f/--force to override.
5210
5214
5211 If no revision is given, the parent of the working directory is
5215 If no revision is given, the parent of the working directory is
5212 used.
5216 used.
5213
5217
5214 To facilitate version control, distribution, and merging of tags,
5218 To facilitate version control, distribution, and merging of tags,
5215 they are stored as a file named ".hgtags" which is managed similarly
5219 they are stored as a file named ".hgtags" which is managed similarly
5216 to other project files and can be hand-edited if necessary. This
5220 to other project files and can be hand-edited if necessary. This
5217 also means that tagging creates a new commit. The file
5221 also means that tagging creates a new commit. The file
5218 ".hg/localtags" is used for local tags (not shared among
5222 ".hg/localtags" is used for local tags (not shared among
5219 repositories).
5223 repositories).
5220
5224
5221 Tag commits are usually made at the head of a branch. If the parent
5225 Tag commits are usually made at the head of a branch. If the parent
5222 of the working directory is not a branch head, :hg:`tag` aborts; use
5226 of the working directory is not a branch head, :hg:`tag` aborts; use
5223 -f/--force to force the tag commit to be based on a non-head
5227 -f/--force to force the tag commit to be based on a non-head
5224 changeset.
5228 changeset.
5225
5229
5226 See :hg:`help dates` for a list of formats valid for -d/--date.
5230 See :hg:`help dates` for a list of formats valid for -d/--date.
5227
5231
5228 Since tag names have priority over branch names during revision
5232 Since tag names have priority over branch names during revision
5229 lookup, using an existing branch name as a tag name is discouraged.
5233 lookup, using an existing branch name as a tag name is discouraged.
5230
5234
5231 Returns 0 on success.
5235 Returns 0 on success.
5232 """
5236 """
5233 opts = pycompat.byteskwargs(opts)
5237 opts = pycompat.byteskwargs(opts)
5234 wlock = lock = None
5238 wlock = lock = None
5235 try:
5239 try:
5236 wlock = repo.wlock()
5240 wlock = repo.wlock()
5237 lock = repo.lock()
5241 lock = repo.lock()
5238 rev_ = "."
5242 rev_ = "."
5239 names = [t.strip() for t in (name1,) + names]
5243 names = [t.strip() for t in (name1,) + names]
5240 if len(names) != len(set(names)):
5244 if len(names) != len(set(names)):
5241 raise error.Abort(_('tag names must be unique'))
5245 raise error.Abort(_('tag names must be unique'))
5242 for n in names:
5246 for n in names:
5243 scmutil.checknewlabel(repo, n, 'tag')
5247 scmutil.checknewlabel(repo, n, 'tag')
5244 if not n:
5248 if not n:
5245 raise error.Abort(_('tag names cannot consist entirely of '
5249 raise error.Abort(_('tag names cannot consist entirely of '
5246 'whitespace'))
5250 'whitespace'))
5247 if opts.get('rev') and opts.get('remove'):
5251 if opts.get('rev') and opts.get('remove'):
5248 raise error.Abort(_("--rev and --remove are incompatible"))
5252 raise error.Abort(_("--rev and --remove are incompatible"))
5249 if opts.get('rev'):
5253 if opts.get('rev'):
5250 rev_ = opts['rev']
5254 rev_ = opts['rev']
5251 message = opts.get('message')
5255 message = opts.get('message')
5252 if opts.get('remove'):
5256 if opts.get('remove'):
5253 if opts.get('local'):
5257 if opts.get('local'):
5254 expectedtype = 'local'
5258 expectedtype = 'local'
5255 else:
5259 else:
5256 expectedtype = 'global'
5260 expectedtype = 'global'
5257
5261
5258 for n in names:
5262 for n in names:
5259 if not repo.tagtype(n):
5263 if not repo.tagtype(n):
5260 raise error.Abort(_("tag '%s' does not exist") % n)
5264 raise error.Abort(_("tag '%s' does not exist") % n)
5261 if repo.tagtype(n) != expectedtype:
5265 if repo.tagtype(n) != expectedtype:
5262 if expectedtype == 'global':
5266 if expectedtype == 'global':
5263 raise error.Abort(_("tag '%s' is not a global tag") % n)
5267 raise error.Abort(_("tag '%s' is not a global tag") % n)
5264 else:
5268 else:
5265 raise error.Abort(_("tag '%s' is not a local tag") % n)
5269 raise error.Abort(_("tag '%s' is not a local tag") % n)
5266 rev_ = 'null'
5270 rev_ = 'null'
5267 if not message:
5271 if not message:
5268 # we don't translate commit messages
5272 # we don't translate commit messages
5269 message = 'Removed tag %s' % ', '.join(names)
5273 message = 'Removed tag %s' % ', '.join(names)
5270 elif not opts.get('force'):
5274 elif not opts.get('force'):
5271 for n in names:
5275 for n in names:
5272 if n in repo.tags():
5276 if n in repo.tags():
5273 raise error.Abort(_("tag '%s' already exists "
5277 raise error.Abort(_("tag '%s' already exists "
5274 "(use -f to force)") % n)
5278 "(use -f to force)") % n)
5275 if not opts.get('local'):
5279 if not opts.get('local'):
5276 p1, p2 = repo.dirstate.parents()
5280 p1, p2 = repo.dirstate.parents()
5277 if p2 != nullid:
5281 if p2 != nullid:
5278 raise error.Abort(_('uncommitted merge'))
5282 raise error.Abort(_('uncommitted merge'))
5279 bheads = repo.branchheads()
5283 bheads = repo.branchheads()
5280 if not opts.get('force') and bheads and p1 not in bheads:
5284 if not opts.get('force') and bheads and p1 not in bheads:
5281 raise error.Abort(_('working directory is not at a branch head '
5285 raise error.Abort(_('working directory is not at a branch head '
5282 '(use -f to force)'))
5286 '(use -f to force)'))
5283 r = scmutil.revsingle(repo, rev_).node()
5287 r = scmutil.revsingle(repo, rev_).node()
5284
5288
5285 if not message:
5289 if not message:
5286 # we don't translate commit messages
5290 # we don't translate commit messages
5287 message = ('Added tag %s for changeset %s' %
5291 message = ('Added tag %s for changeset %s' %
5288 (', '.join(names), short(r)))
5292 (', '.join(names), short(r)))
5289
5293
5290 date = opts.get('date')
5294 date = opts.get('date')
5291 if date:
5295 if date:
5292 date = util.parsedate(date)
5296 date = util.parsedate(date)
5293
5297
5294 if opts.get('remove'):
5298 if opts.get('remove'):
5295 editform = 'tag.remove'
5299 editform = 'tag.remove'
5296 else:
5300 else:
5297 editform = 'tag.add'
5301 editform = 'tag.add'
5298 editor = cmdutil.getcommiteditor(editform=editform,
5302 editor = cmdutil.getcommiteditor(editform=editform,
5299 **pycompat.strkwargs(opts))
5303 **pycompat.strkwargs(opts))
5300
5304
5301 # don't allow tagging the null rev
5305 # don't allow tagging the null rev
5302 if (not opts.get('remove') and
5306 if (not opts.get('remove') and
5303 scmutil.revsingle(repo, rev_).rev() == nullrev):
5307 scmutil.revsingle(repo, rev_).rev() == nullrev):
5304 raise error.Abort(_("cannot tag null revision"))
5308 raise error.Abort(_("cannot tag null revision"))
5305
5309
5306 tagsmod.tag(repo, names, r, message, opts.get('local'),
5310 tagsmod.tag(repo, names, r, message, opts.get('local'),
5307 opts.get('user'), date, editor=editor)
5311 opts.get('user'), date, editor=editor)
5308 finally:
5312 finally:
5309 release(lock, wlock)
5313 release(lock, wlock)
5310
5314
5311 @command('tags', formatteropts, '')
5315 @command('tags', formatteropts, '')
5312 def tags(ui, repo, **opts):
5316 def tags(ui, repo, **opts):
5313 """list repository tags
5317 """list repository tags
5314
5318
5315 This lists both regular and local tags. When the -v/--verbose
5319 This lists both regular and local tags. When the -v/--verbose
5316 switch is used, a third column "local" is printed for local tags.
5320 switch is used, a third column "local" is printed for local tags.
5317 When the -q/--quiet switch is used, only the tag name is printed.
5321 When the -q/--quiet switch is used, only the tag name is printed.
5318
5322
5319 Returns 0 on success.
5323 Returns 0 on success.
5320 """
5324 """
5321
5325
5322 opts = pycompat.byteskwargs(opts)
5326 opts = pycompat.byteskwargs(opts)
5323 ui.pager('tags')
5327 ui.pager('tags')
5324 fm = ui.formatter('tags', opts)
5328 fm = ui.formatter('tags', opts)
5325 hexfunc = fm.hexfunc
5329 hexfunc = fm.hexfunc
5326 tagtype = ""
5330 tagtype = ""
5327
5331
5328 for t, n in reversed(repo.tagslist()):
5332 for t, n in reversed(repo.tagslist()):
5329 hn = hexfunc(n)
5333 hn = hexfunc(n)
5330 label = 'tags.normal'
5334 label = 'tags.normal'
5331 tagtype = ''
5335 tagtype = ''
5332 if repo.tagtype(t) == 'local':
5336 if repo.tagtype(t) == 'local':
5333 label = 'tags.local'
5337 label = 'tags.local'
5334 tagtype = 'local'
5338 tagtype = 'local'
5335
5339
5336 fm.startitem()
5340 fm.startitem()
5337 fm.write('tag', '%s', t, label=label)
5341 fm.write('tag', '%s', t, label=label)
5338 fmt = " " * (30 - encoding.colwidth(t)) + ' %5d:%s'
5342 fmt = " " * (30 - encoding.colwidth(t)) + ' %5d:%s'
5339 fm.condwrite(not ui.quiet, 'rev node', fmt,
5343 fm.condwrite(not ui.quiet, 'rev node', fmt,
5340 repo.changelog.rev(n), hn, label=label)
5344 repo.changelog.rev(n), hn, label=label)
5341 fm.condwrite(ui.verbose and tagtype, 'type', ' %s',
5345 fm.condwrite(ui.verbose and tagtype, 'type', ' %s',
5342 tagtype, label=label)
5346 tagtype, label=label)
5343 fm.plain('\n')
5347 fm.plain('\n')
5344 fm.end()
5348 fm.end()
5345
5349
5346 @command('tip',
5350 @command('tip',
5347 [('p', 'patch', None, _('show patch')),
5351 [('p', 'patch', None, _('show patch')),
5348 ('g', 'git', None, _('use git extended diff format')),
5352 ('g', 'git', None, _('use git extended diff format')),
5349 ] + templateopts,
5353 ] + templateopts,
5350 _('[-p] [-g]'))
5354 _('[-p] [-g]'))
5351 def tip(ui, repo, **opts):
5355 def tip(ui, repo, **opts):
5352 """show the tip revision (DEPRECATED)
5356 """show the tip revision (DEPRECATED)
5353
5357
5354 The tip revision (usually just called the tip) is the changeset
5358 The tip revision (usually just called the tip) is the changeset
5355 most recently added to the repository (and therefore the most
5359 most recently added to the repository (and therefore the most
5356 recently changed head).
5360 recently changed head).
5357
5361
5358 If you have just made a commit, that commit will be the tip. If
5362 If you have just made a commit, that commit will be the tip. If
5359 you have just pulled changes from another repository, the tip of
5363 you have just pulled changes from another repository, the tip of
5360 that repository becomes the current tip. The "tip" tag is special
5364 that repository becomes the current tip. The "tip" tag is special
5361 and cannot be renamed or assigned to a different changeset.
5365 and cannot be renamed or assigned to a different changeset.
5362
5366
5363 This command is deprecated, please use :hg:`heads` instead.
5367 This command is deprecated, please use :hg:`heads` instead.
5364
5368
5365 Returns 0 on success.
5369 Returns 0 on success.
5366 """
5370 """
5367 opts = pycompat.byteskwargs(opts)
5371 opts = pycompat.byteskwargs(opts)
5368 displayer = cmdutil.show_changeset(ui, repo, opts)
5372 displayer = cmdutil.show_changeset(ui, repo, opts)
5369 displayer.show(repo['tip'])
5373 displayer.show(repo['tip'])
5370 displayer.close()
5374 displayer.close()
5371
5375
5372 @command('unbundle',
5376 @command('unbundle',
5373 [('u', 'update', None,
5377 [('u', 'update', None,
5374 _('update to new branch head if changesets were unbundled'))],
5378 _('update to new branch head if changesets were unbundled'))],
5375 _('[-u] FILE...'))
5379 _('[-u] FILE...'))
5376 def unbundle(ui, repo, fname1, *fnames, **opts):
5380 def unbundle(ui, repo, fname1, *fnames, **opts):
5377 """apply one or more bundle files
5381 """apply one or more bundle files
5378
5382
5379 Apply one or more bundle files generated by :hg:`bundle`.
5383 Apply one or more bundle files generated by :hg:`bundle`.
5380
5384
5381 Returns 0 on success, 1 if an update has unresolved files.
5385 Returns 0 on success, 1 if an update has unresolved files.
5382 """
5386 """
5383 fnames = (fname1,) + fnames
5387 fnames = (fname1,) + fnames
5384
5388
5385 with repo.lock():
5389 with repo.lock():
5386 for fname in fnames:
5390 for fname in fnames:
5387 f = hg.openpath(ui, fname)
5391 f = hg.openpath(ui, fname)
5388 gen = exchange.readbundle(ui, f, fname)
5392 gen = exchange.readbundle(ui, f, fname)
5389 if isinstance(gen, streamclone.streamcloneapplier):
5393 if isinstance(gen, streamclone.streamcloneapplier):
5390 raise error.Abort(
5394 raise error.Abort(
5391 _('packed bundles cannot be applied with '
5395 _('packed bundles cannot be applied with '
5392 '"hg unbundle"'),
5396 '"hg unbundle"'),
5393 hint=_('use "hg debugapplystreamclonebundle"'))
5397 hint=_('use "hg debugapplystreamclonebundle"'))
5394 url = 'bundle:' + fname
5398 url = 'bundle:' + fname
5395 try:
5399 try:
5396 txnname = 'unbundle'
5400 txnname = 'unbundle'
5397 if not isinstance(gen, bundle2.unbundle20):
5401 if not isinstance(gen, bundle2.unbundle20):
5398 txnname = 'unbundle\n%s' % util.hidepassword(url)
5402 txnname = 'unbundle\n%s' % util.hidepassword(url)
5399 with repo.transaction(txnname) as tr:
5403 with repo.transaction(txnname) as tr:
5400 op = bundle2.applybundle(repo, gen, tr, source='unbundle',
5404 op = bundle2.applybundle(repo, gen, tr, source='unbundle',
5401 url=url)
5405 url=url)
5402 except error.BundleUnknownFeatureError as exc:
5406 except error.BundleUnknownFeatureError as exc:
5403 raise error.Abort(
5407 raise error.Abort(
5404 _('%s: unknown bundle feature, %s') % (fname, exc),
5408 _('%s: unknown bundle feature, %s') % (fname, exc),
5405 hint=_("see https://mercurial-scm.org/"
5409 hint=_("see https://mercurial-scm.org/"
5406 "wiki/BundleFeature for more "
5410 "wiki/BundleFeature for more "
5407 "information"))
5411 "information"))
5408 modheads = bundle2.combinechangegroupresults(op)
5412 modheads = bundle2.combinechangegroupresults(op)
5409
5413
5410 return postincoming(ui, repo, modheads, opts.get(r'update'), None, None)
5414 return postincoming(ui, repo, modheads, opts.get(r'update'), None, None)
5411
5415
5412 @command('^update|up|checkout|co',
5416 @command('^update|up|checkout|co',
5413 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
5417 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
5414 ('c', 'check', None, _('require clean working directory')),
5418 ('c', 'check', None, _('require clean working directory')),
5415 ('m', 'merge', None, _('merge uncommitted changes')),
5419 ('m', 'merge', None, _('merge uncommitted changes')),
5416 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5420 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5417 ('r', 'rev', '', _('revision'), _('REV'))
5421 ('r', 'rev', '', _('revision'), _('REV'))
5418 ] + mergetoolopts,
5422 ] + mergetoolopts,
5419 _('[-C|-c|-m] [-d DATE] [[-r] REV]'))
5423 _('[-C|-c|-m] [-d DATE] [[-r] REV]'))
5420 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False,
5424 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False,
5421 merge=None, tool=None):
5425 merge=None, tool=None):
5422 """update working directory (or switch revisions)
5426 """update working directory (or switch revisions)
5423
5427
5424 Update the repository's working directory to the specified
5428 Update the repository's working directory to the specified
5425 changeset. If no changeset is specified, update to the tip of the
5429 changeset. If no changeset is specified, update to the tip of the
5426 current named branch and move the active bookmark (see :hg:`help
5430 current named branch and move the active bookmark (see :hg:`help
5427 bookmarks`).
5431 bookmarks`).
5428
5432
5429 Update sets the working directory's parent revision to the specified
5433 Update sets the working directory's parent revision to the specified
5430 changeset (see :hg:`help parents`).
5434 changeset (see :hg:`help parents`).
5431
5435
5432 If the changeset is not a descendant or ancestor of the working
5436 If the changeset is not a descendant or ancestor of the working
5433 directory's parent and there are uncommitted changes, the update is
5437 directory's parent and there are uncommitted changes, the update is
5434 aborted. With the -c/--check option, the working directory is checked
5438 aborted. With the -c/--check option, the working directory is checked
5435 for uncommitted changes; if none are found, the working directory is
5439 for uncommitted changes; if none are found, the working directory is
5436 updated to the specified changeset.
5440 updated to the specified changeset.
5437
5441
5438 .. container:: verbose
5442 .. container:: verbose
5439
5443
5440 The -C/--clean, -c/--check, and -m/--merge options control what
5444 The -C/--clean, -c/--check, and -m/--merge options control what
5441 happens if the working directory contains uncommitted changes.
5445 happens if the working directory contains uncommitted changes.
5442 At most of one of them can be specified.
5446 At most of one of them can be specified.
5443
5447
5444 1. If no option is specified, and if
5448 1. If no option is specified, and if
5445 the requested changeset is an ancestor or descendant of
5449 the requested changeset is an ancestor or descendant of
5446 the working directory's parent, the uncommitted changes
5450 the working directory's parent, the uncommitted changes
5447 are merged into the requested changeset and the merged
5451 are merged into the requested changeset and the merged
5448 result is left uncommitted. If the requested changeset is
5452 result is left uncommitted. If the requested changeset is
5449 not an ancestor or descendant (that is, it is on another
5453 not an ancestor or descendant (that is, it is on another
5450 branch), the update is aborted and the uncommitted changes
5454 branch), the update is aborted and the uncommitted changes
5451 are preserved.
5455 are preserved.
5452
5456
5453 2. With the -m/--merge option, the update is allowed even if the
5457 2. With the -m/--merge option, the update is allowed even if the
5454 requested changeset is not an ancestor or descendant of
5458 requested changeset is not an ancestor or descendant of
5455 the working directory's parent.
5459 the working directory's parent.
5456
5460
5457 3. With the -c/--check option, the update is aborted and the
5461 3. With the -c/--check option, the update is aborted and the
5458 uncommitted changes are preserved.
5462 uncommitted changes are preserved.
5459
5463
5460 4. With the -C/--clean option, uncommitted changes are discarded and
5464 4. With the -C/--clean option, uncommitted changes are discarded and
5461 the working directory is updated to the requested changeset.
5465 the working directory is updated to the requested changeset.
5462
5466
5463 To cancel an uncommitted merge (and lose your changes), use
5467 To cancel an uncommitted merge (and lose your changes), use
5464 :hg:`update --clean .`.
5468 :hg:`update --clean .`.
5465
5469
5466 Use null as the changeset to remove the working directory (like
5470 Use null as the changeset to remove the working directory (like
5467 :hg:`clone -U`).
5471 :hg:`clone -U`).
5468
5472
5469 If you want to revert just one file to an older revision, use
5473 If you want to revert just one file to an older revision, use
5470 :hg:`revert [-r REV] NAME`.
5474 :hg:`revert [-r REV] NAME`.
5471
5475
5472 See :hg:`help dates` for a list of formats valid for -d/--date.
5476 See :hg:`help dates` for a list of formats valid for -d/--date.
5473
5477
5474 Returns 0 on success, 1 if there are unresolved files.
5478 Returns 0 on success, 1 if there are unresolved files.
5475 """
5479 """
5476 if rev and node:
5480 if rev and node:
5477 raise error.Abort(_("please specify just one revision"))
5481 raise error.Abort(_("please specify just one revision"))
5478
5482
5479 if ui.configbool('commands', 'update.requiredest'):
5483 if ui.configbool('commands', 'update.requiredest'):
5480 if not node and not rev and not date:
5484 if not node and not rev and not date:
5481 raise error.Abort(_('you must specify a destination'),
5485 raise error.Abort(_('you must specify a destination'),
5482 hint=_('for example: hg update ".::"'))
5486 hint=_('for example: hg update ".::"'))
5483
5487
5484 if rev is None or rev == '':
5488 if rev is None or rev == '':
5485 rev = node
5489 rev = node
5486
5490
5487 if date and rev is not None:
5491 if date and rev is not None:
5488 raise error.Abort(_("you can't specify a revision and a date"))
5492 raise error.Abort(_("you can't specify a revision and a date"))
5489
5493
5490 if len([x for x in (clean, check, merge) if x]) > 1:
5494 if len([x for x in (clean, check, merge) if x]) > 1:
5491 raise error.Abort(_("can only specify one of -C/--clean, -c/--check, "
5495 raise error.Abort(_("can only specify one of -C/--clean, -c/--check, "
5492 "or -m/--merge"))
5496 "or -m/--merge"))
5493
5497
5494 updatecheck = None
5498 updatecheck = None
5495 if check:
5499 if check:
5496 updatecheck = 'abort'
5500 updatecheck = 'abort'
5497 elif merge:
5501 elif merge:
5498 updatecheck = 'none'
5502 updatecheck = 'none'
5499
5503
5500 with repo.wlock():
5504 with repo.wlock():
5501 cmdutil.clearunfinished(repo)
5505 cmdutil.clearunfinished(repo)
5502
5506
5503 if date:
5507 if date:
5504 rev = cmdutil.finddate(ui, repo, date)
5508 rev = cmdutil.finddate(ui, repo, date)
5505
5509
5506 # if we defined a bookmark, we have to remember the original name
5510 # if we defined a bookmark, we have to remember the original name
5507 brev = rev
5511 brev = rev
5508 rev = scmutil.revsingle(repo, rev, rev).rev()
5512 rev = scmutil.revsingle(repo, rev, rev).rev()
5509
5513
5510 repo.ui.setconfig('ui', 'forcemerge', tool, 'update')
5514 repo.ui.setconfig('ui', 'forcemerge', tool, 'update')
5511
5515
5512 return hg.updatetotally(ui, repo, rev, brev, clean=clean,
5516 return hg.updatetotally(ui, repo, rev, brev, clean=clean,
5513 updatecheck=updatecheck)
5517 updatecheck=updatecheck)
5514
5518
5515 @command('verify', [])
5519 @command('verify', [])
5516 def verify(ui, repo):
5520 def verify(ui, repo):
5517 """verify the integrity of the repository
5521 """verify the integrity of the repository
5518
5522
5519 Verify the integrity of the current repository.
5523 Verify the integrity of the current repository.
5520
5524
5521 This will perform an extensive check of the repository's
5525 This will perform an extensive check of the repository's
5522 integrity, validating the hashes and checksums of each entry in
5526 integrity, validating the hashes and checksums of each entry in
5523 the changelog, manifest, and tracked files, as well as the
5527 the changelog, manifest, and tracked files, as well as the
5524 integrity of their crosslinks and indices.
5528 integrity of their crosslinks and indices.
5525
5529
5526 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
5530 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
5527 for more information about recovery from corruption of the
5531 for more information about recovery from corruption of the
5528 repository.
5532 repository.
5529
5533
5530 Returns 0 on success, 1 if errors are encountered.
5534 Returns 0 on success, 1 if errors are encountered.
5531 """
5535 """
5532 return hg.verify(repo)
5536 return hg.verify(repo)
5533
5537
5534 @command('version', [] + formatteropts, norepo=True)
5538 @command('version', [] + formatteropts, norepo=True)
5535 def version_(ui, **opts):
5539 def version_(ui, **opts):
5536 """output version and copyright information"""
5540 """output version and copyright information"""
5537 opts = pycompat.byteskwargs(opts)
5541 opts = pycompat.byteskwargs(opts)
5538 if ui.verbose:
5542 if ui.verbose:
5539 ui.pager('version')
5543 ui.pager('version')
5540 fm = ui.formatter("version", opts)
5544 fm = ui.formatter("version", opts)
5541 fm.startitem()
5545 fm.startitem()
5542 fm.write("ver", _("Mercurial Distributed SCM (version %s)\n"),
5546 fm.write("ver", _("Mercurial Distributed SCM (version %s)\n"),
5543 util.version())
5547 util.version())
5544 license = _(
5548 license = _(
5545 "(see https://mercurial-scm.org for more information)\n"
5549 "(see https://mercurial-scm.org for more information)\n"
5546 "\nCopyright (C) 2005-2017 Matt Mackall and others\n"
5550 "\nCopyright (C) 2005-2017 Matt Mackall and others\n"
5547 "This is free software; see the source for copying conditions. "
5551 "This is free software; see the source for copying conditions. "
5548 "There is NO\nwarranty; "
5552 "There is NO\nwarranty; "
5549 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
5553 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
5550 )
5554 )
5551 if not ui.quiet:
5555 if not ui.quiet:
5552 fm.plain(license)
5556 fm.plain(license)
5553
5557
5554 if ui.verbose:
5558 if ui.verbose:
5555 fm.plain(_("\nEnabled extensions:\n\n"))
5559 fm.plain(_("\nEnabled extensions:\n\n"))
5556 # format names and versions into columns
5560 # format names and versions into columns
5557 names = []
5561 names = []
5558 vers = []
5562 vers = []
5559 isinternals = []
5563 isinternals = []
5560 for name, module in extensions.extensions():
5564 for name, module in extensions.extensions():
5561 names.append(name)
5565 names.append(name)
5562 vers.append(extensions.moduleversion(module) or None)
5566 vers.append(extensions.moduleversion(module) or None)
5563 isinternals.append(extensions.ismoduleinternal(module))
5567 isinternals.append(extensions.ismoduleinternal(module))
5564 fn = fm.nested("extensions")
5568 fn = fm.nested("extensions")
5565 if names:
5569 if names:
5566 namefmt = " %%-%ds " % max(len(n) for n in names)
5570 namefmt = " %%-%ds " % max(len(n) for n in names)
5567 places = [_("external"), _("internal")]
5571 places = [_("external"), _("internal")]
5568 for n, v, p in zip(names, vers, isinternals):
5572 for n, v, p in zip(names, vers, isinternals):
5569 fn.startitem()
5573 fn.startitem()
5570 fn.condwrite(ui.verbose, "name", namefmt, n)
5574 fn.condwrite(ui.verbose, "name", namefmt, n)
5571 if ui.verbose:
5575 if ui.verbose:
5572 fn.plain("%s " % places[p])
5576 fn.plain("%s " % places[p])
5573 fn.data(bundled=p)
5577 fn.data(bundled=p)
5574 fn.condwrite(ui.verbose and v, "ver", "%s", v)
5578 fn.condwrite(ui.verbose and v, "ver", "%s", v)
5575 if ui.verbose:
5579 if ui.verbose:
5576 fn.plain("\n")
5580 fn.plain("\n")
5577 fn.end()
5581 fn.end()
5578 fm.end()
5582 fm.end()
5579
5583
5580 def loadcmdtable(ui, name, cmdtable):
5584 def loadcmdtable(ui, name, cmdtable):
5581 """Load command functions from specified cmdtable
5585 """Load command functions from specified cmdtable
5582 """
5586 """
5583 overrides = [cmd for cmd in cmdtable if cmd in table]
5587 overrides = [cmd for cmd in cmdtable if cmd in table]
5584 if overrides:
5588 if overrides:
5585 ui.warn(_("extension '%s' overrides commands: %s\n")
5589 ui.warn(_("extension '%s' overrides commands: %s\n")
5586 % (name, " ".join(overrides)))
5590 % (name, " ".join(overrides)))
5587 table.update(cmdtable)
5591 table.update(cmdtable)
@@ -1,1089 +1,1103 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 difflib
10 import difflib
11 import errno
11 import errno
12 import getopt
12 import getopt
13 import os
13 import os
14 import pdb
14 import pdb
15 import re
15 import re
16 import signal
16 import signal
17 import sys
17 import sys
18 import time
18 import time
19 import traceback
19 import traceback
20
20
21
21
22 from .i18n import _
22 from .i18n import _
23
23
24 from . import (
24 from . import (
25 cmdutil,
25 cmdutil,
26 color,
26 color,
27 commands,
27 commands,
28 demandimport,
28 demandimport,
29 encoding,
29 encoding,
30 error,
30 error,
31 extensions,
31 extensions,
32 fancyopts,
32 fancyopts,
33 help,
33 help,
34 hg,
34 hg,
35 hook,
35 hook,
36 profiling,
36 profiling,
37 pycompat,
37 pycompat,
38 registrar,
38 registrar,
39 scmutil,
39 scmutil,
40 ui as uimod,
40 ui as uimod,
41 util,
41 util,
42 )
42 )
43
43
44 unrecoverablewrite = registrar.command.unrecoverablewrite
44 unrecoverablewrite = registrar.command.unrecoverablewrite
45
45
46 class request(object):
46 class request(object):
47 def __init__(self, args, ui=None, repo=None, fin=None, fout=None,
47 def __init__(self, args, ui=None, repo=None, fin=None, fout=None,
48 ferr=None, prereposetups=None):
48 ferr=None, prereposetups=None):
49 self.args = args
49 self.args = args
50 self.ui = ui
50 self.ui = ui
51 self.repo = repo
51 self.repo = repo
52
52
53 # input/output/error streams
53 # input/output/error streams
54 self.fin = fin
54 self.fin = fin
55 self.fout = fout
55 self.fout = fout
56 self.ferr = ferr
56 self.ferr = ferr
57
57
58 # remember options pre-parsed by _earlyreqopt*()
58 # remember options pre-parsed by _earlyreqopt*()
59 self.earlyoptions = {}
59 self.earlyoptions = {}
60
60
61 # reposetups which run before extensions, useful for chg to pre-fill
61 # reposetups which run before extensions, useful for chg to pre-fill
62 # low-level repo state (for example, changelog) before extensions.
62 # low-level repo state (for example, changelog) before extensions.
63 self.prereposetups = prereposetups or []
63 self.prereposetups = prereposetups or []
64
64
65 def _runexithandlers(self):
65 def _runexithandlers(self):
66 exc = None
66 exc = None
67 handlers = self.ui._exithandlers
67 handlers = self.ui._exithandlers
68 try:
68 try:
69 while handlers:
69 while handlers:
70 func, args, kwargs = handlers.pop()
70 func, args, kwargs = handlers.pop()
71 try:
71 try:
72 func(*args, **kwargs)
72 func(*args, **kwargs)
73 except: # re-raises below
73 except: # re-raises below
74 if exc is None:
74 if exc is None:
75 exc = sys.exc_info()[1]
75 exc = sys.exc_info()[1]
76 self.ui.warn(('error in exit handlers:\n'))
76 self.ui.warn(('error in exit handlers:\n'))
77 self.ui.traceback(force=True)
77 self.ui.traceback(force=True)
78 finally:
78 finally:
79 if exc is not None:
79 if exc is not None:
80 raise exc
80 raise exc
81
81
82 def run():
82 def run():
83 "run the command in sys.argv"
83 "run the command in sys.argv"
84 _initstdio()
84 _initstdio()
85 req = request(pycompat.sysargv[1:])
85 req = request(pycompat.sysargv[1:])
86 err = None
86 err = None
87 try:
87 try:
88 status = (dispatch(req) or 0) & 255
88 status = (dispatch(req) or 0) & 255
89 except error.StdioError as e:
89 except error.StdioError as e:
90 err = e
90 err = e
91 status = -1
91 status = -1
92 if util.safehasattr(req.ui, 'fout'):
92 if util.safehasattr(req.ui, 'fout'):
93 try:
93 try:
94 req.ui.fout.flush()
94 req.ui.fout.flush()
95 except IOError as e:
95 except IOError as e:
96 err = e
96 err = e
97 status = -1
97 status = -1
98 if util.safehasattr(req.ui, 'ferr'):
98 if util.safehasattr(req.ui, 'ferr'):
99 if err is not None and err.errno != errno.EPIPE:
99 if err is not None and err.errno != errno.EPIPE:
100 req.ui.ferr.write('abort: %s\n' %
100 req.ui.ferr.write('abort: %s\n' %
101 encoding.strtolocal(err.strerror))
101 encoding.strtolocal(err.strerror))
102 req.ui.ferr.flush()
102 req.ui.ferr.flush()
103 sys.exit(status & 255)
103 sys.exit(status & 255)
104
104
105 def _initstdio():
105 def _initstdio():
106 for fp in (sys.stdin, sys.stdout, sys.stderr):
106 for fp in (sys.stdin, sys.stdout, sys.stderr):
107 util.setbinary(fp)
107 util.setbinary(fp)
108
108
109 def _getsimilar(symbols, value):
109 def _getsimilar(symbols, value):
110 sim = lambda x: difflib.SequenceMatcher(None, value, x).ratio()
110 sim = lambda x: difflib.SequenceMatcher(None, value, x).ratio()
111 # The cutoff for similarity here is pretty arbitrary. It should
111 # The cutoff for similarity here is pretty arbitrary. It should
112 # probably be investigated and tweaked.
112 # probably be investigated and tweaked.
113 return [s for s in symbols if sim(s) > 0.6]
113 return [s for s in symbols if sim(s) > 0.6]
114
114
115 def _reportsimilar(write, similar):
115 def _reportsimilar(write, similar):
116 if len(similar) == 1:
116 if len(similar) == 1:
117 write(_("(did you mean %s?)\n") % similar[0])
117 write(_("(did you mean %s?)\n") % similar[0])
118 elif similar:
118 elif similar:
119 ss = ", ".join(sorted(similar))
119 ss = ", ".join(sorted(similar))
120 write(_("(did you mean one of %s?)\n") % ss)
120 write(_("(did you mean one of %s?)\n") % ss)
121
121
122 def _formatparse(write, inst):
122 def _formatparse(write, inst):
123 similar = []
123 similar = []
124 if isinstance(inst, error.UnknownIdentifier):
124 if isinstance(inst, error.UnknownIdentifier):
125 # make sure to check fileset first, as revset can invoke fileset
125 # make sure to check fileset first, as revset can invoke fileset
126 similar = _getsimilar(inst.symbols, inst.function)
126 similar = _getsimilar(inst.symbols, inst.function)
127 if len(inst.args) > 1:
127 if len(inst.args) > 1:
128 write(_("hg: parse error at %s: %s\n") %
128 write(_("hg: parse error at %s: %s\n") %
129 (inst.args[1], inst.args[0]))
129 (inst.args[1], inst.args[0]))
130 if (inst.args[0][0] == ' '):
130 if (inst.args[0][0] == ' '):
131 write(_("unexpected leading whitespace\n"))
131 write(_("unexpected leading whitespace\n"))
132 else:
132 else:
133 write(_("hg: parse error: %s\n") % inst.args[0])
133 write(_("hg: parse error: %s\n") % inst.args[0])
134 _reportsimilar(write, similar)
134 _reportsimilar(write, similar)
135 if inst.hint:
135 if inst.hint:
136 write(_("(%s)\n") % inst.hint)
136 write(_("(%s)\n") % inst.hint)
137
137
138 def _formatargs(args):
138 def _formatargs(args):
139 return ' '.join(util.shellquote(a) for a in args)
139 return ' '.join(util.shellquote(a) for a in args)
140
140
141 def dispatch(req):
141 def dispatch(req):
142 "run the command specified in req.args"
142 "run the command specified in req.args"
143 if req.ferr:
143 if req.ferr:
144 ferr = req.ferr
144 ferr = req.ferr
145 elif req.ui:
145 elif req.ui:
146 ferr = req.ui.ferr
146 ferr = req.ui.ferr
147 else:
147 else:
148 ferr = util.stderr
148 ferr = util.stderr
149
149
150 try:
150 try:
151 if not req.ui:
151 if not req.ui:
152 req.ui = uimod.ui.load()
152 req.ui = uimod.ui.load()
153 if _earlyreqoptbool(req, 'traceback', ['--traceback']):
153 if _earlyreqoptbool(req, 'traceback', ['--traceback']):
154 req.ui.setconfig('ui', 'traceback', 'on', '--traceback')
154 req.ui.setconfig('ui', 'traceback', 'on', '--traceback')
155
155
156 # set ui streams from the request
156 # set ui streams from the request
157 if req.fin:
157 if req.fin:
158 req.ui.fin = req.fin
158 req.ui.fin = req.fin
159 if req.fout:
159 if req.fout:
160 req.ui.fout = req.fout
160 req.ui.fout = req.fout
161 if req.ferr:
161 if req.ferr:
162 req.ui.ferr = req.ferr
162 req.ui.ferr = req.ferr
163 except error.Abort as inst:
163 except error.Abort as inst:
164 ferr.write(_("abort: %s\n") % inst)
164 ferr.write(_("abort: %s\n") % inst)
165 if inst.hint:
165 if inst.hint:
166 ferr.write(_("(%s)\n") % inst.hint)
166 ferr.write(_("(%s)\n") % inst.hint)
167 return -1
167 return -1
168 except error.ParseError as inst:
168 except error.ParseError as inst:
169 _formatparse(ferr.write, inst)
169 _formatparse(ferr.write, inst)
170 return -1
170 return -1
171
171
172 msg = _formatargs(req.args)
172 msg = _formatargs(req.args)
173 starttime = util.timer()
173 starttime = util.timer()
174 ret = None
174 ret = None
175 try:
175 try:
176 ret = _runcatch(req)
176 ret = _runcatch(req)
177 except error.ProgrammingError as inst:
177 except error.ProgrammingError as inst:
178 req.ui.warn(_('** ProgrammingError: %s\n') % inst)
178 req.ui.warn(_('** ProgrammingError: %s\n') % inst)
179 if inst.hint:
179 if inst.hint:
180 req.ui.warn(_('** (%s)\n') % inst.hint)
180 req.ui.warn(_('** (%s)\n') % inst.hint)
181 raise
181 raise
182 except KeyboardInterrupt as inst:
182 except KeyboardInterrupt as inst:
183 try:
183 try:
184 if isinstance(inst, error.SignalInterrupt):
184 if isinstance(inst, error.SignalInterrupt):
185 msg = _("killed!\n")
185 msg = _("killed!\n")
186 else:
186 else:
187 msg = _("interrupted!\n")
187 msg = _("interrupted!\n")
188 req.ui.warn(msg)
188 req.ui.warn(msg)
189 except error.SignalInterrupt:
189 except error.SignalInterrupt:
190 # maybe pager would quit without consuming all the output, and
190 # maybe pager would quit without consuming all the output, and
191 # SIGPIPE was raised. we cannot print anything in this case.
191 # SIGPIPE was raised. we cannot print anything in this case.
192 pass
192 pass
193 except IOError as inst:
193 except IOError as inst:
194 if inst.errno != errno.EPIPE:
194 if inst.errno != errno.EPIPE:
195 raise
195 raise
196 ret = -1
196 ret = -1
197 finally:
197 finally:
198 duration = util.timer() - starttime
198 duration = util.timer() - starttime
199 req.ui.flush()
199 req.ui.flush()
200 if req.ui.logblockedtimes:
200 if req.ui.logblockedtimes:
201 req.ui._blockedtimes['command_duration'] = duration * 1000
201 req.ui._blockedtimes['command_duration'] = duration * 1000
202 req.ui.log('uiblocked', 'ui blocked ms', **req.ui._blockedtimes)
202 req.ui.log('uiblocked', 'ui blocked ms', **req.ui._blockedtimes)
203 req.ui.log("commandfinish", "%s exited %d after %0.2f seconds\n",
203 req.ui.log("commandfinish", "%s exited %d after %0.2f seconds\n",
204 msg, ret or 0, duration)
204 msg, ret or 0, duration)
205 try:
205 try:
206 req._runexithandlers()
206 req._runexithandlers()
207 except: # exiting, so no re-raises
207 except: # exiting, so no re-raises
208 ret = ret or -1
208 ret = ret or -1
209 return ret
209 return ret
210
210
211 def _runcatch(req):
211 def _runcatch(req):
212 def catchterm(*args):
212 def catchterm(*args):
213 raise error.SignalInterrupt
213 raise error.SignalInterrupt
214
214
215 ui = req.ui
215 ui = req.ui
216 try:
216 try:
217 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
217 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
218 num = getattr(signal, name, None)
218 num = getattr(signal, name, None)
219 if num:
219 if num:
220 signal.signal(num, catchterm)
220 signal.signal(num, catchterm)
221 except ValueError:
221 except ValueError:
222 pass # happens if called in a thread
222 pass # happens if called in a thread
223
223
224 def _runcatchfunc():
224 def _runcatchfunc():
225 realcmd = None
225 realcmd = None
226 try:
226 try:
227 cmdargs = fancyopts.fancyopts(req.args[:], commands.globalopts, {})
227 cmdargs = fancyopts.fancyopts(req.args[:], commands.globalopts, {})
228 cmd = cmdargs[0]
228 cmd = cmdargs[0]
229 aliases, entry = cmdutil.findcmd(cmd, commands.table, False)
229 aliases, entry = cmdutil.findcmd(cmd, commands.table, False)
230 realcmd = aliases[0]
230 realcmd = aliases[0]
231 except (error.UnknownCommand, error.AmbiguousCommand,
231 except (error.UnknownCommand, error.AmbiguousCommand,
232 IndexError, getopt.GetoptError):
232 IndexError, getopt.GetoptError):
233 # Don't handle this here. We know the command is
233 # Don't handle this here. We know the command is
234 # invalid, but all we're worried about for now is that
234 # invalid, but all we're worried about for now is that
235 # it's not a command that server operators expect to
235 # it's not a command that server operators expect to
236 # be safe to offer to users in a sandbox.
236 # be safe to offer to users in a sandbox.
237 pass
237 pass
238 if realcmd == 'serve' and '--stdio' in cmdargs:
238 if realcmd == 'serve' and '--stdio' in cmdargs:
239 # We want to constrain 'hg serve --stdio' instances pretty
239 # We want to constrain 'hg serve --stdio' instances pretty
240 # closely, as many shared-ssh access tools want to grant
240 # closely, as many shared-ssh access tools want to grant
241 # access to run *only* 'hg -R $repo serve --stdio'. We
241 # access to run *only* 'hg -R $repo serve --stdio'. We
242 # restrict to exactly that set of arguments, and prohibit
242 # restrict to exactly that set of arguments, and prohibit
243 # any repo name that starts with '--' to prevent
243 # any repo name that starts with '--' to prevent
244 # shenanigans wherein a user does something like pass
244 # shenanigans wherein a user does something like pass
245 # --debugger or --config=ui.debugger=1 as a repo
245 # --debugger or --config=ui.debugger=1 as a repo
246 # name. This used to actually run the debugger.
246 # name. This used to actually run the debugger.
247 if (len(req.args) != 4 or
247 if (len(req.args) != 4 or
248 req.args[0] != '-R' or
248 req.args[0] != '-R' or
249 req.args[1].startswith('--') or
249 req.args[1].startswith('--') or
250 req.args[2] != 'serve' or
250 req.args[2] != 'serve' or
251 req.args[3] != '--stdio'):
251 req.args[3] != '--stdio'):
252 raise error.Abort(
252 raise error.Abort(
253 _('potentially unsafe serve --stdio invocation: %r') %
253 _('potentially unsafe serve --stdio invocation: %r') %
254 (req.args,))
254 (req.args,))
255
255
256 try:
256 try:
257 debugger = 'pdb'
257 debugger = 'pdb'
258 debugtrace = {
258 debugtrace = {
259 'pdb': pdb.set_trace
259 'pdb': pdb.set_trace
260 }
260 }
261 debugmortem = {
261 debugmortem = {
262 'pdb': pdb.post_mortem
262 'pdb': pdb.post_mortem
263 }
263 }
264
264
265 # read --config before doing anything else
265 # read --config before doing anything else
266 # (e.g. to change trust settings for reading .hg/hgrc)
266 # (e.g. to change trust settings for reading .hg/hgrc)
267 cfgs = _parseconfig(req.ui, _earlygetopt(['--config'], req.args))
267 cfgs = _parseconfig(req.ui,
268 _earlyreqopt(req, 'config', ['--config']))
268
269
269 if req.repo:
270 if req.repo:
270 # copy configs that were passed on the cmdline (--config) to
271 # copy configs that were passed on the cmdline (--config) to
271 # the repo ui
272 # the repo ui
272 for sec, name, val in cfgs:
273 for sec, name, val in cfgs:
273 req.repo.ui.setconfig(sec, name, val, source='--config')
274 req.repo.ui.setconfig(sec, name, val, source='--config')
274
275
275 # developer config: ui.debugger
276 # developer config: ui.debugger
276 debugger = ui.config("ui", "debugger")
277 debugger = ui.config("ui", "debugger")
277 debugmod = pdb
278 debugmod = pdb
278 if not debugger or ui.plain():
279 if not debugger or ui.plain():
279 # if we are in HGPLAIN mode, then disable custom debugging
280 # if we are in HGPLAIN mode, then disable custom debugging
280 debugger = 'pdb'
281 debugger = 'pdb'
281 elif _earlyreqoptbool(req, 'debugger', ['--debugger']):
282 elif _earlyreqoptbool(req, 'debugger', ['--debugger']):
282 # This import can be slow for fancy debuggers, so only
283 # This import can be slow for fancy debuggers, so only
283 # do it when absolutely necessary, i.e. when actual
284 # do it when absolutely necessary, i.e. when actual
284 # debugging has been requested
285 # debugging has been requested
285 with demandimport.deactivated():
286 with demandimport.deactivated():
286 try:
287 try:
287 debugmod = __import__(debugger)
288 debugmod = __import__(debugger)
288 except ImportError:
289 except ImportError:
289 pass # Leave debugmod = pdb
290 pass # Leave debugmod = pdb
290
291
291 debugtrace[debugger] = debugmod.set_trace
292 debugtrace[debugger] = debugmod.set_trace
292 debugmortem[debugger] = debugmod.post_mortem
293 debugmortem[debugger] = debugmod.post_mortem
293
294
294 # enter the debugger before command execution
295 # enter the debugger before command execution
295 if _earlyreqoptbool(req, 'debugger', ['--debugger']):
296 if _earlyreqoptbool(req, 'debugger', ['--debugger']):
296 ui.warn(_("entering debugger - "
297 ui.warn(_("entering debugger - "
297 "type c to continue starting hg or h for help\n"))
298 "type c to continue starting hg or h for help\n"))
298
299
299 if (debugger != 'pdb' and
300 if (debugger != 'pdb' and
300 debugtrace[debugger] == debugtrace['pdb']):
301 debugtrace[debugger] == debugtrace['pdb']):
301 ui.warn(_("%s debugger specified "
302 ui.warn(_("%s debugger specified "
302 "but its module was not found\n") % debugger)
303 "but its module was not found\n") % debugger)
303 with demandimport.deactivated():
304 with demandimport.deactivated():
304 debugtrace[debugger]()
305 debugtrace[debugger]()
305 try:
306 try:
306 return _dispatch(req)
307 return _dispatch(req)
307 finally:
308 finally:
308 ui.flush()
309 ui.flush()
309 except: # re-raises
310 except: # re-raises
310 # enter the debugger when we hit an exception
311 # enter the debugger when we hit an exception
311 if _earlyreqoptbool(req, 'debugger', ['--debugger']):
312 if _earlyreqoptbool(req, 'debugger', ['--debugger']):
312 traceback.print_exc()
313 traceback.print_exc()
313 debugmortem[debugger](sys.exc_info()[2])
314 debugmortem[debugger](sys.exc_info()[2])
314 raise
315 raise
315
316
316 return _callcatch(ui, _runcatchfunc)
317 return _callcatch(ui, _runcatchfunc)
317
318
318 def _callcatch(ui, func):
319 def _callcatch(ui, func):
319 """like scmutil.callcatch but handles more high-level exceptions about
320 """like scmutil.callcatch but handles more high-level exceptions about
320 config parsing and commands. besides, use handlecommandexception to handle
321 config parsing and commands. besides, use handlecommandexception to handle
321 uncaught exceptions.
322 uncaught exceptions.
322 """
323 """
323 try:
324 try:
324 return scmutil.callcatch(ui, func)
325 return scmutil.callcatch(ui, func)
325 except error.AmbiguousCommand as inst:
326 except error.AmbiguousCommand as inst:
326 ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
327 ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
327 (inst.args[0], " ".join(inst.args[1])))
328 (inst.args[0], " ".join(inst.args[1])))
328 except error.CommandError as inst:
329 except error.CommandError as inst:
329 if inst.args[0]:
330 if inst.args[0]:
330 ui.pager('help')
331 ui.pager('help')
331 msgbytes = pycompat.bytestr(inst.args[1])
332 msgbytes = pycompat.bytestr(inst.args[1])
332 ui.warn(_("hg %s: %s\n") % (inst.args[0], msgbytes))
333 ui.warn(_("hg %s: %s\n") % (inst.args[0], msgbytes))
333 commands.help_(ui, inst.args[0], full=False, command=True)
334 commands.help_(ui, inst.args[0], full=False, command=True)
334 else:
335 else:
335 ui.pager('help')
336 ui.pager('help')
336 ui.warn(_("hg: %s\n") % inst.args[1])
337 ui.warn(_("hg: %s\n") % inst.args[1])
337 commands.help_(ui, 'shortlist')
338 commands.help_(ui, 'shortlist')
338 except error.ParseError as inst:
339 except error.ParseError as inst:
339 _formatparse(ui.warn, inst)
340 _formatparse(ui.warn, inst)
340 return -1
341 return -1
341 except error.UnknownCommand as inst:
342 except error.UnknownCommand as inst:
342 nocmdmsg = _("hg: unknown command '%s'\n") % inst.args[0]
343 nocmdmsg = _("hg: unknown command '%s'\n") % inst.args[0]
343 try:
344 try:
344 # check if the command is in a disabled extension
345 # check if the command is in a disabled extension
345 # (but don't check for extensions themselves)
346 # (but don't check for extensions themselves)
346 formatted = help.formattedhelp(ui, commands, inst.args[0],
347 formatted = help.formattedhelp(ui, commands, inst.args[0],
347 unknowncmd=True)
348 unknowncmd=True)
348 ui.warn(nocmdmsg)
349 ui.warn(nocmdmsg)
349 ui.write(formatted)
350 ui.write(formatted)
350 except (error.UnknownCommand, error.Abort):
351 except (error.UnknownCommand, error.Abort):
351 suggested = False
352 suggested = False
352 if len(inst.args) == 2:
353 if len(inst.args) == 2:
353 sim = _getsimilar(inst.args[1], inst.args[0])
354 sim = _getsimilar(inst.args[1], inst.args[0])
354 if sim:
355 if sim:
355 ui.warn(nocmdmsg)
356 ui.warn(nocmdmsg)
356 _reportsimilar(ui.warn, sim)
357 _reportsimilar(ui.warn, sim)
357 suggested = True
358 suggested = True
358 if not suggested:
359 if not suggested:
359 ui.pager('help')
360 ui.pager('help')
360 ui.warn(nocmdmsg)
361 ui.warn(nocmdmsg)
361 commands.help_(ui, 'shortlist')
362 commands.help_(ui, 'shortlist')
362 except IOError:
363 except IOError:
363 raise
364 raise
364 except KeyboardInterrupt:
365 except KeyboardInterrupt:
365 raise
366 raise
366 except: # probably re-raises
367 except: # probably re-raises
367 if not handlecommandexception(ui):
368 if not handlecommandexception(ui):
368 raise
369 raise
369
370
370 return -1
371 return -1
371
372
372 def aliasargs(fn, givenargs):
373 def aliasargs(fn, givenargs):
373 args = []
374 args = []
374 # only care about alias 'args', ignore 'args' set by extensions.wrapfunction
375 # only care about alias 'args', ignore 'args' set by extensions.wrapfunction
375 if not util.safehasattr(fn, '_origfunc'):
376 if not util.safehasattr(fn, '_origfunc'):
376 args = getattr(fn, 'args', args)
377 args = getattr(fn, 'args', args)
377 if args:
378 if args:
378 cmd = ' '.join(map(util.shellquote, args))
379 cmd = ' '.join(map(util.shellquote, args))
379
380
380 nums = []
381 nums = []
381 def replacer(m):
382 def replacer(m):
382 num = int(m.group(1)) - 1
383 num = int(m.group(1)) - 1
383 nums.append(num)
384 nums.append(num)
384 if num < len(givenargs):
385 if num < len(givenargs):
385 return givenargs[num]
386 return givenargs[num]
386 raise error.Abort(_('too few arguments for command alias'))
387 raise error.Abort(_('too few arguments for command alias'))
387 cmd = re.sub(br'\$(\d+|\$)', replacer, cmd)
388 cmd = re.sub(br'\$(\d+|\$)', replacer, cmd)
388 givenargs = [x for i, x in enumerate(givenargs)
389 givenargs = [x for i, x in enumerate(givenargs)
389 if i not in nums]
390 if i not in nums]
390 args = pycompat.shlexsplit(cmd)
391 args = pycompat.shlexsplit(cmd)
391 return args + givenargs
392 return args + givenargs
392
393
393 def aliasinterpolate(name, args, cmd):
394 def aliasinterpolate(name, args, cmd):
394 '''interpolate args into cmd for shell aliases
395 '''interpolate args into cmd for shell aliases
395
396
396 This also handles $0, $@ and "$@".
397 This also handles $0, $@ and "$@".
397 '''
398 '''
398 # util.interpolate can't deal with "$@" (with quotes) because it's only
399 # util.interpolate can't deal with "$@" (with quotes) because it's only
399 # built to match prefix + patterns.
400 # built to match prefix + patterns.
400 replacemap = dict(('$%d' % (i + 1), arg) for i, arg in enumerate(args))
401 replacemap = dict(('$%d' % (i + 1), arg) for i, arg in enumerate(args))
401 replacemap['$0'] = name
402 replacemap['$0'] = name
402 replacemap['$$'] = '$'
403 replacemap['$$'] = '$'
403 replacemap['$@'] = ' '.join(args)
404 replacemap['$@'] = ' '.join(args)
404 # Typical Unix shells interpolate "$@" (with quotes) as all the positional
405 # Typical Unix shells interpolate "$@" (with quotes) as all the positional
405 # parameters, separated out into words. Emulate the same behavior here by
406 # parameters, separated out into words. Emulate the same behavior here by
406 # quoting the arguments individually. POSIX shells will then typically
407 # quoting the arguments individually. POSIX shells will then typically
407 # tokenize each argument into exactly one word.
408 # tokenize each argument into exactly one word.
408 replacemap['"$@"'] = ' '.join(util.shellquote(arg) for arg in args)
409 replacemap['"$@"'] = ' '.join(util.shellquote(arg) for arg in args)
409 # escape '\$' for regex
410 # escape '\$' for regex
410 regex = '|'.join(replacemap.keys()).replace('$', r'\$')
411 regex = '|'.join(replacemap.keys()).replace('$', r'\$')
411 r = re.compile(regex)
412 r = re.compile(regex)
412 return r.sub(lambda x: replacemap[x.group()], cmd)
413 return r.sub(lambda x: replacemap[x.group()], cmd)
413
414
414 class cmdalias(object):
415 class cmdalias(object):
415 def __init__(self, name, definition, cmdtable, source):
416 def __init__(self, name, definition, cmdtable, source):
416 self.name = self.cmd = name
417 self.name = self.cmd = name
417 self.cmdname = ''
418 self.cmdname = ''
418 self.definition = definition
419 self.definition = definition
419 self.fn = None
420 self.fn = None
420 self.givenargs = []
421 self.givenargs = []
421 self.opts = []
422 self.opts = []
422 self.help = ''
423 self.help = ''
423 self.badalias = None
424 self.badalias = None
424 self.unknowncmd = False
425 self.unknowncmd = False
425 self.source = source
426 self.source = source
426
427
427 try:
428 try:
428 aliases, entry = cmdutil.findcmd(self.name, cmdtable)
429 aliases, entry = cmdutil.findcmd(self.name, cmdtable)
429 for alias, e in cmdtable.iteritems():
430 for alias, e in cmdtable.iteritems():
430 if e is entry:
431 if e is entry:
431 self.cmd = alias
432 self.cmd = alias
432 break
433 break
433 self.shadows = True
434 self.shadows = True
434 except error.UnknownCommand:
435 except error.UnknownCommand:
435 self.shadows = False
436 self.shadows = False
436
437
437 if not self.definition:
438 if not self.definition:
438 self.badalias = _("no definition for alias '%s'") % self.name
439 self.badalias = _("no definition for alias '%s'") % self.name
439 return
440 return
440
441
441 if self.definition.startswith('!'):
442 if self.definition.startswith('!'):
442 self.shell = True
443 self.shell = True
443 def fn(ui, *args):
444 def fn(ui, *args):
444 env = {'HG_ARGS': ' '.join((self.name,) + args)}
445 env = {'HG_ARGS': ' '.join((self.name,) + args)}
445 def _checkvar(m):
446 def _checkvar(m):
446 if m.groups()[0] == '$':
447 if m.groups()[0] == '$':
447 return m.group()
448 return m.group()
448 elif int(m.groups()[0]) <= len(args):
449 elif int(m.groups()[0]) <= len(args):
449 return m.group()
450 return m.group()
450 else:
451 else:
451 ui.debug("No argument found for substitution "
452 ui.debug("No argument found for substitution "
452 "of %i variable in alias '%s' definition."
453 "of %i variable in alias '%s' definition."
453 % (int(m.groups()[0]), self.name))
454 % (int(m.groups()[0]), self.name))
454 return ''
455 return ''
455 cmd = re.sub(r'\$(\d+|\$)', _checkvar, self.definition[1:])
456 cmd = re.sub(r'\$(\d+|\$)', _checkvar, self.definition[1:])
456 cmd = aliasinterpolate(self.name, args, cmd)
457 cmd = aliasinterpolate(self.name, args, cmd)
457 return ui.system(cmd, environ=env,
458 return ui.system(cmd, environ=env,
458 blockedtag='alias_%s' % self.name)
459 blockedtag='alias_%s' % self.name)
459 self.fn = fn
460 self.fn = fn
460 return
461 return
461
462
462 try:
463 try:
463 args = pycompat.shlexsplit(self.definition)
464 args = pycompat.shlexsplit(self.definition)
464 except ValueError as inst:
465 except ValueError as inst:
465 self.badalias = (_("error in definition for alias '%s': %s")
466 self.badalias = (_("error in definition for alias '%s': %s")
466 % (self.name, inst))
467 % (self.name, inst))
467 return
468 return
468 self.cmdname = cmd = args.pop(0)
469 self.cmdname = cmd = args.pop(0)
469 self.givenargs = args
470 self.givenargs = args
470
471
471 for invalidarg in ("--cwd", "-R", "--repository", "--repo", "--config"):
472 for invalidarg in commands.earlyoptflags:
472 if _earlygetopt([invalidarg], args):
473 if _earlygetopt([invalidarg], args):
473 self.badalias = (_("error in definition for alias '%s': %s may "
474 self.badalias = (_("error in definition for alias '%s': %s may "
474 "only be given on the command line")
475 "only be given on the command line")
475 % (self.name, invalidarg))
476 % (self.name, invalidarg))
476 return
477 return
477
478
478 try:
479 try:
479 tableentry = cmdutil.findcmd(cmd, cmdtable, False)[1]
480 tableentry = cmdutil.findcmd(cmd, cmdtable, False)[1]
480 if len(tableentry) > 2:
481 if len(tableentry) > 2:
481 self.fn, self.opts, self.help = tableentry
482 self.fn, self.opts, self.help = tableentry
482 else:
483 else:
483 self.fn, self.opts = tableentry
484 self.fn, self.opts = tableentry
484
485
485 if self.help.startswith("hg " + cmd):
486 if self.help.startswith("hg " + cmd):
486 # drop prefix in old-style help lines so hg shows the alias
487 # drop prefix in old-style help lines so hg shows the alias
487 self.help = self.help[4 + len(cmd):]
488 self.help = self.help[4 + len(cmd):]
488 self.__doc__ = self.fn.__doc__
489 self.__doc__ = self.fn.__doc__
489
490
490 except error.UnknownCommand:
491 except error.UnknownCommand:
491 self.badalias = (_("alias '%s' resolves to unknown command '%s'")
492 self.badalias = (_("alias '%s' resolves to unknown command '%s'")
492 % (self.name, cmd))
493 % (self.name, cmd))
493 self.unknowncmd = True
494 self.unknowncmd = True
494 except error.AmbiguousCommand:
495 except error.AmbiguousCommand:
495 self.badalias = (_("alias '%s' resolves to ambiguous command '%s'")
496 self.badalias = (_("alias '%s' resolves to ambiguous command '%s'")
496 % (self.name, cmd))
497 % (self.name, cmd))
497
498
498 @property
499 @property
499 def args(self):
500 def args(self):
500 args = pycompat.maplist(util.expandpath, self.givenargs)
501 args = pycompat.maplist(util.expandpath, self.givenargs)
501 return aliasargs(self.fn, args)
502 return aliasargs(self.fn, args)
502
503
503 def __getattr__(self, name):
504 def __getattr__(self, name):
504 adefaults = {r'norepo': True, r'cmdtype': unrecoverablewrite,
505 adefaults = {r'norepo': True, r'cmdtype': unrecoverablewrite,
505 r'optionalrepo': False, r'inferrepo': False}
506 r'optionalrepo': False, r'inferrepo': False}
506 if name not in adefaults:
507 if name not in adefaults:
507 raise AttributeError(name)
508 raise AttributeError(name)
508 if self.badalias or util.safehasattr(self, 'shell'):
509 if self.badalias or util.safehasattr(self, 'shell'):
509 return adefaults[name]
510 return adefaults[name]
510 return getattr(self.fn, name)
511 return getattr(self.fn, name)
511
512
512 def __call__(self, ui, *args, **opts):
513 def __call__(self, ui, *args, **opts):
513 if self.badalias:
514 if self.badalias:
514 hint = None
515 hint = None
515 if self.unknowncmd:
516 if self.unknowncmd:
516 try:
517 try:
517 # check if the command is in a disabled extension
518 # check if the command is in a disabled extension
518 cmd, ext = extensions.disabledcmd(ui, self.cmdname)[:2]
519 cmd, ext = extensions.disabledcmd(ui, self.cmdname)[:2]
519 hint = _("'%s' is provided by '%s' extension") % (cmd, ext)
520 hint = _("'%s' is provided by '%s' extension") % (cmd, ext)
520 except error.UnknownCommand:
521 except error.UnknownCommand:
521 pass
522 pass
522 raise error.Abort(self.badalias, hint=hint)
523 raise error.Abort(self.badalias, hint=hint)
523 if self.shadows:
524 if self.shadows:
524 ui.debug("alias '%s' shadows command '%s'\n" %
525 ui.debug("alias '%s' shadows command '%s'\n" %
525 (self.name, self.cmdname))
526 (self.name, self.cmdname))
526
527
527 ui.log('commandalias', "alias '%s' expands to '%s'\n",
528 ui.log('commandalias', "alias '%s' expands to '%s'\n",
528 self.name, self.definition)
529 self.name, self.definition)
529 if util.safehasattr(self, 'shell'):
530 if util.safehasattr(self, 'shell'):
530 return self.fn(ui, *args, **opts)
531 return self.fn(ui, *args, **opts)
531 else:
532 else:
532 try:
533 try:
533 return util.checksignature(self.fn)(ui, *args, **opts)
534 return util.checksignature(self.fn)(ui, *args, **opts)
534 except error.SignatureError:
535 except error.SignatureError:
535 args = ' '.join([self.cmdname] + self.args)
536 args = ' '.join([self.cmdname] + self.args)
536 ui.debug("alias '%s' expands to '%s'\n" % (self.name, args))
537 ui.debug("alias '%s' expands to '%s'\n" % (self.name, args))
537 raise
538 raise
538
539
539 class lazyaliasentry(object):
540 class lazyaliasentry(object):
540 """like a typical command entry (func, opts, help), but is lazy"""
541 """like a typical command entry (func, opts, help), but is lazy"""
541
542
542 def __init__(self, name, definition, cmdtable, source):
543 def __init__(self, name, definition, cmdtable, source):
543 self.name = name
544 self.name = name
544 self.definition = definition
545 self.definition = definition
545 self.cmdtable = cmdtable.copy()
546 self.cmdtable = cmdtable.copy()
546 self.source = source
547 self.source = source
547
548
548 @util.propertycache
549 @util.propertycache
549 def _aliasdef(self):
550 def _aliasdef(self):
550 return cmdalias(self.name, self.definition, self.cmdtable, self.source)
551 return cmdalias(self.name, self.definition, self.cmdtable, self.source)
551
552
552 def __getitem__(self, n):
553 def __getitem__(self, n):
553 aliasdef = self._aliasdef
554 aliasdef = self._aliasdef
554 if n == 0:
555 if n == 0:
555 return aliasdef
556 return aliasdef
556 elif n == 1:
557 elif n == 1:
557 return aliasdef.opts
558 return aliasdef.opts
558 elif n == 2:
559 elif n == 2:
559 return aliasdef.help
560 return aliasdef.help
560 else:
561 else:
561 raise IndexError
562 raise IndexError
562
563
563 def __iter__(self):
564 def __iter__(self):
564 for i in range(3):
565 for i in range(3):
565 yield self[i]
566 yield self[i]
566
567
567 def __len__(self):
568 def __len__(self):
568 return 3
569 return 3
569
570
570 def addaliases(ui, cmdtable):
571 def addaliases(ui, cmdtable):
571 # aliases are processed after extensions have been loaded, so they
572 # aliases are processed after extensions have been loaded, so they
572 # may use extension commands. Aliases can also use other alias definitions,
573 # may use extension commands. Aliases can also use other alias definitions,
573 # but only if they have been defined prior to the current definition.
574 # but only if they have been defined prior to the current definition.
574 for alias, definition in ui.configitems('alias'):
575 for alias, definition in ui.configitems('alias'):
575 try:
576 try:
576 if cmdtable[alias].definition == definition:
577 if cmdtable[alias].definition == definition:
577 continue
578 continue
578 except (KeyError, AttributeError):
579 except (KeyError, AttributeError):
579 # definition might not exist or it might not be a cmdalias
580 # definition might not exist or it might not be a cmdalias
580 pass
581 pass
581
582
582 source = ui.configsource('alias', alias)
583 source = ui.configsource('alias', alias)
583 entry = lazyaliasentry(alias, definition, cmdtable, source)
584 entry = lazyaliasentry(alias, definition, cmdtable, source)
584 cmdtable[alias] = entry
585 cmdtable[alias] = entry
585
586
586 def _parse(ui, args):
587 def _parse(ui, args):
587 options = {}
588 options = {}
588 cmdoptions = {}
589 cmdoptions = {}
589
590
590 try:
591 try:
591 args = fancyopts.fancyopts(args, commands.globalopts, options)
592 args = fancyopts.fancyopts(args, commands.globalopts, options)
592 except getopt.GetoptError as inst:
593 except getopt.GetoptError as inst:
593 raise error.CommandError(None, inst)
594 raise error.CommandError(None, inst)
594
595
595 if args:
596 if args:
596 cmd, args = args[0], args[1:]
597 cmd, args = args[0], args[1:]
597 aliases, entry = cmdutil.findcmd(cmd, commands.table,
598 aliases, entry = cmdutil.findcmd(cmd, commands.table,
598 ui.configbool("ui", "strict"))
599 ui.configbool("ui", "strict"))
599 cmd = aliases[0]
600 cmd = aliases[0]
600 args = aliasargs(entry[0], args)
601 args = aliasargs(entry[0], args)
601 defaults = ui.config("defaults", cmd)
602 defaults = ui.config("defaults", cmd)
602 if defaults:
603 if defaults:
603 args = pycompat.maplist(
604 args = pycompat.maplist(
604 util.expandpath, pycompat.shlexsplit(defaults)) + args
605 util.expandpath, pycompat.shlexsplit(defaults)) + args
605 c = list(entry[1])
606 c = list(entry[1])
606 else:
607 else:
607 cmd = None
608 cmd = None
608 c = []
609 c = []
609
610
610 # combine global options into local
611 # combine global options into local
611 for o in commands.globalopts:
612 for o in commands.globalopts:
612 c.append((o[0], o[1], options[o[1]], o[3]))
613 c.append((o[0], o[1], options[o[1]], o[3]))
613
614
614 try:
615 try:
615 args = fancyopts.fancyopts(args, c, cmdoptions, gnu=True)
616 args = fancyopts.fancyopts(args, c, cmdoptions, gnu=True)
616 except getopt.GetoptError as inst:
617 except getopt.GetoptError as inst:
617 raise error.CommandError(cmd, inst)
618 raise error.CommandError(cmd, inst)
618
619
619 # separate global options back out
620 # separate global options back out
620 for o in commands.globalopts:
621 for o in commands.globalopts:
621 n = o[1]
622 n = o[1]
622 options[n] = cmdoptions[n]
623 options[n] = cmdoptions[n]
623 del cmdoptions[n]
624 del cmdoptions[n]
624
625
625 return (cmd, cmd and entry[0] or None, args, options, cmdoptions)
626 return (cmd, cmd and entry[0] or None, args, options, cmdoptions)
626
627
627 def _parseconfig(ui, config):
628 def _parseconfig(ui, config):
628 """parse the --config options from the command line"""
629 """parse the --config options from the command line"""
629 configs = []
630 configs = []
630
631
631 for cfg in config:
632 for cfg in config:
632 try:
633 try:
633 name, value = [cfgelem.strip()
634 name, value = [cfgelem.strip()
634 for cfgelem in cfg.split('=', 1)]
635 for cfgelem in cfg.split('=', 1)]
635 section, name = name.split('.', 1)
636 section, name = name.split('.', 1)
636 if not section or not name:
637 if not section or not name:
637 raise IndexError
638 raise IndexError
638 ui.setconfig(section, name, value, '--config')
639 ui.setconfig(section, name, value, '--config')
639 configs.append((section, name, value))
640 configs.append((section, name, value))
640 except (IndexError, ValueError):
641 except (IndexError, ValueError):
641 raise error.Abort(_('malformed --config option: %r '
642 raise error.Abort(_('malformed --config option: %r '
642 '(use --config section.name=value)') % cfg)
643 '(use --config section.name=value)') % cfg)
643
644
644 return configs
645 return configs
645
646
646 def _earlygetopt(aliases, args, strip=True):
647 def _earlygetopt(aliases, args, strip=True):
647 """Return list of values for an option (or aliases).
648 """Return list of values for an option (or aliases).
648
649
649 The values are listed in the order they appear in args.
650 The values are listed in the order they appear in args.
650 The options and values are removed from args if strip=True.
651 The options and values are removed from args if strip=True.
651
652
652 >>> args = [b'x', b'--cwd', b'foo', b'y']
653 >>> args = [b'x', b'--cwd', b'foo', b'y']
653 >>> _earlygetopt([b'--cwd'], args), args
654 >>> _earlygetopt([b'--cwd'], args), args
654 (['foo'], ['x', 'y'])
655 (['foo'], ['x', 'y'])
655
656
656 >>> args = [b'x', b'--cwd=bar', b'y']
657 >>> args = [b'x', b'--cwd=bar', b'y']
657 >>> _earlygetopt([b'--cwd'], args), args
658 >>> _earlygetopt([b'--cwd'], args), args
658 (['bar'], ['x', 'y'])
659 (['bar'], ['x', 'y'])
659
660
660 >>> args = [b'x', b'--cwd=bar', b'y']
661 >>> args = [b'x', b'--cwd=bar', b'y']
661 >>> _earlygetopt([b'--cwd'], args, strip=False), args
662 >>> _earlygetopt([b'--cwd'], args, strip=False), args
662 (['bar'], ['x', '--cwd=bar', 'y'])
663 (['bar'], ['x', '--cwd=bar', 'y'])
663
664
664 >>> args = [b'x', b'-R', b'foo', b'y']
665 >>> args = [b'x', b'-R', b'foo', b'y']
665 >>> _earlygetopt([b'-R'], args), args
666 >>> _earlygetopt([b'-R'], args), args
666 (['foo'], ['x', 'y'])
667 (['foo'], ['x', 'y'])
667
668
668 >>> args = [b'x', b'-R', b'foo', b'y']
669 >>> args = [b'x', b'-R', b'foo', b'y']
669 >>> _earlygetopt([b'-R'], args, strip=False), args
670 >>> _earlygetopt([b'-R'], args, strip=False), args
670 (['foo'], ['x', '-R', 'foo', 'y'])
671 (['foo'], ['x', '-R', 'foo', 'y'])
671
672
672 >>> args = [b'x', b'-Rbar', b'y']
673 >>> args = [b'x', b'-Rbar', b'y']
673 >>> _earlygetopt([b'-R'], args), args
674 >>> _earlygetopt([b'-R'], args), args
674 (['bar'], ['x', 'y'])
675 (['bar'], ['x', 'y'])
675
676
676 >>> args = [b'x', b'-Rbar', b'y']
677 >>> args = [b'x', b'-Rbar', b'y']
677 >>> _earlygetopt([b'-R'], args, strip=False), args
678 >>> _earlygetopt([b'-R'], args, strip=False), args
678 (['bar'], ['x', '-Rbar', 'y'])
679 (['bar'], ['x', '-Rbar', 'y'])
679
680
680 >>> args = [b'x', b'-R=bar', b'y']
681 >>> args = [b'x', b'-R=bar', b'y']
681 >>> _earlygetopt([b'-R'], args), args
682 >>> _earlygetopt([b'-R'], args), args
682 (['=bar'], ['x', 'y'])
683 (['=bar'], ['x', 'y'])
683
684
684 >>> args = [b'x', b'-R', b'--', b'y']
685 >>> args = [b'x', b'-R', b'--', b'y']
685 >>> _earlygetopt([b'-R'], args), args
686 >>> _earlygetopt([b'-R'], args), args
686 ([], ['x', '-R', '--', 'y'])
687 ([], ['x', '-R', '--', 'y'])
687 """
688 """
688 try:
689 try:
689 argcount = args.index("--")
690 argcount = args.index("--")
690 except ValueError:
691 except ValueError:
691 argcount = len(args)
692 argcount = len(args)
692 shortopts = [opt for opt in aliases if len(opt) == 2]
693 shortopts = [opt for opt in aliases if len(opt) == 2]
693 values = []
694 values = []
694 pos = 0
695 pos = 0
695 while pos < argcount:
696 while pos < argcount:
696 fullarg = arg = args[pos]
697 fullarg = arg = args[pos]
697 equals = -1
698 equals = -1
698 if arg.startswith('--'):
699 if arg.startswith('--'):
699 equals = arg.find('=')
700 equals = arg.find('=')
700 if equals > -1:
701 if equals > -1:
701 arg = arg[:equals]
702 arg = arg[:equals]
702 if arg in aliases:
703 if arg in aliases:
703 if equals > -1:
704 if equals > -1:
704 values.append(fullarg[equals + 1:])
705 values.append(fullarg[equals + 1:])
705 if strip:
706 if strip:
706 del args[pos]
707 del args[pos]
707 argcount -= 1
708 argcount -= 1
708 else:
709 else:
709 pos += 1
710 pos += 1
710 else:
711 else:
711 if pos + 1 >= argcount:
712 if pos + 1 >= argcount:
712 # ignore and let getopt report an error if there is no value
713 # ignore and let getopt report an error if there is no value
713 break
714 break
714 values.append(args[pos + 1])
715 values.append(args[pos + 1])
715 if strip:
716 if strip:
716 del args[pos:pos + 2]
717 del args[pos:pos + 2]
717 argcount -= 2
718 argcount -= 2
718 else:
719 else:
719 pos += 2
720 pos += 2
720 elif arg[:2] in shortopts:
721 elif arg[:2] in shortopts:
721 # short option can have no following space, e.g. hg log -Rfoo
722 # short option can have no following space, e.g. hg log -Rfoo
722 values.append(args[pos][2:])
723 values.append(args[pos][2:])
723 if strip:
724 if strip:
724 del args[pos]
725 del args[pos]
725 argcount -= 1
726 argcount -= 1
726 else:
727 else:
727 pos += 1
728 pos += 1
728 else:
729 else:
729 pos += 1
730 pos += 1
730 return values
731 return values
731
732
733 def _earlyreqopt(req, name, aliases):
734 """Peek a list option without using a full options table"""
735 values = _earlygetopt(aliases, req.args, strip=False)
736 req.earlyoptions[name] = values
737 return values
738
739 def _earlyreqoptstr(req, name, aliases):
740 """Peek a string option without using a full options table"""
741 value = (_earlygetopt(aliases, req.args, strip=False) or [''])[-1]
742 req.earlyoptions[name] = value
743 return value
744
732 def _earlyreqoptbool(req, name, aliases):
745 def _earlyreqoptbool(req, name, aliases):
733 """Peek a boolean option without using a full options table
746 """Peek a boolean option without using a full options table
734
747
735 >>> req = request([b'x', b'--debugger'])
748 >>> req = request([b'x', b'--debugger'])
736 >>> _earlyreqoptbool(req, b'debugger', [b'--debugger'])
749 >>> _earlyreqoptbool(req, b'debugger', [b'--debugger'])
737 True
750 True
738
751
739 >>> req = request([b'x', b'--', b'--debugger'])
752 >>> req = request([b'x', b'--', b'--debugger'])
740 >>> _earlyreqoptbool(req, b'debugger', [b'--debugger'])
753 >>> _earlyreqoptbool(req, b'debugger', [b'--debugger'])
741 """
754 """
742 try:
755 try:
743 argcount = req.args.index("--")
756 argcount = req.args.index("--")
744 except ValueError:
757 except ValueError:
745 argcount = len(req.args)
758 argcount = len(req.args)
746 value = None
759 value = None
747 pos = 0
760 pos = 0
748 while pos < argcount:
761 while pos < argcount:
749 arg = req.args[pos]
762 arg = req.args[pos]
750 if arg in aliases:
763 if arg in aliases:
751 value = True
764 value = True
752 pos += 1
765 pos += 1
753 req.earlyoptions[name] = value
766 req.earlyoptions[name] = value
754 return value
767 return value
755
768
756 def runcommand(lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions):
769 def runcommand(lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions):
757 # run pre-hook, and abort if it fails
770 # run pre-hook, and abort if it fails
758 hook.hook(lui, repo, "pre-%s" % cmd, True, args=" ".join(fullargs),
771 hook.hook(lui, repo, "pre-%s" % cmd, True, args=" ".join(fullargs),
759 pats=cmdpats, opts=cmdoptions)
772 pats=cmdpats, opts=cmdoptions)
760 try:
773 try:
761 ret = _runcommand(ui, options, cmd, d)
774 ret = _runcommand(ui, options, cmd, d)
762 # run post-hook, passing command result
775 # run post-hook, passing command result
763 hook.hook(lui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
776 hook.hook(lui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
764 result=ret, pats=cmdpats, opts=cmdoptions)
777 result=ret, pats=cmdpats, opts=cmdoptions)
765 except Exception:
778 except Exception:
766 # run failure hook and re-raise
779 # run failure hook and re-raise
767 hook.hook(lui, repo, "fail-%s" % cmd, False, args=" ".join(fullargs),
780 hook.hook(lui, repo, "fail-%s" % cmd, False, args=" ".join(fullargs),
768 pats=cmdpats, opts=cmdoptions)
781 pats=cmdpats, opts=cmdoptions)
769 raise
782 raise
770 return ret
783 return ret
771
784
772 def _getlocal(ui, rpath, wd=None):
785 def _getlocal(ui, rpath, wd=None):
773 """Return (path, local ui object) for the given target path.
786 """Return (path, local ui object) for the given target path.
774
787
775 Takes paths in [cwd]/.hg/hgrc into account."
788 Takes paths in [cwd]/.hg/hgrc into account."
776 """
789 """
777 if wd is None:
790 if wd is None:
778 try:
791 try:
779 wd = pycompat.getcwd()
792 wd = pycompat.getcwd()
780 except OSError as e:
793 except OSError as e:
781 raise error.Abort(_("error getting current working directory: %s") %
794 raise error.Abort(_("error getting current working directory: %s") %
782 encoding.strtolocal(e.strerror))
795 encoding.strtolocal(e.strerror))
783 path = cmdutil.findrepo(wd) or ""
796 path = cmdutil.findrepo(wd) or ""
784 if not path:
797 if not path:
785 lui = ui
798 lui = ui
786 else:
799 else:
787 lui = ui.copy()
800 lui = ui.copy()
788 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
801 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
789
802
790 if rpath:
803 if rpath:
791 path = lui.expandpath(rpath)
804 path = lui.expandpath(rpath)
792 lui = ui.copy()
805 lui = ui.copy()
793 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
806 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
794
807
795 return path, lui
808 return path, lui
796
809
797 def _checkshellalias(lui, ui, args):
810 def _checkshellalias(lui, ui, args):
798 """Return the function to run the shell alias, if it is required"""
811 """Return the function to run the shell alias, if it is required"""
799 options = {}
812 options = {}
800
813
801 try:
814 try:
802 args = fancyopts.fancyopts(args, commands.globalopts, options)
815 args = fancyopts.fancyopts(args, commands.globalopts, options)
803 except getopt.GetoptError:
816 except getopt.GetoptError:
804 return
817 return
805
818
806 if not args:
819 if not args:
807 return
820 return
808
821
809 cmdtable = commands.table
822 cmdtable = commands.table
810
823
811 cmd = args[0]
824 cmd = args[0]
812 try:
825 try:
813 strict = ui.configbool("ui", "strict")
826 strict = ui.configbool("ui", "strict")
814 aliases, entry = cmdutil.findcmd(cmd, cmdtable, strict)
827 aliases, entry = cmdutil.findcmd(cmd, cmdtable, strict)
815 except (error.AmbiguousCommand, error.UnknownCommand):
828 except (error.AmbiguousCommand, error.UnknownCommand):
816 return
829 return
817
830
818 cmd = aliases[0]
831 cmd = aliases[0]
819 fn = entry[0]
832 fn = entry[0]
820
833
821 if cmd and util.safehasattr(fn, 'shell'):
834 if cmd and util.safehasattr(fn, 'shell'):
835 # shell alias shouldn't receive early options which are consumed by hg
836 args = args[:]
837 _earlygetopt(commands.earlyoptflags, args, strip=True)
822 d = lambda: fn(ui, *args[1:])
838 d = lambda: fn(ui, *args[1:])
823 return lambda: runcommand(lui, None, cmd, args[:1], ui, options, d,
839 return lambda: runcommand(lui, None, cmd, args[:1], ui, options, d,
824 [], {})
840 [], {})
825
841
826 def _dispatch(req):
842 def _dispatch(req):
827 args = req.args
843 args = req.args
828 ui = req.ui
844 ui = req.ui
829
845
830 # check for cwd
846 # check for cwd
831 cwd = _earlygetopt(['--cwd'], args)
847 cwd = _earlyreqoptstr(req, 'cwd', ['--cwd'])
832 cwd = cwd and cwd[-1] or ''
833 if cwd:
848 if cwd:
834 os.chdir(cwd)
849 os.chdir(cwd)
835
850
836 rpath = _earlygetopt(["-R", "--repository", "--repo"], args)
851 rpath = _earlyreqoptstr(req, 'repository', ["-R", "--repository", "--repo"])
837 rpath = rpath and rpath[-1] or ''
838 path, lui = _getlocal(ui, rpath)
852 path, lui = _getlocal(ui, rpath)
839
853
840 uis = {ui, lui}
854 uis = {ui, lui}
841
855
842 if req.repo:
856 if req.repo:
843 uis.add(req.repo.ui)
857 uis.add(req.repo.ui)
844
858
845 if _earlyreqoptbool(req, 'profile', ['--profile']):
859 if _earlyreqoptbool(req, 'profile', ['--profile']):
846 for ui_ in uis:
860 for ui_ in uis:
847 ui_.setconfig('profiling', 'enabled', 'true', '--profile')
861 ui_.setconfig('profiling', 'enabled', 'true', '--profile')
848
862
849 profile = lui.configbool('profiling', 'enabled')
863 profile = lui.configbool('profiling', 'enabled')
850 with profiling.profile(lui, enabled=profile) as profiler:
864 with profiling.profile(lui, enabled=profile) as profiler:
851 # Configure extensions in phases: uisetup, extsetup, cmdtable, and
865 # Configure extensions in phases: uisetup, extsetup, cmdtable, and
852 # reposetup
866 # reposetup
853 extensions.loadall(lui)
867 extensions.loadall(lui)
854 # Propagate any changes to lui.__class__ by extensions
868 # Propagate any changes to lui.__class__ by extensions
855 ui.__class__ = lui.__class__
869 ui.__class__ = lui.__class__
856
870
857 # (uisetup and extsetup are handled in extensions.loadall)
871 # (uisetup and extsetup are handled in extensions.loadall)
858
872
859 # (reposetup is handled in hg.repository)
873 # (reposetup is handled in hg.repository)
860
874
861 addaliases(lui, commands.table)
875 addaliases(lui, commands.table)
862
876
863 # All aliases and commands are completely defined, now.
877 # All aliases and commands are completely defined, now.
864 # Check abbreviation/ambiguity of shell alias.
878 # Check abbreviation/ambiguity of shell alias.
865 shellaliasfn = _checkshellalias(lui, ui, args)
879 shellaliasfn = _checkshellalias(lui, ui, args)
866 if shellaliasfn:
880 if shellaliasfn:
867 return shellaliasfn()
881 return shellaliasfn()
868
882
869 # check for fallback encoding
883 # check for fallback encoding
870 fallback = lui.config('ui', 'fallbackencoding')
884 fallback = lui.config('ui', 'fallbackencoding')
871 if fallback:
885 if fallback:
872 encoding.fallbackencoding = fallback
886 encoding.fallbackencoding = fallback
873
887
874 fullargs = args
888 fullargs = args
875 cmd, func, args, options, cmdoptions = _parse(lui, args)
889 cmd, func, args, options, cmdoptions = _parse(lui, args)
876
890
877 if options["config"]:
891 if options["config"] != req.earlyoptions["config"]:
878 raise error.Abort(_("option --config may not be abbreviated!"))
892 raise error.Abort(_("option --config may not be abbreviated!"))
879 if options["cwd"]:
893 if options["cwd"] != req.earlyoptions["cwd"]:
880 raise error.Abort(_("option --cwd may not be abbreviated!"))
894 raise error.Abort(_("option --cwd may not be abbreviated!"))
881 if options["repository"]:
895 if options["repository"] != req.earlyoptions["repository"]:
882 raise error.Abort(_(
896 raise error.Abort(_(
883 "option -R has to be separated from other options (e.g. not "
897 "option -R has to be separated from other options (e.g. not "
884 "-qR) and --repository may only be abbreviated as --repo!"))
898 "-qR) and --repository may only be abbreviated as --repo!"))
885 if options["debugger"] != req.earlyoptions["debugger"]:
899 if options["debugger"] != req.earlyoptions["debugger"]:
886 raise error.Abort(_("option --debugger may not be abbreviated!"))
900 raise error.Abort(_("option --debugger may not be abbreviated!"))
887 # don't validate --profile/--traceback, which can be enabled from now
901 # don't validate --profile/--traceback, which can be enabled from now
888
902
889 if options["encoding"]:
903 if options["encoding"]:
890 encoding.encoding = options["encoding"]
904 encoding.encoding = options["encoding"]
891 if options["encodingmode"]:
905 if options["encodingmode"]:
892 encoding.encodingmode = options["encodingmode"]
906 encoding.encodingmode = options["encodingmode"]
893 if options["time"]:
907 if options["time"]:
894 def get_times():
908 def get_times():
895 t = os.times()
909 t = os.times()
896 if t[4] == 0.0:
910 if t[4] == 0.0:
897 # Windows leaves this as zero, so use time.clock()
911 # Windows leaves this as zero, so use time.clock()
898 t = (t[0], t[1], t[2], t[3], time.clock())
912 t = (t[0], t[1], t[2], t[3], time.clock())
899 return t
913 return t
900 s = get_times()
914 s = get_times()
901 def print_time():
915 def print_time():
902 t = get_times()
916 t = get_times()
903 ui.warn(
917 ui.warn(
904 _("time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
918 _("time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
905 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
919 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
906 ui.atexit(print_time)
920 ui.atexit(print_time)
907 if options["profile"]:
921 if options["profile"]:
908 profiler.start()
922 profiler.start()
909
923
910 if options['verbose'] or options['debug'] or options['quiet']:
924 if options['verbose'] or options['debug'] or options['quiet']:
911 for opt in ('verbose', 'debug', 'quiet'):
925 for opt in ('verbose', 'debug', 'quiet'):
912 val = str(bool(options[opt]))
926 val = str(bool(options[opt]))
913 if pycompat.ispy3:
927 if pycompat.ispy3:
914 val = val.encode('ascii')
928 val = val.encode('ascii')
915 for ui_ in uis:
929 for ui_ in uis:
916 ui_.setconfig('ui', opt, val, '--' + opt)
930 ui_.setconfig('ui', opt, val, '--' + opt)
917
931
918 if options['traceback']:
932 if options['traceback']:
919 for ui_ in uis:
933 for ui_ in uis:
920 ui_.setconfig('ui', 'traceback', 'on', '--traceback')
934 ui_.setconfig('ui', 'traceback', 'on', '--traceback')
921
935
922 if options['noninteractive']:
936 if options['noninteractive']:
923 for ui_ in uis:
937 for ui_ in uis:
924 ui_.setconfig('ui', 'interactive', 'off', '-y')
938 ui_.setconfig('ui', 'interactive', 'off', '-y')
925
939
926 if cmdoptions.get('insecure', False):
940 if cmdoptions.get('insecure', False):
927 for ui_ in uis:
941 for ui_ in uis:
928 ui_.insecureconnections = True
942 ui_.insecureconnections = True
929
943
930 # setup color handling before pager, because setting up pager
944 # setup color handling before pager, because setting up pager
931 # might cause incorrect console information
945 # might cause incorrect console information
932 coloropt = options['color']
946 coloropt = options['color']
933 for ui_ in uis:
947 for ui_ in uis:
934 if coloropt:
948 if coloropt:
935 ui_.setconfig('ui', 'color', coloropt, '--color')
949 ui_.setconfig('ui', 'color', coloropt, '--color')
936 color.setup(ui_)
950 color.setup(ui_)
937
951
938 if util.parsebool(options['pager']):
952 if util.parsebool(options['pager']):
939 # ui.pager() expects 'internal-always-' prefix in this case
953 # ui.pager() expects 'internal-always-' prefix in this case
940 ui.pager('internal-always-' + cmd)
954 ui.pager('internal-always-' + cmd)
941 elif options['pager'] != 'auto':
955 elif options['pager'] != 'auto':
942 for ui_ in uis:
956 for ui_ in uis:
943 ui_.disablepager()
957 ui_.disablepager()
944
958
945 if options['version']:
959 if options['version']:
946 return commands.version_(ui)
960 return commands.version_(ui)
947 if options['help']:
961 if options['help']:
948 return commands.help_(ui, cmd, command=cmd is not None)
962 return commands.help_(ui, cmd, command=cmd is not None)
949 elif not cmd:
963 elif not cmd:
950 return commands.help_(ui, 'shortlist')
964 return commands.help_(ui, 'shortlist')
951
965
952 repo = None
966 repo = None
953 cmdpats = args[:]
967 cmdpats = args[:]
954 if not func.norepo:
968 if not func.norepo:
955 # use the repo from the request only if we don't have -R
969 # use the repo from the request only if we don't have -R
956 if not rpath and not cwd:
970 if not rpath and not cwd:
957 repo = req.repo
971 repo = req.repo
958
972
959 if repo:
973 if repo:
960 # set the descriptors of the repo ui to those of ui
974 # set the descriptors of the repo ui to those of ui
961 repo.ui.fin = ui.fin
975 repo.ui.fin = ui.fin
962 repo.ui.fout = ui.fout
976 repo.ui.fout = ui.fout
963 repo.ui.ferr = ui.ferr
977 repo.ui.ferr = ui.ferr
964 else:
978 else:
965 try:
979 try:
966 repo = hg.repository(ui, path=path,
980 repo = hg.repository(ui, path=path,
967 presetupfuncs=req.prereposetups)
981 presetupfuncs=req.prereposetups)
968 if not repo.local():
982 if not repo.local():
969 raise error.Abort(_("repository '%s' is not local")
983 raise error.Abort(_("repository '%s' is not local")
970 % path)
984 % path)
971 repo.ui.setconfig("bundle", "mainreporoot", repo.root,
985 repo.ui.setconfig("bundle", "mainreporoot", repo.root,
972 'repo')
986 'repo')
973 except error.RequirementError:
987 except error.RequirementError:
974 raise
988 raise
975 except error.RepoError:
989 except error.RepoError:
976 if rpath: # invalid -R path
990 if rpath: # invalid -R path
977 raise
991 raise
978 if not func.optionalrepo:
992 if not func.optionalrepo:
979 if func.inferrepo and args and not path:
993 if func.inferrepo and args and not path:
980 # try to infer -R from command args
994 # try to infer -R from command args
981 repos = map(cmdutil.findrepo, args)
995 repos = map(cmdutil.findrepo, args)
982 guess = repos[0]
996 guess = repos[0]
983 if guess and repos.count(guess) == len(repos):
997 if guess and repos.count(guess) == len(repos):
984 req.args = ['--repository', guess] + fullargs
998 req.args = ['--repository', guess] + fullargs
985 return _dispatch(req)
999 return _dispatch(req)
986 if not path:
1000 if not path:
987 raise error.RepoError(_("no repository found in"
1001 raise error.RepoError(_("no repository found in"
988 " '%s' (.hg not found)")
1002 " '%s' (.hg not found)")
989 % pycompat.getcwd())
1003 % pycompat.getcwd())
990 raise
1004 raise
991 if repo:
1005 if repo:
992 ui = repo.ui
1006 ui = repo.ui
993 if options['hidden']:
1007 if options['hidden']:
994 repo = repo.unfiltered()
1008 repo = repo.unfiltered()
995 args.insert(0, repo)
1009 args.insert(0, repo)
996 elif rpath:
1010 elif rpath:
997 ui.warn(_("warning: --repository ignored\n"))
1011 ui.warn(_("warning: --repository ignored\n"))
998
1012
999 msg = _formatargs(fullargs)
1013 msg = _formatargs(fullargs)
1000 ui.log("command", '%s\n', msg)
1014 ui.log("command", '%s\n', msg)
1001 strcmdopt = pycompat.strkwargs(cmdoptions)
1015 strcmdopt = pycompat.strkwargs(cmdoptions)
1002 d = lambda: util.checksignature(func)(ui, *args, **strcmdopt)
1016 d = lambda: util.checksignature(func)(ui, *args, **strcmdopt)
1003 try:
1017 try:
1004 return runcommand(lui, repo, cmd, fullargs, ui, options, d,
1018 return runcommand(lui, repo, cmd, fullargs, ui, options, d,
1005 cmdpats, cmdoptions)
1019 cmdpats, cmdoptions)
1006 finally:
1020 finally:
1007 if repo and repo != req.repo:
1021 if repo and repo != req.repo:
1008 repo.close()
1022 repo.close()
1009
1023
1010 def _runcommand(ui, options, cmd, cmdfunc):
1024 def _runcommand(ui, options, cmd, cmdfunc):
1011 """Run a command function, possibly with profiling enabled."""
1025 """Run a command function, possibly with profiling enabled."""
1012 try:
1026 try:
1013 return cmdfunc()
1027 return cmdfunc()
1014 except error.SignatureError:
1028 except error.SignatureError:
1015 raise error.CommandError(cmd, _('invalid arguments'))
1029 raise error.CommandError(cmd, _('invalid arguments'))
1016
1030
1017 def _exceptionwarning(ui):
1031 def _exceptionwarning(ui):
1018 """Produce a warning message for the current active exception"""
1032 """Produce a warning message for the current active exception"""
1019
1033
1020 # For compatibility checking, we discard the portion of the hg
1034 # For compatibility checking, we discard the portion of the hg
1021 # version after the + on the assumption that if a "normal
1035 # version after the + on the assumption that if a "normal
1022 # user" is running a build with a + in it the packager
1036 # user" is running a build with a + in it the packager
1023 # probably built from fairly close to a tag and anyone with a
1037 # probably built from fairly close to a tag and anyone with a
1024 # 'make local' copy of hg (where the version number can be out
1038 # 'make local' copy of hg (where the version number can be out
1025 # of date) will be clueful enough to notice the implausible
1039 # of date) will be clueful enough to notice the implausible
1026 # version number and try updating.
1040 # version number and try updating.
1027 ct = util.versiontuple(n=2)
1041 ct = util.versiontuple(n=2)
1028 worst = None, ct, ''
1042 worst = None, ct, ''
1029 if ui.config('ui', 'supportcontact') is None:
1043 if ui.config('ui', 'supportcontact') is None:
1030 for name, mod in extensions.extensions():
1044 for name, mod in extensions.extensions():
1031 testedwith = getattr(mod, 'testedwith', '')
1045 testedwith = getattr(mod, 'testedwith', '')
1032 if pycompat.ispy3 and isinstance(testedwith, str):
1046 if pycompat.ispy3 and isinstance(testedwith, str):
1033 testedwith = testedwith.encode(u'utf-8')
1047 testedwith = testedwith.encode(u'utf-8')
1034 report = getattr(mod, 'buglink', _('the extension author.'))
1048 report = getattr(mod, 'buglink', _('the extension author.'))
1035 if not testedwith.strip():
1049 if not testedwith.strip():
1036 # We found an untested extension. It's likely the culprit.
1050 # We found an untested extension. It's likely the culprit.
1037 worst = name, 'unknown', report
1051 worst = name, 'unknown', report
1038 break
1052 break
1039
1053
1040 # Never blame on extensions bundled with Mercurial.
1054 # Never blame on extensions bundled with Mercurial.
1041 if extensions.ismoduleinternal(mod):
1055 if extensions.ismoduleinternal(mod):
1042 continue
1056 continue
1043
1057
1044 tested = [util.versiontuple(t, 2) for t in testedwith.split()]
1058 tested = [util.versiontuple(t, 2) for t in testedwith.split()]
1045 if ct in tested:
1059 if ct in tested:
1046 continue
1060 continue
1047
1061
1048 lower = [t for t in tested if t < ct]
1062 lower = [t for t in tested if t < ct]
1049 nearest = max(lower or tested)
1063 nearest = max(lower or tested)
1050 if worst[0] is None or nearest < worst[1]:
1064 if worst[0] is None or nearest < worst[1]:
1051 worst = name, nearest, report
1065 worst = name, nearest, report
1052 if worst[0] is not None:
1066 if worst[0] is not None:
1053 name, testedwith, report = worst
1067 name, testedwith, report = worst
1054 if not isinstance(testedwith, (bytes, str)):
1068 if not isinstance(testedwith, (bytes, str)):
1055 testedwith = '.'.join([str(c) for c in testedwith])
1069 testedwith = '.'.join([str(c) for c in testedwith])
1056 warning = (_('** Unknown exception encountered with '
1070 warning = (_('** Unknown exception encountered with '
1057 'possibly-broken third-party extension %s\n'
1071 'possibly-broken third-party extension %s\n'
1058 '** which supports versions %s of Mercurial.\n'
1072 '** which supports versions %s of Mercurial.\n'
1059 '** Please disable %s and try your action again.\n'
1073 '** Please disable %s and try your action again.\n'
1060 '** If that fixes the bug please report it to %s\n')
1074 '** If that fixes the bug please report it to %s\n')
1061 % (name, testedwith, name, report))
1075 % (name, testedwith, name, report))
1062 else:
1076 else:
1063 bugtracker = ui.config('ui', 'supportcontact')
1077 bugtracker = ui.config('ui', 'supportcontact')
1064 if bugtracker is None:
1078 if bugtracker is None:
1065 bugtracker = _("https://mercurial-scm.org/wiki/BugTracker")
1079 bugtracker = _("https://mercurial-scm.org/wiki/BugTracker")
1066 warning = (_("** unknown exception encountered, "
1080 warning = (_("** unknown exception encountered, "
1067 "please report by visiting\n** ") + bugtracker + '\n')
1081 "please report by visiting\n** ") + bugtracker + '\n')
1068 if pycompat.ispy3:
1082 if pycompat.ispy3:
1069 sysversion = sys.version.encode(u'utf-8')
1083 sysversion = sys.version.encode(u'utf-8')
1070 else:
1084 else:
1071 sysversion = sys.version
1085 sysversion = sys.version
1072 sysversion = sysversion.replace('\n', '')
1086 sysversion = sysversion.replace('\n', '')
1073 warning += ((_("** Python %s\n") % sysversion) +
1087 warning += ((_("** Python %s\n") % sysversion) +
1074 (_("** Mercurial Distributed SCM (version %s)\n") %
1088 (_("** Mercurial Distributed SCM (version %s)\n") %
1075 util.version()) +
1089 util.version()) +
1076 (_("** Extensions loaded: %s\n") %
1090 (_("** Extensions loaded: %s\n") %
1077 ", ".join([x[0] for x in extensions.extensions()])))
1091 ", ".join([x[0] for x in extensions.extensions()])))
1078 return warning
1092 return warning
1079
1093
1080 def handlecommandexception(ui):
1094 def handlecommandexception(ui):
1081 """Produce a warning message for broken commands
1095 """Produce a warning message for broken commands
1082
1096
1083 Called when handling an exception; the exception is reraised if
1097 Called when handling an exception; the exception is reraised if
1084 this function returns False, ignored otherwise.
1098 this function returns False, ignored otherwise.
1085 """
1099 """
1086 warning = _exceptionwarning(ui)
1100 warning = _exceptionwarning(ui)
1087 ui.log("commandexception", "%s\n%s\n", warning, traceback.format_exc())
1101 ui.log("commandexception", "%s\n%s\n", warning, traceback.format_exc())
1088 ui.warn(warning)
1102 ui.warn(warning)
1089 return False # re-raise the exception
1103 return False # re-raise the exception
@@ -1,343 +1,343 b''
1 setup
1 setup
2 $ cat >> $HGRCPATH <<EOF
2 $ cat >> $HGRCPATH <<EOF
3 > [extensions]
3 > [extensions]
4 > blackbox=
4 > blackbox=
5 > mock=$TESTDIR/mockblackbox.py
5 > mock=$TESTDIR/mockblackbox.py
6 > mq=
6 > mq=
7 > [alias]
7 > [alias]
8 > confuse = log --limit 3
8 > confuse = log --limit 3
9 > so-confusing = confuse --style compact
9 > so-confusing = confuse --style compact
10 > EOF
10 > EOF
11 $ hg init blackboxtest
11 $ hg init blackboxtest
12 $ cd blackboxtest
12 $ cd blackboxtest
13
13
14 command, exit codes, and duration
14 command, exit codes, and duration
15
15
16 $ echo a > a
16 $ echo a > a
17 $ hg add a
17 $ hg add a
18 $ hg blackbox --config blackbox.dirty=True
18 $ hg blackbox --config blackbox.dirty=True
19 1970/01/01 00:00:00 bob @0000000000000000000000000000000000000000 (5000)> init blackboxtest exited 0 after * seconds (glob)
19 1970/01/01 00:00:00 bob @0000000000000000000000000000000000000000 (5000)> init blackboxtest exited 0 after * seconds (glob)
20 1970/01/01 00:00:00 bob @0000000000000000000000000000000000000000 (5000)> add a
20 1970/01/01 00:00:00 bob @0000000000000000000000000000000000000000 (5000)> add a
21 1970/01/01 00:00:00 bob @0000000000000000000000000000000000000000 (5000)> add a exited 0 after * seconds (glob)
21 1970/01/01 00:00:00 bob @0000000000000000000000000000000000000000 (5000)> add a exited 0 after * seconds (glob)
22 1970/01/01 00:00:00 bob @0000000000000000000000000000000000000000+ (5000)> blackbox
22 1970/01/01 00:00:00 bob @0000000000000000000000000000000000000000+ (5000)> blackbox --config *blackbox.dirty=True* (glob)
23
23
24 alias expansion is logged
24 alias expansion is logged
25 $ rm ./.hg/blackbox.log
25 $ rm ./.hg/blackbox.log
26 $ hg confuse
26 $ hg confuse
27 $ hg blackbox
27 $ hg blackbox
28 1970/01/01 00:00:00 bob @0000000000000000000000000000000000000000 (5000)> confuse
28 1970/01/01 00:00:00 bob @0000000000000000000000000000000000000000 (5000)> confuse
29 1970/01/01 00:00:00 bob @0000000000000000000000000000000000000000 (5000)> alias 'confuse' expands to 'log --limit 3'
29 1970/01/01 00:00:00 bob @0000000000000000000000000000000000000000 (5000)> alias 'confuse' expands to 'log --limit 3'
30 1970/01/01 00:00:00 bob @0000000000000000000000000000000000000000 (5000)> confuse exited 0 after * seconds (glob)
30 1970/01/01 00:00:00 bob @0000000000000000000000000000000000000000 (5000)> confuse exited 0 after * seconds (glob)
31 1970/01/01 00:00:00 bob @0000000000000000000000000000000000000000 (5000)> blackbox
31 1970/01/01 00:00:00 bob @0000000000000000000000000000000000000000 (5000)> blackbox
32
32
33 recursive aliases work correctly
33 recursive aliases work correctly
34 $ rm ./.hg/blackbox.log
34 $ rm ./.hg/blackbox.log
35 $ hg so-confusing
35 $ hg so-confusing
36 $ hg blackbox
36 $ hg blackbox
37 1970/01/01 00:00:00 bob @0000000000000000000000000000000000000000 (5000)> so-confusing
37 1970/01/01 00:00:00 bob @0000000000000000000000000000000000000000 (5000)> so-confusing
38 1970/01/01 00:00:00 bob @0000000000000000000000000000000000000000 (5000)> alias 'so-confusing' expands to 'confuse --style compact'
38 1970/01/01 00:00:00 bob @0000000000000000000000000000000000000000 (5000)> alias 'so-confusing' expands to 'confuse --style compact'
39 1970/01/01 00:00:00 bob @0000000000000000000000000000000000000000 (5000)> alias 'confuse' expands to 'log --limit 3'
39 1970/01/01 00:00:00 bob @0000000000000000000000000000000000000000 (5000)> alias 'confuse' expands to 'log --limit 3'
40 1970/01/01 00:00:00 bob @0000000000000000000000000000000000000000 (5000)> so-confusing exited 0 after * seconds (glob)
40 1970/01/01 00:00:00 bob @0000000000000000000000000000000000000000 (5000)> so-confusing exited 0 after * seconds (glob)
41 1970/01/01 00:00:00 bob @0000000000000000000000000000000000000000 (5000)> blackbox
41 1970/01/01 00:00:00 bob @0000000000000000000000000000000000000000 (5000)> blackbox
42
42
43 incoming change tracking
43 incoming change tracking
44
44
45 create two heads to verify that we only see one change in the log later
45 create two heads to verify that we only see one change in the log later
46 $ hg commit -ma
46 $ hg commit -ma
47 $ hg up null
47 $ hg up null
48 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
48 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
49 $ echo b > b
49 $ echo b > b
50 $ hg commit -Amb
50 $ hg commit -Amb
51 adding b
51 adding b
52 created new head
52 created new head
53
53
54 clone, commit, pull
54 clone, commit, pull
55 $ hg clone . ../blackboxtest2
55 $ hg clone . ../blackboxtest2
56 updating to branch default
56 updating to branch default
57 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
57 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
58 $ echo c > c
58 $ echo c > c
59 $ hg commit -Amc
59 $ hg commit -Amc
60 adding c
60 adding c
61 $ cd ../blackboxtest2
61 $ cd ../blackboxtest2
62 $ hg pull
62 $ hg pull
63 pulling from $TESTTMP/blackboxtest (glob)
63 pulling from $TESTTMP/blackboxtest (glob)
64 searching for changes
64 searching for changes
65 adding changesets
65 adding changesets
66 adding manifests
66 adding manifests
67 adding file changes
67 adding file changes
68 added 1 changesets with 1 changes to 1 files
68 added 1 changesets with 1 changes to 1 files
69 new changesets d02f48003e62
69 new changesets d02f48003e62
70 (run 'hg update' to get a working copy)
70 (run 'hg update' to get a working copy)
71 $ hg blackbox -l 6
71 $ hg blackbox -l 6
72 1970/01/01 00:00:00 bob @6563da9dcf87b1949716e38ff3e3dfaa3198eb06 (5000)> pull
72 1970/01/01 00:00:00 bob @6563da9dcf87b1949716e38ff3e3dfaa3198eb06 (5000)> pull
73 1970/01/01 00:00:00 bob @6563da9dcf87b1949716e38ff3e3dfaa3198eb06 (5000)> updated served branch cache in * seconds (glob)
73 1970/01/01 00:00:00 bob @6563da9dcf87b1949716e38ff3e3dfaa3198eb06 (5000)> updated served branch cache in * seconds (glob)
74 1970/01/01 00:00:00 bob @6563da9dcf87b1949716e38ff3e3dfaa3198eb06 (5000)> wrote served branch cache with 1 labels and 2 nodes
74 1970/01/01 00:00:00 bob @6563da9dcf87b1949716e38ff3e3dfaa3198eb06 (5000)> wrote served branch cache with 1 labels and 2 nodes
75 1970/01/01 00:00:00 bob @6563da9dcf87b1949716e38ff3e3dfaa3198eb06 (5000)> 1 incoming changes - new heads: d02f48003e62
75 1970/01/01 00:00:00 bob @6563da9dcf87b1949716e38ff3e3dfaa3198eb06 (5000)> 1 incoming changes - new heads: d02f48003e62
76 1970/01/01 00:00:00 bob @6563da9dcf87b1949716e38ff3e3dfaa3198eb06 (5000)> pull exited 0 after * seconds (glob)
76 1970/01/01 00:00:00 bob @6563da9dcf87b1949716e38ff3e3dfaa3198eb06 (5000)> pull exited 0 after * seconds (glob)
77 1970/01/01 00:00:00 bob @6563da9dcf87b1949716e38ff3e3dfaa3198eb06 (5000)> blackbox -l 6
77 1970/01/01 00:00:00 bob @6563da9dcf87b1949716e38ff3e3dfaa3198eb06 (5000)> blackbox -l 6
78
78
79 we must not cause a failure if we cannot write to the log
79 we must not cause a failure if we cannot write to the log
80
80
81 $ hg rollback
81 $ hg rollback
82 repository tip rolled back to revision 1 (undo pull)
82 repository tip rolled back to revision 1 (undo pull)
83
83
84 $ mv .hg/blackbox.log .hg/blackbox.log-
84 $ mv .hg/blackbox.log .hg/blackbox.log-
85 $ mkdir .hg/blackbox.log
85 $ mkdir .hg/blackbox.log
86 $ hg --debug incoming
86 $ hg --debug incoming
87 warning: cannot write to blackbox.log: * (glob)
87 warning: cannot write to blackbox.log: * (glob)
88 comparing with $TESTTMP/blackboxtest (glob)
88 comparing with $TESTTMP/blackboxtest (glob)
89 query 1; heads
89 query 1; heads
90 searching for changes
90 searching for changes
91 all local heads known remotely
91 all local heads known remotely
92 changeset: 2:d02f48003e62c24e2659d97d30f2a83abe5d5d51
92 changeset: 2:d02f48003e62c24e2659d97d30f2a83abe5d5d51
93 tag: tip
93 tag: tip
94 phase: draft
94 phase: draft
95 parent: 1:6563da9dcf87b1949716e38ff3e3dfaa3198eb06
95 parent: 1:6563da9dcf87b1949716e38ff3e3dfaa3198eb06
96 parent: -1:0000000000000000000000000000000000000000
96 parent: -1:0000000000000000000000000000000000000000
97 manifest: 2:ab9d46b053ebf45b7996f2922b9893ff4b63d892
97 manifest: 2:ab9d46b053ebf45b7996f2922b9893ff4b63d892
98 user: test
98 user: test
99 date: Thu Jan 01 00:00:00 1970 +0000
99 date: Thu Jan 01 00:00:00 1970 +0000
100 files+: c
100 files+: c
101 extra: branch=default
101 extra: branch=default
102 description:
102 description:
103 c
103 c
104
104
105
105
106 $ hg pull
106 $ hg pull
107 pulling from $TESTTMP/blackboxtest (glob)
107 pulling from $TESTTMP/blackboxtest (glob)
108 searching for changes
108 searching for changes
109 adding changesets
109 adding changesets
110 adding manifests
110 adding manifests
111 adding file changes
111 adding file changes
112 added 1 changesets with 1 changes to 1 files
112 added 1 changesets with 1 changes to 1 files
113 new changesets d02f48003e62
113 new changesets d02f48003e62
114 (run 'hg update' to get a working copy)
114 (run 'hg update' to get a working copy)
115
115
116 a failure reading from the log is fatal
116 a failure reading from the log is fatal
117
117
118 $ hg blackbox -l 3
118 $ hg blackbox -l 3
119 abort: *$TESTTMP/blackboxtest2/.hg/blackbox.log* (glob)
119 abort: *$TESTTMP/blackboxtest2/.hg/blackbox.log* (glob)
120 [255]
120 [255]
121
121
122 $ rmdir .hg/blackbox.log
122 $ rmdir .hg/blackbox.log
123 $ mv .hg/blackbox.log- .hg/blackbox.log
123 $ mv .hg/blackbox.log- .hg/blackbox.log
124
124
125 backup bundles get logged
125 backup bundles get logged
126
126
127 $ touch d
127 $ touch d
128 $ hg commit -Amd
128 $ hg commit -Amd
129 adding d
129 adding d
130 created new head
130 created new head
131 $ hg strip tip
131 $ hg strip tip
132 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
132 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
133 saved backup bundle to $TESTTMP/blackboxtest2/.hg/strip-backup/*-backup.hg (glob)
133 saved backup bundle to $TESTTMP/blackboxtest2/.hg/strip-backup/*-backup.hg (glob)
134 $ hg blackbox -l 6
134 $ hg blackbox -l 6
135 1970/01/01 00:00:00 bob @73f6ee326b27d820b0472f1a825e3a50f3dc489b (5000)> strip tip
135 1970/01/01 00:00:00 bob @73f6ee326b27d820b0472f1a825e3a50f3dc489b (5000)> strip tip
136 1970/01/01 00:00:00 bob @6563da9dcf87b1949716e38ff3e3dfaa3198eb06 (5000)> saved backup bundle to $TESTTMP/blackboxtest2/.hg/strip-backup/73f6ee326b27-7612e004-backup.hg (glob)
136 1970/01/01 00:00:00 bob @6563da9dcf87b1949716e38ff3e3dfaa3198eb06 (5000)> saved backup bundle to $TESTTMP/blackboxtest2/.hg/strip-backup/73f6ee326b27-7612e004-backup.hg (glob)
137 1970/01/01 00:00:00 bob @6563da9dcf87b1949716e38ff3e3dfaa3198eb06 (5000)> updated base branch cache in * seconds (glob)
137 1970/01/01 00:00:00 bob @6563da9dcf87b1949716e38ff3e3dfaa3198eb06 (5000)> updated base branch cache in * seconds (glob)
138 1970/01/01 00:00:00 bob @6563da9dcf87b1949716e38ff3e3dfaa3198eb06 (5000)> wrote base branch cache with 1 labels and 2 nodes
138 1970/01/01 00:00:00 bob @6563da9dcf87b1949716e38ff3e3dfaa3198eb06 (5000)> wrote base branch cache with 1 labels and 2 nodes
139 1970/01/01 00:00:00 bob @6563da9dcf87b1949716e38ff3e3dfaa3198eb06 (5000)> strip tip exited 0 after * seconds (glob)
139 1970/01/01 00:00:00 bob @6563da9dcf87b1949716e38ff3e3dfaa3198eb06 (5000)> strip tip exited 0 after * seconds (glob)
140 1970/01/01 00:00:00 bob @6563da9dcf87b1949716e38ff3e3dfaa3198eb06 (5000)> blackbox -l 6
140 1970/01/01 00:00:00 bob @6563da9dcf87b1949716e38ff3e3dfaa3198eb06 (5000)> blackbox -l 6
141
141
142 extension and python hooks - use the eol extension for a pythonhook
142 extension and python hooks - use the eol extension for a pythonhook
143
143
144 $ echo '[extensions]' >> .hg/hgrc
144 $ echo '[extensions]' >> .hg/hgrc
145 $ echo 'eol=' >> .hg/hgrc
145 $ echo 'eol=' >> .hg/hgrc
146 $ echo '[hooks]' >> .hg/hgrc
146 $ echo '[hooks]' >> .hg/hgrc
147 $ echo 'update = echo hooked' >> .hg/hgrc
147 $ echo 'update = echo hooked' >> .hg/hgrc
148 $ hg update
148 $ hg update
149 The fsmonitor extension is incompatible with the eol extension and has been disabled. (fsmonitor !)
149 The fsmonitor extension is incompatible with the eol extension and has been disabled. (fsmonitor !)
150 hooked
150 hooked
151 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
151 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
152 updated to "d02f48003e62: c"
152 updated to "d02f48003e62: c"
153 1 other heads for branch "default"
153 1 other heads for branch "default"
154 $ cat >> .hg/hgrc <<EOF
154 $ cat >> .hg/hgrc <<EOF
155 > [extensions]
155 > [extensions]
156 > # disable eol, because it is not needed for subsequent tests
156 > # disable eol, because it is not needed for subsequent tests
157 > # (in addition, keeping it requires extra care for fsmonitor)
157 > # (in addition, keeping it requires extra care for fsmonitor)
158 > eol=!
158 > eol=!
159 > EOF
159 > EOF
160 $ hg blackbox -l 6
160 $ hg blackbox -l 6
161 1970/01/01 00:00:00 bob @6563da9dcf87b1949716e38ff3e3dfaa3198eb06 (5000)> update (no-chg !)
161 1970/01/01 00:00:00 bob @6563da9dcf87b1949716e38ff3e3dfaa3198eb06 (5000)> update (no-chg !)
162 1970/01/01 00:00:00 bob @6563da9dcf87b1949716e38ff3e3dfaa3198eb06 (5000)> writing .hg/cache/tags2-visible with 0 tags
162 1970/01/01 00:00:00 bob @6563da9dcf87b1949716e38ff3e3dfaa3198eb06 (5000)> writing .hg/cache/tags2-visible with 0 tags
163 1970/01/01 00:00:00 bob @6563da9dcf87b1949716e38ff3e3dfaa3198eb06 (5000)> pythonhook-preupdate: hgext.eol.preupdate finished in * seconds (glob)
163 1970/01/01 00:00:00 bob @6563da9dcf87b1949716e38ff3e3dfaa3198eb06 (5000)> pythonhook-preupdate: hgext.eol.preupdate finished in * seconds (glob)
164 1970/01/01 00:00:00 bob @d02f48003e62c24e2659d97d30f2a83abe5d5d51 (5000)> exthook-update: echo hooked finished in * seconds (glob)
164 1970/01/01 00:00:00 bob @d02f48003e62c24e2659d97d30f2a83abe5d5d51 (5000)> exthook-update: echo hooked finished in * seconds (glob)
165 1970/01/01 00:00:00 bob @d02f48003e62c24e2659d97d30f2a83abe5d5d51 (5000)> update exited 0 after * seconds (glob)
165 1970/01/01 00:00:00 bob @d02f48003e62c24e2659d97d30f2a83abe5d5d51 (5000)> update exited 0 after * seconds (glob)
166 1970/01/01 00:00:00 bob @d02f48003e62c24e2659d97d30f2a83abe5d5d51 (5000)> serve --cmdserver chgunix --address $TESTTMP.chgsock/server.* --daemon-postexec 'chdir:/' (glob) (chg !)
166 1970/01/01 00:00:00 bob @d02f48003e62c24e2659d97d30f2a83abe5d5d51 (5000)> serve --cmdserver chgunix --address $TESTTMP.chgsock/server.* --daemon-postexec 'chdir:/' (glob) (chg !)
167 1970/01/01 00:00:00 bob @d02f48003e62c24e2659d97d30f2a83abe5d5d51 (5000)> blackbox -l 6
167 1970/01/01 00:00:00 bob @d02f48003e62c24e2659d97d30f2a83abe5d5d51 (5000)> blackbox -l 6
168
168
169 log rotation
169 log rotation
170
170
171 $ echo '[blackbox]' >> .hg/hgrc
171 $ echo '[blackbox]' >> .hg/hgrc
172 $ echo 'maxsize = 20 b' >> .hg/hgrc
172 $ echo 'maxsize = 20 b' >> .hg/hgrc
173 $ echo 'maxfiles = 3' >> .hg/hgrc
173 $ echo 'maxfiles = 3' >> .hg/hgrc
174 $ hg status
174 $ hg status
175 $ hg status
175 $ hg status
176 $ hg status
176 $ hg status
177 $ hg tip -q
177 $ hg tip -q
178 2:d02f48003e62
178 2:d02f48003e62
179 $ ls .hg/blackbox.log*
179 $ ls .hg/blackbox.log*
180 .hg/blackbox.log
180 .hg/blackbox.log
181 .hg/blackbox.log.1
181 .hg/blackbox.log.1
182 .hg/blackbox.log.2
182 .hg/blackbox.log.2
183 $ cd ..
183 $ cd ..
184
184
185 $ hg init blackboxtest3
185 $ hg init blackboxtest3
186 $ cd blackboxtest3
186 $ cd blackboxtest3
187 $ hg blackbox
187 $ hg blackbox
188 1970/01/01 00:00:00 bob @0000000000000000000000000000000000000000 (5000)> init blackboxtest3 exited 0 after * seconds (glob)
188 1970/01/01 00:00:00 bob @0000000000000000000000000000000000000000 (5000)> init blackboxtest3 exited 0 after * seconds (glob)
189 1970/01/01 00:00:00 bob @0000000000000000000000000000000000000000 (5000)> blackbox
189 1970/01/01 00:00:00 bob @0000000000000000000000000000000000000000 (5000)> blackbox
190 $ mv .hg/blackbox.log .hg/blackbox.log-
190 $ mv .hg/blackbox.log .hg/blackbox.log-
191 $ mkdir .hg/blackbox.log
191 $ mkdir .hg/blackbox.log
192 $ sed -e 's/\(.*test1.*\)/#\1/; s#\(.*commit2.*\)#os.rmdir(".hg/blackbox.log")\
192 $ sed -e 's/\(.*test1.*\)/#\1/; s#\(.*commit2.*\)#os.rmdir(".hg/blackbox.log")\
193 > os.rename(".hg/blackbox.log-", ".hg/blackbox.log")\
193 > os.rename(".hg/blackbox.log-", ".hg/blackbox.log")\
194 > \1#' $TESTDIR/test-dispatch.py > ../test-dispatch.py
194 > \1#' $TESTDIR/test-dispatch.py > ../test-dispatch.py
195 $ $PYTHON $TESTDIR/blackbox-readonly-dispatch.py
195 $ $PYTHON $TESTDIR/blackbox-readonly-dispatch.py
196 running: add foo
196 running: add foo
197 result: 0
197 result: 0
198 running: commit -m commit1 -d 2000-01-01 foo
198 running: commit -m commit1 -d 2000-01-01 foo
199 result: None
199 result: None
200 running: commit -m commit2 -d 2000-01-02 foo
200 running: commit -m commit2 -d 2000-01-02 foo
201 result: None
201 result: None
202 running: log -r 0
202 running: log -r 0
203 changeset: 0:0e4634943879
203 changeset: 0:0e4634943879
204 user: test
204 user: test
205 date: Sat Jan 01 00:00:00 2000 +0000
205 date: Sat Jan 01 00:00:00 2000 +0000
206 summary: commit1
206 summary: commit1
207
207
208 result: None
208 result: None
209 running: log -r tip
209 running: log -r tip
210 changeset: 1:45589e459b2e
210 changeset: 1:45589e459b2e
211 tag: tip
211 tag: tip
212 user: test
212 user: test
213 date: Sun Jan 02 00:00:00 2000 +0000
213 date: Sun Jan 02 00:00:00 2000 +0000
214 summary: commit2
214 summary: commit2
215
215
216 result: None
216 result: None
217 $ hg blackbox
217 $ hg blackbox
218 1970/01/01 00:00:00 bob @0e46349438790c460c5c9f7546bfcd39b267bbd2 (5000)> commit -m commit2 -d 2000-01-02 foo
218 1970/01/01 00:00:00 bob @0e46349438790c460c5c9f7546bfcd39b267bbd2 (5000)> commit -m commit2 -d 2000-01-02 foo
219 1970/01/01 00:00:00 bob @45589e459b2edfbf3dbde7e01f611d2c1e7453d7 (5000)> updated served branch cache in * seconds (glob)
219 1970/01/01 00:00:00 bob @45589e459b2edfbf3dbde7e01f611d2c1e7453d7 (5000)> updated served branch cache in * seconds (glob)
220 1970/01/01 00:00:00 bob @45589e459b2edfbf3dbde7e01f611d2c1e7453d7 (5000)> wrote served branch cache with 1 labels and 1 nodes
220 1970/01/01 00:00:00 bob @45589e459b2edfbf3dbde7e01f611d2c1e7453d7 (5000)> wrote served branch cache with 1 labels and 1 nodes
221 1970/01/01 00:00:00 bob @45589e459b2edfbf3dbde7e01f611d2c1e7453d7 (5000)> commit -m commit2 -d 2000-01-02 foo exited 0 after * seconds (glob)
221 1970/01/01 00:00:00 bob @45589e459b2edfbf3dbde7e01f611d2c1e7453d7 (5000)> commit -m commit2 -d 2000-01-02 foo exited 0 after * seconds (glob)
222 1970/01/01 00:00:00 bob @45589e459b2edfbf3dbde7e01f611d2c1e7453d7 (5000)> log -r 0
222 1970/01/01 00:00:00 bob @45589e459b2edfbf3dbde7e01f611d2c1e7453d7 (5000)> log -r 0
223 1970/01/01 00:00:00 bob @45589e459b2edfbf3dbde7e01f611d2c1e7453d7 (5000)> writing .hg/cache/tags2-visible with 0 tags
223 1970/01/01 00:00:00 bob @45589e459b2edfbf3dbde7e01f611d2c1e7453d7 (5000)> writing .hg/cache/tags2-visible with 0 tags
224 1970/01/01 00:00:00 bob @45589e459b2edfbf3dbde7e01f611d2c1e7453d7 (5000)> log -r 0 exited 0 after * seconds (glob)
224 1970/01/01 00:00:00 bob @45589e459b2edfbf3dbde7e01f611d2c1e7453d7 (5000)> log -r 0 exited 0 after * seconds (glob)
225 1970/01/01 00:00:00 bob @45589e459b2edfbf3dbde7e01f611d2c1e7453d7 (5000)> log -r tip
225 1970/01/01 00:00:00 bob @45589e459b2edfbf3dbde7e01f611d2c1e7453d7 (5000)> log -r tip
226 1970/01/01 00:00:00 bob @45589e459b2edfbf3dbde7e01f611d2c1e7453d7 (5000)> log -r tip exited 0 after * seconds (glob)
226 1970/01/01 00:00:00 bob @45589e459b2edfbf3dbde7e01f611d2c1e7453d7 (5000)> log -r tip exited 0 after * seconds (glob)
227 1970/01/01 00:00:00 bob @45589e459b2edfbf3dbde7e01f611d2c1e7453d7 (5000)> blackbox
227 1970/01/01 00:00:00 bob @45589e459b2edfbf3dbde7e01f611d2c1e7453d7 (5000)> blackbox
228
228
229 Test log recursion from dirty status check
229 Test log recursion from dirty status check
230
230
231 $ cat > ../r.py <<EOF
231 $ cat > ../r.py <<EOF
232 > from mercurial import context, error, extensions
232 > from mercurial import context, error, extensions
233 > x=[False]
233 > x=[False]
234 > def status(orig, *args, **opts):
234 > def status(orig, *args, **opts):
235 > args[0].repo().ui.log("broken", "recursion?")
235 > args[0].repo().ui.log("broken", "recursion?")
236 > return orig(*args, **opts)
236 > return orig(*args, **opts)
237 > def reposetup(ui, repo):
237 > def reposetup(ui, repo):
238 > extensions.wrapfunction(context.basectx, 'status', status)
238 > extensions.wrapfunction(context.basectx, 'status', status)
239 > EOF
239 > EOF
240 $ hg id --config extensions.x=../r.py --config blackbox.dirty=True
240 $ hg id --config extensions.x=../r.py --config blackbox.dirty=True
241 45589e459b2e tip
241 45589e459b2e tip
242
242
243 cleanup
243 cleanup
244 $ cd ..
244 $ cd ..
245
245
246 #if chg
246 #if chg
247
247
248 when using chg, blackbox.log should get rotated correctly
248 when using chg, blackbox.log should get rotated correctly
249
249
250 $ cat > $TESTTMP/noop.py << EOF
250 $ cat > $TESTTMP/noop.py << EOF
251 > from __future__ import absolute_import
251 > from __future__ import absolute_import
252 > import time
252 > import time
253 > from mercurial import registrar, scmutil
253 > from mercurial import registrar, scmutil
254 > cmdtable = {}
254 > cmdtable = {}
255 > command = registrar.command(cmdtable)
255 > command = registrar.command(cmdtable)
256 > @command('noop')
256 > @command('noop')
257 > def noop(ui, repo):
257 > def noop(ui, repo):
258 > pass
258 > pass
259 > EOF
259 > EOF
260
260
261 $ hg init blackbox-chg
261 $ hg init blackbox-chg
262 $ cd blackbox-chg
262 $ cd blackbox-chg
263
263
264 $ cat > .hg/hgrc << EOF
264 $ cat > .hg/hgrc << EOF
265 > [blackbox]
265 > [blackbox]
266 > maxsize = 500B
266 > maxsize = 500B
267 > [extensions]
267 > [extensions]
268 > # extension change forces chg to restart
268 > # extension change forces chg to restart
269 > noop=$TESTTMP/noop.py
269 > noop=$TESTTMP/noop.py
270 > EOF
270 > EOF
271
271
272 $ $PYTHON -c 'print("a" * 400)' > .hg/blackbox.log
272 $ $PYTHON -c 'print("a" * 400)' > .hg/blackbox.log
273 $ chg noop
273 $ chg noop
274 $ chg noop
274 $ chg noop
275 $ chg noop
275 $ chg noop
276 $ chg noop
276 $ chg noop
277 $ chg noop
277 $ chg noop
278
278
279 $ cat > showsize.py << 'EOF'
279 $ cat > showsize.py << 'EOF'
280 > import os, sys
280 > import os, sys
281 > limit = 500
281 > limit = 500
282 > for p in sys.argv[1:]:
282 > for p in sys.argv[1:]:
283 > size = os.stat(p).st_size
283 > size = os.stat(p).st_size
284 > if size >= limit:
284 > if size >= limit:
285 > desc = '>='
285 > desc = '>='
286 > else:
286 > else:
287 > desc = '<'
287 > desc = '<'
288 > print('%s: %s %d' % (p, desc, limit))
288 > print('%s: %s %d' % (p, desc, limit))
289 > EOF
289 > EOF
290
290
291 $ $PYTHON showsize.py .hg/blackbox*
291 $ $PYTHON showsize.py .hg/blackbox*
292 .hg/blackbox.log: < 500
292 .hg/blackbox.log: < 500
293 .hg/blackbox.log.1: >= 500
293 .hg/blackbox.log.1: >= 500
294 .hg/blackbox.log.2: >= 500
294 .hg/blackbox.log.2: >= 500
295
295
296 $ cd ..
296 $ cd ..
297
297
298 With chg, blackbox should not create the log file if the repo is gone
298 With chg, blackbox should not create the log file if the repo is gone
299
299
300 $ hg init repo1
300 $ hg init repo1
301 $ hg --config extensions.a=! -R repo1 log
301 $ hg --config extensions.a=! -R repo1 log
302 $ rm -rf $TESTTMP/repo1
302 $ rm -rf $TESTTMP/repo1
303 $ hg --config extensions.a=! init repo1
303 $ hg --config extensions.a=! init repo1
304
304
305 #endif
305 #endif
306
306
307 blackbox should work if repo.ui.log is not called (issue5518)
307 blackbox should work if repo.ui.log is not called (issue5518)
308
308
309 $ cat > $TESTTMP/raise.py << EOF
309 $ cat > $TESTTMP/raise.py << EOF
310 > from __future__ import absolute_import
310 > from __future__ import absolute_import
311 > from mercurial import registrar, scmutil
311 > from mercurial import registrar, scmutil
312 > cmdtable = {}
312 > cmdtable = {}
313 > command = registrar.command(cmdtable)
313 > command = registrar.command(cmdtable)
314 > @command('raise')
314 > @command('raise')
315 > def raisecmd(*args):
315 > def raisecmd(*args):
316 > raise RuntimeError('raise')
316 > raise RuntimeError('raise')
317 > EOF
317 > EOF
318
318
319 $ cat >> $HGRCPATH << EOF
319 $ cat >> $HGRCPATH << EOF
320 > [blackbox]
320 > [blackbox]
321 > track = commandexception
321 > track = commandexception
322 > [extensions]
322 > [extensions]
323 > raise=$TESTTMP/raise.py
323 > raise=$TESTTMP/raise.py
324 > EOF
324 > EOF
325
325
326 $ hg init $TESTTMP/blackbox-exception-only
326 $ hg init $TESTTMP/blackbox-exception-only
327 $ cd $TESTTMP/blackbox-exception-only
327 $ cd $TESTTMP/blackbox-exception-only
328
328
329 #if chg
329 #if chg
330 (chg exits 255 because it fails to receive an exit code)
330 (chg exits 255 because it fails to receive an exit code)
331 $ hg raise 2>/dev/null
331 $ hg raise 2>/dev/null
332 [255]
332 [255]
333 #else
333 #else
334 (hg exits 1 because Python default exit code for uncaught exception is 1)
334 (hg exits 1 because Python default exit code for uncaught exception is 1)
335 $ hg raise 2>/dev/null
335 $ hg raise 2>/dev/null
336 [1]
336 [1]
337 #endif
337 #endif
338
338
339 $ head -1 .hg/blackbox.log
339 $ head -1 .hg/blackbox.log
340 1970/01/01 00:00:00 bob @0000000000000000000000000000000000000000 (5000)> ** Unknown exception encountered with possibly-broken third-party extension mock
340 1970/01/01 00:00:00 bob @0000000000000000000000000000000000000000 (5000)> ** Unknown exception encountered with possibly-broken third-party extension mock
341 $ tail -2 .hg/blackbox.log
341 $ tail -2 .hg/blackbox.log
342 RuntimeError: raise
342 RuntimeError: raise
343
343
@@ -1,107 +1,163 b''
1 test command parsing and dispatch
1 test command parsing and dispatch
2
2
3 $ hg init a
3 $ hg init a
4 $ cd a
4 $ cd a
5
5
6 Redundant options used to crash (issue436):
6 Redundant options used to crash (issue436):
7 $ hg -v log -v
7 $ hg -v log -v
8 $ hg -v log -v x
8 $ hg -v log -v x
9
9
10 $ echo a > a
10 $ echo a > a
11 $ hg ci -Ama
11 $ hg ci -Ama
12 adding a
12 adding a
13
13
14 Missing arg:
14 Missing arg:
15
15
16 $ hg cat
16 $ hg cat
17 hg cat: invalid arguments
17 hg cat: invalid arguments
18 hg cat [OPTION]... FILE...
18 hg cat [OPTION]... FILE...
19
19
20 output the current or given revision of files
20 output the current or given revision of files
21
21
22 options ([+] can be repeated):
22 options ([+] can be repeated):
23
23
24 -o --output FORMAT print output to file with formatted name
24 -o --output FORMAT print output to file with formatted name
25 -r --rev REV print the given revision
25 -r --rev REV print the given revision
26 --decode apply any matching decode filter
26 --decode apply any matching decode filter
27 -I --include PATTERN [+] include names matching the given patterns
27 -I --include PATTERN [+] include names matching the given patterns
28 -X --exclude PATTERN [+] exclude names matching the given patterns
28 -X --exclude PATTERN [+] exclude names matching the given patterns
29
29
30 (use 'hg cat -h' to show more help)
30 (use 'hg cat -h' to show more help)
31 [255]
31 [255]
32
32
33 Missing parameter for early option:
33 Missing parameter for early option:
34
34
35 $ hg log -R 2>&1 | grep 'hg log'
35 $ hg log -R 2>&1 | grep 'hg log'
36 hg log: option -R requires argument
36 hg log: option -R requires argument
37 hg log [OPTION]... [FILE]
37 hg log [OPTION]... [FILE]
38 (use 'hg log -h' to show more help)
38 (use 'hg log -h' to show more help)
39
39
40 $ hg log -R -- 2>&1 | grep 'hg log'
40 $ hg log -R -- 2>&1 | grep 'hg log'
41 hg log: option -R requires argument
41 hg log: option -R requires argument
42 hg log [OPTION]... [FILE]
42 hg log [OPTION]... [FILE]
43 (use 'hg log -h' to show more help)
43 (use 'hg log -h' to show more help)
44
44
45 Parsing of early options should stop at "--":
45 Parsing of early options should stop at "--":
46
46
47 $ hg cat -- --config=hooks.pre-cat=false
47 $ hg cat -- --config=hooks.pre-cat=false
48 --config=hooks.pre-cat=false: no such file in rev cb9a9f314b8b
48 --config=hooks.pre-cat=false: no such file in rev cb9a9f314b8b
49 [1]
49 [1]
50 $ hg cat -- --debugger
50 $ hg cat -- --debugger
51 --debugger: no such file in rev cb9a9f314b8b
51 --debugger: no such file in rev cb9a9f314b8b
52 [1]
52 [1]
53
53
54 Unparsable form of early options:
54 Unparsable form of early options:
55
55
56 $ hg cat --debugg
56 $ hg cat --debugg
57 abort: option --debugger may not be abbreviated!
57 abort: option --debugger may not be abbreviated!
58 [255]
58 [255]
59
59
60 Parsing failure of early options should be detected before executing the
61 command:
62
63 $ hg log -b '--config=hooks.pre-log=false' default
64 abort: option --config may not be abbreviated!
65 [255]
66 $ hg log -b -R. default
67 abort: option -R has to be separated from other options (e.g. not -qR) and --repository may only be abbreviated as --repo!
68 [255]
69 $ hg log --cwd .. -b --cwd=. default
70 abort: option --cwd may not be abbreviated!
71 [255]
72
73 However, we can't prevent it from loading extensions and configs:
74
75 $ cat <<EOF > bad.py
76 > raise Exception('bad')
77 > EOF
78 $ hg log -b '--config=extensions.bad=bad.py' default
79 *** failed to import extension bad from bad.py: bad
80 abort: option --config may not be abbreviated!
81 [255]
82
83 $ mkdir -p badrepo/.hg
84 $ echo 'invalid-syntax' > badrepo/.hg/hgrc
85 $ hg log -b -Rbadrepo default
86 hg: parse error at badrepo/.hg/hgrc:1: invalid-syntax
87 [255]
88
89 $ hg log -b --cwd=inexistent default
90 abort: No such file or directory: 'inexistent'
91 [255]
92
93 $ hg log -b '--config=ui.traceback=yes' 2>&1 | grep '^Traceback'
94 Traceback (most recent call last):
95 $ hg log -b '--config=profiling.enabled=yes' 2>&1 | grep -i sample
96 Sample count: .*|No samples recorded\. (re)
97
98 Early options can't be specified in [aliases] and [defaults] because they are
99 applied before the command name is resolved:
100
101 $ hg log -b '--config=alias.log=log --config=hooks.pre-log=false'
102 hg log: option -b not recognized
103 error in definition for alias 'log': --config may only be given on the command
104 line
105 [255]
106
107 $ hg log -b '--config=defaults.log=--config=hooks.pre-log=false'
108 abort: option --config may not be abbreviated!
109 [255]
110
111 Shell aliases bypass any command parsing rules but for the early one:
112
113 $ hg log -b '--config=alias.log=!echo howdy'
114 howdy
115
60 [defaults]
116 [defaults]
61
117
62 $ hg cat a
118 $ hg cat a
63 a
119 a
64 $ cat >> $HGRCPATH <<EOF
120 $ cat >> $HGRCPATH <<EOF
65 > [defaults]
121 > [defaults]
66 > cat = -r null
122 > cat = -r null
67 > EOF
123 > EOF
68 $ hg cat a
124 $ hg cat a
69 a: no such file in rev 000000000000
125 a: no such file in rev 000000000000
70 [1]
126 [1]
71
127
72 $ cd "$TESTTMP"
128 $ cd "$TESTTMP"
73
129
74 OSError "No such file or directory" / "The system cannot find the path
130 OSError "No such file or directory" / "The system cannot find the path
75 specified" should include filename even when it is empty
131 specified" should include filename even when it is empty
76
132
77 $ hg -R a archive ''
133 $ hg -R a archive ''
78 abort: *: '' (glob)
134 abort: *: '' (glob)
79 [255]
135 [255]
80
136
81 #if no-outer-repo
137 #if no-outer-repo
82
138
83 No repo:
139 No repo:
84
140
85 $ hg cat
141 $ hg cat
86 abort: no repository found in '$TESTTMP' (.hg not found)!
142 abort: no repository found in '$TESTTMP' (.hg not found)!
87 [255]
143 [255]
88
144
89 #endif
145 #endif
90
146
91 #if rmcwd
147 #if rmcwd
92
148
93 Current directory removed:
149 Current directory removed:
94
150
95 $ mkdir $TESTTMP/repo1
151 $ mkdir $TESTTMP/repo1
96 $ cd $TESTTMP/repo1
152 $ cd $TESTTMP/repo1
97 $ rm -rf $TESTTMP/repo1
153 $ rm -rf $TESTTMP/repo1
98
154
99 The output could be one of the following and something else:
155 The output could be one of the following and something else:
100 chg: abort: failed to getcwd (errno = *) (glob)
156 chg: abort: failed to getcwd (errno = *) (glob)
101 abort: error getting current working directory: * (glob)
157 abort: error getting current working directory: * (glob)
102 sh: 0: getcwd() failed: No such file or directory
158 sh: 0: getcwd() failed: No such file or directory
103 Since the exact behavior depends on the shell, only check it returns non-zero.
159 Since the exact behavior depends on the shell, only check it returns non-zero.
104 $ HGDEMANDIMPORT=disable hg version -q 2>/dev/null || false
160 $ HGDEMANDIMPORT=disable hg version -q 2>/dev/null || false
105 [1]
161 [1]
106
162
107 #endif
163 #endif
@@ -1,139 +1,139 b''
1 $ hg init
1 $ hg init
2
2
3 $ echo a > a
3 $ echo a > a
4 $ hg ci -qAm 'add a'
4 $ hg ci -qAm 'add a'
5
5
6 $ hg init subrepo
6 $ hg init subrepo
7 $ echo 'subrepo = http://example.net/libfoo' > .hgsub
7 $ echo 'subrepo = http://example.net/libfoo' > .hgsub
8 $ hg ci -qAm 'added subrepo'
8 $ hg ci -qAm 'added subrepo'
9
9
10 $ hg up -qC 0
10 $ hg up -qC 0
11 $ echo ax > a
11 $ echo ax > a
12 $ hg ci -m 'changed a'
12 $ hg ci -m 'changed a'
13 created new head
13 created new head
14
14
15 $ hg up -qC 1
15 $ hg up -qC 1
16 $ cd subrepo
16 $ cd subrepo
17 $ echo b > b
17 $ echo b > b
18 $ hg add b
18 $ hg add b
19 $ cd ..
19 $ cd ..
20
20
21 Should fail, since there are added files to subrepo:
21 Should fail, since there are added files to subrepo:
22
22
23 $ hg merge
23 $ hg merge
24 abort: uncommitted changes in subrepository "subrepo"
24 abort: uncommitted changes in subrepository "subrepo"
25 [255]
25 [255]
26
26
27 Deleted files trigger a '+' marker in top level repos. Deleted files are also
27 Deleted files trigger a '+' marker in top level repos. Deleted files are also
28 noticed by `update --check` in the top level repo.
28 noticed by `update --check` in the top level repo.
29
29
30 $ hg ci -Sqm 'add b'
30 $ hg ci -Sqm 'add b'
31 $ echo change > subrepo/b
31 $ echo change > subrepo/b
32
32
33 $ hg ci -Sm 'change b'
33 $ hg ci -Sm 'change b'
34 committing subrepository subrepo
34 committing subrepository subrepo
35
35
36 $ rm a
36 $ rm a
37 $ hg id
37 $ hg id
38 9bfe45a197d7+ tip
38 9bfe45a197d7+ tip
39 $ hg sum
39 $ hg sum
40 parent: 4:9bfe45a197d7 tip
40 parent: 4:9bfe45a197d7 tip
41 change b
41 change b
42 branch: default
42 branch: default
43 commit: 1 deleted (clean)
43 commit: 1 deleted (clean)
44 update: 1 new changesets, 2 branch heads (merge)
44 update: 1 new changesets, 2 branch heads (merge)
45 phases: 5 draft
45 phases: 5 draft
46
46
47 $ hg up --check -r '.^'
47 $ hg up --check -r '.^'
48 abort: uncommitted changes
48 abort: uncommitted changes
49 [255]
49 [255]
50 $ hg st -S
50 $ hg st -S
51 ! a
51 ! a
52 $ hg up -Cq .
52 $ hg up -Cq .
53
53
54 Test that dirty is consistent through subrepos
54 Test that dirty is consistent through subrepos
55
55
56 $ rm subrepo/b
56 $ rm subrepo/b
57
57
58 A deleted subrepo file is flagged as dirty, like the top level repo
58 A deleted subrepo file is flagged as dirty, like the top level repo
59
59
60 $ hg id --config extensions.blackbox= --config blackbox.dirty=True
60 $ hg id --config extensions.blackbox= --config blackbox.dirty=True
61 9bfe45a197d7+ tip
61 9bfe45a197d7+ tip
62 $ cat .hg/blackbox.log
62 $ cat .hg/blackbox.log
63 * @9bfe45a197d7b0ab09bf287729dd57e9619c9da5+ (*)> serve --cmdserver chgunix * (glob) (chg !)
63 * @9bfe45a197d7b0ab09bf287729dd57e9619c9da5+ (*)> serve --cmdserver chgunix * (glob) (chg !)
64 * @9bfe45a197d7b0ab09bf287729dd57e9619c9da5+ (*)> id (glob)
64 * @9bfe45a197d7b0ab09bf287729dd57e9619c9da5+ (*)> id --config *extensions.blackbox=* --config *blackbox.dirty=True* (glob)
65 * @9bfe45a197d7b0ab09bf287729dd57e9619c9da5+ (*)> id --config *extensions.blackbox=* --config *blackbox.dirty=True* exited 0 * (glob)
65 * @9bfe45a197d7b0ab09bf287729dd57e9619c9da5+ (*)> id --config *extensions.blackbox=* --config *blackbox.dirty=True* exited 0 * (glob)
66
66
67 TODO: a deleted file should be listed as such, like the top level repo
67 TODO: a deleted file should be listed as such, like the top level repo
68
68
69 $ hg sum
69 $ hg sum
70 parent: 4:9bfe45a197d7 tip
70 parent: 4:9bfe45a197d7 tip
71 change b
71 change b
72 branch: default
72 branch: default
73 commit: (clean)
73 commit: (clean)
74 update: 1 new changesets, 2 branch heads (merge)
74 update: 1 new changesets, 2 branch heads (merge)
75 phases: 5 draft
75 phases: 5 draft
76
76
77 Modified subrepo files are noticed by `update --check` and `summary`
77 Modified subrepo files are noticed by `update --check` and `summary`
78
78
79 $ echo mod > subrepo/b
79 $ echo mod > subrepo/b
80 $ hg st -S
80 $ hg st -S
81 M subrepo/b
81 M subrepo/b
82
82
83 $ hg up -r '.^' --check
83 $ hg up -r '.^' --check
84 abort: uncommitted changes in subrepository "subrepo"
84 abort: uncommitted changes in subrepository "subrepo"
85 [255]
85 [255]
86
86
87 $ hg sum
87 $ hg sum
88 parent: 4:9bfe45a197d7 tip
88 parent: 4:9bfe45a197d7 tip
89 change b
89 change b
90 branch: default
90 branch: default
91 commit: 1 subrepos
91 commit: 1 subrepos
92 update: 1 new changesets, 2 branch heads (merge)
92 update: 1 new changesets, 2 branch heads (merge)
93 phases: 5 draft
93 phases: 5 draft
94
94
95 TODO: why is -R needed here? If it's because the subrepo is treated as a
95 TODO: why is -R needed here? If it's because the subrepo is treated as a
96 discrete unit, then this should probably warn or something.
96 discrete unit, then this should probably warn or something.
97 $ hg revert -R subrepo --no-backup subrepo/b -r .
97 $ hg revert -R subrepo --no-backup subrepo/b -r .
98
98
99 $ rm subrepo/b
99 $ rm subrepo/b
100 $ hg st -S
100 $ hg st -S
101 ! subrepo/b
101 ! subrepo/b
102
102
103 `hg update --check` notices a subrepo with a missing file, like it notices a
103 `hg update --check` notices a subrepo with a missing file, like it notices a
104 missing file in the top level repo.
104 missing file in the top level repo.
105
105
106 $ hg up -r '.^' --check
106 $ hg up -r '.^' --check
107 abort: uncommitted changes in subrepository "subrepo"
107 abort: uncommitted changes in subrepository "subrepo"
108 [255]
108 [255]
109
109
110 $ hg up -r '.^' --config ui.interactive=True << EOF
110 $ hg up -r '.^' --config ui.interactive=True << EOF
111 > d
111 > d
112 > EOF
112 > EOF
113 other [destination] changed b which local [working copy] deleted
113 other [destination] changed b which local [working copy] deleted
114 use (c)hanged version, leave (d)eleted, or leave (u)nresolved? d
114 use (c)hanged version, leave (d)eleted, or leave (u)nresolved? d
115 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
115 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
116
116
117 XXX: There's a difference between wdir() and '.', so there should be a status.
117 XXX: There's a difference between wdir() and '.', so there should be a status.
118 `hg files -S` from the top is also missing 'subrepo/b'.
118 `hg files -S` from the top is also missing 'subrepo/b'.
119
119
120 $ hg st -S
120 $ hg st -S
121 $ hg st -R subrepo
121 $ hg st -R subrepo
122 $ hg files -R subrepo
122 $ hg files -R subrepo
123 [1]
123 [1]
124 $ hg files -R subrepo -r '.'
124 $ hg files -R subrepo -r '.'
125 subrepo/b (glob)
125 subrepo/b (glob)
126
126
127 $ hg bookmark -r tip @other
127 $ hg bookmark -r tip @other
128 $ echo xyz > subrepo/c
128 $ echo xyz > subrepo/c
129 $ hg ci -SAm 'add c'
129 $ hg ci -SAm 'add c'
130 adding subrepo/c
130 adding subrepo/c
131 committing subrepository subrepo
131 committing subrepository subrepo
132 created new head
132 created new head
133 $ rm subrepo/c
133 $ rm subrepo/c
134
134
135 Merge sees deleted subrepo files as an uncommitted change
135 Merge sees deleted subrepo files as an uncommitted change
136
136
137 $ hg merge @other
137 $ hg merge @other
138 abort: uncommitted changes in subrepository "subrepo"
138 abort: uncommitted changes in subrepository "subrepo"
139 [255]
139 [255]
@@ -1,413 +1,413 b''
1
1
2 Function to test discovery between two repos in both directions, using both the local shortcut
2 Function to test discovery between two repos in both directions, using both the local shortcut
3 (which is currently not activated by default) and the full remotable protocol:
3 (which is currently not activated by default) and the full remotable protocol:
4
4
5 $ testdesc() { # revs_a, revs_b, dagdesc
5 $ testdesc() { # revs_a, revs_b, dagdesc
6 > if [ -d foo ]; then rm -rf foo; fi
6 > if [ -d foo ]; then rm -rf foo; fi
7 > hg init foo
7 > hg init foo
8 > cd foo
8 > cd foo
9 > hg debugbuilddag "$3"
9 > hg debugbuilddag "$3"
10 > hg clone . a $1 --quiet
10 > hg clone . a $1 --quiet
11 > hg clone . b $2 --quiet
11 > hg clone . b $2 --quiet
12 > echo
12 > echo
13 > echo "% -- a -> b tree"
13 > echo "% -- a -> b tree"
14 > hg -R a debugdiscovery b --verbose --old
14 > hg -R a debugdiscovery b --verbose --old
15 > echo
15 > echo
16 > echo "% -- a -> b set"
16 > echo "% -- a -> b set"
17 > hg -R a debugdiscovery b --verbose --debug --config progress.debug=true
17 > hg -R a debugdiscovery b --verbose --debug --config progress.debug=true
18 > echo
18 > echo
19 > echo "% -- b -> a tree"
19 > echo "% -- b -> a tree"
20 > hg -R b debugdiscovery a --verbose --old
20 > hg -R b debugdiscovery a --verbose --old
21 > echo
21 > echo
22 > echo "% -- b -> a set"
22 > echo "% -- b -> a set"
23 > hg -R b debugdiscovery a --verbose --debug --config progress.debug=true
23 > hg -R b debugdiscovery a --verbose --debug --config progress.debug=true
24 > cd ..
24 > cd ..
25 > }
25 > }
26
26
27
27
28 Small superset:
28 Small superset:
29
29
30 $ testdesc '-ra1 -ra2' '-rb1 -rb2 -rb3' '
30 $ testdesc '-ra1 -ra2' '-rb1 -rb2 -rb3' '
31 > +2:f +1:a1:b1
31 > +2:f +1:a1:b1
32 > <f +4 :a2
32 > <f +4 :a2
33 > +5 :b2
33 > +5 :b2
34 > <f +3 :b3'
34 > <f +3 :b3'
35
35
36 % -- a -> b tree
36 % -- a -> b tree
37 comparing with b
37 comparing with b
38 searching for changes
38 searching for changes
39 unpruned common: 01241442b3c2 66f7d451a68b b5714e113bc0
39 unpruned common: 01241442b3c2 66f7d451a68b b5714e113bc0
40 common heads: 01241442b3c2 b5714e113bc0
40 common heads: 01241442b3c2 b5714e113bc0
41 local is subset
41 local is subset
42
42
43 % -- a -> b set
43 % -- a -> b set
44 comparing with b
44 comparing with b
45 query 1; heads
45 query 1; heads
46 searching for changes
46 searching for changes
47 all local heads known remotely
47 all local heads known remotely
48 common heads: 01241442b3c2 b5714e113bc0
48 common heads: 01241442b3c2 b5714e113bc0
49 local is subset
49 local is subset
50
50
51 % -- b -> a tree
51 % -- b -> a tree
52 comparing with a
52 comparing with a
53 searching for changes
53 searching for changes
54 unpruned common: 01241442b3c2 b5714e113bc0
54 unpruned common: 01241442b3c2 b5714e113bc0
55 common heads: 01241442b3c2 b5714e113bc0
55 common heads: 01241442b3c2 b5714e113bc0
56 remote is subset
56 remote is subset
57
57
58 % -- b -> a set
58 % -- b -> a set
59 comparing with a
59 comparing with a
60 query 1; heads
60 query 1; heads
61 searching for changes
61 searching for changes
62 all remote heads known locally
62 all remote heads known locally
63 common heads: 01241442b3c2 b5714e113bc0
63 common heads: 01241442b3c2 b5714e113bc0
64 remote is subset
64 remote is subset
65
65
66
66
67 Many new:
67 Many new:
68
68
69 $ testdesc '-ra1 -ra2' '-rb' '
69 $ testdesc '-ra1 -ra2' '-rb' '
70 > +2:f +3:a1 +3:b
70 > +2:f +3:a1 +3:b
71 > <f +30 :a2'
71 > <f +30 :a2'
72
72
73 % -- a -> b tree
73 % -- a -> b tree
74 comparing with b
74 comparing with b
75 searching for changes
75 searching for changes
76 unpruned common: bebd167eb94d
76 unpruned common: bebd167eb94d
77 common heads: bebd167eb94d
77 common heads: bebd167eb94d
78
78
79 % -- a -> b set
79 % -- a -> b set
80 comparing with b
80 comparing with b
81 query 1; heads
81 query 1; heads
82 searching for changes
82 searching for changes
83 taking initial sample
83 taking initial sample
84 searching: 2 queries
84 searching: 2 queries
85 query 2; still undecided: 29, sample size is: 29
85 query 2; still undecided: 29, sample size is: 29
86 2 total queries in *.????s (glob)
86 2 total queries in *.????s (glob)
87 common heads: bebd167eb94d
87 common heads: bebd167eb94d
88
88
89 % -- b -> a tree
89 % -- b -> a tree
90 comparing with a
90 comparing with a
91 searching for changes
91 searching for changes
92 unpruned common: 66f7d451a68b bebd167eb94d
92 unpruned common: 66f7d451a68b bebd167eb94d
93 common heads: bebd167eb94d
93 common heads: bebd167eb94d
94
94
95 % -- b -> a set
95 % -- b -> a set
96 comparing with a
96 comparing with a
97 query 1; heads
97 query 1; heads
98 searching for changes
98 searching for changes
99 taking initial sample
99 taking initial sample
100 searching: 2 queries
100 searching: 2 queries
101 query 2; still undecided: 2, sample size is: 2
101 query 2; still undecided: 2, sample size is: 2
102 2 total queries in *.????s (glob)
102 2 total queries in *.????s (glob)
103 common heads: bebd167eb94d
103 common heads: bebd167eb94d
104
104
105 Both sides many new with stub:
105 Both sides many new with stub:
106
106
107 $ testdesc '-ra1 -ra2' '-rb' '
107 $ testdesc '-ra1 -ra2' '-rb' '
108 > +2:f +2:a1 +30 :b
108 > +2:f +2:a1 +30 :b
109 > <f +30 :a2'
109 > <f +30 :a2'
110
110
111 % -- a -> b tree
111 % -- a -> b tree
112 comparing with b
112 comparing with b
113 searching for changes
113 searching for changes
114 unpruned common: 2dc09a01254d
114 unpruned common: 2dc09a01254d
115 common heads: 2dc09a01254d
115 common heads: 2dc09a01254d
116
116
117 % -- a -> b set
117 % -- a -> b set
118 comparing with b
118 comparing with b
119 query 1; heads
119 query 1; heads
120 searching for changes
120 searching for changes
121 taking initial sample
121 taking initial sample
122 searching: 2 queries
122 searching: 2 queries
123 query 2; still undecided: 29, sample size is: 29
123 query 2; still undecided: 29, sample size is: 29
124 2 total queries in *.????s (glob)
124 2 total queries in *.????s (glob)
125 common heads: 2dc09a01254d
125 common heads: 2dc09a01254d
126
126
127 % -- b -> a tree
127 % -- b -> a tree
128 comparing with a
128 comparing with a
129 searching for changes
129 searching for changes
130 unpruned common: 2dc09a01254d 66f7d451a68b
130 unpruned common: 2dc09a01254d 66f7d451a68b
131 common heads: 2dc09a01254d
131 common heads: 2dc09a01254d
132
132
133 % -- b -> a set
133 % -- b -> a set
134 comparing with a
134 comparing with a
135 query 1; heads
135 query 1; heads
136 searching for changes
136 searching for changes
137 taking initial sample
137 taking initial sample
138 searching: 2 queries
138 searching: 2 queries
139 query 2; still undecided: 29, sample size is: 29
139 query 2; still undecided: 29, sample size is: 29
140 2 total queries in *.????s (glob)
140 2 total queries in *.????s (glob)
141 common heads: 2dc09a01254d
141 common heads: 2dc09a01254d
142
142
143
143
144 Both many new:
144 Both many new:
145
145
146 $ testdesc '-ra' '-rb' '
146 $ testdesc '-ra' '-rb' '
147 > +2:f +30 :b
147 > +2:f +30 :b
148 > <f +30 :a'
148 > <f +30 :a'
149
149
150 % -- a -> b tree
150 % -- a -> b tree
151 comparing with b
151 comparing with b
152 searching for changes
152 searching for changes
153 unpruned common: 66f7d451a68b
153 unpruned common: 66f7d451a68b
154 common heads: 66f7d451a68b
154 common heads: 66f7d451a68b
155
155
156 % -- a -> b set
156 % -- a -> b set
157 comparing with b
157 comparing with b
158 query 1; heads
158 query 1; heads
159 searching for changes
159 searching for changes
160 taking quick initial sample
160 taking quick initial sample
161 searching: 2 queries
161 searching: 2 queries
162 query 2; still undecided: 31, sample size is: 31
162 query 2; still undecided: 31, sample size is: 31
163 2 total queries in *.????s (glob)
163 2 total queries in *.????s (glob)
164 common heads: 66f7d451a68b
164 common heads: 66f7d451a68b
165
165
166 % -- b -> a tree
166 % -- b -> a tree
167 comparing with a
167 comparing with a
168 searching for changes
168 searching for changes
169 unpruned common: 66f7d451a68b
169 unpruned common: 66f7d451a68b
170 common heads: 66f7d451a68b
170 common heads: 66f7d451a68b
171
171
172 % -- b -> a set
172 % -- b -> a set
173 comparing with a
173 comparing with a
174 query 1; heads
174 query 1; heads
175 searching for changes
175 searching for changes
176 taking quick initial sample
176 taking quick initial sample
177 searching: 2 queries
177 searching: 2 queries
178 query 2; still undecided: 31, sample size is: 31
178 query 2; still undecided: 31, sample size is: 31
179 2 total queries in *.????s (glob)
179 2 total queries in *.????s (glob)
180 common heads: 66f7d451a68b
180 common heads: 66f7d451a68b
181
181
182
182
183 Both many new skewed:
183 Both many new skewed:
184
184
185 $ testdesc '-ra' '-rb' '
185 $ testdesc '-ra' '-rb' '
186 > +2:f +30 :b
186 > +2:f +30 :b
187 > <f +50 :a'
187 > <f +50 :a'
188
188
189 % -- a -> b tree
189 % -- a -> b tree
190 comparing with b
190 comparing with b
191 searching for changes
191 searching for changes
192 unpruned common: 66f7d451a68b
192 unpruned common: 66f7d451a68b
193 common heads: 66f7d451a68b
193 common heads: 66f7d451a68b
194
194
195 % -- a -> b set
195 % -- a -> b set
196 comparing with b
196 comparing with b
197 query 1; heads
197 query 1; heads
198 searching for changes
198 searching for changes
199 taking quick initial sample
199 taking quick initial sample
200 searching: 2 queries
200 searching: 2 queries
201 query 2; still undecided: 51, sample size is: 51
201 query 2; still undecided: 51, sample size is: 51
202 2 total queries in *.????s (glob)
202 2 total queries in *.????s (glob)
203 common heads: 66f7d451a68b
203 common heads: 66f7d451a68b
204
204
205 % -- b -> a tree
205 % -- b -> a tree
206 comparing with a
206 comparing with a
207 searching for changes
207 searching for changes
208 unpruned common: 66f7d451a68b
208 unpruned common: 66f7d451a68b
209 common heads: 66f7d451a68b
209 common heads: 66f7d451a68b
210
210
211 % -- b -> a set
211 % -- b -> a set
212 comparing with a
212 comparing with a
213 query 1; heads
213 query 1; heads
214 searching for changes
214 searching for changes
215 taking quick initial sample
215 taking quick initial sample
216 searching: 2 queries
216 searching: 2 queries
217 query 2; still undecided: 31, sample size is: 31
217 query 2; still undecided: 31, sample size is: 31
218 2 total queries in *.????s (glob)
218 2 total queries in *.????s (glob)
219 common heads: 66f7d451a68b
219 common heads: 66f7d451a68b
220
220
221
221
222 Both many new on top of long history:
222 Both many new on top of long history:
223
223
224 $ testdesc '-ra' '-rb' '
224 $ testdesc '-ra' '-rb' '
225 > +1000:f +30 :b
225 > +1000:f +30 :b
226 > <f +50 :a'
226 > <f +50 :a'
227
227
228 % -- a -> b tree
228 % -- a -> b tree
229 comparing with b
229 comparing with b
230 searching for changes
230 searching for changes
231 unpruned common: 7ead0cba2838
231 unpruned common: 7ead0cba2838
232 common heads: 7ead0cba2838
232 common heads: 7ead0cba2838
233
233
234 % -- a -> b set
234 % -- a -> b set
235 comparing with b
235 comparing with b
236 query 1; heads
236 query 1; heads
237 searching for changes
237 searching for changes
238 taking quick initial sample
238 taking quick initial sample
239 searching: 2 queries
239 searching: 2 queries
240 query 2; still undecided: 1049, sample size is: 11
240 query 2; still undecided: 1049, sample size is: 11
241 sampling from both directions
241 sampling from both directions
242 searching: 3 queries
242 searching: 3 queries
243 query 3; still undecided: 31, sample size is: 31
243 query 3; still undecided: 31, sample size is: 31
244 3 total queries in *.????s (glob)
244 3 total queries in *.????s (glob)
245 common heads: 7ead0cba2838
245 common heads: 7ead0cba2838
246
246
247 % -- b -> a tree
247 % -- b -> a tree
248 comparing with a
248 comparing with a
249 searching for changes
249 searching for changes
250 unpruned common: 7ead0cba2838
250 unpruned common: 7ead0cba2838
251 common heads: 7ead0cba2838
251 common heads: 7ead0cba2838
252
252
253 % -- b -> a set
253 % -- b -> a set
254 comparing with a
254 comparing with a
255 query 1; heads
255 query 1; heads
256 searching for changes
256 searching for changes
257 taking quick initial sample
257 taking quick initial sample
258 searching: 2 queries
258 searching: 2 queries
259 query 2; still undecided: 1029, sample size is: 11
259 query 2; still undecided: 1029, sample size is: 11
260 sampling from both directions
260 sampling from both directions
261 searching: 3 queries
261 searching: 3 queries
262 query 3; still undecided: 15, sample size is: 15
262 query 3; still undecided: 15, sample size is: 15
263 3 total queries in *.????s (glob)
263 3 total queries in *.????s (glob)
264 common heads: 7ead0cba2838
264 common heads: 7ead0cba2838
265
265
266
266
267 One with >200 heads, which used to use up all of the sample:
267 One with >200 heads, which used to use up all of the sample:
268
268
269 $ hg init manyheads
269 $ hg init manyheads
270 $ cd manyheads
270 $ cd manyheads
271 $ echo "+300:r @a" >dagdesc
271 $ echo "+300:r @a" >dagdesc
272 $ echo "*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3 *r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3" >>dagdesc # 20 heads
272 $ echo "*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3 *r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3" >>dagdesc # 20 heads
273 $ echo "*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3 *r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3" >>dagdesc # 20 heads
273 $ echo "*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3 *r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3" >>dagdesc # 20 heads
274 $ echo "*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3 *r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3" >>dagdesc # 20 heads
274 $ echo "*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3 *r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3" >>dagdesc # 20 heads
275 $ echo "*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3 *r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3" >>dagdesc # 20 heads
275 $ echo "*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3 *r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3" >>dagdesc # 20 heads
276 $ echo "*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3 *r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3" >>dagdesc # 20 heads
276 $ echo "*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3 *r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3" >>dagdesc # 20 heads
277 $ echo "*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3 *r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3" >>dagdesc # 20 heads
277 $ echo "*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3 *r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3" >>dagdesc # 20 heads
278 $ echo "*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3 *r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3" >>dagdesc # 20 heads
278 $ echo "*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3 *r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3" >>dagdesc # 20 heads
279 $ echo "*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3 *r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3" >>dagdesc # 20 heads
279 $ echo "*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3 *r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3" >>dagdesc # 20 heads
280 $ echo "*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3 *r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3" >>dagdesc # 20 heads
280 $ echo "*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3 *r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3" >>dagdesc # 20 heads
281 $ echo "*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3 *r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3" >>dagdesc # 20 heads
281 $ echo "*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3 *r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3" >>dagdesc # 20 heads
282 $ echo "*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3 *r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3" >>dagdesc # 20 heads
282 $ echo "*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3 *r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3" >>dagdesc # 20 heads
283 $ echo "*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3 *r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3" >>dagdesc # 20 heads
283 $ echo "*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3 *r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3" >>dagdesc # 20 heads
284 $ echo "*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3 *r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3" >>dagdesc # 20 heads
284 $ echo "*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3 *r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3*r+3" >>dagdesc # 20 heads
285 $ echo "@b *r+3" >>dagdesc # one more head
285 $ echo "@b *r+3" >>dagdesc # one more head
286 $ hg debugbuilddag <dagdesc
286 $ hg debugbuilddag <dagdesc
287 reading DAG from stdin
287 reading DAG from stdin
288
288
289 $ hg heads -t --template . | wc -c
289 $ hg heads -t --template . | wc -c
290 \s*261 (re)
290 \s*261 (re)
291
291
292 $ hg clone -b a . a
292 $ hg clone -b a . a
293 adding changesets
293 adding changesets
294 adding manifests
294 adding manifests
295 adding file changes
295 adding file changes
296 added 1340 changesets with 0 changes to 0 files (+259 heads)
296 added 1340 changesets with 0 changes to 0 files (+259 heads)
297 new changesets 1ea73414a91b:1c51e2c80832
297 new changesets 1ea73414a91b:1c51e2c80832
298 updating to branch a
298 updating to branch a
299 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
299 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
300 $ hg clone -b b . b
300 $ hg clone -b b . b
301 adding changesets
301 adding changesets
302 adding manifests
302 adding manifests
303 adding file changes
303 adding file changes
304 added 304 changesets with 0 changes to 0 files
304 added 304 changesets with 0 changes to 0 files
305 new changesets 1ea73414a91b:513314ca8b3a
305 new changesets 1ea73414a91b:513314ca8b3a
306 updating to branch b
306 updating to branch b
307 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
307 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
308
308
309 $ hg -R a debugdiscovery b --debug --verbose --config progress.debug=true
309 $ hg -R a debugdiscovery b --debug --verbose --config progress.debug=true
310 comparing with b
310 comparing with b
311 query 1; heads
311 query 1; heads
312 searching for changes
312 searching for changes
313 taking quick initial sample
313 taking quick initial sample
314 searching: 2 queries
314 searching: 2 queries
315 query 2; still undecided: 1240, sample size is: 100
315 query 2; still undecided: 1240, sample size is: 100
316 sampling from both directions
316 sampling from both directions
317 searching: 3 queries
317 searching: 3 queries
318 query 3; still undecided: 1140, sample size is: 200
318 query 3; still undecided: 1140, sample size is: 200
319 sampling from both directions
319 sampling from both directions
320 searching: 4 queries
320 searching: 4 queries
321 query 4; still undecided: \d+, sample size is: 200 (re)
321 query 4; still undecided: \d+, sample size is: 200 (re)
322 sampling from both directions
322 sampling from both directions
323 searching: 5 queries
323 searching: 5 queries
324 query 5; still undecided: \d+, sample size is: 200 (re)
324 query 5; still undecided: \d+, sample size is: 200 (re)
325 sampling from both directions
325 sampling from both directions
326 searching: 6 queries
326 searching: 6 queries
327 query 6; still undecided: \d+, sample size is: \d+ (re)
327 query 6; still undecided: \d+, sample size is: \d+ (re)
328 6 total queries in *.????s (glob)
328 6 total queries in *.????s (glob)
329 common heads: 3ee37d65064a
329 common heads: 3ee37d65064a
330
330
331 Test actual protocol when pulling one new head in addition to common heads
331 Test actual protocol when pulling one new head in addition to common heads
332
332
333 $ hg clone -U b c
333 $ hg clone -U b c
334 $ hg -R c id -ir tip
334 $ hg -R c id -ir tip
335 513314ca8b3a
335 513314ca8b3a
336 $ hg -R c up -qr default
336 $ hg -R c up -qr default
337 $ touch c/f
337 $ touch c/f
338 $ hg -R c ci -Aqm "extra head"
338 $ hg -R c ci -Aqm "extra head"
339 $ hg -R c id -i
339 $ hg -R c id -i
340 e64a39e7da8b
340 e64a39e7da8b
341
341
342 $ hg serve -R c -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
342 $ hg serve -R c -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
343 $ cat hg.pid >> $DAEMON_PIDS
343 $ cat hg.pid >> $DAEMON_PIDS
344
344
345 $ hg -R b incoming http://localhost:$HGPORT/ -T '{node|short}\n'
345 $ hg -R b incoming http://localhost:$HGPORT/ -T '{node|short}\n'
346 comparing with http://localhost:$HGPORT/
346 comparing with http://localhost:$HGPORT/
347 searching for changes
347 searching for changes
348 e64a39e7da8b
348 e64a39e7da8b
349
349
350 $ killdaemons.py
350 $ killdaemons.py
351 $ cut -d' ' -f6- access.log | grep -v cmd=known # cmd=known uses random sampling
351 $ cut -d' ' -f6- access.log | grep -v cmd=known # cmd=known uses random sampling
352 "GET /?cmd=capabilities HTTP/1.1" 200 -
352 "GET /?cmd=capabilities HTTP/1.1" 200 -
353 "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D513314ca8b3ae4dac8eec56966265b00fcf866db x-hgproto-1:0.1 0.2 comp=*zlib,none,bzip2 (glob)
353 "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D513314ca8b3ae4dac8eec56966265b00fcf866db x-hgproto-1:0.1 0.2 comp=*zlib,none,bzip2 (glob)
354 "GET /?cmd=getbundle HTTP/1.1" 200 - x-hgarg-1:bundlecaps=HG20%2Cbundle2%3DHG20%250Achangegroup%253D01%252C02%250Adigests%253Dmd5%252Csha1%252Csha512%250Aerror%253Dabort%252Cunsupportedcontent%252Cpushraced%252Cpushkey%250Ahgtagsfnodes%250Alistkeys%250Aphases%253Dheads%250Apushkey%250Aremote-changegroup%253Dhttp%252Chttps&cg=1&common=513314ca8b3ae4dac8eec56966265b00fcf866db&heads=e64a39e7da8b0d54bc63e81169aff001c13b3477 x-hgproto-1:0.1 0.2 comp=*zlib,none,bzip2 (glob)
354 "GET /?cmd=getbundle HTTP/1.1" 200 - x-hgarg-1:bundlecaps=HG20%2Cbundle2%3DHG20%250Achangegroup%253D01%252C02%250Adigests%253Dmd5%252Csha1%252Csha512%250Aerror%253Dabort%252Cunsupportedcontent%252Cpushraced%252Cpushkey%250Ahgtagsfnodes%250Alistkeys%250Aphases%253Dheads%250Apushkey%250Aremote-changegroup%253Dhttp%252Chttps&cg=1&common=513314ca8b3ae4dac8eec56966265b00fcf866db&heads=e64a39e7da8b0d54bc63e81169aff001c13b3477 x-hgproto-1:0.1 0.2 comp=*zlib,none,bzip2 (glob)
355 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=*zlib,none,bzip2 (glob)
355 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=*zlib,none,bzip2 (glob)
356 $ cat errors.log
356 $ cat errors.log
357
357
358 $ cd ..
358 $ cd ..
359
359
360
360
361 Issue 4438 - test coverage for 3ef893520a85 issues.
361 Issue 4438 - test coverage for 3ef893520a85 issues.
362
362
363 $ mkdir issue4438
363 $ mkdir issue4438
364 $ cd issue4438
364 $ cd issue4438
365 #if false
365 #if false
366 generate new bundles:
366 generate new bundles:
367 $ hg init r1
367 $ hg init r1
368 $ for i in `$PYTHON $TESTDIR/seq.py 101`; do hg -R r1 up -qr null && hg -R r1 branch -q b$i && hg -R r1 ci -qmb$i; done
368 $ for i in `$PYTHON $TESTDIR/seq.py 101`; do hg -R r1 up -qr null && hg -R r1 branch -q b$i && hg -R r1 ci -qmb$i; done
369 $ hg clone -q r1 r2
369 $ hg clone -q r1 r2
370 $ for i in `$PYTHON $TESTDIR/seq.py 10`; do hg -R r1 up -qr null && hg -R r1 branch -q c$i && hg -R r1 ci -qmc$i; done
370 $ for i in `$PYTHON $TESTDIR/seq.py 10`; do hg -R r1 up -qr null && hg -R r1 branch -q c$i && hg -R r1 ci -qmc$i; done
371 $ hg -R r2 branch -q r2change && hg -R r2 ci -qmr2change
371 $ hg -R r2 branch -q r2change && hg -R r2 ci -qmr2change
372 $ hg -R r1 bundle -qa $TESTDIR/bundles/issue4438-r1.hg
372 $ hg -R r1 bundle -qa $TESTDIR/bundles/issue4438-r1.hg
373 $ hg -R r2 bundle -qa $TESTDIR/bundles/issue4438-r2.hg
373 $ hg -R r2 bundle -qa $TESTDIR/bundles/issue4438-r2.hg
374 #else
374 #else
375 use existing bundles:
375 use existing bundles:
376 $ hg clone -q $TESTDIR/bundles/issue4438-r1.hg r1
376 $ hg clone -q $TESTDIR/bundles/issue4438-r1.hg r1
377 $ hg clone -q $TESTDIR/bundles/issue4438-r2.hg r2
377 $ hg clone -q $TESTDIR/bundles/issue4438-r2.hg r2
378 #endif
378 #endif
379
379
380 Set iteration order could cause wrong and unstable results - fixed in 73cfaa348650:
380 Set iteration order could cause wrong and unstable results - fixed in 73cfaa348650:
381
381
382 $ hg -R r1 outgoing r2 -T'{rev} '
382 $ hg -R r1 outgoing r2 -T'{rev} '
383 comparing with r2
383 comparing with r2
384 searching for changes
384 searching for changes
385 101 102 103 104 105 106 107 108 109 110 (no-eol)
385 101 102 103 104 105 106 107 108 109 110 (no-eol)
386
386
387 The case where all the 'initialsamplesize' samples already were common would
387 The case where all the 'initialsamplesize' samples already were common would
388 give 'all remote heads known locally' without checking the remaining heads -
388 give 'all remote heads known locally' without checking the remaining heads -
389 fixed in 86c35b7ae300:
389 fixed in 86c35b7ae300:
390
390
391 $ cat >> $TESTTMP/unrandomsample.py << EOF
391 $ cat >> $TESTTMP/unrandomsample.py << EOF
392 > import random
392 > import random
393 > def sample(population, k):
393 > def sample(population, k):
394 > return sorted(population)[:k]
394 > return sorted(population)[:k]
395 > random.sample = sample
395 > random.sample = sample
396 > EOF
396 > EOF
397
397
398 $ cat >> r1/.hg/hgrc << EOF
398 $ cat >> r1/.hg/hgrc << EOF
399 > [extensions]
399 > [extensions]
400 > unrandomsample = $TESTTMP/unrandomsample.py
400 > unrandomsample = $TESTTMP/unrandomsample.py
401 > EOF
401 > EOF
402
402
403 $ hg -R r1 outgoing r2 -T'{rev} ' --config extensions.blackbox=
403 $ hg -R r1 outgoing r2 -T'{rev} ' --config extensions.blackbox=
404 comparing with r2
404 comparing with r2
405 searching for changes
405 searching for changes
406 101 102 103 104 105 106 107 108 109 110 (no-eol)
406 101 102 103 104 105 106 107 108 109 110 (no-eol)
407 $ hg -R r1 --config extensions.blackbox= blackbox
407 $ hg -R r1 --config extensions.blackbox= blackbox
408 * @5d0b986a083e0d91f116de4691e2aaa54d5bbec0 (*)> serve --cmdserver chgunix * (glob) (chg !)
408 * @5d0b986a083e0d91f116de4691e2aaa54d5bbec0 (*)> serve --cmdserver chgunix * (glob) (chg !)
409 * @5d0b986a083e0d91f116de4691e2aaa54d5bbec0 (*)> outgoing r2 *-T{rev} * (glob)
409 * @5d0b986a083e0d91f116de4691e2aaa54d5bbec0 (*)> -R r1 outgoing r2 *-T{rev} * --config *extensions.blackbox=* (glob)
410 * @5d0b986a083e0d91f116de4691e2aaa54d5bbec0 (*)> found 101 common and 1 unknown server heads, 2 roundtrips in *.????s (glob)
410 * @5d0b986a083e0d91f116de4691e2aaa54d5bbec0 (*)> found 101 common and 1 unknown server heads, 2 roundtrips in *.????s (glob)
411 * @5d0b986a083e0d91f116de4691e2aaa54d5bbec0 (*)> -R r1 outgoing r2 *-T{rev} * --config *extensions.blackbox=* exited 0 after *.?? seconds (glob)
411 * @5d0b986a083e0d91f116de4691e2aaa54d5bbec0 (*)> -R r1 outgoing r2 *-T{rev} * --config *extensions.blackbox=* exited 0 after *.?? seconds (glob)
412 * @5d0b986a083e0d91f116de4691e2aaa54d5bbec0 (*)> blackbox (glob)
412 * @5d0b986a083e0d91f116de4691e2aaa54d5bbec0 (*)> -R r1 --config *extensions.blackbox=* blackbox (glob)
413 $ cd ..
413 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now