##// END OF EJS Templates
commit: amending with --close-branch (issue3445)...
Iulian Stana -
r19305:b500a663 stable
parent child Browse files
Show More
@@ -1,5868 +1,5875 b''
1 1 # commands.py - command processing for mercurial
2 2 #
3 3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from node import hex, bin, nullid, nullrev, short
9 9 from lock import release
10 10 from i18n import _
11 11 import os, re, difflib, time, tempfile, errno
12 12 import hg, scmutil, util, revlog, copies, error, bookmarks
13 13 import patch, help, encoding, templatekw, discovery
14 14 import archival, changegroup, cmdutil, hbisect
15 15 import sshserver, hgweb, hgweb.server, commandserver
16 16 import merge as mergemod
17 17 import minirst, revset, fileset
18 18 import dagparser, context, simplemerge, graphmod
19 19 import random, setdiscovery, treediscovery, dagutil, pvec, localrepo
20 20 import phases, obsolete
21 21
22 22 table = {}
23 23
24 24 command = cmdutil.command(table)
25 25
26 26 # common command options
27 27
28 28 globalopts = [
29 29 ('R', 'repository', '',
30 30 _('repository root directory or name of overlay bundle file'),
31 31 _('REPO')),
32 32 ('', 'cwd', '',
33 33 _('change working directory'), _('DIR')),
34 34 ('y', 'noninteractive', None,
35 35 _('do not prompt, automatically pick the first choice for all prompts')),
36 36 ('q', 'quiet', None, _('suppress output')),
37 37 ('v', 'verbose', None, _('enable additional output')),
38 38 ('', 'config', [],
39 39 _('set/override config option (use \'section.name=value\')'),
40 40 _('CONFIG')),
41 41 ('', 'debug', None, _('enable debugging output')),
42 42 ('', 'debugger', None, _('start debugger')),
43 43 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
44 44 _('ENCODE')),
45 45 ('', 'encodingmode', encoding.encodingmode,
46 46 _('set the charset encoding mode'), _('MODE')),
47 47 ('', 'traceback', None, _('always print a traceback on exception')),
48 48 ('', 'time', None, _('time how long the command takes')),
49 49 ('', 'profile', None, _('print command execution profile')),
50 50 ('', 'version', None, _('output version information and exit')),
51 51 ('h', 'help', None, _('display help and exit')),
52 52 ('', 'hidden', False, _('consider hidden changesets')),
53 53 ]
54 54
55 55 dryrunopts = [('n', 'dry-run', None,
56 56 _('do not perform actions, just print output'))]
57 57
58 58 remoteopts = [
59 59 ('e', 'ssh', '',
60 60 _('specify ssh command to use'), _('CMD')),
61 61 ('', 'remotecmd', '',
62 62 _('specify hg command to run on the remote side'), _('CMD')),
63 63 ('', 'insecure', None,
64 64 _('do not verify server certificate (ignoring web.cacerts config)')),
65 65 ]
66 66
67 67 walkopts = [
68 68 ('I', 'include', [],
69 69 _('include names matching the given patterns'), _('PATTERN')),
70 70 ('X', 'exclude', [],
71 71 _('exclude names matching the given patterns'), _('PATTERN')),
72 72 ]
73 73
74 74 commitopts = [
75 75 ('m', 'message', '',
76 76 _('use text as commit message'), _('TEXT')),
77 77 ('l', 'logfile', '',
78 78 _('read commit message from file'), _('FILE')),
79 79 ]
80 80
81 81 commitopts2 = [
82 82 ('d', 'date', '',
83 83 _('record the specified date as commit date'), _('DATE')),
84 84 ('u', 'user', '',
85 85 _('record the specified user as committer'), _('USER')),
86 86 ]
87 87
88 88 templateopts = [
89 89 ('', 'style', '',
90 90 _('display using template map file'), _('STYLE')),
91 91 ('', 'template', '',
92 92 _('display with template'), _('TEMPLATE')),
93 93 ]
94 94
95 95 logopts = [
96 96 ('p', 'patch', None, _('show patch')),
97 97 ('g', 'git', None, _('use git extended diff format')),
98 98 ('l', 'limit', '',
99 99 _('limit number of changes displayed'), _('NUM')),
100 100 ('M', 'no-merges', None, _('do not show merges')),
101 101 ('', 'stat', None, _('output diffstat-style summary of changes')),
102 102 ('G', 'graph', None, _("show the revision DAG")),
103 103 ] + templateopts
104 104
105 105 diffopts = [
106 106 ('a', 'text', None, _('treat all files as text')),
107 107 ('g', 'git', None, _('use git extended diff format')),
108 108 ('', 'nodates', None, _('omit dates from diff headers'))
109 109 ]
110 110
111 111 diffwsopts = [
112 112 ('w', 'ignore-all-space', None,
113 113 _('ignore white space when comparing lines')),
114 114 ('b', 'ignore-space-change', None,
115 115 _('ignore changes in the amount of white space')),
116 116 ('B', 'ignore-blank-lines', None,
117 117 _('ignore changes whose lines are all blank')),
118 118 ]
119 119
120 120 diffopts2 = [
121 121 ('p', 'show-function', None, _('show which function each change is in')),
122 122 ('', 'reverse', None, _('produce a diff that undoes the changes')),
123 123 ] + diffwsopts + [
124 124 ('U', 'unified', '',
125 125 _('number of lines of context to show'), _('NUM')),
126 126 ('', 'stat', None, _('output diffstat-style summary of changes')),
127 127 ]
128 128
129 129 mergetoolopts = [
130 130 ('t', 'tool', '', _('specify merge tool')),
131 131 ]
132 132
133 133 similarityopts = [
134 134 ('s', 'similarity', '',
135 135 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
136 136 ]
137 137
138 138 subrepoopts = [
139 139 ('S', 'subrepos', None,
140 140 _('recurse into subrepositories'))
141 141 ]
142 142
143 143 # Commands start here, listed alphabetically
144 144
145 145 @command('^add',
146 146 walkopts + subrepoopts + dryrunopts,
147 147 _('[OPTION]... [FILE]...'))
148 148 def add(ui, repo, *pats, **opts):
149 149 """add the specified files on the next commit
150 150
151 151 Schedule files to be version controlled and added to the
152 152 repository.
153 153
154 154 The files will be added to the repository at the next commit. To
155 155 undo an add before that, see :hg:`forget`.
156 156
157 157 If no names are given, add all files to the repository.
158 158
159 159 .. container:: verbose
160 160
161 161 An example showing how new (unknown) files are added
162 162 automatically by :hg:`add`::
163 163
164 164 $ ls
165 165 foo.c
166 166 $ hg status
167 167 ? foo.c
168 168 $ hg add
169 169 adding foo.c
170 170 $ hg status
171 171 A foo.c
172 172
173 173 Returns 0 if all files are successfully added.
174 174 """
175 175
176 176 m = scmutil.match(repo[None], pats, opts)
177 177 rejected = cmdutil.add(ui, repo, m, opts.get('dry_run'),
178 178 opts.get('subrepos'), prefix="", explicitonly=False)
179 179 return rejected and 1 or 0
180 180
181 181 @command('addremove',
182 182 similarityopts + walkopts + dryrunopts,
183 183 _('[OPTION]... [FILE]...'))
184 184 def addremove(ui, repo, *pats, **opts):
185 185 """add all new files, delete all missing files
186 186
187 187 Add all new files and remove all missing files from the
188 188 repository.
189 189
190 190 New files are ignored if they match any of the patterns in
191 191 ``.hgignore``. As with add, these changes take effect at the next
192 192 commit.
193 193
194 194 Use the -s/--similarity option to detect renamed files. This
195 195 option takes a percentage between 0 (disabled) and 100 (files must
196 196 be identical) as its parameter. With a parameter greater than 0,
197 197 this compares every removed file with every added file and records
198 198 those similar enough as renames. Detecting renamed files this way
199 199 can be expensive. After using this option, :hg:`status -C` can be
200 200 used to check which files were identified as moved or renamed. If
201 201 not specified, -s/--similarity defaults to 100 and only renames of
202 202 identical files are detected.
203 203
204 204 Returns 0 if all files are successfully added.
205 205 """
206 206 try:
207 207 sim = float(opts.get('similarity') or 100)
208 208 except ValueError:
209 209 raise util.Abort(_('similarity must be a number'))
210 210 if sim < 0 or sim > 100:
211 211 raise util.Abort(_('similarity must be between 0 and 100'))
212 212 return scmutil.addremove(repo, pats, opts, similarity=sim / 100.0)
213 213
214 214 @command('^annotate|blame',
215 215 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
216 216 ('', 'follow', None,
217 217 _('follow copies/renames and list the filename (DEPRECATED)')),
218 218 ('', 'no-follow', None, _("don't follow copies and renames")),
219 219 ('a', 'text', None, _('treat all files as text')),
220 220 ('u', 'user', None, _('list the author (long with -v)')),
221 221 ('f', 'file', None, _('list the filename')),
222 222 ('d', 'date', None, _('list the date (short with -q)')),
223 223 ('n', 'number', None, _('list the revision number (default)')),
224 224 ('c', 'changeset', None, _('list the changeset')),
225 225 ('l', 'line-number', None, _('show line number at the first appearance'))
226 226 ] + diffwsopts + walkopts,
227 227 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'))
228 228 def annotate(ui, repo, *pats, **opts):
229 229 """show changeset information by line for each file
230 230
231 231 List changes in files, showing the revision id responsible for
232 232 each line
233 233
234 234 This command is useful for discovering when a change was made and
235 235 by whom.
236 236
237 237 Without the -a/--text option, annotate will avoid processing files
238 238 it detects as binary. With -a, annotate will annotate the file
239 239 anyway, although the results will probably be neither useful
240 240 nor desirable.
241 241
242 242 Returns 0 on success.
243 243 """
244 244 if opts.get('follow'):
245 245 # --follow is deprecated and now just an alias for -f/--file
246 246 # to mimic the behavior of Mercurial before version 1.5
247 247 opts['file'] = True
248 248
249 249 datefunc = ui.quiet and util.shortdate or util.datestr
250 250 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
251 251
252 252 if not pats:
253 253 raise util.Abort(_('at least one filename or pattern is required'))
254 254
255 255 hexfn = ui.debugflag and hex or short
256 256
257 257 opmap = [('user', ' ', lambda x: ui.shortuser(x[0].user())),
258 258 ('number', ' ', lambda x: str(x[0].rev())),
259 259 ('changeset', ' ', lambda x: hexfn(x[0].node())),
260 260 ('date', ' ', getdate),
261 261 ('file', ' ', lambda x: x[0].path()),
262 262 ('line_number', ':', lambda x: str(x[1])),
263 263 ]
264 264
265 265 if (not opts.get('user') and not opts.get('changeset')
266 266 and not opts.get('date') and not opts.get('file')):
267 267 opts['number'] = True
268 268
269 269 linenumber = opts.get('line_number') is not None
270 270 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
271 271 raise util.Abort(_('at least one of -n/-c is required for -l'))
272 272
273 273 funcmap = [(func, sep) for op, sep, func in opmap if opts.get(op)]
274 274 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
275 275
276 276 def bad(x, y):
277 277 raise util.Abort("%s: %s" % (x, y))
278 278
279 279 ctx = scmutil.revsingle(repo, opts.get('rev'))
280 280 m = scmutil.match(ctx, pats, opts)
281 281 m.bad = bad
282 282 follow = not opts.get('no_follow')
283 283 diffopts = patch.diffopts(ui, opts, section='annotate')
284 284 for abs in ctx.walk(m):
285 285 fctx = ctx[abs]
286 286 if not opts.get('text') and util.binary(fctx.data()):
287 287 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
288 288 continue
289 289
290 290 lines = fctx.annotate(follow=follow, linenumber=linenumber,
291 291 diffopts=diffopts)
292 292 pieces = []
293 293
294 294 for f, sep in funcmap:
295 295 l = [f(n) for n, dummy in lines]
296 296 if l:
297 297 sized = [(x, encoding.colwidth(x)) for x in l]
298 298 ml = max([w for x, w in sized])
299 299 pieces.append(["%s%s%s" % (sep, ' ' * (ml - w), x)
300 300 for x, w in sized])
301 301
302 302 if pieces:
303 303 for p, l in zip(zip(*pieces), lines):
304 304 ui.write("%s: %s" % ("".join(p), l[1]))
305 305
306 306 if lines and not lines[-1][1].endswith('\n'):
307 307 ui.write('\n')
308 308
309 309 @command('archive',
310 310 [('', 'no-decode', None, _('do not pass files through decoders')),
311 311 ('p', 'prefix', '', _('directory prefix for files in archive'),
312 312 _('PREFIX')),
313 313 ('r', 'rev', '', _('revision to distribute'), _('REV')),
314 314 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
315 315 ] + subrepoopts + walkopts,
316 316 _('[OPTION]... DEST'))
317 317 def archive(ui, repo, dest, **opts):
318 318 '''create an unversioned archive of a repository revision
319 319
320 320 By default, the revision used is the parent of the working
321 321 directory; use -r/--rev to specify a different revision.
322 322
323 323 The archive type is automatically detected based on file
324 324 extension (or override using -t/--type).
325 325
326 326 .. container:: verbose
327 327
328 328 Examples:
329 329
330 330 - create a zip file containing the 1.0 release::
331 331
332 332 hg archive -r 1.0 project-1.0.zip
333 333
334 334 - create a tarball excluding .hg files::
335 335
336 336 hg archive project.tar.gz -X ".hg*"
337 337
338 338 Valid types are:
339 339
340 340 :``files``: a directory full of files (default)
341 341 :``tar``: tar archive, uncompressed
342 342 :``tbz2``: tar archive, compressed using bzip2
343 343 :``tgz``: tar archive, compressed using gzip
344 344 :``uzip``: zip archive, uncompressed
345 345 :``zip``: zip archive, compressed using deflate
346 346
347 347 The exact name of the destination archive or directory is given
348 348 using a format string; see :hg:`help export` for details.
349 349
350 350 Each member added to an archive file has a directory prefix
351 351 prepended. Use -p/--prefix to specify a format string for the
352 352 prefix. The default is the basename of the archive, with suffixes
353 353 removed.
354 354
355 355 Returns 0 on success.
356 356 '''
357 357
358 358 ctx = scmutil.revsingle(repo, opts.get('rev'))
359 359 if not ctx:
360 360 raise util.Abort(_('no working directory: please specify a revision'))
361 361 node = ctx.node()
362 362 dest = cmdutil.makefilename(repo, dest, node)
363 363 if os.path.realpath(dest) == repo.root:
364 364 raise util.Abort(_('repository root cannot be destination'))
365 365
366 366 kind = opts.get('type') or archival.guesskind(dest) or 'files'
367 367 prefix = opts.get('prefix')
368 368
369 369 if dest == '-':
370 370 if kind == 'files':
371 371 raise util.Abort(_('cannot archive plain files to stdout'))
372 372 dest = cmdutil.makefileobj(repo, dest)
373 373 if not prefix:
374 374 prefix = os.path.basename(repo.root) + '-%h'
375 375
376 376 prefix = cmdutil.makefilename(repo, prefix, node)
377 377 matchfn = scmutil.match(ctx, [], opts)
378 378 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
379 379 matchfn, prefix, subrepos=opts.get('subrepos'))
380 380
381 381 @command('backout',
382 382 [('', 'merge', None, _('merge with old dirstate parent after backout')),
383 383 ('', 'parent', '',
384 384 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
385 385 ('r', 'rev', '', _('revision to backout'), _('REV')),
386 386 ] + mergetoolopts + walkopts + commitopts + commitopts2,
387 387 _('[OPTION]... [-r] REV'))
388 388 def backout(ui, repo, node=None, rev=None, **opts):
389 389 '''reverse effect of earlier changeset
390 390
391 391 Prepare a new changeset with the effect of REV undone in the
392 392 current working directory.
393 393
394 394 If REV is the parent of the working directory, then this new changeset
395 395 is committed automatically. Otherwise, hg needs to merge the
396 396 changes and the merged result is left uncommitted.
397 397
398 398 .. note::
399 399 backout cannot be used to fix either an unwanted or
400 400 incorrect merge.
401 401
402 402 .. container:: verbose
403 403
404 404 By default, the pending changeset will have one parent,
405 405 maintaining a linear history. With --merge, the pending
406 406 changeset will instead have two parents: the old parent of the
407 407 working directory and a new child of REV that simply undoes REV.
408 408
409 409 Before version 1.7, the behavior without --merge was equivalent
410 410 to specifying --merge followed by :hg:`update --clean .` to
411 411 cancel the merge and leave the child of REV as a head to be
412 412 merged separately.
413 413
414 414 See :hg:`help dates` for a list of formats valid for -d/--date.
415 415
416 416 Returns 0 on success.
417 417 '''
418 418 if rev and node:
419 419 raise util.Abort(_("please specify just one revision"))
420 420
421 421 if not rev:
422 422 rev = node
423 423
424 424 if not rev:
425 425 raise util.Abort(_("please specify a revision to backout"))
426 426
427 427 date = opts.get('date')
428 428 if date:
429 429 opts['date'] = util.parsedate(date)
430 430
431 431 cmdutil.bailifchanged(repo)
432 432 node = scmutil.revsingle(repo, rev).node()
433 433
434 434 op1, op2 = repo.dirstate.parents()
435 435 a = repo.changelog.ancestor(op1, node)
436 436 if a != node:
437 437 raise util.Abort(_('cannot backout change on a different branch'))
438 438
439 439 p1, p2 = repo.changelog.parents(node)
440 440 if p1 == nullid:
441 441 raise util.Abort(_('cannot backout a change with no parents'))
442 442 if p2 != nullid:
443 443 if not opts.get('parent'):
444 444 raise util.Abort(_('cannot backout a merge changeset'))
445 445 p = repo.lookup(opts['parent'])
446 446 if p not in (p1, p2):
447 447 raise util.Abort(_('%s is not a parent of %s') %
448 448 (short(p), short(node)))
449 449 parent = p
450 450 else:
451 451 if opts.get('parent'):
452 452 raise util.Abort(_('cannot use --parent on non-merge changeset'))
453 453 parent = p1
454 454
455 455 # the backout should appear on the same branch
456 456 wlock = repo.wlock()
457 457 try:
458 458 branch = repo.dirstate.branch()
459 459 bheads = repo.branchheads(branch)
460 460 hg.clean(repo, node, show_stats=False)
461 461 repo.dirstate.setbranch(branch)
462 462 rctx = scmutil.revsingle(repo, hex(parent))
463 463 cmdutil.revert(ui, repo, rctx, repo.dirstate.parents())
464 464 if not opts.get('merge') and op1 != node:
465 465 try:
466 466 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
467 467 return hg.update(repo, op1)
468 468 finally:
469 469 ui.setconfig('ui', 'forcemerge', '')
470 470
471 471 e = cmdutil.commiteditor
472 472 if not opts['message'] and not opts['logfile']:
473 473 # we don't translate commit messages
474 474 opts['message'] = "Backed out changeset %s" % short(node)
475 475 e = cmdutil.commitforceeditor
476 476
477 477 def commitfunc(ui, repo, message, match, opts):
478 478 return repo.commit(message, opts.get('user'), opts.get('date'),
479 479 match, editor=e)
480 480 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
481 481 cmdutil.commitstatus(repo, newnode, branch, bheads)
482 482
483 483 def nice(node):
484 484 return '%d:%s' % (repo.changelog.rev(node), short(node))
485 485 ui.status(_('changeset %s backs out changeset %s\n') %
486 486 (nice(repo.changelog.tip()), nice(node)))
487 487 if opts.get('merge') and op1 != node:
488 488 hg.clean(repo, op1, show_stats=False)
489 489 ui.status(_('merging with changeset %s\n')
490 490 % nice(repo.changelog.tip()))
491 491 try:
492 492 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
493 493 return hg.merge(repo, hex(repo.changelog.tip()))
494 494 finally:
495 495 ui.setconfig('ui', 'forcemerge', '')
496 496 finally:
497 497 wlock.release()
498 498 return 0
499 499
500 500 @command('bisect',
501 501 [('r', 'reset', False, _('reset bisect state')),
502 502 ('g', 'good', False, _('mark changeset good')),
503 503 ('b', 'bad', False, _('mark changeset bad')),
504 504 ('s', 'skip', False, _('skip testing changeset')),
505 505 ('e', 'extend', False, _('extend the bisect range')),
506 506 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
507 507 ('U', 'noupdate', False, _('do not update to target'))],
508 508 _("[-gbsr] [-U] [-c CMD] [REV]"))
509 509 def bisect(ui, repo, rev=None, extra=None, command=None,
510 510 reset=None, good=None, bad=None, skip=None, extend=None,
511 511 noupdate=None):
512 512 """subdivision search of changesets
513 513
514 514 This command helps to find changesets which introduce problems. To
515 515 use, mark the earliest changeset you know exhibits the problem as
516 516 bad, then mark the latest changeset which is free from the problem
517 517 as good. Bisect will update your working directory to a revision
518 518 for testing (unless the -U/--noupdate option is specified). Once
519 519 you have performed tests, mark the working directory as good or
520 520 bad, and bisect will either update to another candidate changeset
521 521 or announce that it has found the bad revision.
522 522
523 523 As a shortcut, you can also use the revision argument to mark a
524 524 revision as good or bad without checking it out first.
525 525
526 526 If you supply a command, it will be used for automatic bisection.
527 527 The environment variable HG_NODE will contain the ID of the
528 528 changeset being tested. The exit status of the command will be
529 529 used to mark revisions as good or bad: status 0 means good, 125
530 530 means to skip the revision, 127 (command not found) will abort the
531 531 bisection, and any other non-zero exit status means the revision
532 532 is bad.
533 533
534 534 .. container:: verbose
535 535
536 536 Some examples:
537 537
538 538 - start a bisection with known bad revision 12, and good revision 34::
539 539
540 540 hg bisect --bad 34
541 541 hg bisect --good 12
542 542
543 543 - advance the current bisection by marking current revision as good or
544 544 bad::
545 545
546 546 hg bisect --good
547 547 hg bisect --bad
548 548
549 549 - mark the current revision, or a known revision, to be skipped (e.g. if
550 550 that revision is not usable because of another issue)::
551 551
552 552 hg bisect --skip
553 553 hg bisect --skip 23
554 554
555 555 - skip all revisions that do not touch directories ``foo`` or ``bar``
556 556
557 557 hg bisect --skip '!( file("path:foo") & file("path:bar") )'
558 558
559 559 - forget the current bisection::
560 560
561 561 hg bisect --reset
562 562
563 563 - use 'make && make tests' to automatically find the first broken
564 564 revision::
565 565
566 566 hg bisect --reset
567 567 hg bisect --bad 34
568 568 hg bisect --good 12
569 569 hg bisect --command 'make && make tests'
570 570
571 571 - see all changesets whose states are already known in the current
572 572 bisection::
573 573
574 574 hg log -r "bisect(pruned)"
575 575
576 576 - see the changeset currently being bisected (especially useful
577 577 if running with -U/--noupdate)::
578 578
579 579 hg log -r "bisect(current)"
580 580
581 581 - see all changesets that took part in the current bisection::
582 582
583 583 hg log -r "bisect(range)"
584 584
585 585 - with the graphlog extension, you can even get a nice graph::
586 586
587 587 hg log --graph -r "bisect(range)"
588 588
589 589 See :hg:`help revsets` for more about the `bisect()` keyword.
590 590
591 591 Returns 0 on success.
592 592 """
593 593 def extendbisectrange(nodes, good):
594 594 # bisect is incomplete when it ends on a merge node and
595 595 # one of the parent was not checked.
596 596 parents = repo[nodes[0]].parents()
597 597 if len(parents) > 1:
598 598 side = good and state['bad'] or state['good']
599 599 num = len(set(i.node() for i in parents) & set(side))
600 600 if num == 1:
601 601 return parents[0].ancestor(parents[1])
602 602 return None
603 603
604 604 def print_result(nodes, good):
605 605 displayer = cmdutil.show_changeset(ui, repo, {})
606 606 if len(nodes) == 1:
607 607 # narrowed it down to a single revision
608 608 if good:
609 609 ui.write(_("The first good revision is:\n"))
610 610 else:
611 611 ui.write(_("The first bad revision is:\n"))
612 612 displayer.show(repo[nodes[0]])
613 613 extendnode = extendbisectrange(nodes, good)
614 614 if extendnode is not None:
615 615 ui.write(_('Not all ancestors of this changeset have been'
616 616 ' checked.\nUse bisect --extend to continue the '
617 617 'bisection from\nthe common ancestor, %s.\n')
618 618 % extendnode)
619 619 else:
620 620 # multiple possible revisions
621 621 if good:
622 622 ui.write(_("Due to skipped revisions, the first "
623 623 "good revision could be any of:\n"))
624 624 else:
625 625 ui.write(_("Due to skipped revisions, the first "
626 626 "bad revision could be any of:\n"))
627 627 for n in nodes:
628 628 displayer.show(repo[n])
629 629 displayer.close()
630 630
631 631 def check_state(state, interactive=True):
632 632 if not state['good'] or not state['bad']:
633 633 if (good or bad or skip or reset) and interactive:
634 634 return
635 635 if not state['good']:
636 636 raise util.Abort(_('cannot bisect (no known good revisions)'))
637 637 else:
638 638 raise util.Abort(_('cannot bisect (no known bad revisions)'))
639 639 return True
640 640
641 641 # backward compatibility
642 642 if rev in "good bad reset init".split():
643 643 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
644 644 cmd, rev, extra = rev, extra, None
645 645 if cmd == "good":
646 646 good = True
647 647 elif cmd == "bad":
648 648 bad = True
649 649 else:
650 650 reset = True
651 651 elif extra or good + bad + skip + reset + extend + bool(command) > 1:
652 652 raise util.Abort(_('incompatible arguments'))
653 653
654 654 if reset:
655 655 p = repo.join("bisect.state")
656 656 if os.path.exists(p):
657 657 os.unlink(p)
658 658 return
659 659
660 660 state = hbisect.load_state(repo)
661 661
662 662 if command:
663 663 changesets = 1
664 664 try:
665 665 node = state['current'][0]
666 666 except LookupError:
667 667 if noupdate:
668 668 raise util.Abort(_('current bisect revision is unknown - '
669 669 'start a new bisect to fix'))
670 670 node, p2 = repo.dirstate.parents()
671 671 if p2 != nullid:
672 672 raise util.Abort(_('current bisect revision is a merge'))
673 673 try:
674 674 while changesets:
675 675 # update state
676 676 state['current'] = [node]
677 677 hbisect.save_state(repo, state)
678 678 status = util.system(command,
679 679 environ={'HG_NODE': hex(node)},
680 680 out=ui.fout)
681 681 if status == 125:
682 682 transition = "skip"
683 683 elif status == 0:
684 684 transition = "good"
685 685 # status < 0 means process was killed
686 686 elif status == 127:
687 687 raise util.Abort(_("failed to execute %s") % command)
688 688 elif status < 0:
689 689 raise util.Abort(_("%s killed") % command)
690 690 else:
691 691 transition = "bad"
692 692 ctx = scmutil.revsingle(repo, rev, node)
693 693 rev = None # clear for future iterations
694 694 state[transition].append(ctx.node())
695 695 ui.status(_('changeset %d:%s: %s\n') % (ctx, ctx, transition))
696 696 check_state(state, interactive=False)
697 697 # bisect
698 698 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
699 699 # update to next check
700 700 node = nodes[0]
701 701 if not noupdate:
702 702 cmdutil.bailifchanged(repo)
703 703 hg.clean(repo, node, show_stats=False)
704 704 finally:
705 705 state['current'] = [node]
706 706 hbisect.save_state(repo, state)
707 707 print_result(nodes, good)
708 708 return
709 709
710 710 # update state
711 711
712 712 if rev:
713 713 nodes = [repo.lookup(i) for i in scmutil.revrange(repo, [rev])]
714 714 else:
715 715 nodes = [repo.lookup('.')]
716 716
717 717 if good or bad or skip:
718 718 if good:
719 719 state['good'] += nodes
720 720 elif bad:
721 721 state['bad'] += nodes
722 722 elif skip:
723 723 state['skip'] += nodes
724 724 hbisect.save_state(repo, state)
725 725
726 726 if not check_state(state):
727 727 return
728 728
729 729 # actually bisect
730 730 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
731 731 if extend:
732 732 if not changesets:
733 733 extendnode = extendbisectrange(nodes, good)
734 734 if extendnode is not None:
735 735 ui.write(_("Extending search to changeset %d:%s\n"
736 736 % (extendnode.rev(), extendnode)))
737 737 state['current'] = [extendnode.node()]
738 738 hbisect.save_state(repo, state)
739 739 if noupdate:
740 740 return
741 741 cmdutil.bailifchanged(repo)
742 742 return hg.clean(repo, extendnode.node())
743 743 raise util.Abort(_("nothing to extend"))
744 744
745 745 if changesets == 0:
746 746 print_result(nodes, good)
747 747 else:
748 748 assert len(nodes) == 1 # only a single node can be tested next
749 749 node = nodes[0]
750 750 # compute the approximate number of remaining tests
751 751 tests, size = 0, 2
752 752 while size <= changesets:
753 753 tests, size = tests + 1, size * 2
754 754 rev = repo.changelog.rev(node)
755 755 ui.write(_("Testing changeset %d:%s "
756 756 "(%d changesets remaining, ~%d tests)\n")
757 757 % (rev, short(node), changesets, tests))
758 758 state['current'] = [node]
759 759 hbisect.save_state(repo, state)
760 760 if not noupdate:
761 761 cmdutil.bailifchanged(repo)
762 762 return hg.clean(repo, node)
763 763
764 764 @command('bookmarks|bookmark',
765 765 [('f', 'force', False, _('force')),
766 766 ('r', 'rev', '', _('revision'), _('REV')),
767 767 ('d', 'delete', False, _('delete a given bookmark')),
768 768 ('m', 'rename', '', _('rename a given bookmark'), _('NAME')),
769 769 ('i', 'inactive', False, _('mark a bookmark inactive'))],
770 770 _('hg bookmarks [-f] [-d] [-i] [-m NAME] [-r REV] [NAME]'))
771 771 def bookmark(ui, repo, mark=None, rev=None, force=False, delete=False,
772 772 rename=None, inactive=False):
773 773 '''track a line of development with movable markers
774 774
775 775 Bookmarks are pointers to certain commits that move when committing.
776 776 Bookmarks are local. They can be renamed, copied and deleted. It is
777 777 possible to use :hg:`merge NAME` to merge from a given bookmark, and
778 778 :hg:`update NAME` to update to a given bookmark.
779 779
780 780 You can use :hg:`bookmark NAME` to set a bookmark on the working
781 781 directory's parent revision with the given name. If you specify
782 782 a revision using -r REV (where REV may be an existing bookmark),
783 783 the bookmark is assigned to that revision.
784 784
785 785 Bookmarks can be pushed and pulled between repositories (see :hg:`help
786 786 push` and :hg:`help pull`). This requires both the local and remote
787 787 repositories to support bookmarks. For versions prior to 1.8, this means
788 788 the bookmarks extension must be enabled.
789 789
790 790 If you set a bookmark called '@', new clones of the repository will
791 791 have that revision checked out (and the bookmark made active) by
792 792 default.
793 793
794 794 With -i/--inactive, the new bookmark will not be made the active
795 795 bookmark. If -r/--rev is given, the new bookmark will not be made
796 796 active even if -i/--inactive is not given. If no NAME is given, the
797 797 current active bookmark will be marked inactive.
798 798 '''
799 799 hexfn = ui.debugflag and hex or short
800 800 marks = repo._bookmarks
801 801 cur = repo.changectx('.').node()
802 802
803 803 def checkformat(mark):
804 804 mark = mark.strip()
805 805 if not mark:
806 806 raise util.Abort(_("bookmark names cannot consist entirely of "
807 807 "whitespace"))
808 808 scmutil.checknewlabel(repo, mark, 'bookmark')
809 809 return mark
810 810
811 811 def checkconflict(repo, mark, force=False, target=None):
812 812 if mark in marks and not force:
813 813 if target:
814 814 if marks[mark] == target and target == cur:
815 815 # re-activating a bookmark
816 816 return
817 817 anc = repo.changelog.ancestors([repo[target].rev()])
818 818 bmctx = repo[marks[mark]]
819 819 divs = [repo[b].node() for b in marks
820 820 if b.split('@', 1)[0] == mark.split('@', 1)[0]]
821 821
822 822 # allow resolving a single divergent bookmark even if moving
823 823 # the bookmark across branches when a revision is specified
824 824 # that contains a divergent bookmark
825 825 if bmctx.rev() not in anc and target in divs:
826 826 bookmarks.deletedivergent(repo, [target], mark)
827 827 return
828 828
829 829 deletefrom = [b for b in divs
830 830 if repo[b].rev() in anc or b == target]
831 831 bookmarks.deletedivergent(repo, deletefrom, mark)
832 832 if bmctx.rev() in anc:
833 833 ui.status(_("moving bookmark '%s' forward from %s\n") %
834 834 (mark, short(bmctx.node())))
835 835 return
836 836 raise util.Abort(_("bookmark '%s' already exists "
837 837 "(use -f to force)") % mark)
838 838 if ((mark in repo.branchmap() or mark == repo.dirstate.branch())
839 839 and not force):
840 840 raise util.Abort(
841 841 _("a bookmark cannot have the name of an existing branch"))
842 842
843 843 if delete and rename:
844 844 raise util.Abort(_("--delete and --rename are incompatible"))
845 845 if delete and rev:
846 846 raise util.Abort(_("--rev is incompatible with --delete"))
847 847 if rename and rev:
848 848 raise util.Abort(_("--rev is incompatible with --rename"))
849 849 if mark is None and (delete or rev):
850 850 raise util.Abort(_("bookmark name required"))
851 851
852 852 if delete:
853 853 if mark not in marks:
854 854 raise util.Abort(_("bookmark '%s' does not exist") % mark)
855 855 if mark == repo._bookmarkcurrent:
856 856 bookmarks.setcurrent(repo, None)
857 857 del marks[mark]
858 858 marks.write()
859 859
860 860 elif rename:
861 861 if mark is None:
862 862 raise util.Abort(_("new bookmark name required"))
863 863 mark = checkformat(mark)
864 864 if rename not in marks:
865 865 raise util.Abort(_("bookmark '%s' does not exist") % rename)
866 866 checkconflict(repo, mark, force)
867 867 marks[mark] = marks[rename]
868 868 if repo._bookmarkcurrent == rename and not inactive:
869 869 bookmarks.setcurrent(repo, mark)
870 870 del marks[rename]
871 871 marks.write()
872 872
873 873 elif mark is not None:
874 874 mark = checkformat(mark)
875 875 if inactive and mark == repo._bookmarkcurrent:
876 876 bookmarks.setcurrent(repo, None)
877 877 return
878 878 tgt = cur
879 879 if rev:
880 880 tgt = scmutil.revsingle(repo, rev).node()
881 881 checkconflict(repo, mark, force, tgt)
882 882 marks[mark] = tgt
883 883 if not inactive and cur == marks[mark] and not rev:
884 884 bookmarks.setcurrent(repo, mark)
885 885 elif cur != tgt and mark == repo._bookmarkcurrent:
886 886 bookmarks.setcurrent(repo, None)
887 887 marks.write()
888 888
889 889 # Same message whether trying to deactivate the current bookmark (-i
890 890 # with no NAME) or listing bookmarks
891 891 elif len(marks) == 0:
892 892 ui.status(_("no bookmarks set\n"))
893 893
894 894 elif inactive:
895 895 if not repo._bookmarkcurrent:
896 896 ui.status(_("no active bookmark\n"))
897 897 else:
898 898 bookmarks.setcurrent(repo, None)
899 899
900 900 else: # show bookmarks
901 901 for bmark, n in sorted(marks.iteritems()):
902 902 current = repo._bookmarkcurrent
903 903 if bmark == current:
904 904 prefix, label = '*', 'bookmarks.current'
905 905 else:
906 906 prefix, label = ' ', ''
907 907
908 908 if ui.quiet:
909 909 ui.write("%s\n" % bmark, label=label)
910 910 else:
911 911 ui.write(" %s %-25s %d:%s\n" % (
912 912 prefix, bmark, repo.changelog.rev(n), hexfn(n)),
913 913 label=label)
914 914
915 915 @command('branch',
916 916 [('f', 'force', None,
917 917 _('set branch name even if it shadows an existing branch')),
918 918 ('C', 'clean', None, _('reset branch name to parent branch name'))],
919 919 _('[-fC] [NAME]'))
920 920 def branch(ui, repo, label=None, **opts):
921 921 """set or show the current branch name
922 922
923 923 .. note::
924 924 Branch names are permanent and global. Use :hg:`bookmark` to create a
925 925 light-weight bookmark instead. See :hg:`help glossary` for more
926 926 information about named branches and bookmarks.
927 927
928 928 With no argument, show the current branch name. With one argument,
929 929 set the working directory branch name (the branch will not exist
930 930 in the repository until the next commit). Standard practice
931 931 recommends that primary development take place on the 'default'
932 932 branch.
933 933
934 934 Unless -f/--force is specified, branch will not let you set a
935 935 branch name that already exists, even if it's inactive.
936 936
937 937 Use -C/--clean to reset the working directory branch to that of
938 938 the parent of the working directory, negating a previous branch
939 939 change.
940 940
941 941 Use the command :hg:`update` to switch to an existing branch. Use
942 942 :hg:`commit --close-branch` to mark this branch as closed.
943 943
944 944 Returns 0 on success.
945 945 """
946 946 if label:
947 947 label = label.strip()
948 948
949 949 if not opts.get('clean') and not label:
950 950 ui.write("%s\n" % repo.dirstate.branch())
951 951 return
952 952
953 953 wlock = repo.wlock()
954 954 try:
955 955 if opts.get('clean'):
956 956 label = repo[None].p1().branch()
957 957 repo.dirstate.setbranch(label)
958 958 ui.status(_('reset working directory to branch %s\n') % label)
959 959 elif label:
960 960 if not opts.get('force') and label in repo.branchmap():
961 961 if label not in [p.branch() for p in repo.parents()]:
962 962 raise util.Abort(_('a branch of the same name already'
963 963 ' exists'),
964 964 # i18n: "it" refers to an existing branch
965 965 hint=_("use 'hg update' to switch to it"))
966 966 scmutil.checknewlabel(repo, label, 'branch')
967 967 repo.dirstate.setbranch(label)
968 968 ui.status(_('marked working directory as branch %s\n') % label)
969 969 ui.status(_('(branches are permanent and global, '
970 970 'did you want a bookmark?)\n'))
971 971 finally:
972 972 wlock.release()
973 973
974 974 @command('branches',
975 975 [('a', 'active', False, _('show only branches that have unmerged heads')),
976 976 ('c', 'closed', False, _('show normal and closed branches'))],
977 977 _('[-ac]'))
978 978 def branches(ui, repo, active=False, closed=False):
979 979 """list repository named branches
980 980
981 981 List the repository's named branches, indicating which ones are
982 982 inactive. If -c/--closed is specified, also list branches which have
983 983 been marked closed (see :hg:`commit --close-branch`).
984 984
985 985 If -a/--active is specified, only show active branches. A branch
986 986 is considered active if it contains repository heads.
987 987
988 988 Use the command :hg:`update` to switch to an existing branch.
989 989
990 990 Returns 0.
991 991 """
992 992
993 993 hexfunc = ui.debugflag and hex or short
994 994
995 995 activebranches = set([repo[n].branch() for n in repo.heads()])
996 996 branches = []
997 997 for tag, heads in repo.branchmap().iteritems():
998 998 for h in reversed(heads):
999 999 ctx = repo[h]
1000 1000 isopen = not ctx.closesbranch()
1001 1001 if isopen:
1002 1002 tip = ctx
1003 1003 break
1004 1004 else:
1005 1005 tip = repo[heads[-1]]
1006 1006 isactive = tag in activebranches and isopen
1007 1007 branches.append((tip, isactive, isopen))
1008 1008 branches.sort(key=lambda i: (i[1], i[0].rev(), i[0].branch(), i[2]),
1009 1009 reverse=True)
1010 1010
1011 1011 for ctx, isactive, isopen in branches:
1012 1012 if (not active) or isactive:
1013 1013 if isactive:
1014 1014 label = 'branches.active'
1015 1015 notice = ''
1016 1016 elif not isopen:
1017 1017 if not closed:
1018 1018 continue
1019 1019 label = 'branches.closed'
1020 1020 notice = _(' (closed)')
1021 1021 else:
1022 1022 label = 'branches.inactive'
1023 1023 notice = _(' (inactive)')
1024 1024 if ctx.branch() == repo.dirstate.branch():
1025 1025 label = 'branches.current'
1026 1026 rev = str(ctx.rev()).rjust(31 - encoding.colwidth(ctx.branch()))
1027 1027 rev = ui.label('%s:%s' % (rev, hexfunc(ctx.node())),
1028 1028 'log.changeset changeset.%s' % ctx.phasestr())
1029 1029 tag = ui.label(ctx.branch(), label)
1030 1030 if ui.quiet:
1031 1031 ui.write("%s\n" % tag)
1032 1032 else:
1033 1033 ui.write("%s %s%s\n" % (tag, rev, notice))
1034 1034
1035 1035 @command('bundle',
1036 1036 [('f', 'force', None, _('run even when the destination is unrelated')),
1037 1037 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
1038 1038 _('REV')),
1039 1039 ('b', 'branch', [], _('a specific branch you would like to bundle'),
1040 1040 _('BRANCH')),
1041 1041 ('', 'base', [],
1042 1042 _('a base changeset assumed to be available at the destination'),
1043 1043 _('REV')),
1044 1044 ('a', 'all', None, _('bundle all changesets in the repository')),
1045 1045 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
1046 1046 ] + remoteopts,
1047 1047 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]'))
1048 1048 def bundle(ui, repo, fname, dest=None, **opts):
1049 1049 """create a changegroup file
1050 1050
1051 1051 Generate a compressed changegroup file collecting changesets not
1052 1052 known to be in another repository.
1053 1053
1054 1054 If you omit the destination repository, then hg assumes the
1055 1055 destination will have all the nodes you specify with --base
1056 1056 parameters. To create a bundle containing all changesets, use
1057 1057 -a/--all (or --base null).
1058 1058
1059 1059 You can change compression method with the -t/--type option.
1060 1060 The available compression methods are: none, bzip2, and
1061 1061 gzip (by default, bundles are compressed using bzip2).
1062 1062
1063 1063 The bundle file can then be transferred using conventional means
1064 1064 and applied to another repository with the unbundle or pull
1065 1065 command. This is useful when direct push and pull are not
1066 1066 available or when exporting an entire repository is undesirable.
1067 1067
1068 1068 Applying bundles preserves all changeset contents including
1069 1069 permissions, copy/rename information, and revision history.
1070 1070
1071 1071 Returns 0 on success, 1 if no changes found.
1072 1072 """
1073 1073 revs = None
1074 1074 if 'rev' in opts:
1075 1075 revs = scmutil.revrange(repo, opts['rev'])
1076 1076
1077 1077 bundletype = opts.get('type', 'bzip2').lower()
1078 1078 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
1079 1079 bundletype = btypes.get(bundletype)
1080 1080 if bundletype not in changegroup.bundletypes:
1081 1081 raise util.Abort(_('unknown bundle type specified with --type'))
1082 1082
1083 1083 if opts.get('all'):
1084 1084 base = ['null']
1085 1085 else:
1086 1086 base = scmutil.revrange(repo, opts.get('base'))
1087 1087 if base:
1088 1088 if dest:
1089 1089 raise util.Abort(_("--base is incompatible with specifying "
1090 1090 "a destination"))
1091 1091 common = [repo.lookup(rev) for rev in base]
1092 1092 heads = revs and map(repo.lookup, revs) or revs
1093 1093 cg = repo.getbundle('bundle', heads=heads, common=common)
1094 1094 outgoing = None
1095 1095 else:
1096 1096 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1097 1097 dest, branches = hg.parseurl(dest, opts.get('branch'))
1098 1098 other = hg.peer(repo, opts, dest)
1099 1099 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1100 1100 heads = revs and map(repo.lookup, revs) or revs
1101 1101 outgoing = discovery.findcommonoutgoing(repo, other,
1102 1102 onlyheads=heads,
1103 1103 force=opts.get('force'),
1104 1104 portable=True)
1105 1105 cg = repo.getlocalbundle('bundle', outgoing)
1106 1106 if not cg:
1107 1107 scmutil.nochangesfound(ui, repo, outgoing and outgoing.excluded)
1108 1108 return 1
1109 1109
1110 1110 changegroup.writebundle(cg, fname, bundletype)
1111 1111
1112 1112 @command('cat',
1113 1113 [('o', 'output', '',
1114 1114 _('print output to file with formatted name'), _('FORMAT')),
1115 1115 ('r', 'rev', '', _('print the given revision'), _('REV')),
1116 1116 ('', 'decode', None, _('apply any matching decode filter')),
1117 1117 ] + walkopts,
1118 1118 _('[OPTION]... FILE...'))
1119 1119 def cat(ui, repo, file1, *pats, **opts):
1120 1120 """output the current or given revision of files
1121 1121
1122 1122 Print the specified files as they were at the given revision. If
1123 1123 no revision is given, the parent of the working directory is used,
1124 1124 or tip if no revision is checked out.
1125 1125
1126 1126 Output may be to a file, in which case the name of the file is
1127 1127 given using a format string. The formatting rules are the same as
1128 1128 for the export command, with the following additions:
1129 1129
1130 1130 :``%s``: basename of file being printed
1131 1131 :``%d``: dirname of file being printed, or '.' if in repository root
1132 1132 :``%p``: root-relative path name of file being printed
1133 1133
1134 1134 Returns 0 on success.
1135 1135 """
1136 1136 ctx = scmutil.revsingle(repo, opts.get('rev'))
1137 1137 err = 1
1138 1138 m = scmutil.match(ctx, (file1,) + pats, opts)
1139 1139 for abs in ctx.walk(m):
1140 1140 fp = cmdutil.makefileobj(repo, opts.get('output'), ctx.node(),
1141 1141 pathname=abs)
1142 1142 data = ctx[abs].data()
1143 1143 if opts.get('decode'):
1144 1144 data = repo.wwritedata(abs, data)
1145 1145 fp.write(data)
1146 1146 fp.close()
1147 1147 err = 0
1148 1148 return err
1149 1149
1150 1150 @command('^clone',
1151 1151 [('U', 'noupdate', None,
1152 1152 _('the clone will include an empty working copy (only a repository)')),
1153 1153 ('u', 'updaterev', '', _('revision, tag or branch to check out'), _('REV')),
1154 1154 ('r', 'rev', [], _('include the specified changeset'), _('REV')),
1155 1155 ('b', 'branch', [], _('clone only the specified branch'), _('BRANCH')),
1156 1156 ('', 'pull', None, _('use pull protocol to copy metadata')),
1157 1157 ('', 'uncompressed', None, _('use uncompressed transfer (fast over LAN)')),
1158 1158 ] + remoteopts,
1159 1159 _('[OPTION]... SOURCE [DEST]'))
1160 1160 def clone(ui, source, dest=None, **opts):
1161 1161 """make a copy of an existing repository
1162 1162
1163 1163 Create a copy of an existing repository in a new directory.
1164 1164
1165 1165 If no destination directory name is specified, it defaults to the
1166 1166 basename of the source.
1167 1167
1168 1168 The location of the source is added to the new repository's
1169 1169 ``.hg/hgrc`` file, as the default to be used for future pulls.
1170 1170
1171 1171 Only local paths and ``ssh://`` URLs are supported as
1172 1172 destinations. For ``ssh://`` destinations, no working directory or
1173 1173 ``.hg/hgrc`` will be created on the remote side.
1174 1174
1175 1175 To pull only a subset of changesets, specify one or more revisions
1176 1176 identifiers with -r/--rev or branches with -b/--branch. The
1177 1177 resulting clone will contain only the specified changesets and
1178 1178 their ancestors. These options (or 'clone src#rev dest') imply
1179 1179 --pull, even for local source repositories. Note that specifying a
1180 1180 tag will include the tagged changeset but not the changeset
1181 1181 containing the tag.
1182 1182
1183 1183 If the source repository has a bookmark called '@' set, that
1184 1184 revision will be checked out in the new repository by default.
1185 1185
1186 1186 To check out a particular version, use -u/--update, or
1187 1187 -U/--noupdate to create a clone with no working directory.
1188 1188
1189 1189 .. container:: verbose
1190 1190
1191 1191 For efficiency, hardlinks are used for cloning whenever the
1192 1192 source and destination are on the same filesystem (note this
1193 1193 applies only to the repository data, not to the working
1194 1194 directory). Some filesystems, such as AFS, implement hardlinking
1195 1195 incorrectly, but do not report errors. In these cases, use the
1196 1196 --pull option to avoid hardlinking.
1197 1197
1198 1198 In some cases, you can clone repositories and the working
1199 1199 directory using full hardlinks with ::
1200 1200
1201 1201 $ cp -al REPO REPOCLONE
1202 1202
1203 1203 This is the fastest way to clone, but it is not always safe. The
1204 1204 operation is not atomic (making sure REPO is not modified during
1205 1205 the operation is up to you) and you have to make sure your
1206 1206 editor breaks hardlinks (Emacs and most Linux Kernel tools do
1207 1207 so). Also, this is not compatible with certain extensions that
1208 1208 place their metadata under the .hg directory, such as mq.
1209 1209
1210 1210 Mercurial will update the working directory to the first applicable
1211 1211 revision from this list:
1212 1212
1213 1213 a) null if -U or the source repository has no changesets
1214 1214 b) if -u . and the source repository is local, the first parent of
1215 1215 the source repository's working directory
1216 1216 c) the changeset specified with -u (if a branch name, this means the
1217 1217 latest head of that branch)
1218 1218 d) the changeset specified with -r
1219 1219 e) the tipmost head specified with -b
1220 1220 f) the tipmost head specified with the url#branch source syntax
1221 1221 g) the revision marked with the '@' bookmark, if present
1222 1222 h) the tipmost head of the default branch
1223 1223 i) tip
1224 1224
1225 1225 Examples:
1226 1226
1227 1227 - clone a remote repository to a new directory named hg/::
1228 1228
1229 1229 hg clone http://selenic.com/hg
1230 1230
1231 1231 - create a lightweight local clone::
1232 1232
1233 1233 hg clone project/ project-feature/
1234 1234
1235 1235 - clone from an absolute path on an ssh server (note double-slash)::
1236 1236
1237 1237 hg clone ssh://user@server//home/projects/alpha/
1238 1238
1239 1239 - do a high-speed clone over a LAN while checking out a
1240 1240 specified version::
1241 1241
1242 1242 hg clone --uncompressed http://server/repo -u 1.5
1243 1243
1244 1244 - create a repository without changesets after a particular revision::
1245 1245
1246 1246 hg clone -r 04e544 experimental/ good/
1247 1247
1248 1248 - clone (and track) a particular named branch::
1249 1249
1250 1250 hg clone http://selenic.com/hg#stable
1251 1251
1252 1252 See :hg:`help urls` for details on specifying URLs.
1253 1253
1254 1254 Returns 0 on success.
1255 1255 """
1256 1256 if opts.get('noupdate') and opts.get('updaterev'):
1257 1257 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
1258 1258
1259 1259 r = hg.clone(ui, opts, source, dest,
1260 1260 pull=opts.get('pull'),
1261 1261 stream=opts.get('uncompressed'),
1262 1262 rev=opts.get('rev'),
1263 1263 update=opts.get('updaterev') or not opts.get('noupdate'),
1264 1264 branch=opts.get('branch'))
1265 1265
1266 1266 return r is None
1267 1267
1268 1268 @command('^commit|ci',
1269 1269 [('A', 'addremove', None,
1270 1270 _('mark new/missing files as added/removed before committing')),
1271 1271 ('', 'close-branch', None,
1272 1272 _('mark a branch as closed, hiding it from the branch list')),
1273 1273 ('', 'amend', None, _('amend the parent of the working dir')),
1274 1274 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1275 1275 _('[OPTION]... [FILE]...'))
1276 1276 def commit(ui, repo, *pats, **opts):
1277 1277 """commit the specified files or all outstanding changes
1278 1278
1279 1279 Commit changes to the given files into the repository. Unlike a
1280 1280 centralized SCM, this operation is a local operation. See
1281 1281 :hg:`push` for a way to actively distribute your changes.
1282 1282
1283 1283 If a list of files is omitted, all changes reported by :hg:`status`
1284 1284 will be committed.
1285 1285
1286 1286 If you are committing the result of a merge, do not provide any
1287 1287 filenames or -I/-X filters.
1288 1288
1289 1289 If no commit message is specified, Mercurial starts your
1290 1290 configured editor where you can enter a message. In case your
1291 1291 commit fails, you will find a backup of your message in
1292 1292 ``.hg/last-message.txt``.
1293 1293
1294 1294 The --amend flag can be used to amend the parent of the
1295 1295 working directory with a new commit that contains the changes
1296 1296 in the parent in addition to those currently reported by :hg:`status`,
1297 1297 if there are any. The old commit is stored in a backup bundle in
1298 1298 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1299 1299 on how to restore it).
1300 1300
1301 1301 Message, user and date are taken from the amended commit unless
1302 1302 specified. When a message isn't specified on the command line,
1303 1303 the editor will open with the message of the amended commit.
1304 1304
1305 1305 It is not possible to amend public changesets (see :hg:`help phases`)
1306 1306 or changesets that have children.
1307 1307
1308 1308 See :hg:`help dates` for a list of formats valid for -d/--date.
1309 1309
1310 1310 Returns 0 on success, 1 if nothing changed.
1311 1311 """
1312 1312 if opts.get('subrepos'):
1313 1313 if opts.get('amend'):
1314 1314 raise util.Abort(_('cannot amend with --subrepos'))
1315 1315 # Let --subrepos on the command line override config setting.
1316 1316 ui.setconfig('ui', 'commitsubrepos', True)
1317 1317
1318 1318 if repo.vfs.exists('graftstate'):
1319 1319 raise util.Abort(_('cannot commit an interrupted graft operation'),
1320 1320 hint=_('use "hg graft -c" to continue graft'))
1321 1321
1322 branch = repo[None].branch()
1323 bheads = repo.branchheads(branch)
1324
1322 1325 extra = {}
1323 1326 if opts.get('close_branch'):
1324 1327 extra['close'] = 1
1325 1328
1326 branch = repo[None].branch()
1327 bheads = repo.branchheads(branch)
1329 if not bheads:
1330 raise util.Abort(_('can only close branch heads'))
1331 elif opts.get('amend'):
1332 if repo.parents()[0].p1().branch() != branch and \
1333 repo.parents()[0].p2().branch() != branch:
1334 raise util.Abort(_('can only close branch heads'))
1328 1335
1329 1336 if opts.get('amend'):
1330 1337 if ui.configbool('ui', 'commitsubrepos'):
1331 1338 raise util.Abort(_('cannot amend with ui.commitsubrepos enabled'))
1332 1339
1333 1340 old = repo['.']
1334 1341 if old.phase() == phases.public:
1335 1342 raise util.Abort(_('cannot amend public changesets'))
1336 1343 if len(repo[None].parents()) > 1:
1337 1344 raise util.Abort(_('cannot amend while merging'))
1338 1345 if (not obsolete._enabled) and old.children():
1339 1346 raise util.Abort(_('cannot amend changeset with children'))
1340 1347
1341 1348 e = cmdutil.commiteditor
1342 1349 if opts.get('force_editor'):
1343 1350 e = cmdutil.commitforceeditor
1344 1351
1345 1352 def commitfunc(ui, repo, message, match, opts):
1346 1353 editor = e
1347 1354 # message contains text from -m or -l, if it's empty,
1348 1355 # open the editor with the old message
1349 1356 if not message:
1350 1357 message = old.description()
1351 1358 editor = cmdutil.commitforceeditor
1352 1359 return repo.commit(message,
1353 1360 opts.get('user') or old.user(),
1354 1361 opts.get('date') or old.date(),
1355 1362 match,
1356 1363 editor=editor,
1357 1364 extra=extra)
1358 1365
1359 1366 current = repo._bookmarkcurrent
1360 1367 marks = old.bookmarks()
1361 1368 node = cmdutil.amend(ui, repo, commitfunc, old, extra, pats, opts)
1362 1369 if node == old.node():
1363 1370 ui.status(_("nothing changed\n"))
1364 1371 return 1
1365 1372 elif marks:
1366 1373 ui.debug('moving bookmarks %r from %s to %s\n' %
1367 1374 (marks, old.hex(), hex(node)))
1368 1375 newmarks = repo._bookmarks
1369 1376 for bm in marks:
1370 1377 newmarks[bm] = node
1371 1378 if bm == current:
1372 1379 bookmarks.setcurrent(repo, bm)
1373 1380 newmarks.write()
1374 1381 else:
1375 1382 e = cmdutil.commiteditor
1376 1383 if opts.get('force_editor'):
1377 1384 e = cmdutil.commitforceeditor
1378 1385
1379 1386 def commitfunc(ui, repo, message, match, opts):
1380 1387 return repo.commit(message, opts.get('user'), opts.get('date'),
1381 1388 match, editor=e, extra=extra)
1382 1389
1383 1390 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1384 1391
1385 1392 if not node:
1386 1393 stat = repo.status(match=scmutil.match(repo[None], pats, opts))
1387 1394 if stat[3]:
1388 1395 ui.status(_("nothing changed (%d missing files, see "
1389 1396 "'hg status')\n") % len(stat[3]))
1390 1397 else:
1391 1398 ui.status(_("nothing changed\n"))
1392 1399 return 1
1393 1400
1394 1401 cmdutil.commitstatus(repo, node, branch, bheads, opts)
1395 1402
1396 1403 @command('copy|cp',
1397 1404 [('A', 'after', None, _('record a copy that has already occurred')),
1398 1405 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1399 1406 ] + walkopts + dryrunopts,
1400 1407 _('[OPTION]... [SOURCE]... DEST'))
1401 1408 def copy(ui, repo, *pats, **opts):
1402 1409 """mark files as copied for the next commit
1403 1410
1404 1411 Mark dest as having copies of source files. If dest is a
1405 1412 directory, copies are put in that directory. If dest is a file,
1406 1413 the source must be a single file.
1407 1414
1408 1415 By default, this command copies the contents of files as they
1409 1416 exist in the working directory. If invoked with -A/--after, the
1410 1417 operation is recorded, but no copying is performed.
1411 1418
1412 1419 This command takes effect with the next commit. To undo a copy
1413 1420 before that, see :hg:`revert`.
1414 1421
1415 1422 Returns 0 on success, 1 if errors are encountered.
1416 1423 """
1417 1424 wlock = repo.wlock(False)
1418 1425 try:
1419 1426 return cmdutil.copy(ui, repo, pats, opts)
1420 1427 finally:
1421 1428 wlock.release()
1422 1429
1423 1430 @command('debugancestor', [], _('[INDEX] REV1 REV2'))
1424 1431 def debugancestor(ui, repo, *args):
1425 1432 """find the ancestor revision of two revisions in a given index"""
1426 1433 if len(args) == 3:
1427 1434 index, rev1, rev2 = args
1428 1435 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), index)
1429 1436 lookup = r.lookup
1430 1437 elif len(args) == 2:
1431 1438 if not repo:
1432 1439 raise util.Abort(_("there is no Mercurial repository here "
1433 1440 "(.hg not found)"))
1434 1441 rev1, rev2 = args
1435 1442 r = repo.changelog
1436 1443 lookup = repo.lookup
1437 1444 else:
1438 1445 raise util.Abort(_('either two or three arguments required'))
1439 1446 a = r.ancestor(lookup(rev1), lookup(rev2))
1440 1447 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1441 1448
1442 1449 @command('debugbuilddag',
1443 1450 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
1444 1451 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
1445 1452 ('n', 'new-file', None, _('add new file at each rev'))],
1446 1453 _('[OPTION]... [TEXT]'))
1447 1454 def debugbuilddag(ui, repo, text=None,
1448 1455 mergeable_file=False,
1449 1456 overwritten_file=False,
1450 1457 new_file=False):
1451 1458 """builds a repo with a given DAG from scratch in the current empty repo
1452 1459
1453 1460 The description of the DAG is read from stdin if not given on the
1454 1461 command line.
1455 1462
1456 1463 Elements:
1457 1464
1458 1465 - "+n" is a linear run of n nodes based on the current default parent
1459 1466 - "." is a single node based on the current default parent
1460 1467 - "$" resets the default parent to null (implied at the start);
1461 1468 otherwise the default parent is always the last node created
1462 1469 - "<p" sets the default parent to the backref p
1463 1470 - "*p" is a fork at parent p, which is a backref
1464 1471 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
1465 1472 - "/p2" is a merge of the preceding node and p2
1466 1473 - ":tag" defines a local tag for the preceding node
1467 1474 - "@branch" sets the named branch for subsequent nodes
1468 1475 - "#...\\n" is a comment up to the end of the line
1469 1476
1470 1477 Whitespace between the above elements is ignored.
1471 1478
1472 1479 A backref is either
1473 1480
1474 1481 - a number n, which references the node curr-n, where curr is the current
1475 1482 node, or
1476 1483 - the name of a local tag you placed earlier using ":tag", or
1477 1484 - empty to denote the default parent.
1478 1485
1479 1486 All string valued-elements are either strictly alphanumeric, or must
1480 1487 be enclosed in double quotes ("..."), with "\\" as escape character.
1481 1488 """
1482 1489
1483 1490 if text is None:
1484 1491 ui.status(_("reading DAG from stdin\n"))
1485 1492 text = ui.fin.read()
1486 1493
1487 1494 cl = repo.changelog
1488 1495 if len(cl) > 0:
1489 1496 raise util.Abort(_('repository is not empty'))
1490 1497
1491 1498 # determine number of revs in DAG
1492 1499 total = 0
1493 1500 for type, data in dagparser.parsedag(text):
1494 1501 if type == 'n':
1495 1502 total += 1
1496 1503
1497 1504 if mergeable_file:
1498 1505 linesperrev = 2
1499 1506 # make a file with k lines per rev
1500 1507 initialmergedlines = [str(i) for i in xrange(0, total * linesperrev)]
1501 1508 initialmergedlines.append("")
1502 1509
1503 1510 tags = []
1504 1511
1505 1512 lock = tr = None
1506 1513 try:
1507 1514 lock = repo.lock()
1508 1515 tr = repo.transaction("builddag")
1509 1516
1510 1517 at = -1
1511 1518 atbranch = 'default'
1512 1519 nodeids = []
1513 1520 id = 0
1514 1521 ui.progress(_('building'), id, unit=_('revisions'), total=total)
1515 1522 for type, data in dagparser.parsedag(text):
1516 1523 if type == 'n':
1517 1524 ui.note(('node %s\n' % str(data)))
1518 1525 id, ps = data
1519 1526
1520 1527 files = []
1521 1528 fctxs = {}
1522 1529
1523 1530 p2 = None
1524 1531 if mergeable_file:
1525 1532 fn = "mf"
1526 1533 p1 = repo[ps[0]]
1527 1534 if len(ps) > 1:
1528 1535 p2 = repo[ps[1]]
1529 1536 pa = p1.ancestor(p2)
1530 1537 base, local, other = [x[fn].data() for x in (pa, p1,
1531 1538 p2)]
1532 1539 m3 = simplemerge.Merge3Text(base, local, other)
1533 1540 ml = [l.strip() for l in m3.merge_lines()]
1534 1541 ml.append("")
1535 1542 elif at > 0:
1536 1543 ml = p1[fn].data().split("\n")
1537 1544 else:
1538 1545 ml = initialmergedlines
1539 1546 ml[id * linesperrev] += " r%i" % id
1540 1547 mergedtext = "\n".join(ml)
1541 1548 files.append(fn)
1542 1549 fctxs[fn] = context.memfilectx(fn, mergedtext)
1543 1550
1544 1551 if overwritten_file:
1545 1552 fn = "of"
1546 1553 files.append(fn)
1547 1554 fctxs[fn] = context.memfilectx(fn, "r%i\n" % id)
1548 1555
1549 1556 if new_file:
1550 1557 fn = "nf%i" % id
1551 1558 files.append(fn)
1552 1559 fctxs[fn] = context.memfilectx(fn, "r%i\n" % id)
1553 1560 if len(ps) > 1:
1554 1561 if not p2:
1555 1562 p2 = repo[ps[1]]
1556 1563 for fn in p2:
1557 1564 if fn.startswith("nf"):
1558 1565 files.append(fn)
1559 1566 fctxs[fn] = p2[fn]
1560 1567
1561 1568 def fctxfn(repo, cx, path):
1562 1569 return fctxs.get(path)
1563 1570
1564 1571 if len(ps) == 0 or ps[0] < 0:
1565 1572 pars = [None, None]
1566 1573 elif len(ps) == 1:
1567 1574 pars = [nodeids[ps[0]], None]
1568 1575 else:
1569 1576 pars = [nodeids[p] for p in ps]
1570 1577 cx = context.memctx(repo, pars, "r%i" % id, files, fctxfn,
1571 1578 date=(id, 0),
1572 1579 user="debugbuilddag",
1573 1580 extra={'branch': atbranch})
1574 1581 nodeid = repo.commitctx(cx)
1575 1582 nodeids.append(nodeid)
1576 1583 at = id
1577 1584 elif type == 'l':
1578 1585 id, name = data
1579 1586 ui.note(('tag %s\n' % name))
1580 1587 tags.append("%s %s\n" % (hex(repo.changelog.node(id)), name))
1581 1588 elif type == 'a':
1582 1589 ui.note(('branch %s\n' % data))
1583 1590 atbranch = data
1584 1591 ui.progress(_('building'), id, unit=_('revisions'), total=total)
1585 1592 tr.close()
1586 1593
1587 1594 if tags:
1588 1595 repo.opener.write("localtags", "".join(tags))
1589 1596 finally:
1590 1597 ui.progress(_('building'), None)
1591 1598 release(tr, lock)
1592 1599
1593 1600 @command('debugbundle', [('a', 'all', None, _('show all details'))], _('FILE'))
1594 1601 def debugbundle(ui, bundlepath, all=None, **opts):
1595 1602 """lists the contents of a bundle"""
1596 1603 f = hg.openpath(ui, bundlepath)
1597 1604 try:
1598 1605 gen = changegroup.readbundle(f, bundlepath)
1599 1606 if all:
1600 1607 ui.write(("format: id, p1, p2, cset, delta base, len(delta)\n"))
1601 1608
1602 1609 def showchunks(named):
1603 1610 ui.write("\n%s\n" % named)
1604 1611 chain = None
1605 1612 while True:
1606 1613 chunkdata = gen.deltachunk(chain)
1607 1614 if not chunkdata:
1608 1615 break
1609 1616 node = chunkdata['node']
1610 1617 p1 = chunkdata['p1']
1611 1618 p2 = chunkdata['p2']
1612 1619 cs = chunkdata['cs']
1613 1620 deltabase = chunkdata['deltabase']
1614 1621 delta = chunkdata['delta']
1615 1622 ui.write("%s %s %s %s %s %s\n" %
1616 1623 (hex(node), hex(p1), hex(p2),
1617 1624 hex(cs), hex(deltabase), len(delta)))
1618 1625 chain = node
1619 1626
1620 1627 chunkdata = gen.changelogheader()
1621 1628 showchunks("changelog")
1622 1629 chunkdata = gen.manifestheader()
1623 1630 showchunks("manifest")
1624 1631 while True:
1625 1632 chunkdata = gen.filelogheader()
1626 1633 if not chunkdata:
1627 1634 break
1628 1635 fname = chunkdata['filename']
1629 1636 showchunks(fname)
1630 1637 else:
1631 1638 chunkdata = gen.changelogheader()
1632 1639 chain = None
1633 1640 while True:
1634 1641 chunkdata = gen.deltachunk(chain)
1635 1642 if not chunkdata:
1636 1643 break
1637 1644 node = chunkdata['node']
1638 1645 ui.write("%s\n" % hex(node))
1639 1646 chain = node
1640 1647 finally:
1641 1648 f.close()
1642 1649
1643 1650 @command('debugcheckstate', [], '')
1644 1651 def debugcheckstate(ui, repo):
1645 1652 """validate the correctness of the current dirstate"""
1646 1653 parent1, parent2 = repo.dirstate.parents()
1647 1654 m1 = repo[parent1].manifest()
1648 1655 m2 = repo[parent2].manifest()
1649 1656 errors = 0
1650 1657 for f in repo.dirstate:
1651 1658 state = repo.dirstate[f]
1652 1659 if state in "nr" and f not in m1:
1653 1660 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1654 1661 errors += 1
1655 1662 if state in "a" and f in m1:
1656 1663 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1657 1664 errors += 1
1658 1665 if state in "m" and f not in m1 and f not in m2:
1659 1666 ui.warn(_("%s in state %s, but not in either manifest\n") %
1660 1667 (f, state))
1661 1668 errors += 1
1662 1669 for f in m1:
1663 1670 state = repo.dirstate[f]
1664 1671 if state not in "nrm":
1665 1672 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1666 1673 errors += 1
1667 1674 if errors:
1668 1675 error = _(".hg/dirstate inconsistent with current parent's manifest")
1669 1676 raise util.Abort(error)
1670 1677
1671 1678 @command('debugcommands', [], _('[COMMAND]'))
1672 1679 def debugcommands(ui, cmd='', *args):
1673 1680 """list all available commands and options"""
1674 1681 for cmd, vals in sorted(table.iteritems()):
1675 1682 cmd = cmd.split('|')[0].strip('^')
1676 1683 opts = ', '.join([i[1] for i in vals[1]])
1677 1684 ui.write('%s: %s\n' % (cmd, opts))
1678 1685
1679 1686 @command('debugcomplete',
1680 1687 [('o', 'options', None, _('show the command options'))],
1681 1688 _('[-o] CMD'))
1682 1689 def debugcomplete(ui, cmd='', **opts):
1683 1690 """returns the completion list associated with the given command"""
1684 1691
1685 1692 if opts.get('options'):
1686 1693 options = []
1687 1694 otables = [globalopts]
1688 1695 if cmd:
1689 1696 aliases, entry = cmdutil.findcmd(cmd, table, False)
1690 1697 otables.append(entry[1])
1691 1698 for t in otables:
1692 1699 for o in t:
1693 1700 if "(DEPRECATED)" in o[3]:
1694 1701 continue
1695 1702 if o[0]:
1696 1703 options.append('-%s' % o[0])
1697 1704 options.append('--%s' % o[1])
1698 1705 ui.write("%s\n" % "\n".join(options))
1699 1706 return
1700 1707
1701 1708 cmdlist = cmdutil.findpossible(cmd, table)
1702 1709 if ui.verbose:
1703 1710 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1704 1711 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1705 1712
1706 1713 @command('debugdag',
1707 1714 [('t', 'tags', None, _('use tags as labels')),
1708 1715 ('b', 'branches', None, _('annotate with branch names')),
1709 1716 ('', 'dots', None, _('use dots for runs')),
1710 1717 ('s', 'spaces', None, _('separate elements by spaces'))],
1711 1718 _('[OPTION]... [FILE [REV]...]'))
1712 1719 def debugdag(ui, repo, file_=None, *revs, **opts):
1713 1720 """format the changelog or an index DAG as a concise textual description
1714 1721
1715 1722 If you pass a revlog index, the revlog's DAG is emitted. If you list
1716 1723 revision numbers, they get labeled in the output as rN.
1717 1724
1718 1725 Otherwise, the changelog DAG of the current repo is emitted.
1719 1726 """
1720 1727 spaces = opts.get('spaces')
1721 1728 dots = opts.get('dots')
1722 1729 if file_:
1723 1730 rlog = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1724 1731 revs = set((int(r) for r in revs))
1725 1732 def events():
1726 1733 for r in rlog:
1727 1734 yield 'n', (r, list(set(p for p in rlog.parentrevs(r)
1728 1735 if p != -1)))
1729 1736 if r in revs:
1730 1737 yield 'l', (r, "r%i" % r)
1731 1738 elif repo:
1732 1739 cl = repo.changelog
1733 1740 tags = opts.get('tags')
1734 1741 branches = opts.get('branches')
1735 1742 if tags:
1736 1743 labels = {}
1737 1744 for l, n in repo.tags().items():
1738 1745 labels.setdefault(cl.rev(n), []).append(l)
1739 1746 def events():
1740 1747 b = "default"
1741 1748 for r in cl:
1742 1749 if branches:
1743 1750 newb = cl.read(cl.node(r))[5]['branch']
1744 1751 if newb != b:
1745 1752 yield 'a', newb
1746 1753 b = newb
1747 1754 yield 'n', (r, list(set(p for p in cl.parentrevs(r)
1748 1755 if p != -1)))
1749 1756 if tags:
1750 1757 ls = labels.get(r)
1751 1758 if ls:
1752 1759 for l in ls:
1753 1760 yield 'l', (r, l)
1754 1761 else:
1755 1762 raise util.Abort(_('need repo for changelog dag'))
1756 1763
1757 1764 for line in dagparser.dagtextlines(events(),
1758 1765 addspaces=spaces,
1759 1766 wraplabels=True,
1760 1767 wrapannotations=True,
1761 1768 wrapnonlinear=dots,
1762 1769 usedots=dots,
1763 1770 maxlinewidth=70):
1764 1771 ui.write(line)
1765 1772 ui.write("\n")
1766 1773
1767 1774 @command('debugdata',
1768 1775 [('c', 'changelog', False, _('open changelog')),
1769 1776 ('m', 'manifest', False, _('open manifest'))],
1770 1777 _('-c|-m|FILE REV'))
1771 1778 def debugdata(ui, repo, file_, rev = None, **opts):
1772 1779 """dump the contents of a data file revision"""
1773 1780 if opts.get('changelog') or opts.get('manifest'):
1774 1781 file_, rev = None, file_
1775 1782 elif rev is None:
1776 1783 raise error.CommandError('debugdata', _('invalid arguments'))
1777 1784 r = cmdutil.openrevlog(repo, 'debugdata', file_, opts)
1778 1785 try:
1779 1786 ui.write(r.revision(r.lookup(rev)))
1780 1787 except KeyError:
1781 1788 raise util.Abort(_('invalid revision identifier %s') % rev)
1782 1789
1783 1790 @command('debugdate',
1784 1791 [('e', 'extended', None, _('try extended date formats'))],
1785 1792 _('[-e] DATE [RANGE]'))
1786 1793 def debugdate(ui, date, range=None, **opts):
1787 1794 """parse and display a date"""
1788 1795 if opts["extended"]:
1789 1796 d = util.parsedate(date, util.extendeddateformats)
1790 1797 else:
1791 1798 d = util.parsedate(date)
1792 1799 ui.write(("internal: %s %s\n") % d)
1793 1800 ui.write(("standard: %s\n") % util.datestr(d))
1794 1801 if range:
1795 1802 m = util.matchdate(range)
1796 1803 ui.write(("match: %s\n") % m(d[0]))
1797 1804
1798 1805 @command('debugdiscovery',
1799 1806 [('', 'old', None, _('use old-style discovery')),
1800 1807 ('', 'nonheads', None,
1801 1808 _('use old-style discovery with non-heads included')),
1802 1809 ] + remoteopts,
1803 1810 _('[-l REV] [-r REV] [-b BRANCH]... [OTHER]'))
1804 1811 def debugdiscovery(ui, repo, remoteurl="default", **opts):
1805 1812 """runs the changeset discovery protocol in isolation"""
1806 1813 remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl),
1807 1814 opts.get('branch'))
1808 1815 remote = hg.peer(repo, opts, remoteurl)
1809 1816 ui.status(_('comparing with %s\n') % util.hidepassword(remoteurl))
1810 1817
1811 1818 # make sure tests are repeatable
1812 1819 random.seed(12323)
1813 1820
1814 1821 def doit(localheads, remoteheads, remote=remote):
1815 1822 if opts.get('old'):
1816 1823 if localheads:
1817 1824 raise util.Abort('cannot use localheads with old style '
1818 1825 'discovery')
1819 1826 if not util.safehasattr(remote, 'branches'):
1820 1827 # enable in-client legacy support
1821 1828 remote = localrepo.locallegacypeer(remote.local())
1822 1829 common, _in, hds = treediscovery.findcommonincoming(repo, remote,
1823 1830 force=True)
1824 1831 common = set(common)
1825 1832 if not opts.get('nonheads'):
1826 1833 ui.write(("unpruned common: %s\n") %
1827 1834 " ".join(sorted(short(n) for n in common)))
1828 1835 dag = dagutil.revlogdag(repo.changelog)
1829 1836 all = dag.ancestorset(dag.internalizeall(common))
1830 1837 common = dag.externalizeall(dag.headsetofconnecteds(all))
1831 1838 else:
1832 1839 common, any, hds = setdiscovery.findcommonheads(ui, repo, remote)
1833 1840 common = set(common)
1834 1841 rheads = set(hds)
1835 1842 lheads = set(repo.heads())
1836 1843 ui.write(("common heads: %s\n") %
1837 1844 " ".join(sorted(short(n) for n in common)))
1838 1845 if lheads <= common:
1839 1846 ui.write(("local is subset\n"))
1840 1847 elif rheads <= common:
1841 1848 ui.write(("remote is subset\n"))
1842 1849
1843 1850 serverlogs = opts.get('serverlog')
1844 1851 if serverlogs:
1845 1852 for filename in serverlogs:
1846 1853 logfile = open(filename, 'r')
1847 1854 try:
1848 1855 line = logfile.readline()
1849 1856 while line:
1850 1857 parts = line.strip().split(';')
1851 1858 op = parts[1]
1852 1859 if op == 'cg':
1853 1860 pass
1854 1861 elif op == 'cgss':
1855 1862 doit(parts[2].split(' '), parts[3].split(' '))
1856 1863 elif op == 'unb':
1857 1864 doit(parts[3].split(' '), parts[2].split(' '))
1858 1865 line = logfile.readline()
1859 1866 finally:
1860 1867 logfile.close()
1861 1868
1862 1869 else:
1863 1870 remoterevs, _checkout = hg.addbranchrevs(repo, remote, branches,
1864 1871 opts.get('remote_head'))
1865 1872 localrevs = opts.get('local_head')
1866 1873 doit(localrevs, remoterevs)
1867 1874
1868 1875 @command('debugfileset',
1869 1876 [('r', 'rev', '', _('apply the filespec on this revision'), _('REV'))],
1870 1877 _('[-r REV] FILESPEC'))
1871 1878 def debugfileset(ui, repo, expr, **opts):
1872 1879 '''parse and apply a fileset specification'''
1873 1880 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
1874 1881 if ui.verbose:
1875 1882 tree = fileset.parse(expr)[0]
1876 1883 ui.note(tree, "\n")
1877 1884
1878 1885 for f in fileset.getfileset(ctx, expr):
1879 1886 ui.write("%s\n" % f)
1880 1887
1881 1888 @command('debugfsinfo', [], _('[PATH]'))
1882 1889 def debugfsinfo(ui, path = "."):
1883 1890 """show information detected about current filesystem"""
1884 1891 util.writefile('.debugfsinfo', '')
1885 1892 ui.write(('exec: %s\n') % (util.checkexec(path) and 'yes' or 'no'))
1886 1893 ui.write(('symlink: %s\n') % (util.checklink(path) and 'yes' or 'no'))
1887 1894 ui.write(('case-sensitive: %s\n') % (util.checkcase('.debugfsinfo')
1888 1895 and 'yes' or 'no'))
1889 1896 os.unlink('.debugfsinfo')
1890 1897
1891 1898 @command('debuggetbundle',
1892 1899 [('H', 'head', [], _('id of head node'), _('ID')),
1893 1900 ('C', 'common', [], _('id of common node'), _('ID')),
1894 1901 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE'))],
1895 1902 _('REPO FILE [-H|-C ID]...'))
1896 1903 def debuggetbundle(ui, repopath, bundlepath, head=None, common=None, **opts):
1897 1904 """retrieves a bundle from a repo
1898 1905
1899 1906 Every ID must be a full-length hex node id string. Saves the bundle to the
1900 1907 given file.
1901 1908 """
1902 1909 repo = hg.peer(ui, opts, repopath)
1903 1910 if not repo.capable('getbundle'):
1904 1911 raise util.Abort("getbundle() not supported by target repository")
1905 1912 args = {}
1906 1913 if common:
1907 1914 args['common'] = [bin(s) for s in common]
1908 1915 if head:
1909 1916 args['heads'] = [bin(s) for s in head]
1910 1917 bundle = repo.getbundle('debug', **args)
1911 1918
1912 1919 bundletype = opts.get('type', 'bzip2').lower()
1913 1920 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
1914 1921 bundletype = btypes.get(bundletype)
1915 1922 if bundletype not in changegroup.bundletypes:
1916 1923 raise util.Abort(_('unknown bundle type specified with --type'))
1917 1924 changegroup.writebundle(bundle, bundlepath, bundletype)
1918 1925
1919 1926 @command('debugignore', [], '')
1920 1927 def debugignore(ui, repo, *values, **opts):
1921 1928 """display the combined ignore pattern"""
1922 1929 ignore = repo.dirstate._ignore
1923 1930 includepat = getattr(ignore, 'includepat', None)
1924 1931 if includepat is not None:
1925 1932 ui.write("%s\n" % includepat)
1926 1933 else:
1927 1934 raise util.Abort(_("no ignore patterns found"))
1928 1935
1929 1936 @command('debugindex',
1930 1937 [('c', 'changelog', False, _('open changelog')),
1931 1938 ('m', 'manifest', False, _('open manifest')),
1932 1939 ('f', 'format', 0, _('revlog format'), _('FORMAT'))],
1933 1940 _('[-f FORMAT] -c|-m|FILE'))
1934 1941 def debugindex(ui, repo, file_ = None, **opts):
1935 1942 """dump the contents of an index file"""
1936 1943 r = cmdutil.openrevlog(repo, 'debugindex', file_, opts)
1937 1944 format = opts.get('format', 0)
1938 1945 if format not in (0, 1):
1939 1946 raise util.Abort(_("unknown format %d") % format)
1940 1947
1941 1948 generaldelta = r.version & revlog.REVLOGGENERALDELTA
1942 1949 if generaldelta:
1943 1950 basehdr = ' delta'
1944 1951 else:
1945 1952 basehdr = ' base'
1946 1953
1947 1954 if format == 0:
1948 1955 ui.write(" rev offset length " + basehdr + " linkrev"
1949 1956 " nodeid p1 p2\n")
1950 1957 elif format == 1:
1951 1958 ui.write(" rev flag offset length"
1952 1959 " size " + basehdr + " link p1 p2"
1953 1960 " nodeid\n")
1954 1961
1955 1962 for i in r:
1956 1963 node = r.node(i)
1957 1964 if generaldelta:
1958 1965 base = r.deltaparent(i)
1959 1966 else:
1960 1967 base = r.chainbase(i)
1961 1968 if format == 0:
1962 1969 try:
1963 1970 pp = r.parents(node)
1964 1971 except Exception:
1965 1972 pp = [nullid, nullid]
1966 1973 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1967 1974 i, r.start(i), r.length(i), base, r.linkrev(i),
1968 1975 short(node), short(pp[0]), short(pp[1])))
1969 1976 elif format == 1:
1970 1977 pr = r.parentrevs(i)
1971 1978 ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d % 6d %s\n" % (
1972 1979 i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
1973 1980 base, r.linkrev(i), pr[0], pr[1], short(node)))
1974 1981
1975 1982 @command('debugindexdot', [], _('FILE'))
1976 1983 def debugindexdot(ui, repo, file_):
1977 1984 """dump an index DAG as a graphviz dot file"""
1978 1985 r = None
1979 1986 if repo:
1980 1987 filelog = repo.file(file_)
1981 1988 if len(filelog):
1982 1989 r = filelog
1983 1990 if not r:
1984 1991 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1985 1992 ui.write(("digraph G {\n"))
1986 1993 for i in r:
1987 1994 node = r.node(i)
1988 1995 pp = r.parents(node)
1989 1996 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1990 1997 if pp[1] != nullid:
1991 1998 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1992 1999 ui.write("}\n")
1993 2000
1994 2001 @command('debuginstall', [], '')
1995 2002 def debuginstall(ui):
1996 2003 '''test Mercurial installation
1997 2004
1998 2005 Returns 0 on success.
1999 2006 '''
2000 2007
2001 2008 def writetemp(contents):
2002 2009 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
2003 2010 f = os.fdopen(fd, "wb")
2004 2011 f.write(contents)
2005 2012 f.close()
2006 2013 return name
2007 2014
2008 2015 problems = 0
2009 2016
2010 2017 # encoding
2011 2018 ui.status(_("checking encoding (%s)...\n") % encoding.encoding)
2012 2019 try:
2013 2020 encoding.fromlocal("test")
2014 2021 except util.Abort, inst:
2015 2022 ui.write(" %s\n" % inst)
2016 2023 ui.write(_(" (check that your locale is properly set)\n"))
2017 2024 problems += 1
2018 2025
2019 2026 # Python lib
2020 2027 ui.status(_("checking Python lib (%s)...\n")
2021 2028 % os.path.dirname(os.__file__))
2022 2029
2023 2030 # compiled modules
2024 2031 ui.status(_("checking installed modules (%s)...\n")
2025 2032 % os.path.dirname(__file__))
2026 2033 try:
2027 2034 import bdiff, mpatch, base85, osutil
2028 2035 dir(bdiff), dir(mpatch), dir(base85), dir(osutil) # quiet pyflakes
2029 2036 except Exception, inst:
2030 2037 ui.write(" %s\n" % inst)
2031 2038 ui.write(_(" One or more extensions could not be found"))
2032 2039 ui.write(_(" (check that you compiled the extensions)\n"))
2033 2040 problems += 1
2034 2041
2035 2042 # templates
2036 2043 import templater
2037 2044 p = templater.templatepath()
2038 2045 ui.status(_("checking templates (%s)...\n") % ' '.join(p))
2039 2046 try:
2040 2047 templater.templater(templater.templatepath("map-cmdline.default"))
2041 2048 except Exception, inst:
2042 2049 ui.write(" %s\n" % inst)
2043 2050 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
2044 2051 problems += 1
2045 2052
2046 2053 # editor
2047 2054 ui.status(_("checking commit editor...\n"))
2048 2055 editor = ui.geteditor()
2049 2056 cmdpath = util.findexe(editor) or util.findexe(editor.split()[0])
2050 2057 if not cmdpath:
2051 2058 if editor == 'vi':
2052 2059 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
2053 2060 ui.write(_(" (specify a commit editor in your configuration"
2054 2061 " file)\n"))
2055 2062 else:
2056 2063 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
2057 2064 ui.write(_(" (specify a commit editor in your configuration"
2058 2065 " file)\n"))
2059 2066 problems += 1
2060 2067
2061 2068 # check username
2062 2069 ui.status(_("checking username...\n"))
2063 2070 try:
2064 2071 ui.username()
2065 2072 except util.Abort, e:
2066 2073 ui.write(" %s\n" % e)
2067 2074 ui.write(_(" (specify a username in your configuration file)\n"))
2068 2075 problems += 1
2069 2076
2070 2077 if not problems:
2071 2078 ui.status(_("no problems detected\n"))
2072 2079 else:
2073 2080 ui.write(_("%s problems detected,"
2074 2081 " please check your install!\n") % problems)
2075 2082
2076 2083 return problems
2077 2084
2078 2085 @command('debugknown', [], _('REPO ID...'))
2079 2086 def debugknown(ui, repopath, *ids, **opts):
2080 2087 """test whether node ids are known to a repo
2081 2088
2082 2089 Every ID must be a full-length hex node id string. Returns a list of 0s
2083 2090 and 1s indicating unknown/known.
2084 2091 """
2085 2092 repo = hg.peer(ui, opts, repopath)
2086 2093 if not repo.capable('known'):
2087 2094 raise util.Abort("known() not supported by target repository")
2088 2095 flags = repo.known([bin(s) for s in ids])
2089 2096 ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
2090 2097
2091 2098 @command('debuglabelcomplete', [], _('LABEL...'))
2092 2099 def debuglabelcomplete(ui, repo, *args):
2093 2100 '''complete "labels" - tags, open branch names, bookmark names'''
2094 2101
2095 2102 labels = set()
2096 2103 labels.update(t[0] for t in repo.tagslist())
2097 2104 labels.update(repo._bookmarks.keys())
2098 2105 for heads in repo.branchmap().itervalues():
2099 2106 for h in heads:
2100 2107 ctx = repo[h]
2101 2108 if not ctx.closesbranch():
2102 2109 labels.add(ctx.branch())
2103 2110 completions = set()
2104 2111 if not args:
2105 2112 args = ['']
2106 2113 for a in args:
2107 2114 completions.update(l for l in labels if l.startswith(a))
2108 2115 ui.write('\n'.join(sorted(completions)))
2109 2116 ui.write('\n')
2110 2117
2111 2118 @command('debugobsolete',
2112 2119 [('', 'flags', 0, _('markers flag')),
2113 2120 ] + commitopts2,
2114 2121 _('[OBSOLETED [REPLACEMENT] [REPL... ]'))
2115 2122 def debugobsolete(ui, repo, precursor=None, *successors, **opts):
2116 2123 """create arbitrary obsolete marker
2117 2124
2118 2125 With no arguments, displays the list of obsolescence markers."""
2119 2126 def parsenodeid(s):
2120 2127 try:
2121 2128 # We do not use revsingle/revrange functions here to accept
2122 2129 # arbitrary node identifiers, possibly not present in the
2123 2130 # local repository.
2124 2131 n = bin(s)
2125 2132 if len(n) != len(nullid):
2126 2133 raise TypeError()
2127 2134 return n
2128 2135 except TypeError:
2129 2136 raise util.Abort('changeset references must be full hexadecimal '
2130 2137 'node identifiers')
2131 2138
2132 2139 if precursor is not None:
2133 2140 metadata = {}
2134 2141 if 'date' in opts:
2135 2142 metadata['date'] = opts['date']
2136 2143 metadata['user'] = opts['user'] or ui.username()
2137 2144 succs = tuple(parsenodeid(succ) for succ in successors)
2138 2145 l = repo.lock()
2139 2146 try:
2140 2147 tr = repo.transaction('debugobsolete')
2141 2148 try:
2142 2149 repo.obsstore.create(tr, parsenodeid(precursor), succs,
2143 2150 opts['flags'], metadata)
2144 2151 tr.close()
2145 2152 finally:
2146 2153 tr.release()
2147 2154 finally:
2148 2155 l.release()
2149 2156 else:
2150 2157 for m in obsolete.allmarkers(repo):
2151 2158 ui.write(hex(m.precnode()))
2152 2159 for repl in m.succnodes():
2153 2160 ui.write(' ')
2154 2161 ui.write(hex(repl))
2155 2162 ui.write(' %X ' % m._data[2])
2156 2163 ui.write('{%s}' % (', '.join('%r: %r' % t for t in
2157 2164 sorted(m.metadata().items()))))
2158 2165 ui.write('\n')
2159 2166
2160 2167 @command('debugpathcomplete',
2161 2168 [('f', 'full', None, _('complete an entire path')),
2162 2169 ('n', 'normal', None, _('show only normal files')),
2163 2170 ('a', 'added', None, _('show only added files')),
2164 2171 ('r', 'removed', None, _('show only removed files'))],
2165 2172 _('FILESPEC...'))
2166 2173 def debugpathcomplete(ui, repo, *specs, **opts):
2167 2174 '''complete part or all of a tracked path
2168 2175
2169 2176 This command supports shells that offer path name completion. It
2170 2177 currently completes only files already known to the dirstate.
2171 2178
2172 2179 Completion extends only to the next path segment unless
2173 2180 --full is specified, in which case entire paths are used.'''
2174 2181
2175 2182 def complete(path, acceptable):
2176 2183 dirstate = repo.dirstate
2177 2184 spec = os.path.normpath(os.path.join(os.getcwd(), path))
2178 2185 rootdir = repo.root + os.sep
2179 2186 if spec != repo.root and not spec.startswith(rootdir):
2180 2187 return [], []
2181 2188 if os.path.isdir(spec):
2182 2189 spec += '/'
2183 2190 spec = spec[len(rootdir):]
2184 2191 fixpaths = os.sep != '/'
2185 2192 if fixpaths:
2186 2193 spec = spec.replace(os.sep, '/')
2187 2194 speclen = len(spec)
2188 2195 fullpaths = opts['full']
2189 2196 files, dirs = set(), set()
2190 2197 adddir, addfile = dirs.add, files.add
2191 2198 for f, st in dirstate.iteritems():
2192 2199 if f.startswith(spec) and st[0] in acceptable:
2193 2200 if fixpaths:
2194 2201 f = f.replace('/', os.sep)
2195 2202 if fullpaths:
2196 2203 addfile(f)
2197 2204 continue
2198 2205 s = f.find(os.sep, speclen)
2199 2206 if s >= 0:
2200 2207 adddir(f[:s + 1])
2201 2208 else:
2202 2209 addfile(f)
2203 2210 return files, dirs
2204 2211
2205 2212 acceptable = ''
2206 2213 if opts['normal']:
2207 2214 acceptable += 'nm'
2208 2215 if opts['added']:
2209 2216 acceptable += 'a'
2210 2217 if opts['removed']:
2211 2218 acceptable += 'r'
2212 2219 cwd = repo.getcwd()
2213 2220 if not specs:
2214 2221 specs = ['.']
2215 2222
2216 2223 files, dirs = set(), set()
2217 2224 for spec in specs:
2218 2225 f, d = complete(spec, acceptable or 'nmar')
2219 2226 files.update(f)
2220 2227 dirs.update(d)
2221 2228 if not files and len(dirs) == 1:
2222 2229 # force the shell to consider a completion that matches one
2223 2230 # directory and zero files to be ambiguous
2224 2231 dirs.add(iter(dirs).next() + '.')
2225 2232 files.update(dirs)
2226 2233 ui.write('\n'.join(repo.pathto(p, cwd) for p in sorted(files)))
2227 2234 ui.write('\n')
2228 2235
2229 2236 @command('debugpushkey', [], _('REPO NAMESPACE [KEY OLD NEW]'))
2230 2237 def debugpushkey(ui, repopath, namespace, *keyinfo, **opts):
2231 2238 '''access the pushkey key/value protocol
2232 2239
2233 2240 With two args, list the keys in the given namespace.
2234 2241
2235 2242 With five args, set a key to new if it currently is set to old.
2236 2243 Reports success or failure.
2237 2244 '''
2238 2245
2239 2246 target = hg.peer(ui, {}, repopath)
2240 2247 if keyinfo:
2241 2248 key, old, new = keyinfo
2242 2249 r = target.pushkey(namespace, key, old, new)
2243 2250 ui.status(str(r) + '\n')
2244 2251 return not r
2245 2252 else:
2246 2253 for k, v in sorted(target.listkeys(namespace).iteritems()):
2247 2254 ui.write("%s\t%s\n" % (k.encode('string-escape'),
2248 2255 v.encode('string-escape')))
2249 2256
2250 2257 @command('debugpvec', [], _('A B'))
2251 2258 def debugpvec(ui, repo, a, b=None):
2252 2259 ca = scmutil.revsingle(repo, a)
2253 2260 cb = scmutil.revsingle(repo, b)
2254 2261 pa = pvec.ctxpvec(ca)
2255 2262 pb = pvec.ctxpvec(cb)
2256 2263 if pa == pb:
2257 2264 rel = "="
2258 2265 elif pa > pb:
2259 2266 rel = ">"
2260 2267 elif pa < pb:
2261 2268 rel = "<"
2262 2269 elif pa | pb:
2263 2270 rel = "|"
2264 2271 ui.write(_("a: %s\n") % pa)
2265 2272 ui.write(_("b: %s\n") % pb)
2266 2273 ui.write(_("depth(a): %d depth(b): %d\n") % (pa._depth, pb._depth))
2267 2274 ui.write(_("delta: %d hdist: %d distance: %d relation: %s\n") %
2268 2275 (abs(pa._depth - pb._depth), pvec._hamming(pa._vec, pb._vec),
2269 2276 pa.distance(pb), rel))
2270 2277
2271 2278 @command('debugrebuilddirstate|debugrebuildstate',
2272 2279 [('r', 'rev', '', _('revision to rebuild to'), _('REV'))],
2273 2280 _('[-r REV]'))
2274 2281 def debugrebuilddirstate(ui, repo, rev):
2275 2282 """rebuild the dirstate as it would look like for the given revision
2276 2283
2277 2284 If no revision is specified the first current parent will be used.
2278 2285
2279 2286 The dirstate will be set to the files of the given revision.
2280 2287 The actual working directory content or existing dirstate
2281 2288 information such as adds or removes is not considered.
2282 2289
2283 2290 One use of this command is to make the next :hg:`status` invocation
2284 2291 check the actual file content.
2285 2292 """
2286 2293 ctx = scmutil.revsingle(repo, rev)
2287 2294 wlock = repo.wlock()
2288 2295 try:
2289 2296 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
2290 2297 finally:
2291 2298 wlock.release()
2292 2299
2293 2300 @command('debugrename',
2294 2301 [('r', 'rev', '', _('revision to debug'), _('REV'))],
2295 2302 _('[-r REV] FILE'))
2296 2303 def debugrename(ui, repo, file1, *pats, **opts):
2297 2304 """dump rename information"""
2298 2305
2299 2306 ctx = scmutil.revsingle(repo, opts.get('rev'))
2300 2307 m = scmutil.match(ctx, (file1,) + pats, opts)
2301 2308 for abs in ctx.walk(m):
2302 2309 fctx = ctx[abs]
2303 2310 o = fctx.filelog().renamed(fctx.filenode())
2304 2311 rel = m.rel(abs)
2305 2312 if o:
2306 2313 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
2307 2314 else:
2308 2315 ui.write(_("%s not renamed\n") % rel)
2309 2316
2310 2317 @command('debugrevlog',
2311 2318 [('c', 'changelog', False, _('open changelog')),
2312 2319 ('m', 'manifest', False, _('open manifest')),
2313 2320 ('d', 'dump', False, _('dump index data'))],
2314 2321 _('-c|-m|FILE'))
2315 2322 def debugrevlog(ui, repo, file_ = None, **opts):
2316 2323 """show data and statistics about a revlog"""
2317 2324 r = cmdutil.openrevlog(repo, 'debugrevlog', file_, opts)
2318 2325
2319 2326 if opts.get("dump"):
2320 2327 numrevs = len(r)
2321 2328 ui.write("# rev p1rev p2rev start end deltastart base p1 p2"
2322 2329 " rawsize totalsize compression heads\n")
2323 2330 ts = 0
2324 2331 heads = set()
2325 2332 for rev in xrange(numrevs):
2326 2333 dbase = r.deltaparent(rev)
2327 2334 if dbase == -1:
2328 2335 dbase = rev
2329 2336 cbase = r.chainbase(rev)
2330 2337 p1, p2 = r.parentrevs(rev)
2331 2338 rs = r.rawsize(rev)
2332 2339 ts = ts + rs
2333 2340 heads -= set(r.parentrevs(rev))
2334 2341 heads.add(rev)
2335 2342 ui.write("%d %d %d %d %d %d %d %d %d %d %d %d %d\n" %
2336 2343 (rev, p1, p2, r.start(rev), r.end(rev),
2337 2344 r.start(dbase), r.start(cbase),
2338 2345 r.start(p1), r.start(p2),
2339 2346 rs, ts, ts / r.end(rev), len(heads)))
2340 2347 return 0
2341 2348
2342 2349 v = r.version
2343 2350 format = v & 0xFFFF
2344 2351 flags = []
2345 2352 gdelta = False
2346 2353 if v & revlog.REVLOGNGINLINEDATA:
2347 2354 flags.append('inline')
2348 2355 if v & revlog.REVLOGGENERALDELTA:
2349 2356 gdelta = True
2350 2357 flags.append('generaldelta')
2351 2358 if not flags:
2352 2359 flags = ['(none)']
2353 2360
2354 2361 nummerges = 0
2355 2362 numfull = 0
2356 2363 numprev = 0
2357 2364 nump1 = 0
2358 2365 nump2 = 0
2359 2366 numother = 0
2360 2367 nump1prev = 0
2361 2368 nump2prev = 0
2362 2369 chainlengths = []
2363 2370
2364 2371 datasize = [None, 0, 0L]
2365 2372 fullsize = [None, 0, 0L]
2366 2373 deltasize = [None, 0, 0L]
2367 2374
2368 2375 def addsize(size, l):
2369 2376 if l[0] is None or size < l[0]:
2370 2377 l[0] = size
2371 2378 if size > l[1]:
2372 2379 l[1] = size
2373 2380 l[2] += size
2374 2381
2375 2382 numrevs = len(r)
2376 2383 for rev in xrange(numrevs):
2377 2384 p1, p2 = r.parentrevs(rev)
2378 2385 delta = r.deltaparent(rev)
2379 2386 if format > 0:
2380 2387 addsize(r.rawsize(rev), datasize)
2381 2388 if p2 != nullrev:
2382 2389 nummerges += 1
2383 2390 size = r.length(rev)
2384 2391 if delta == nullrev:
2385 2392 chainlengths.append(0)
2386 2393 numfull += 1
2387 2394 addsize(size, fullsize)
2388 2395 else:
2389 2396 chainlengths.append(chainlengths[delta] + 1)
2390 2397 addsize(size, deltasize)
2391 2398 if delta == rev - 1:
2392 2399 numprev += 1
2393 2400 if delta == p1:
2394 2401 nump1prev += 1
2395 2402 elif delta == p2:
2396 2403 nump2prev += 1
2397 2404 elif delta == p1:
2398 2405 nump1 += 1
2399 2406 elif delta == p2:
2400 2407 nump2 += 1
2401 2408 elif delta != nullrev:
2402 2409 numother += 1
2403 2410
2404 2411 # Adjust size min value for empty cases
2405 2412 for size in (datasize, fullsize, deltasize):
2406 2413 if size[0] is None:
2407 2414 size[0] = 0
2408 2415
2409 2416 numdeltas = numrevs - numfull
2410 2417 numoprev = numprev - nump1prev - nump2prev
2411 2418 totalrawsize = datasize[2]
2412 2419 datasize[2] /= numrevs
2413 2420 fulltotal = fullsize[2]
2414 2421 fullsize[2] /= numfull
2415 2422 deltatotal = deltasize[2]
2416 2423 if numrevs - numfull > 0:
2417 2424 deltasize[2] /= numrevs - numfull
2418 2425 totalsize = fulltotal + deltatotal
2419 2426 avgchainlen = sum(chainlengths) / numrevs
2420 2427 compratio = totalrawsize / totalsize
2421 2428
2422 2429 basedfmtstr = '%%%dd\n'
2423 2430 basepcfmtstr = '%%%dd %s(%%5.2f%%%%)\n'
2424 2431
2425 2432 def dfmtstr(max):
2426 2433 return basedfmtstr % len(str(max))
2427 2434 def pcfmtstr(max, padding=0):
2428 2435 return basepcfmtstr % (len(str(max)), ' ' * padding)
2429 2436
2430 2437 def pcfmt(value, total):
2431 2438 return (value, 100 * float(value) / total)
2432 2439
2433 2440 ui.write(('format : %d\n') % format)
2434 2441 ui.write(('flags : %s\n') % ', '.join(flags))
2435 2442
2436 2443 ui.write('\n')
2437 2444 fmt = pcfmtstr(totalsize)
2438 2445 fmt2 = dfmtstr(totalsize)
2439 2446 ui.write(('revisions : ') + fmt2 % numrevs)
2440 2447 ui.write((' merges : ') + fmt % pcfmt(nummerges, numrevs))
2441 2448 ui.write((' normal : ') + fmt % pcfmt(numrevs - nummerges, numrevs))
2442 2449 ui.write(('revisions : ') + fmt2 % numrevs)
2443 2450 ui.write((' full : ') + fmt % pcfmt(numfull, numrevs))
2444 2451 ui.write((' deltas : ') + fmt % pcfmt(numdeltas, numrevs))
2445 2452 ui.write(('revision size : ') + fmt2 % totalsize)
2446 2453 ui.write((' full : ') + fmt % pcfmt(fulltotal, totalsize))
2447 2454 ui.write((' deltas : ') + fmt % pcfmt(deltatotal, totalsize))
2448 2455
2449 2456 ui.write('\n')
2450 2457 fmt = dfmtstr(max(avgchainlen, compratio))
2451 2458 ui.write(('avg chain length : ') + fmt % avgchainlen)
2452 2459 ui.write(('compression ratio : ') + fmt % compratio)
2453 2460
2454 2461 if format > 0:
2455 2462 ui.write('\n')
2456 2463 ui.write(('uncompressed data size (min/max/avg) : %d / %d / %d\n')
2457 2464 % tuple(datasize))
2458 2465 ui.write(('full revision size (min/max/avg) : %d / %d / %d\n')
2459 2466 % tuple(fullsize))
2460 2467 ui.write(('delta size (min/max/avg) : %d / %d / %d\n')
2461 2468 % tuple(deltasize))
2462 2469
2463 2470 if numdeltas > 0:
2464 2471 ui.write('\n')
2465 2472 fmt = pcfmtstr(numdeltas)
2466 2473 fmt2 = pcfmtstr(numdeltas, 4)
2467 2474 ui.write(('deltas against prev : ') + fmt % pcfmt(numprev, numdeltas))
2468 2475 if numprev > 0:
2469 2476 ui.write((' where prev = p1 : ') + fmt2 % pcfmt(nump1prev,
2470 2477 numprev))
2471 2478 ui.write((' where prev = p2 : ') + fmt2 % pcfmt(nump2prev,
2472 2479 numprev))
2473 2480 ui.write((' other : ') + fmt2 % pcfmt(numoprev,
2474 2481 numprev))
2475 2482 if gdelta:
2476 2483 ui.write(('deltas against p1 : ')
2477 2484 + fmt % pcfmt(nump1, numdeltas))
2478 2485 ui.write(('deltas against p2 : ')
2479 2486 + fmt % pcfmt(nump2, numdeltas))
2480 2487 ui.write(('deltas against other : ') + fmt % pcfmt(numother,
2481 2488 numdeltas))
2482 2489
2483 2490 @command('debugrevspec', [], ('REVSPEC'))
2484 2491 def debugrevspec(ui, repo, expr):
2485 2492 """parse and apply a revision specification
2486 2493
2487 2494 Use --verbose to print the parsed tree before and after aliases
2488 2495 expansion.
2489 2496 """
2490 2497 if ui.verbose:
2491 2498 tree = revset.parse(expr)[0]
2492 2499 ui.note(revset.prettyformat(tree), "\n")
2493 2500 newtree = revset.findaliases(ui, tree)
2494 2501 if newtree != tree:
2495 2502 ui.note(revset.prettyformat(newtree), "\n")
2496 2503 func = revset.match(ui, expr)
2497 2504 for c in func(repo, range(len(repo))):
2498 2505 ui.write("%s\n" % c)
2499 2506
2500 2507 @command('debugsetparents', [], _('REV1 [REV2]'))
2501 2508 def debugsetparents(ui, repo, rev1, rev2=None):
2502 2509 """manually set the parents of the current working directory
2503 2510
2504 2511 This is useful for writing repository conversion tools, but should
2505 2512 be used with care.
2506 2513
2507 2514 Returns 0 on success.
2508 2515 """
2509 2516
2510 2517 r1 = scmutil.revsingle(repo, rev1).node()
2511 2518 r2 = scmutil.revsingle(repo, rev2, 'null').node()
2512 2519
2513 2520 wlock = repo.wlock()
2514 2521 try:
2515 2522 repo.setparents(r1, r2)
2516 2523 finally:
2517 2524 wlock.release()
2518 2525
2519 2526 @command('debugdirstate|debugstate',
2520 2527 [('', 'nodates', None, _('do not display the saved mtime')),
2521 2528 ('', 'datesort', None, _('sort by saved mtime'))],
2522 2529 _('[OPTION]...'))
2523 2530 def debugstate(ui, repo, nodates=None, datesort=None):
2524 2531 """show the contents of the current dirstate"""
2525 2532 timestr = ""
2526 2533 showdate = not nodates
2527 2534 if datesort:
2528 2535 keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
2529 2536 else:
2530 2537 keyfunc = None # sort by filename
2531 2538 for file_, ent in sorted(repo.dirstate._map.iteritems(), key=keyfunc):
2532 2539 if showdate:
2533 2540 if ent[3] == -1:
2534 2541 # Pad or slice to locale representation
2535 2542 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ",
2536 2543 time.localtime(0)))
2537 2544 timestr = 'unset'
2538 2545 timestr = (timestr[:locale_len] +
2539 2546 ' ' * (locale_len - len(timestr)))
2540 2547 else:
2541 2548 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
2542 2549 time.localtime(ent[3]))
2543 2550 if ent[1] & 020000:
2544 2551 mode = 'lnk'
2545 2552 else:
2546 2553 mode = '%3o' % (ent[1] & 0777 & ~util.umask)
2547 2554 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
2548 2555 for f in repo.dirstate.copies():
2549 2556 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
2550 2557
2551 2558 @command('debugsub',
2552 2559 [('r', 'rev', '',
2553 2560 _('revision to check'), _('REV'))],
2554 2561 _('[-r REV] [REV]'))
2555 2562 def debugsub(ui, repo, rev=None):
2556 2563 ctx = scmutil.revsingle(repo, rev, None)
2557 2564 for k, v in sorted(ctx.substate.items()):
2558 2565 ui.write(('path %s\n') % k)
2559 2566 ui.write((' source %s\n') % v[0])
2560 2567 ui.write((' revision %s\n') % v[1])
2561 2568
2562 2569 @command('debugsuccessorssets',
2563 2570 [],
2564 2571 _('[REV]'))
2565 2572 def debugsuccessorssets(ui, repo, *revs):
2566 2573 """show set of successors for revision
2567 2574
2568 2575 A successors set of changeset A is a consistent group of revisions that
2569 2576 succeed A. It contains non-obsolete changesets only.
2570 2577
2571 2578 In most cases a changeset A has a single successors set containing a single
2572 2579 successor (changeset A replaced by A').
2573 2580
2574 2581 A changeset that is made obsolete with no successors are called "pruned".
2575 2582 Such changesets have no successors sets at all.
2576 2583
2577 2584 A changeset that has been "split" will have a successors set containing
2578 2585 more than one successor.
2579 2586
2580 2587 A changeset that has been rewritten in multiple different ways is called
2581 2588 "divergent". Such changesets have multiple successor sets (each of which
2582 2589 may also be split, i.e. have multiple successors).
2583 2590
2584 2591 Results are displayed as follows::
2585 2592
2586 2593 <rev1>
2587 2594 <successors-1A>
2588 2595 <rev2>
2589 2596 <successors-2A>
2590 2597 <successors-2B1> <successors-2B2> <successors-2B3>
2591 2598
2592 2599 Here rev2 has two possible (i.e. divergent) successors sets. The first
2593 2600 holds one element, whereas the second holds three (i.e. the changeset has
2594 2601 been split).
2595 2602 """
2596 2603 # passed to successorssets caching computation from one call to another
2597 2604 cache = {}
2598 2605 ctx2str = str
2599 2606 node2str = short
2600 2607 if ui.debug():
2601 2608 def ctx2str(ctx):
2602 2609 return ctx.hex()
2603 2610 node2str = hex
2604 2611 for rev in scmutil.revrange(repo, revs):
2605 2612 ctx = repo[rev]
2606 2613 ui.write('%s\n'% ctx2str(ctx))
2607 2614 for succsset in obsolete.successorssets(repo, ctx.node(), cache):
2608 2615 if succsset:
2609 2616 ui.write(' ')
2610 2617 ui.write(node2str(succsset[0]))
2611 2618 for node in succsset[1:]:
2612 2619 ui.write(' ')
2613 2620 ui.write(node2str(node))
2614 2621 ui.write('\n')
2615 2622
2616 2623 @command('debugwalk', walkopts, _('[OPTION]... [FILE]...'))
2617 2624 def debugwalk(ui, repo, *pats, **opts):
2618 2625 """show how files match on given patterns"""
2619 2626 m = scmutil.match(repo[None], pats, opts)
2620 2627 items = list(repo.walk(m))
2621 2628 if not items:
2622 2629 return
2623 2630 f = lambda fn: fn
2624 2631 if ui.configbool('ui', 'slash') and os.sep != '/':
2625 2632 f = lambda fn: util.normpath(fn)
2626 2633 fmt = 'f %%-%ds %%-%ds %%s' % (
2627 2634 max([len(abs) for abs in items]),
2628 2635 max([len(m.rel(abs)) for abs in items]))
2629 2636 for abs in items:
2630 2637 line = fmt % (abs, f(m.rel(abs)), m.exact(abs) and 'exact' or '')
2631 2638 ui.write("%s\n" % line.rstrip())
2632 2639
2633 2640 @command('debugwireargs',
2634 2641 [('', 'three', '', 'three'),
2635 2642 ('', 'four', '', 'four'),
2636 2643 ('', 'five', '', 'five'),
2637 2644 ] + remoteopts,
2638 2645 _('REPO [OPTIONS]... [ONE [TWO]]'))
2639 2646 def debugwireargs(ui, repopath, *vals, **opts):
2640 2647 repo = hg.peer(ui, opts, repopath)
2641 2648 for opt in remoteopts:
2642 2649 del opts[opt[1]]
2643 2650 args = {}
2644 2651 for k, v in opts.iteritems():
2645 2652 if v:
2646 2653 args[k] = v
2647 2654 # run twice to check that we don't mess up the stream for the next command
2648 2655 res1 = repo.debugwireargs(*vals, **args)
2649 2656 res2 = repo.debugwireargs(*vals, **args)
2650 2657 ui.write("%s\n" % res1)
2651 2658 if res1 != res2:
2652 2659 ui.warn("%s\n" % res2)
2653 2660
2654 2661 @command('^diff',
2655 2662 [('r', 'rev', [], _('revision'), _('REV')),
2656 2663 ('c', 'change', '', _('change made by revision'), _('REV'))
2657 2664 ] + diffopts + diffopts2 + walkopts + subrepoopts,
2658 2665 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'))
2659 2666 def diff(ui, repo, *pats, **opts):
2660 2667 """diff repository (or selected files)
2661 2668
2662 2669 Show differences between revisions for the specified files.
2663 2670
2664 2671 Differences between files are shown using the unified diff format.
2665 2672
2666 2673 .. note::
2667 2674 diff may generate unexpected results for merges, as it will
2668 2675 default to comparing against the working directory's first
2669 2676 parent changeset if no revisions are specified.
2670 2677
2671 2678 When two revision arguments are given, then changes are shown
2672 2679 between those revisions. If only one revision is specified then
2673 2680 that revision is compared to the working directory, and, when no
2674 2681 revisions are specified, the working directory files are compared
2675 2682 to its parent.
2676 2683
2677 2684 Alternatively you can specify -c/--change with a revision to see
2678 2685 the changes in that changeset relative to its first parent.
2679 2686
2680 2687 Without the -a/--text option, diff will avoid generating diffs of
2681 2688 files it detects as binary. With -a, diff will generate a diff
2682 2689 anyway, probably with undesirable results.
2683 2690
2684 2691 Use the -g/--git option to generate diffs in the git extended diff
2685 2692 format. For more information, read :hg:`help diffs`.
2686 2693
2687 2694 .. container:: verbose
2688 2695
2689 2696 Examples:
2690 2697
2691 2698 - compare a file in the current working directory to its parent::
2692 2699
2693 2700 hg diff foo.c
2694 2701
2695 2702 - compare two historical versions of a directory, with rename info::
2696 2703
2697 2704 hg diff --git -r 1.0:1.2 lib/
2698 2705
2699 2706 - get change stats relative to the last change on some date::
2700 2707
2701 2708 hg diff --stat -r "date('may 2')"
2702 2709
2703 2710 - diff all newly-added files that contain a keyword::
2704 2711
2705 2712 hg diff "set:added() and grep(GNU)"
2706 2713
2707 2714 - compare a revision and its parents::
2708 2715
2709 2716 hg diff -c 9353 # compare against first parent
2710 2717 hg diff -r 9353^:9353 # same using revset syntax
2711 2718 hg diff -r 9353^2:9353 # compare against the second parent
2712 2719
2713 2720 Returns 0 on success.
2714 2721 """
2715 2722
2716 2723 revs = opts.get('rev')
2717 2724 change = opts.get('change')
2718 2725 stat = opts.get('stat')
2719 2726 reverse = opts.get('reverse')
2720 2727
2721 2728 if revs and change:
2722 2729 msg = _('cannot specify --rev and --change at the same time')
2723 2730 raise util.Abort(msg)
2724 2731 elif change:
2725 2732 node2 = scmutil.revsingle(repo, change, None).node()
2726 2733 node1 = repo[node2].p1().node()
2727 2734 else:
2728 2735 node1, node2 = scmutil.revpair(repo, revs)
2729 2736
2730 2737 if reverse:
2731 2738 node1, node2 = node2, node1
2732 2739
2733 2740 diffopts = patch.diffopts(ui, opts)
2734 2741 m = scmutil.match(repo[node2], pats, opts)
2735 2742 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
2736 2743 listsubrepos=opts.get('subrepos'))
2737 2744
2738 2745 @command('^export',
2739 2746 [('o', 'output', '',
2740 2747 _('print output to file with formatted name'), _('FORMAT')),
2741 2748 ('', 'switch-parent', None, _('diff against the second parent')),
2742 2749 ('r', 'rev', [], _('revisions to export'), _('REV')),
2743 2750 ] + diffopts,
2744 2751 _('[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'))
2745 2752 def export(ui, repo, *changesets, **opts):
2746 2753 """dump the header and diffs for one or more changesets
2747 2754
2748 2755 Print the changeset header and diffs for one or more revisions.
2749 2756 If no revision is given, the parent of the working directory is used.
2750 2757
2751 2758 The information shown in the changeset header is: author, date,
2752 2759 branch name (if non-default), changeset hash, parent(s) and commit
2753 2760 comment.
2754 2761
2755 2762 .. note::
2756 2763 export may generate unexpected diff output for merge
2757 2764 changesets, as it will compare the merge changeset against its
2758 2765 first parent only.
2759 2766
2760 2767 Output may be to a file, in which case the name of the file is
2761 2768 given using a format string. The formatting rules are as follows:
2762 2769
2763 2770 :``%%``: literal "%" character
2764 2771 :``%H``: changeset hash (40 hexadecimal digits)
2765 2772 :``%N``: number of patches being generated
2766 2773 :``%R``: changeset revision number
2767 2774 :``%b``: basename of the exporting repository
2768 2775 :``%h``: short-form changeset hash (12 hexadecimal digits)
2769 2776 :``%m``: first line of the commit message (only alphanumeric characters)
2770 2777 :``%n``: zero-padded sequence number, starting at 1
2771 2778 :``%r``: zero-padded changeset revision number
2772 2779
2773 2780 Without the -a/--text option, export will avoid generating diffs
2774 2781 of files it detects as binary. With -a, export will generate a
2775 2782 diff anyway, probably with undesirable results.
2776 2783
2777 2784 Use the -g/--git option to generate diffs in the git extended diff
2778 2785 format. See :hg:`help diffs` for more information.
2779 2786
2780 2787 With the --switch-parent option, the diff will be against the
2781 2788 second parent. It can be useful to review a merge.
2782 2789
2783 2790 .. container:: verbose
2784 2791
2785 2792 Examples:
2786 2793
2787 2794 - use export and import to transplant a bugfix to the current
2788 2795 branch::
2789 2796
2790 2797 hg export -r 9353 | hg import -
2791 2798
2792 2799 - export all the changesets between two revisions to a file with
2793 2800 rename information::
2794 2801
2795 2802 hg export --git -r 123:150 > changes.txt
2796 2803
2797 2804 - split outgoing changes into a series of patches with
2798 2805 descriptive names::
2799 2806
2800 2807 hg export -r "outgoing()" -o "%n-%m.patch"
2801 2808
2802 2809 Returns 0 on success.
2803 2810 """
2804 2811 changesets += tuple(opts.get('rev', []))
2805 2812 if not changesets:
2806 2813 changesets = ['.']
2807 2814 revs = scmutil.revrange(repo, changesets)
2808 2815 if not revs:
2809 2816 raise util.Abort(_("export requires at least one changeset"))
2810 2817 if len(revs) > 1:
2811 2818 ui.note(_('exporting patches:\n'))
2812 2819 else:
2813 2820 ui.note(_('exporting patch:\n'))
2814 2821 cmdutil.export(repo, revs, template=opts.get('output'),
2815 2822 switch_parent=opts.get('switch_parent'),
2816 2823 opts=patch.diffopts(ui, opts))
2817 2824
2818 2825 @command('^forget', walkopts, _('[OPTION]... FILE...'))
2819 2826 def forget(ui, repo, *pats, **opts):
2820 2827 """forget the specified files on the next commit
2821 2828
2822 2829 Mark the specified files so they will no longer be tracked
2823 2830 after the next commit.
2824 2831
2825 2832 This only removes files from the current branch, not from the
2826 2833 entire project history, and it does not delete them from the
2827 2834 working directory.
2828 2835
2829 2836 To undo a forget before the next commit, see :hg:`add`.
2830 2837
2831 2838 .. container:: verbose
2832 2839
2833 2840 Examples:
2834 2841
2835 2842 - forget newly-added binary files::
2836 2843
2837 2844 hg forget "set:added() and binary()"
2838 2845
2839 2846 - forget files that would be excluded by .hgignore::
2840 2847
2841 2848 hg forget "set:hgignore()"
2842 2849
2843 2850 Returns 0 on success.
2844 2851 """
2845 2852
2846 2853 if not pats:
2847 2854 raise util.Abort(_('no files specified'))
2848 2855
2849 2856 m = scmutil.match(repo[None], pats, opts)
2850 2857 rejected = cmdutil.forget(ui, repo, m, prefix="", explicitonly=False)[0]
2851 2858 return rejected and 1 or 0
2852 2859
2853 2860 @command(
2854 2861 'graft',
2855 2862 [('r', 'rev', [], _('revisions to graft'), _('REV')),
2856 2863 ('c', 'continue', False, _('resume interrupted graft')),
2857 2864 ('e', 'edit', False, _('invoke editor on commit messages')),
2858 2865 ('', 'log', None, _('append graft info to log message')),
2859 2866 ('D', 'currentdate', False,
2860 2867 _('record the current date as commit date')),
2861 2868 ('U', 'currentuser', False,
2862 2869 _('record the current user as committer'), _('DATE'))]
2863 2870 + commitopts2 + mergetoolopts + dryrunopts,
2864 2871 _('[OPTION]... [-r] REV...'))
2865 2872 def graft(ui, repo, *revs, **opts):
2866 2873 '''copy changes from other branches onto the current branch
2867 2874
2868 2875 This command uses Mercurial's merge logic to copy individual
2869 2876 changes from other branches without merging branches in the
2870 2877 history graph. This is sometimes known as 'backporting' or
2871 2878 'cherry-picking'. By default, graft will copy user, date, and
2872 2879 description from the source changesets.
2873 2880
2874 2881 Changesets that are ancestors of the current revision, that have
2875 2882 already been grafted, or that are merges will be skipped.
2876 2883
2877 2884 If --log is specified, log messages will have a comment appended
2878 2885 of the form::
2879 2886
2880 2887 (grafted from CHANGESETHASH)
2881 2888
2882 2889 If a graft merge results in conflicts, the graft process is
2883 2890 interrupted so that the current merge can be manually resolved.
2884 2891 Once all conflicts are addressed, the graft process can be
2885 2892 continued with the -c/--continue option.
2886 2893
2887 2894 .. note::
2888 2895 The -c/--continue option does not reapply earlier options.
2889 2896
2890 2897 .. container:: verbose
2891 2898
2892 2899 Examples:
2893 2900
2894 2901 - copy a single change to the stable branch and edit its description::
2895 2902
2896 2903 hg update stable
2897 2904 hg graft --edit 9393
2898 2905
2899 2906 - graft a range of changesets with one exception, updating dates::
2900 2907
2901 2908 hg graft -D "2085::2093 and not 2091"
2902 2909
2903 2910 - continue a graft after resolving conflicts::
2904 2911
2905 2912 hg graft -c
2906 2913
2907 2914 - show the source of a grafted changeset::
2908 2915
2909 2916 hg log --debug -r tip
2910 2917
2911 2918 Returns 0 on successful completion.
2912 2919 '''
2913 2920
2914 2921 revs = list(revs)
2915 2922 revs.extend(opts['rev'])
2916 2923
2917 2924 if not opts.get('user') and opts.get('currentuser'):
2918 2925 opts['user'] = ui.username()
2919 2926 if not opts.get('date') and opts.get('currentdate'):
2920 2927 opts['date'] = "%d %d" % util.makedate()
2921 2928
2922 2929 editor = None
2923 2930 if opts.get('edit'):
2924 2931 editor = cmdutil.commitforceeditor
2925 2932
2926 2933 cont = False
2927 2934 if opts['continue']:
2928 2935 cont = True
2929 2936 if revs:
2930 2937 raise util.Abort(_("can't specify --continue and revisions"))
2931 2938 # read in unfinished revisions
2932 2939 try:
2933 2940 nodes = repo.opener.read('graftstate').splitlines()
2934 2941 revs = [repo[node].rev() for node in nodes]
2935 2942 except IOError, inst:
2936 2943 if inst.errno != errno.ENOENT:
2937 2944 raise
2938 2945 raise util.Abort(_("no graft state found, can't continue"))
2939 2946 else:
2940 2947 cmdutil.bailifchanged(repo)
2941 2948 if not revs:
2942 2949 raise util.Abort(_('no revisions specified'))
2943 2950 revs = scmutil.revrange(repo, revs)
2944 2951
2945 2952 # check for merges
2946 2953 for rev in repo.revs('%ld and merge()', revs):
2947 2954 ui.warn(_('skipping ungraftable merge revision %s\n') % rev)
2948 2955 revs.remove(rev)
2949 2956 if not revs:
2950 2957 return -1
2951 2958
2952 2959 # check for ancestors of dest branch
2953 2960 crev = repo['.'].rev()
2954 2961 ancestors = repo.changelog.ancestors([crev], inclusive=True)
2955 2962 # don't mutate while iterating, create a copy
2956 2963 for rev in list(revs):
2957 2964 if rev in ancestors:
2958 2965 ui.warn(_('skipping ancestor revision %s\n') % rev)
2959 2966 revs.remove(rev)
2960 2967 if not revs:
2961 2968 return -1
2962 2969
2963 2970 # analyze revs for earlier grafts
2964 2971 ids = {}
2965 2972 for ctx in repo.set("%ld", revs):
2966 2973 ids[ctx.hex()] = ctx.rev()
2967 2974 n = ctx.extra().get('source')
2968 2975 if n:
2969 2976 ids[n] = ctx.rev()
2970 2977
2971 2978 # check ancestors for earlier grafts
2972 2979 ui.debug('scanning for duplicate grafts\n')
2973 2980
2974 2981 for rev in repo.changelog.findmissingrevs(revs, [crev]):
2975 2982 ctx = repo[rev]
2976 2983 n = ctx.extra().get('source')
2977 2984 if n in ids:
2978 2985 r = repo[n].rev()
2979 2986 if r in revs:
2980 2987 ui.warn(_('skipping already grafted revision %s\n') % r)
2981 2988 revs.remove(r)
2982 2989 elif ids[n] in revs:
2983 2990 ui.warn(_('skipping already grafted revision %s '
2984 2991 '(same origin %d)\n') % (ids[n], r))
2985 2992 revs.remove(ids[n])
2986 2993 elif ctx.hex() in ids:
2987 2994 r = ids[ctx.hex()]
2988 2995 ui.warn(_('skipping already grafted revision %s '
2989 2996 '(was grafted from %d)\n') % (r, rev))
2990 2997 revs.remove(r)
2991 2998 if not revs:
2992 2999 return -1
2993 3000
2994 3001 wlock = repo.wlock()
2995 3002 try:
2996 3003 current = repo['.']
2997 3004 for pos, ctx in enumerate(repo.set("%ld", revs)):
2998 3005
2999 3006 ui.status(_('grafting revision %s\n') % ctx.rev())
3000 3007 if opts.get('dry_run'):
3001 3008 continue
3002 3009
3003 3010 source = ctx.extra().get('source')
3004 3011 if not source:
3005 3012 source = ctx.hex()
3006 3013 extra = {'source': source}
3007 3014 user = ctx.user()
3008 3015 if opts.get('user'):
3009 3016 user = opts['user']
3010 3017 date = ctx.date()
3011 3018 if opts.get('date'):
3012 3019 date = opts['date']
3013 3020 message = ctx.description()
3014 3021 if opts.get('log'):
3015 3022 message += '\n(grafted from %s)' % ctx.hex()
3016 3023
3017 3024 # we don't merge the first commit when continuing
3018 3025 if not cont:
3019 3026 # perform the graft merge with p1(rev) as 'ancestor'
3020 3027 try:
3021 3028 # ui.forcemerge is an internal variable, do not document
3022 3029 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
3023 3030 stats = mergemod.update(repo, ctx.node(), True, True, False,
3024 3031 ctx.p1().node())
3025 3032 finally:
3026 3033 repo.ui.setconfig('ui', 'forcemerge', '')
3027 3034 # report any conflicts
3028 3035 if stats and stats[3] > 0:
3029 3036 # write out state for --continue
3030 3037 nodelines = [repo[rev].hex() + "\n" for rev in revs[pos:]]
3031 3038 repo.opener.write('graftstate', ''.join(nodelines))
3032 3039 raise util.Abort(
3033 3040 _("unresolved conflicts, can't continue"),
3034 3041 hint=_('use hg resolve and hg graft --continue'))
3035 3042 else:
3036 3043 cont = False
3037 3044
3038 3045 # drop the second merge parent
3039 3046 repo.setparents(current.node(), nullid)
3040 3047 repo.dirstate.write()
3041 3048 # fix up dirstate for copies and renames
3042 3049 cmdutil.duplicatecopies(repo, ctx.rev(), ctx.p1().rev())
3043 3050
3044 3051 # commit
3045 3052 node = repo.commit(text=message, user=user,
3046 3053 date=date, extra=extra, editor=editor)
3047 3054 if node is None:
3048 3055 ui.status(_('graft for revision %s is empty\n') % ctx.rev())
3049 3056 else:
3050 3057 current = repo[node]
3051 3058 finally:
3052 3059 wlock.release()
3053 3060
3054 3061 # remove state when we complete successfully
3055 3062 if not opts.get('dry_run'):
3056 3063 util.unlinkpath(repo.join('graftstate'), ignoremissing=True)
3057 3064
3058 3065 return 0
3059 3066
3060 3067 @command('grep',
3061 3068 [('0', 'print0', None, _('end fields with NUL')),
3062 3069 ('', 'all', None, _('print all revisions that match')),
3063 3070 ('a', 'text', None, _('treat all files as text')),
3064 3071 ('f', 'follow', None,
3065 3072 _('follow changeset history,'
3066 3073 ' or file history across copies and renames')),
3067 3074 ('i', 'ignore-case', None, _('ignore case when matching')),
3068 3075 ('l', 'files-with-matches', None,
3069 3076 _('print only filenames and revisions that match')),
3070 3077 ('n', 'line-number', None, _('print matching line numbers')),
3071 3078 ('r', 'rev', [],
3072 3079 _('only search files changed within revision range'), _('REV')),
3073 3080 ('u', 'user', None, _('list the author (long with -v)')),
3074 3081 ('d', 'date', None, _('list the date (short with -q)')),
3075 3082 ] + walkopts,
3076 3083 _('[OPTION]... PATTERN [FILE]...'))
3077 3084 def grep(ui, repo, pattern, *pats, **opts):
3078 3085 """search for a pattern in specified files and revisions
3079 3086
3080 3087 Search revisions of files for a regular expression.
3081 3088
3082 3089 This command behaves differently than Unix grep. It only accepts
3083 3090 Python/Perl regexps. It searches repository history, not the
3084 3091 working directory. It always prints the revision number in which a
3085 3092 match appears.
3086 3093
3087 3094 By default, grep only prints output for the first revision of a
3088 3095 file in which it finds a match. To get it to print every revision
3089 3096 that contains a change in match status ("-" for a match that
3090 3097 becomes a non-match, or "+" for a non-match that becomes a match),
3091 3098 use the --all flag.
3092 3099
3093 3100 Returns 0 if a match is found, 1 otherwise.
3094 3101 """
3095 3102 reflags = re.M
3096 3103 if opts.get('ignore_case'):
3097 3104 reflags |= re.I
3098 3105 try:
3099 3106 regexp = util.compilere(pattern, reflags)
3100 3107 except re.error, inst:
3101 3108 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
3102 3109 return 1
3103 3110 sep, eol = ':', '\n'
3104 3111 if opts.get('print0'):
3105 3112 sep = eol = '\0'
3106 3113
3107 3114 getfile = util.lrucachefunc(repo.file)
3108 3115
3109 3116 def matchlines(body):
3110 3117 begin = 0
3111 3118 linenum = 0
3112 3119 while begin < len(body):
3113 3120 match = regexp.search(body, begin)
3114 3121 if not match:
3115 3122 break
3116 3123 mstart, mend = match.span()
3117 3124 linenum += body.count('\n', begin, mstart) + 1
3118 3125 lstart = body.rfind('\n', begin, mstart) + 1 or begin
3119 3126 begin = body.find('\n', mend) + 1 or len(body) + 1
3120 3127 lend = begin - 1
3121 3128 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
3122 3129
3123 3130 class linestate(object):
3124 3131 def __init__(self, line, linenum, colstart, colend):
3125 3132 self.line = line
3126 3133 self.linenum = linenum
3127 3134 self.colstart = colstart
3128 3135 self.colend = colend
3129 3136
3130 3137 def __hash__(self):
3131 3138 return hash((self.linenum, self.line))
3132 3139
3133 3140 def __eq__(self, other):
3134 3141 return self.line == other.line
3135 3142
3136 3143 matches = {}
3137 3144 copies = {}
3138 3145 def grepbody(fn, rev, body):
3139 3146 matches[rev].setdefault(fn, [])
3140 3147 m = matches[rev][fn]
3141 3148 for lnum, cstart, cend, line in matchlines(body):
3142 3149 s = linestate(line, lnum, cstart, cend)
3143 3150 m.append(s)
3144 3151
3145 3152 def difflinestates(a, b):
3146 3153 sm = difflib.SequenceMatcher(None, a, b)
3147 3154 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
3148 3155 if tag == 'insert':
3149 3156 for i in xrange(blo, bhi):
3150 3157 yield ('+', b[i])
3151 3158 elif tag == 'delete':
3152 3159 for i in xrange(alo, ahi):
3153 3160 yield ('-', a[i])
3154 3161 elif tag == 'replace':
3155 3162 for i in xrange(alo, ahi):
3156 3163 yield ('-', a[i])
3157 3164 for i in xrange(blo, bhi):
3158 3165 yield ('+', b[i])
3159 3166
3160 3167 def display(fn, ctx, pstates, states):
3161 3168 rev = ctx.rev()
3162 3169 datefunc = ui.quiet and util.shortdate or util.datestr
3163 3170 found = False
3164 3171 filerevmatches = {}
3165 3172 def binary():
3166 3173 flog = getfile(fn)
3167 3174 return util.binary(flog.read(ctx.filenode(fn)))
3168 3175
3169 3176 if opts.get('all'):
3170 3177 iter = difflinestates(pstates, states)
3171 3178 else:
3172 3179 iter = [('', l) for l in states]
3173 3180 for change, l in iter:
3174 3181 cols = [(fn, 'grep.filename'), (str(rev), 'grep.rev')]
3175 3182 before, match, after = None, None, None
3176 3183
3177 3184 if opts.get('line_number'):
3178 3185 cols.append((str(l.linenum), 'grep.linenumber'))
3179 3186 if opts.get('all'):
3180 3187 cols.append((change, 'grep.change'))
3181 3188 if opts.get('user'):
3182 3189 cols.append((ui.shortuser(ctx.user()), 'grep.user'))
3183 3190 if opts.get('date'):
3184 3191 cols.append((datefunc(ctx.date()), 'grep.date'))
3185 3192 if opts.get('files_with_matches'):
3186 3193 c = (fn, rev)
3187 3194 if c in filerevmatches:
3188 3195 continue
3189 3196 filerevmatches[c] = 1
3190 3197 else:
3191 3198 before = l.line[:l.colstart]
3192 3199 match = l.line[l.colstart:l.colend]
3193 3200 after = l.line[l.colend:]
3194 3201 for col, label in cols[:-1]:
3195 3202 ui.write(col, label=label)
3196 3203 ui.write(sep, label='grep.sep')
3197 3204 ui.write(cols[-1][0], label=cols[-1][1])
3198 3205 if before is not None:
3199 3206 ui.write(sep, label='grep.sep')
3200 3207 if not opts.get('text') and binary():
3201 3208 ui.write(" Binary file matches")
3202 3209 else:
3203 3210 ui.write(before)
3204 3211 ui.write(match, label='grep.match')
3205 3212 ui.write(after)
3206 3213 ui.write(eol)
3207 3214 found = True
3208 3215 return found
3209 3216
3210 3217 skip = {}
3211 3218 revfiles = {}
3212 3219 matchfn = scmutil.match(repo[None], pats, opts)
3213 3220 found = False
3214 3221 follow = opts.get('follow')
3215 3222
3216 3223 def prep(ctx, fns):
3217 3224 rev = ctx.rev()
3218 3225 pctx = ctx.p1()
3219 3226 parent = pctx.rev()
3220 3227 matches.setdefault(rev, {})
3221 3228 matches.setdefault(parent, {})
3222 3229 files = revfiles.setdefault(rev, [])
3223 3230 for fn in fns:
3224 3231 flog = getfile(fn)
3225 3232 try:
3226 3233 fnode = ctx.filenode(fn)
3227 3234 except error.LookupError:
3228 3235 continue
3229 3236
3230 3237 copied = flog.renamed(fnode)
3231 3238 copy = follow and copied and copied[0]
3232 3239 if copy:
3233 3240 copies.setdefault(rev, {})[fn] = copy
3234 3241 if fn in skip:
3235 3242 if copy:
3236 3243 skip[copy] = True
3237 3244 continue
3238 3245 files.append(fn)
3239 3246
3240 3247 if fn not in matches[rev]:
3241 3248 grepbody(fn, rev, flog.read(fnode))
3242 3249
3243 3250 pfn = copy or fn
3244 3251 if pfn not in matches[parent]:
3245 3252 try:
3246 3253 fnode = pctx.filenode(pfn)
3247 3254 grepbody(pfn, parent, flog.read(fnode))
3248 3255 except error.LookupError:
3249 3256 pass
3250 3257
3251 3258 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
3252 3259 rev = ctx.rev()
3253 3260 parent = ctx.p1().rev()
3254 3261 for fn in sorted(revfiles.get(rev, [])):
3255 3262 states = matches[rev][fn]
3256 3263 copy = copies.get(rev, {}).get(fn)
3257 3264 if fn in skip:
3258 3265 if copy:
3259 3266 skip[copy] = True
3260 3267 continue
3261 3268 pstates = matches.get(parent, {}).get(copy or fn, [])
3262 3269 if pstates or states:
3263 3270 r = display(fn, ctx, pstates, states)
3264 3271 found = found or r
3265 3272 if r and not opts.get('all'):
3266 3273 skip[fn] = True
3267 3274 if copy:
3268 3275 skip[copy] = True
3269 3276 del matches[rev]
3270 3277 del revfiles[rev]
3271 3278
3272 3279 return not found
3273 3280
3274 3281 @command('heads',
3275 3282 [('r', 'rev', '',
3276 3283 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
3277 3284 ('t', 'topo', False, _('show topological heads only')),
3278 3285 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
3279 3286 ('c', 'closed', False, _('show normal and closed branch heads')),
3280 3287 ] + templateopts,
3281 3288 _('[-ct] [-r STARTREV] [REV]...'))
3282 3289 def heads(ui, repo, *branchrevs, **opts):
3283 3290 """show current repository heads or show branch heads
3284 3291
3285 3292 With no arguments, show all repository branch heads.
3286 3293
3287 3294 Repository "heads" are changesets with no child changesets. They are
3288 3295 where development generally takes place and are the usual targets
3289 3296 for update and merge operations. Branch heads are changesets that have
3290 3297 no child changeset on the same branch.
3291 3298
3292 3299 If one or more REVs are given, only branch heads on the branches
3293 3300 associated with the specified changesets are shown. This means
3294 3301 that you can use :hg:`heads foo` to see the heads on a branch
3295 3302 named ``foo``.
3296 3303
3297 3304 If -c/--closed is specified, also show branch heads marked closed
3298 3305 (see :hg:`commit --close-branch`).
3299 3306
3300 3307 If STARTREV is specified, only those heads that are descendants of
3301 3308 STARTREV will be displayed.
3302 3309
3303 3310 If -t/--topo is specified, named branch mechanics will be ignored and only
3304 3311 changesets without children will be shown.
3305 3312
3306 3313 Returns 0 if matching heads are found, 1 if not.
3307 3314 """
3308 3315
3309 3316 start = None
3310 3317 if 'rev' in opts:
3311 3318 start = scmutil.revsingle(repo, opts['rev'], None).node()
3312 3319
3313 3320 if opts.get('topo'):
3314 3321 heads = [repo[h] for h in repo.heads(start)]
3315 3322 else:
3316 3323 heads = []
3317 3324 for branch in repo.branchmap():
3318 3325 heads += repo.branchheads(branch, start, opts.get('closed'))
3319 3326 heads = [repo[h] for h in heads]
3320 3327
3321 3328 if branchrevs:
3322 3329 branches = set(repo[br].branch() for br in branchrevs)
3323 3330 heads = [h for h in heads if h.branch() in branches]
3324 3331
3325 3332 if opts.get('active') and branchrevs:
3326 3333 dagheads = repo.heads(start)
3327 3334 heads = [h for h in heads if h.node() in dagheads]
3328 3335
3329 3336 if branchrevs:
3330 3337 haveheads = set(h.branch() for h in heads)
3331 3338 if branches - haveheads:
3332 3339 headless = ', '.join(b for b in branches - haveheads)
3333 3340 msg = _('no open branch heads found on branches %s')
3334 3341 if opts.get('rev'):
3335 3342 msg += _(' (started at %s)') % opts['rev']
3336 3343 ui.warn((msg + '\n') % headless)
3337 3344
3338 3345 if not heads:
3339 3346 return 1
3340 3347
3341 3348 heads = sorted(heads, key=lambda x: -x.rev())
3342 3349 displayer = cmdutil.show_changeset(ui, repo, opts)
3343 3350 for ctx in heads:
3344 3351 displayer.show(ctx)
3345 3352 displayer.close()
3346 3353
3347 3354 @command('help',
3348 3355 [('e', 'extension', None, _('show only help for extensions')),
3349 3356 ('c', 'command', None, _('show only help for commands')),
3350 3357 ('k', 'keyword', '', _('show topics matching keyword')),
3351 3358 ],
3352 3359 _('[-ec] [TOPIC]'))
3353 3360 def help_(ui, name=None, **opts):
3354 3361 """show help for a given topic or a help overview
3355 3362
3356 3363 With no arguments, print a list of commands with short help messages.
3357 3364
3358 3365 Given a topic, extension, or command name, print help for that
3359 3366 topic.
3360 3367
3361 3368 Returns 0 if successful.
3362 3369 """
3363 3370
3364 3371 textwidth = min(ui.termwidth(), 80) - 2
3365 3372
3366 3373 keep = ui.verbose and ['verbose'] or []
3367 3374 text = help.help_(ui, name, **opts)
3368 3375
3369 3376 formatted, pruned = minirst.format(text, textwidth, keep=keep)
3370 3377 if 'verbose' in pruned:
3371 3378 keep.append('omitted')
3372 3379 else:
3373 3380 keep.append('notomitted')
3374 3381 formatted, pruned = minirst.format(text, textwidth, keep=keep)
3375 3382 ui.write(formatted)
3376 3383
3377 3384
3378 3385 @command('identify|id',
3379 3386 [('r', 'rev', '',
3380 3387 _('identify the specified revision'), _('REV')),
3381 3388 ('n', 'num', None, _('show local revision number')),
3382 3389 ('i', 'id', None, _('show global revision id')),
3383 3390 ('b', 'branch', None, _('show branch')),
3384 3391 ('t', 'tags', None, _('show tags')),
3385 3392 ('B', 'bookmarks', None, _('show bookmarks')),
3386 3393 ] + remoteopts,
3387 3394 _('[-nibtB] [-r REV] [SOURCE]'))
3388 3395 def identify(ui, repo, source=None, rev=None,
3389 3396 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
3390 3397 """identify the working copy or specified revision
3391 3398
3392 3399 Print a summary identifying the repository state at REV using one or
3393 3400 two parent hash identifiers, followed by a "+" if the working
3394 3401 directory has uncommitted changes, the branch name (if not default),
3395 3402 a list of tags, and a list of bookmarks.
3396 3403
3397 3404 When REV is not given, print a summary of the current state of the
3398 3405 repository.
3399 3406
3400 3407 Specifying a path to a repository root or Mercurial bundle will
3401 3408 cause lookup to operate on that repository/bundle.
3402 3409
3403 3410 .. container:: verbose
3404 3411
3405 3412 Examples:
3406 3413
3407 3414 - generate a build identifier for the working directory::
3408 3415
3409 3416 hg id --id > build-id.dat
3410 3417
3411 3418 - find the revision corresponding to a tag::
3412 3419
3413 3420 hg id -n -r 1.3
3414 3421
3415 3422 - check the most recent revision of a remote repository::
3416 3423
3417 3424 hg id -r tip http://selenic.com/hg/
3418 3425
3419 3426 Returns 0 if successful.
3420 3427 """
3421 3428
3422 3429 if not repo and not source:
3423 3430 raise util.Abort(_("there is no Mercurial repository here "
3424 3431 "(.hg not found)"))
3425 3432
3426 3433 hexfunc = ui.debugflag and hex or short
3427 3434 default = not (num or id or branch or tags or bookmarks)
3428 3435 output = []
3429 3436 revs = []
3430 3437
3431 3438 if source:
3432 3439 source, branches = hg.parseurl(ui.expandpath(source))
3433 3440 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
3434 3441 repo = peer.local()
3435 3442 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
3436 3443
3437 3444 if not repo:
3438 3445 if num or branch or tags:
3439 3446 raise util.Abort(
3440 3447 _("can't query remote revision number, branch, or tags"))
3441 3448 if not rev and revs:
3442 3449 rev = revs[0]
3443 3450 if not rev:
3444 3451 rev = "tip"
3445 3452
3446 3453 remoterev = peer.lookup(rev)
3447 3454 if default or id:
3448 3455 output = [hexfunc(remoterev)]
3449 3456
3450 3457 def getbms():
3451 3458 bms = []
3452 3459
3453 3460 if 'bookmarks' in peer.listkeys('namespaces'):
3454 3461 hexremoterev = hex(remoterev)
3455 3462 bms = [bm for bm, bmr in peer.listkeys('bookmarks').iteritems()
3456 3463 if bmr == hexremoterev]
3457 3464
3458 3465 return sorted(bms)
3459 3466
3460 3467 if bookmarks:
3461 3468 output.extend(getbms())
3462 3469 elif default and not ui.quiet:
3463 3470 # multiple bookmarks for a single parent separated by '/'
3464 3471 bm = '/'.join(getbms())
3465 3472 if bm:
3466 3473 output.append(bm)
3467 3474 else:
3468 3475 if not rev:
3469 3476 ctx = repo[None]
3470 3477 parents = ctx.parents()
3471 3478 changed = ""
3472 3479 if default or id or num:
3473 3480 if (util.any(repo.status())
3474 3481 or util.any(ctx.sub(s).dirty() for s in ctx.substate)):
3475 3482 changed = '+'
3476 3483 if default or id:
3477 3484 output = ["%s%s" %
3478 3485 ('+'.join([hexfunc(p.node()) for p in parents]), changed)]
3479 3486 if num:
3480 3487 output.append("%s%s" %
3481 3488 ('+'.join([str(p.rev()) for p in parents]), changed))
3482 3489 else:
3483 3490 ctx = scmutil.revsingle(repo, rev)
3484 3491 if default or id:
3485 3492 output = [hexfunc(ctx.node())]
3486 3493 if num:
3487 3494 output.append(str(ctx.rev()))
3488 3495
3489 3496 if default and not ui.quiet:
3490 3497 b = ctx.branch()
3491 3498 if b != 'default':
3492 3499 output.append("(%s)" % b)
3493 3500
3494 3501 # multiple tags for a single parent separated by '/'
3495 3502 t = '/'.join(ctx.tags())
3496 3503 if t:
3497 3504 output.append(t)
3498 3505
3499 3506 # multiple bookmarks for a single parent separated by '/'
3500 3507 bm = '/'.join(ctx.bookmarks())
3501 3508 if bm:
3502 3509 output.append(bm)
3503 3510 else:
3504 3511 if branch:
3505 3512 output.append(ctx.branch())
3506 3513
3507 3514 if tags:
3508 3515 output.extend(ctx.tags())
3509 3516
3510 3517 if bookmarks:
3511 3518 output.extend(ctx.bookmarks())
3512 3519
3513 3520 ui.write("%s\n" % ' '.join(output))
3514 3521
3515 3522 @command('import|patch',
3516 3523 [('p', 'strip', 1,
3517 3524 _('directory strip option for patch. This has the same '
3518 3525 'meaning as the corresponding patch option'), _('NUM')),
3519 3526 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
3520 3527 ('e', 'edit', False, _('invoke editor on commit messages')),
3521 3528 ('f', 'force', None, _('skip check for outstanding uncommitted changes')),
3522 3529 ('', 'no-commit', None,
3523 3530 _("don't commit, just update the working directory")),
3524 3531 ('', 'bypass', None,
3525 3532 _("apply patch without touching the working directory")),
3526 3533 ('', 'exact', None,
3527 3534 _('apply patch to the nodes from which it was generated')),
3528 3535 ('', 'import-branch', None,
3529 3536 _('use any branch information in patch (implied by --exact)'))] +
3530 3537 commitopts + commitopts2 + similarityopts,
3531 3538 _('[OPTION]... PATCH...'))
3532 3539 def import_(ui, repo, patch1=None, *patches, **opts):
3533 3540 """import an ordered set of patches
3534 3541
3535 3542 Import a list of patches and commit them individually (unless
3536 3543 --no-commit is specified).
3537 3544
3538 3545 If there are outstanding changes in the working directory, import
3539 3546 will abort unless given the -f/--force flag.
3540 3547
3541 3548 You can import a patch straight from a mail message. Even patches
3542 3549 as attachments work (to use the body part, it must have type
3543 3550 text/plain or text/x-patch). From and Subject headers of email
3544 3551 message are used as default committer and commit message. All
3545 3552 text/plain body parts before first diff are added to commit
3546 3553 message.
3547 3554
3548 3555 If the imported patch was generated by :hg:`export`, user and
3549 3556 description from patch override values from message headers and
3550 3557 body. Values given on command line with -m/--message and -u/--user
3551 3558 override these.
3552 3559
3553 3560 If --exact is specified, import will set the working directory to
3554 3561 the parent of each patch before applying it, and will abort if the
3555 3562 resulting changeset has a different ID than the one recorded in
3556 3563 the patch. This may happen due to character set problems or other
3557 3564 deficiencies in the text patch format.
3558 3565
3559 3566 Use --bypass to apply and commit patches directly to the
3560 3567 repository, not touching the working directory. Without --exact,
3561 3568 patches will be applied on top of the working directory parent
3562 3569 revision.
3563 3570
3564 3571 With -s/--similarity, hg will attempt to discover renames and
3565 3572 copies in the patch in the same way as :hg:`addremove`.
3566 3573
3567 3574 To read a patch from standard input, use "-" as the patch name. If
3568 3575 a URL is specified, the patch will be downloaded from it.
3569 3576 See :hg:`help dates` for a list of formats valid for -d/--date.
3570 3577
3571 3578 .. container:: verbose
3572 3579
3573 3580 Examples:
3574 3581
3575 3582 - import a traditional patch from a website and detect renames::
3576 3583
3577 3584 hg import -s 80 http://example.com/bugfix.patch
3578 3585
3579 3586 - import a changeset from an hgweb server::
3580 3587
3581 3588 hg import http://www.selenic.com/hg/rev/5ca8c111e9aa
3582 3589
3583 3590 - import all the patches in an Unix-style mbox::
3584 3591
3585 3592 hg import incoming-patches.mbox
3586 3593
3587 3594 - attempt to exactly restore an exported changeset (not always
3588 3595 possible)::
3589 3596
3590 3597 hg import --exact proposed-fix.patch
3591 3598
3592 3599 Returns 0 on success.
3593 3600 """
3594 3601
3595 3602 if not patch1:
3596 3603 raise util.Abort(_('need at least one patch to import'))
3597 3604
3598 3605 patches = (patch1,) + patches
3599 3606
3600 3607 date = opts.get('date')
3601 3608 if date:
3602 3609 opts['date'] = util.parsedate(date)
3603 3610
3604 3611 editor = cmdutil.commiteditor
3605 3612 if opts.get('edit'):
3606 3613 editor = cmdutil.commitforceeditor
3607 3614
3608 3615 update = not opts.get('bypass')
3609 3616 if not update and opts.get('no_commit'):
3610 3617 raise util.Abort(_('cannot use --no-commit with --bypass'))
3611 3618 try:
3612 3619 sim = float(opts.get('similarity') or 0)
3613 3620 except ValueError:
3614 3621 raise util.Abort(_('similarity must be a number'))
3615 3622 if sim < 0 or sim > 100:
3616 3623 raise util.Abort(_('similarity must be between 0 and 100'))
3617 3624 if sim and not update:
3618 3625 raise util.Abort(_('cannot use --similarity with --bypass'))
3619 3626
3620 3627 if (opts.get('exact') or not opts.get('force')) and update:
3621 3628 cmdutil.bailifchanged(repo)
3622 3629
3623 3630 base = opts["base"]
3624 3631 strip = opts["strip"]
3625 3632 wlock = lock = tr = None
3626 3633 msgs = []
3627 3634
3628 3635 def tryone(ui, hunk, parents):
3629 3636 tmpname, message, user, date, branch, nodeid, p1, p2 = \
3630 3637 patch.extract(ui, hunk)
3631 3638
3632 3639 if not tmpname:
3633 3640 return (None, None)
3634 3641 msg = _('applied to working directory')
3635 3642
3636 3643 try:
3637 3644 cmdline_message = cmdutil.logmessage(ui, opts)
3638 3645 if cmdline_message:
3639 3646 # pickup the cmdline msg
3640 3647 message = cmdline_message
3641 3648 elif message:
3642 3649 # pickup the patch msg
3643 3650 message = message.strip()
3644 3651 else:
3645 3652 # launch the editor
3646 3653 message = None
3647 3654 ui.debug('message:\n%s\n' % message)
3648 3655
3649 3656 if len(parents) == 1:
3650 3657 parents.append(repo[nullid])
3651 3658 if opts.get('exact'):
3652 3659 if not nodeid or not p1:
3653 3660 raise util.Abort(_('not a Mercurial patch'))
3654 3661 p1 = repo[p1]
3655 3662 p2 = repo[p2 or nullid]
3656 3663 elif p2:
3657 3664 try:
3658 3665 p1 = repo[p1]
3659 3666 p2 = repo[p2]
3660 3667 # Without any options, consider p2 only if the
3661 3668 # patch is being applied on top of the recorded
3662 3669 # first parent.
3663 3670 if p1 != parents[0]:
3664 3671 p1 = parents[0]
3665 3672 p2 = repo[nullid]
3666 3673 except error.RepoError:
3667 3674 p1, p2 = parents
3668 3675 else:
3669 3676 p1, p2 = parents
3670 3677
3671 3678 n = None
3672 3679 if update:
3673 3680 if p1 != parents[0]:
3674 3681 hg.clean(repo, p1.node())
3675 3682 if p2 != parents[1]:
3676 3683 repo.setparents(p1.node(), p2.node())
3677 3684
3678 3685 if opts.get('exact') or opts.get('import_branch'):
3679 3686 repo.dirstate.setbranch(branch or 'default')
3680 3687
3681 3688 files = set()
3682 3689 patch.patch(ui, repo, tmpname, strip=strip, files=files,
3683 3690 eolmode=None, similarity=sim / 100.0)
3684 3691 files = list(files)
3685 3692 if opts.get('no_commit'):
3686 3693 if message:
3687 3694 msgs.append(message)
3688 3695 else:
3689 3696 if opts.get('exact') or p2:
3690 3697 # If you got here, you either use --force and know what
3691 3698 # you are doing or used --exact or a merge patch while
3692 3699 # being updated to its first parent.
3693 3700 m = None
3694 3701 else:
3695 3702 m = scmutil.matchfiles(repo, files or [])
3696 3703 n = repo.commit(message, opts.get('user') or user,
3697 3704 opts.get('date') or date, match=m,
3698 3705 editor=editor)
3699 3706 else:
3700 3707 if opts.get('exact') or opts.get('import_branch'):
3701 3708 branch = branch or 'default'
3702 3709 else:
3703 3710 branch = p1.branch()
3704 3711 store = patch.filestore()
3705 3712 try:
3706 3713 files = set()
3707 3714 try:
3708 3715 patch.patchrepo(ui, repo, p1, store, tmpname, strip,
3709 3716 files, eolmode=None)
3710 3717 except patch.PatchError, e:
3711 3718 raise util.Abort(str(e))
3712 3719 memctx = patch.makememctx(repo, (p1.node(), p2.node()),
3713 3720 message,
3714 3721 opts.get('user') or user,
3715 3722 opts.get('date') or date,
3716 3723 branch, files, store,
3717 3724 editor=cmdutil.commiteditor)
3718 3725 repo.savecommitmessage(memctx.description())
3719 3726 n = memctx.commit()
3720 3727 finally:
3721 3728 store.close()
3722 3729 if opts.get('exact') and hex(n) != nodeid:
3723 3730 raise util.Abort(_('patch is damaged or loses information'))
3724 3731 if n:
3725 3732 # i18n: refers to a short changeset id
3726 3733 msg = _('created %s') % short(n)
3727 3734 return (msg, n)
3728 3735 finally:
3729 3736 os.unlink(tmpname)
3730 3737
3731 3738 try:
3732 3739 try:
3733 3740 wlock = repo.wlock()
3734 3741 if not opts.get('no_commit'):
3735 3742 lock = repo.lock()
3736 3743 tr = repo.transaction('import')
3737 3744 parents = repo.parents()
3738 3745 for patchurl in patches:
3739 3746 if patchurl == '-':
3740 3747 ui.status(_('applying patch from stdin\n'))
3741 3748 patchfile = ui.fin
3742 3749 patchurl = 'stdin' # for error message
3743 3750 else:
3744 3751 patchurl = os.path.join(base, patchurl)
3745 3752 ui.status(_('applying %s\n') % patchurl)
3746 3753 patchfile = hg.openpath(ui, patchurl)
3747 3754
3748 3755 haspatch = False
3749 3756 for hunk in patch.split(patchfile):
3750 3757 (msg, node) = tryone(ui, hunk, parents)
3751 3758 if msg:
3752 3759 haspatch = True
3753 3760 ui.note(msg + '\n')
3754 3761 if update or opts.get('exact'):
3755 3762 parents = repo.parents()
3756 3763 else:
3757 3764 parents = [repo[node]]
3758 3765
3759 3766 if not haspatch:
3760 3767 raise util.Abort(_('%s: no diffs found') % patchurl)
3761 3768
3762 3769 if tr:
3763 3770 tr.close()
3764 3771 if msgs:
3765 3772 repo.savecommitmessage('\n* * *\n'.join(msgs))
3766 3773 except: # re-raises
3767 3774 # wlock.release() indirectly calls dirstate.write(): since
3768 3775 # we're crashing, we do not want to change the working dir
3769 3776 # parent after all, so make sure it writes nothing
3770 3777 repo.dirstate.invalidate()
3771 3778 raise
3772 3779 finally:
3773 3780 if tr:
3774 3781 tr.release()
3775 3782 release(lock, wlock)
3776 3783
3777 3784 @command('incoming|in',
3778 3785 [('f', 'force', None,
3779 3786 _('run even if remote repository is unrelated')),
3780 3787 ('n', 'newest-first', None, _('show newest record first')),
3781 3788 ('', 'bundle', '',
3782 3789 _('file to store the bundles into'), _('FILE')),
3783 3790 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3784 3791 ('B', 'bookmarks', False, _("compare bookmarks")),
3785 3792 ('b', 'branch', [],
3786 3793 _('a specific branch you would like to pull'), _('BRANCH')),
3787 3794 ] + logopts + remoteopts + subrepoopts,
3788 3795 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
3789 3796 def incoming(ui, repo, source="default", **opts):
3790 3797 """show new changesets found in source
3791 3798
3792 3799 Show new changesets found in the specified path/URL or the default
3793 3800 pull location. These are the changesets that would have been pulled
3794 3801 if a pull at the time you issued this command.
3795 3802
3796 3803 For remote repository, using --bundle avoids downloading the
3797 3804 changesets twice if the incoming is followed by a pull.
3798 3805
3799 3806 See pull for valid source format details.
3800 3807
3801 3808 Returns 0 if there are incoming changes, 1 otherwise.
3802 3809 """
3803 3810 if opts.get('graph'):
3804 3811 cmdutil.checkunsupportedgraphflags([], opts)
3805 3812 def display(other, chlist, displayer):
3806 3813 revdag = cmdutil.graphrevs(other, chlist, opts)
3807 3814 showparents = [ctx.node() for ctx in repo[None].parents()]
3808 3815 cmdutil.displaygraph(ui, revdag, displayer, showparents,
3809 3816 graphmod.asciiedges)
3810 3817
3811 3818 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
3812 3819 return 0
3813 3820
3814 3821 if opts.get('bundle') and opts.get('subrepos'):
3815 3822 raise util.Abort(_('cannot combine --bundle and --subrepos'))
3816 3823
3817 3824 if opts.get('bookmarks'):
3818 3825 source, branches = hg.parseurl(ui.expandpath(source),
3819 3826 opts.get('branch'))
3820 3827 other = hg.peer(repo, opts, source)
3821 3828 if 'bookmarks' not in other.listkeys('namespaces'):
3822 3829 ui.warn(_("remote doesn't support bookmarks\n"))
3823 3830 return 0
3824 3831 ui.status(_('comparing with %s\n') % util.hidepassword(source))
3825 3832 return bookmarks.diff(ui, repo, other)
3826 3833
3827 3834 repo._subtoppath = ui.expandpath(source)
3828 3835 try:
3829 3836 return hg.incoming(ui, repo, source, opts)
3830 3837 finally:
3831 3838 del repo._subtoppath
3832 3839
3833 3840
3834 3841 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'))
3835 3842 def init(ui, dest=".", **opts):
3836 3843 """create a new repository in the given directory
3837 3844
3838 3845 Initialize a new repository in the given directory. If the given
3839 3846 directory does not exist, it will be created.
3840 3847
3841 3848 If no directory is given, the current directory is used.
3842 3849
3843 3850 It is possible to specify an ``ssh://`` URL as the destination.
3844 3851 See :hg:`help urls` for more information.
3845 3852
3846 3853 Returns 0 on success.
3847 3854 """
3848 3855 hg.peer(ui, opts, ui.expandpath(dest), create=True)
3849 3856
3850 3857 @command('locate',
3851 3858 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3852 3859 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3853 3860 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
3854 3861 ] + walkopts,
3855 3862 _('[OPTION]... [PATTERN]...'))
3856 3863 def locate(ui, repo, *pats, **opts):
3857 3864 """locate files matching specific patterns
3858 3865
3859 3866 Print files under Mercurial control in the working directory whose
3860 3867 names match the given patterns.
3861 3868
3862 3869 By default, this command searches all directories in the working
3863 3870 directory. To search just the current directory and its
3864 3871 subdirectories, use "--include .".
3865 3872
3866 3873 If no patterns are given to match, this command prints the names
3867 3874 of all files under Mercurial control in the working directory.
3868 3875
3869 3876 If you want to feed the output of this command into the "xargs"
3870 3877 command, use the -0 option to both this command and "xargs". This
3871 3878 will avoid the problem of "xargs" treating single filenames that
3872 3879 contain whitespace as multiple filenames.
3873 3880
3874 3881 Returns 0 if a match is found, 1 otherwise.
3875 3882 """
3876 3883 end = opts.get('print0') and '\0' or '\n'
3877 3884 rev = scmutil.revsingle(repo, opts.get('rev'), None).node()
3878 3885
3879 3886 ret = 1
3880 3887 m = scmutil.match(repo[rev], pats, opts, default='relglob')
3881 3888 m.bad = lambda x, y: False
3882 3889 for abs in repo[rev].walk(m):
3883 3890 if not rev and abs not in repo.dirstate:
3884 3891 continue
3885 3892 if opts.get('fullpath'):
3886 3893 ui.write(repo.wjoin(abs), end)
3887 3894 else:
3888 3895 ui.write(((pats and m.rel(abs)) or abs), end)
3889 3896 ret = 0
3890 3897
3891 3898 return ret
3892 3899
3893 3900 @command('^log|history',
3894 3901 [('f', 'follow', None,
3895 3902 _('follow changeset history, or file history across copies and renames')),
3896 3903 ('', 'follow-first', None,
3897 3904 _('only follow the first parent of merge changesets (DEPRECATED)')),
3898 3905 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
3899 3906 ('C', 'copies', None, _('show copied files')),
3900 3907 ('k', 'keyword', [],
3901 3908 _('do case-insensitive search for a given text'), _('TEXT')),
3902 3909 ('r', 'rev', [], _('show the specified revision or range'), _('REV')),
3903 3910 ('', 'removed', None, _('include revisions where files were removed')),
3904 3911 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
3905 3912 ('u', 'user', [], _('revisions committed by user'), _('USER')),
3906 3913 ('', 'only-branch', [],
3907 3914 _('show only changesets within the given named branch (DEPRECATED)'),
3908 3915 _('BRANCH')),
3909 3916 ('b', 'branch', [],
3910 3917 _('show changesets within the given named branch'), _('BRANCH')),
3911 3918 ('P', 'prune', [],
3912 3919 _('do not display revision or any of its ancestors'), _('REV')),
3913 3920 ] + logopts + walkopts,
3914 3921 _('[OPTION]... [FILE]'))
3915 3922 def log(ui, repo, *pats, **opts):
3916 3923 """show revision history of entire repository or files
3917 3924
3918 3925 Print the revision history of the specified files or the entire
3919 3926 project.
3920 3927
3921 3928 If no revision range is specified, the default is ``tip:0`` unless
3922 3929 --follow is set, in which case the working directory parent is
3923 3930 used as the starting revision.
3924 3931
3925 3932 File history is shown without following rename or copy history of
3926 3933 files. Use -f/--follow with a filename to follow history across
3927 3934 renames and copies. --follow without a filename will only show
3928 3935 ancestors or descendants of the starting revision.
3929 3936
3930 3937 By default this command prints revision number and changeset id,
3931 3938 tags, non-trivial parents, user, date and time, and a summary for
3932 3939 each commit. When the -v/--verbose switch is used, the list of
3933 3940 changed files and full commit message are shown.
3934 3941
3935 3942 .. note::
3936 3943 log -p/--patch may generate unexpected diff output for merge
3937 3944 changesets, as it will only compare the merge changeset against
3938 3945 its first parent. Also, only files different from BOTH parents
3939 3946 will appear in files:.
3940 3947
3941 3948 .. note::
3942 3949 for performance reasons, log FILE may omit duplicate changes
3943 3950 made on branches and will not show deletions. To see all
3944 3951 changes including duplicates and deletions, use the --removed
3945 3952 switch.
3946 3953
3947 3954 .. container:: verbose
3948 3955
3949 3956 Some examples:
3950 3957
3951 3958 - changesets with full descriptions and file lists::
3952 3959
3953 3960 hg log -v
3954 3961
3955 3962 - changesets ancestral to the working directory::
3956 3963
3957 3964 hg log -f
3958 3965
3959 3966 - last 10 commits on the current branch::
3960 3967
3961 3968 hg log -l 10 -b .
3962 3969
3963 3970 - changesets showing all modifications of a file, including removals::
3964 3971
3965 3972 hg log --removed file.c
3966 3973
3967 3974 - all changesets that touch a directory, with diffs, excluding merges::
3968 3975
3969 3976 hg log -Mp lib/
3970 3977
3971 3978 - all revision numbers that match a keyword::
3972 3979
3973 3980 hg log -k bug --template "{rev}\\n"
3974 3981
3975 3982 - check if a given changeset is included is a tagged release::
3976 3983
3977 3984 hg log -r "a21ccf and ancestor(1.9)"
3978 3985
3979 3986 - find all changesets by some user in a date range::
3980 3987
3981 3988 hg log -k alice -d "may 2008 to jul 2008"
3982 3989
3983 3990 - summary of all changesets after the last tag::
3984 3991
3985 3992 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
3986 3993
3987 3994 See :hg:`help dates` for a list of formats valid for -d/--date.
3988 3995
3989 3996 See :hg:`help revisions` and :hg:`help revsets` for more about
3990 3997 specifying revisions.
3991 3998
3992 3999 See :hg:`help templates` for more about pre-packaged styles and
3993 4000 specifying custom templates.
3994 4001
3995 4002 Returns 0 on success.
3996 4003 """
3997 4004 if opts.get('graph'):
3998 4005 return cmdutil.graphlog(ui, repo, *pats, **opts)
3999 4006
4000 4007 matchfn = scmutil.match(repo[None], pats, opts)
4001 4008 limit = cmdutil.loglimit(opts)
4002 4009 count = 0
4003 4010
4004 4011 getrenamed, endrev = None, None
4005 4012 if opts.get('copies'):
4006 4013 if opts.get('rev'):
4007 4014 endrev = max(scmutil.revrange(repo, opts.get('rev'))) + 1
4008 4015 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
4009 4016
4010 4017 df = False
4011 4018 if opts.get("date"):
4012 4019 df = util.matchdate(opts["date"])
4013 4020
4014 4021 branches = opts.get('branch', []) + opts.get('only_branch', [])
4015 4022 opts['branch'] = [repo.lookupbranch(b) for b in branches]
4016 4023
4017 4024 displayer = cmdutil.show_changeset(ui, repo, opts, True)
4018 4025 def prep(ctx, fns):
4019 4026 rev = ctx.rev()
4020 4027 parents = [p for p in repo.changelog.parentrevs(rev)
4021 4028 if p != nullrev]
4022 4029 if opts.get('no_merges') and len(parents) == 2:
4023 4030 return
4024 4031 if opts.get('only_merges') and len(parents) != 2:
4025 4032 return
4026 4033 if opts.get('branch') and ctx.branch() not in opts['branch']:
4027 4034 return
4028 4035 if df and not df(ctx.date()[0]):
4029 4036 return
4030 4037
4031 4038 lower = encoding.lower
4032 4039 if opts.get('user'):
4033 4040 luser = lower(ctx.user())
4034 4041 for k in [lower(x) for x in opts['user']]:
4035 4042 if (k in luser):
4036 4043 break
4037 4044 else:
4038 4045 return
4039 4046 if opts.get('keyword'):
4040 4047 luser = lower(ctx.user())
4041 4048 ldesc = lower(ctx.description())
4042 4049 lfiles = lower(" ".join(ctx.files()))
4043 4050 for k in [lower(x) for x in opts['keyword']]:
4044 4051 if (k in luser or k in ldesc or k in lfiles):
4045 4052 break
4046 4053 else:
4047 4054 return
4048 4055
4049 4056 copies = None
4050 4057 if getrenamed is not None and rev:
4051 4058 copies = []
4052 4059 for fn in ctx.files():
4053 4060 rename = getrenamed(fn, rev)
4054 4061 if rename:
4055 4062 copies.append((fn, rename[0]))
4056 4063
4057 4064 revmatchfn = None
4058 4065 if opts.get('patch') or opts.get('stat'):
4059 4066 if opts.get('follow') or opts.get('follow_first'):
4060 4067 # note: this might be wrong when following through merges
4061 4068 revmatchfn = scmutil.match(repo[None], fns, default='path')
4062 4069 else:
4063 4070 revmatchfn = matchfn
4064 4071
4065 4072 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
4066 4073
4067 4074 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
4068 4075 if displayer.flush(ctx.rev()):
4069 4076 count += 1
4070 4077 if count == limit:
4071 4078 break
4072 4079 displayer.close()
4073 4080
4074 4081 @command('manifest',
4075 4082 [('r', 'rev', '', _('revision to display'), _('REV')),
4076 4083 ('', 'all', False, _("list files from all revisions"))],
4077 4084 _('[-r REV]'))
4078 4085 def manifest(ui, repo, node=None, rev=None, **opts):
4079 4086 """output the current or given revision of the project manifest
4080 4087
4081 4088 Print a list of version controlled files for the given revision.
4082 4089 If no revision is given, the first parent of the working directory
4083 4090 is used, or the null revision if no revision is checked out.
4084 4091
4085 4092 With -v, print file permissions, symlink and executable bits.
4086 4093 With --debug, print file revision hashes.
4087 4094
4088 4095 If option --all is specified, the list of all files from all revisions
4089 4096 is printed. This includes deleted and renamed files.
4090 4097
4091 4098 Returns 0 on success.
4092 4099 """
4093 4100
4094 4101 fm = ui.formatter('manifest', opts)
4095 4102
4096 4103 if opts.get('all'):
4097 4104 if rev or node:
4098 4105 raise util.Abort(_("can't specify a revision with --all"))
4099 4106
4100 4107 res = []
4101 4108 prefix = "data/"
4102 4109 suffix = ".i"
4103 4110 plen = len(prefix)
4104 4111 slen = len(suffix)
4105 4112 lock = repo.lock()
4106 4113 try:
4107 4114 for fn, b, size in repo.store.datafiles():
4108 4115 if size != 0 and fn[-slen:] == suffix and fn[:plen] == prefix:
4109 4116 res.append(fn[plen:-slen])
4110 4117 finally:
4111 4118 lock.release()
4112 4119 for f in res:
4113 4120 fm.startitem()
4114 4121 fm.write("path", '%s\n', f)
4115 4122 fm.end()
4116 4123 return
4117 4124
4118 4125 if rev and node:
4119 4126 raise util.Abort(_("please specify just one revision"))
4120 4127
4121 4128 if not node:
4122 4129 node = rev
4123 4130
4124 4131 char = {'l': '@', 'x': '*', '': ''}
4125 4132 mode = {'l': '644', 'x': '755', '': '644'}
4126 4133 ctx = scmutil.revsingle(repo, node)
4127 4134 mf = ctx.manifest()
4128 4135 for f in ctx:
4129 4136 fm.startitem()
4130 4137 fl = ctx[f].flags()
4131 4138 fm.condwrite(ui.debugflag, 'hash', '%s ', hex(mf[f]))
4132 4139 fm.condwrite(ui.verbose, 'mode type', '%s %1s ', mode[fl], char[fl])
4133 4140 fm.write('path', '%s\n', f)
4134 4141 fm.end()
4135 4142
4136 4143 @command('^merge',
4137 4144 [('f', 'force', None, _('force a merge with outstanding changes')),
4138 4145 ('r', 'rev', '', _('revision to merge'), _('REV')),
4139 4146 ('P', 'preview', None,
4140 4147 _('review revisions to merge (no merge is performed)'))
4141 4148 ] + mergetoolopts,
4142 4149 _('[-P] [-f] [[-r] REV]'))
4143 4150 def merge(ui, repo, node=None, **opts):
4144 4151 """merge working directory with another revision
4145 4152
4146 4153 The current working directory is updated with all changes made in
4147 4154 the requested revision since the last common predecessor revision.
4148 4155
4149 4156 Files that changed between either parent are marked as changed for
4150 4157 the next commit and a commit must be performed before any further
4151 4158 updates to the repository are allowed. The next commit will have
4152 4159 two parents.
4153 4160
4154 4161 ``--tool`` can be used to specify the merge tool used for file
4155 4162 merges. It overrides the HGMERGE environment variable and your
4156 4163 configuration files. See :hg:`help merge-tools` for options.
4157 4164
4158 4165 If no revision is specified, the working directory's parent is a
4159 4166 head revision, and the current branch contains exactly one other
4160 4167 head, the other head is merged with by default. Otherwise, an
4161 4168 explicit revision with which to merge with must be provided.
4162 4169
4163 4170 :hg:`resolve` must be used to resolve unresolved files.
4164 4171
4165 4172 To undo an uncommitted merge, use :hg:`update --clean .` which
4166 4173 will check out a clean copy of the original merge parent, losing
4167 4174 all changes.
4168 4175
4169 4176 Returns 0 on success, 1 if there are unresolved files.
4170 4177 """
4171 4178
4172 4179 if opts.get('rev') and node:
4173 4180 raise util.Abort(_("please specify just one revision"))
4174 4181 if not node:
4175 4182 node = opts.get('rev')
4176 4183
4177 4184 if node:
4178 4185 node = scmutil.revsingle(repo, node).node()
4179 4186
4180 4187 if not node and repo._bookmarkcurrent:
4181 4188 bmheads = repo.bookmarkheads(repo._bookmarkcurrent)
4182 4189 curhead = repo[repo._bookmarkcurrent].node()
4183 4190 if len(bmheads) == 2:
4184 4191 if curhead == bmheads[0]:
4185 4192 node = bmheads[1]
4186 4193 else:
4187 4194 node = bmheads[0]
4188 4195 elif len(bmheads) > 2:
4189 4196 raise util.Abort(_("multiple matching bookmarks to merge - "
4190 4197 "please merge with an explicit rev or bookmark"),
4191 4198 hint=_("run 'hg heads' to see all heads"))
4192 4199 elif len(bmheads) <= 1:
4193 4200 raise util.Abort(_("no matching bookmark to merge - "
4194 4201 "please merge with an explicit rev or bookmark"),
4195 4202 hint=_("run 'hg heads' to see all heads"))
4196 4203
4197 4204 if not node and not repo._bookmarkcurrent:
4198 4205 branch = repo[None].branch()
4199 4206 bheads = repo.branchheads(branch)
4200 4207 nbhs = [bh for bh in bheads if not repo[bh].bookmarks()]
4201 4208
4202 4209 if len(nbhs) > 2:
4203 4210 raise util.Abort(_("branch '%s' has %d heads - "
4204 4211 "please merge with an explicit rev")
4205 4212 % (branch, len(bheads)),
4206 4213 hint=_("run 'hg heads .' to see heads"))
4207 4214
4208 4215 parent = repo.dirstate.p1()
4209 4216 if len(nbhs) <= 1:
4210 4217 if len(bheads) > 1:
4211 4218 raise util.Abort(_("heads are bookmarked - "
4212 4219 "please merge with an explicit rev"),
4213 4220 hint=_("run 'hg heads' to see all heads"))
4214 4221 if len(repo.heads()) > 1:
4215 4222 raise util.Abort(_("branch '%s' has one head - "
4216 4223 "please merge with an explicit rev")
4217 4224 % branch,
4218 4225 hint=_("run 'hg heads' to see all heads"))
4219 4226 msg, hint = _('nothing to merge'), None
4220 4227 if parent != repo.lookup(branch):
4221 4228 hint = _("use 'hg update' instead")
4222 4229 raise util.Abort(msg, hint=hint)
4223 4230
4224 4231 if parent not in bheads:
4225 4232 raise util.Abort(_('working directory not at a head revision'),
4226 4233 hint=_("use 'hg update' or merge with an "
4227 4234 "explicit revision"))
4228 4235 if parent == nbhs[0]:
4229 4236 node = nbhs[-1]
4230 4237 else:
4231 4238 node = nbhs[0]
4232 4239
4233 4240 if opts.get('preview'):
4234 4241 # find nodes that are ancestors of p2 but not of p1
4235 4242 p1 = repo.lookup('.')
4236 4243 p2 = repo.lookup(node)
4237 4244 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4238 4245
4239 4246 displayer = cmdutil.show_changeset(ui, repo, opts)
4240 4247 for node in nodes:
4241 4248 displayer.show(repo[node])
4242 4249 displayer.close()
4243 4250 return 0
4244 4251
4245 4252 try:
4246 4253 # ui.forcemerge is an internal variable, do not document
4247 4254 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
4248 4255 return hg.merge(repo, node, force=opts.get('force'))
4249 4256 finally:
4250 4257 ui.setconfig('ui', 'forcemerge', '')
4251 4258
4252 4259 @command('outgoing|out',
4253 4260 [('f', 'force', None, _('run even when the destination is unrelated')),
4254 4261 ('r', 'rev', [],
4255 4262 _('a changeset intended to be included in the destination'), _('REV')),
4256 4263 ('n', 'newest-first', None, _('show newest record first')),
4257 4264 ('B', 'bookmarks', False, _('compare bookmarks')),
4258 4265 ('b', 'branch', [], _('a specific branch you would like to push'),
4259 4266 _('BRANCH')),
4260 4267 ] + logopts + remoteopts + subrepoopts,
4261 4268 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
4262 4269 def outgoing(ui, repo, dest=None, **opts):
4263 4270 """show changesets not found in the destination
4264 4271
4265 4272 Show changesets not found in the specified destination repository
4266 4273 or the default push location. These are the changesets that would
4267 4274 be pushed if a push was requested.
4268 4275
4269 4276 See pull for details of valid destination formats.
4270 4277
4271 4278 Returns 0 if there are outgoing changes, 1 otherwise.
4272 4279 """
4273 4280 if opts.get('graph'):
4274 4281 cmdutil.checkunsupportedgraphflags([], opts)
4275 4282 o = hg._outgoing(ui, repo, dest, opts)
4276 4283 if o is None:
4277 4284 return
4278 4285
4279 4286 revdag = cmdutil.graphrevs(repo, o, opts)
4280 4287 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
4281 4288 showparents = [ctx.node() for ctx in repo[None].parents()]
4282 4289 cmdutil.displaygraph(ui, revdag, displayer, showparents,
4283 4290 graphmod.asciiedges)
4284 4291 return 0
4285 4292
4286 4293 if opts.get('bookmarks'):
4287 4294 dest = ui.expandpath(dest or 'default-push', dest or 'default')
4288 4295 dest, branches = hg.parseurl(dest, opts.get('branch'))
4289 4296 other = hg.peer(repo, opts, dest)
4290 4297 if 'bookmarks' not in other.listkeys('namespaces'):
4291 4298 ui.warn(_("remote doesn't support bookmarks\n"))
4292 4299 return 0
4293 4300 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
4294 4301 return bookmarks.diff(ui, other, repo)
4295 4302
4296 4303 repo._subtoppath = ui.expandpath(dest or 'default-push', dest or 'default')
4297 4304 try:
4298 4305 return hg.outgoing(ui, repo, dest, opts)
4299 4306 finally:
4300 4307 del repo._subtoppath
4301 4308
4302 4309 @command('parents',
4303 4310 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
4304 4311 ] + templateopts,
4305 4312 _('[-r REV] [FILE]'))
4306 4313 def parents(ui, repo, file_=None, **opts):
4307 4314 """show the parents of the working directory or revision
4308 4315
4309 4316 Print the working directory's parent revisions. If a revision is
4310 4317 given via -r/--rev, the parent of that revision will be printed.
4311 4318 If a file argument is given, the revision in which the file was
4312 4319 last changed (before the working directory revision or the
4313 4320 argument to --rev if given) is printed.
4314 4321
4315 4322 Returns 0 on success.
4316 4323 """
4317 4324
4318 4325 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
4319 4326
4320 4327 if file_:
4321 4328 m = scmutil.match(ctx, (file_,), opts)
4322 4329 if m.anypats() or len(m.files()) != 1:
4323 4330 raise util.Abort(_('can only specify an explicit filename'))
4324 4331 file_ = m.files()[0]
4325 4332 filenodes = []
4326 4333 for cp in ctx.parents():
4327 4334 if not cp:
4328 4335 continue
4329 4336 try:
4330 4337 filenodes.append(cp.filenode(file_))
4331 4338 except error.LookupError:
4332 4339 pass
4333 4340 if not filenodes:
4334 4341 raise util.Abort(_("'%s' not found in manifest!") % file_)
4335 4342 fl = repo.file(file_)
4336 4343 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
4337 4344 else:
4338 4345 p = [cp.node() for cp in ctx.parents()]
4339 4346
4340 4347 displayer = cmdutil.show_changeset(ui, repo, opts)
4341 4348 for n in p:
4342 4349 if n != nullid:
4343 4350 displayer.show(repo[n])
4344 4351 displayer.close()
4345 4352
4346 4353 @command('paths', [], _('[NAME]'))
4347 4354 def paths(ui, repo, search=None):
4348 4355 """show aliases for remote repositories
4349 4356
4350 4357 Show definition of symbolic path name NAME. If no name is given,
4351 4358 show definition of all available names.
4352 4359
4353 4360 Option -q/--quiet suppresses all output when searching for NAME
4354 4361 and shows only the path names when listing all definitions.
4355 4362
4356 4363 Path names are defined in the [paths] section of your
4357 4364 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
4358 4365 repository, ``.hg/hgrc`` is used, too.
4359 4366
4360 4367 The path names ``default`` and ``default-push`` have a special
4361 4368 meaning. When performing a push or pull operation, they are used
4362 4369 as fallbacks if no location is specified on the command-line.
4363 4370 When ``default-push`` is set, it will be used for push and
4364 4371 ``default`` will be used for pull; otherwise ``default`` is used
4365 4372 as the fallback for both. When cloning a repository, the clone
4366 4373 source is written as ``default`` in ``.hg/hgrc``. Note that
4367 4374 ``default`` and ``default-push`` apply to all inbound (e.g.
4368 4375 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email` and
4369 4376 :hg:`bundle`) operations.
4370 4377
4371 4378 See :hg:`help urls` for more information.
4372 4379
4373 4380 Returns 0 on success.
4374 4381 """
4375 4382 if search:
4376 4383 for name, path in ui.configitems("paths"):
4377 4384 if name == search:
4378 4385 ui.status("%s\n" % util.hidepassword(path))
4379 4386 return
4380 4387 if not ui.quiet:
4381 4388 ui.warn(_("not found!\n"))
4382 4389 return 1
4383 4390 else:
4384 4391 for name, path in ui.configitems("paths"):
4385 4392 if ui.quiet:
4386 4393 ui.write("%s\n" % name)
4387 4394 else:
4388 4395 ui.write("%s = %s\n" % (name, util.hidepassword(path)))
4389 4396
4390 4397 @command('phase',
4391 4398 [('p', 'public', False, _('set changeset phase to public')),
4392 4399 ('d', 'draft', False, _('set changeset phase to draft')),
4393 4400 ('s', 'secret', False, _('set changeset phase to secret')),
4394 4401 ('f', 'force', False, _('allow to move boundary backward')),
4395 4402 ('r', 'rev', [], _('target revision'), _('REV')),
4396 4403 ],
4397 4404 _('[-p|-d|-s] [-f] [-r] REV...'))
4398 4405 def phase(ui, repo, *revs, **opts):
4399 4406 """set or show the current phase name
4400 4407
4401 4408 With no argument, show the phase name of specified revisions.
4402 4409
4403 4410 With one of -p/--public, -d/--draft or -s/--secret, change the
4404 4411 phase value of the specified revisions.
4405 4412
4406 4413 Unless -f/--force is specified, :hg:`phase` won't move changeset from a
4407 4414 lower phase to an higher phase. Phases are ordered as follows::
4408 4415
4409 4416 public < draft < secret
4410 4417
4411 4418 Return 0 on success, 1 if no phases were changed or some could not
4412 4419 be changed.
4413 4420 """
4414 4421 # search for a unique phase argument
4415 4422 targetphase = None
4416 4423 for idx, name in enumerate(phases.phasenames):
4417 4424 if opts[name]:
4418 4425 if targetphase is not None:
4419 4426 raise util.Abort(_('only one phase can be specified'))
4420 4427 targetphase = idx
4421 4428
4422 4429 # look for specified revision
4423 4430 revs = list(revs)
4424 4431 revs.extend(opts['rev'])
4425 4432 if not revs:
4426 4433 raise util.Abort(_('no revisions specified'))
4427 4434
4428 4435 revs = scmutil.revrange(repo, revs)
4429 4436
4430 4437 lock = None
4431 4438 ret = 0
4432 4439 if targetphase is None:
4433 4440 # display
4434 4441 for r in revs:
4435 4442 ctx = repo[r]
4436 4443 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
4437 4444 else:
4438 4445 lock = repo.lock()
4439 4446 try:
4440 4447 # set phase
4441 4448 if not revs:
4442 4449 raise util.Abort(_('empty revision set'))
4443 4450 nodes = [repo[r].node() for r in revs]
4444 4451 olddata = repo._phasecache.getphaserevs(repo)[:]
4445 4452 phases.advanceboundary(repo, targetphase, nodes)
4446 4453 if opts['force']:
4447 4454 phases.retractboundary(repo, targetphase, nodes)
4448 4455 finally:
4449 4456 lock.release()
4450 4457 # moving revision from public to draft may hide them
4451 4458 # We have to check result on an unfiltered repository
4452 4459 unfi = repo.unfiltered()
4453 4460 newdata = repo._phasecache.getphaserevs(unfi)
4454 4461 changes = sum(o != newdata[i] for i, o in enumerate(olddata))
4455 4462 cl = unfi.changelog
4456 4463 rejected = [n for n in nodes
4457 4464 if newdata[cl.rev(n)] < targetphase]
4458 4465 if rejected:
4459 4466 ui.warn(_('cannot move %i changesets to a more permissive '
4460 4467 'phase, use --force\n') % len(rejected))
4461 4468 ret = 1
4462 4469 if changes:
4463 4470 msg = _('phase changed for %i changesets\n') % changes
4464 4471 if ret:
4465 4472 ui.status(msg)
4466 4473 else:
4467 4474 ui.note(msg)
4468 4475 else:
4469 4476 ui.warn(_('no phases changed\n'))
4470 4477 ret = 1
4471 4478 return ret
4472 4479
4473 4480 def postincoming(ui, repo, modheads, optupdate, checkout):
4474 4481 if modheads == 0:
4475 4482 return
4476 4483 if optupdate:
4477 4484 movemarkfrom = repo['.'].node()
4478 4485 try:
4479 4486 ret = hg.update(repo, checkout)
4480 4487 except util.Abort, inst:
4481 4488 ui.warn(_("not updating: %s\n") % str(inst))
4482 4489 return 0
4483 4490 if not ret and not checkout:
4484 4491 if bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
4485 4492 ui.status(_("updating bookmark %s\n") % repo._bookmarkcurrent)
4486 4493 return ret
4487 4494 if modheads > 1:
4488 4495 currentbranchheads = len(repo.branchheads())
4489 4496 if currentbranchheads == modheads:
4490 4497 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
4491 4498 elif currentbranchheads > 1:
4492 4499 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to "
4493 4500 "merge)\n"))
4494 4501 else:
4495 4502 ui.status(_("(run 'hg heads' to see heads)\n"))
4496 4503 else:
4497 4504 ui.status(_("(run 'hg update' to get a working copy)\n"))
4498 4505
4499 4506 @command('^pull',
4500 4507 [('u', 'update', None,
4501 4508 _('update to new branch head if changesets were pulled')),
4502 4509 ('f', 'force', None, _('run even when remote repository is unrelated')),
4503 4510 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
4504 4511 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
4505 4512 ('b', 'branch', [], _('a specific branch you would like to pull'),
4506 4513 _('BRANCH')),
4507 4514 ] + remoteopts,
4508 4515 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
4509 4516 def pull(ui, repo, source="default", **opts):
4510 4517 """pull changes from the specified source
4511 4518
4512 4519 Pull changes from a remote repository to a local one.
4513 4520
4514 4521 This finds all changes from the repository at the specified path
4515 4522 or URL and adds them to a local repository (the current one unless
4516 4523 -R is specified). By default, this does not update the copy of the
4517 4524 project in the working directory.
4518 4525
4519 4526 Use :hg:`incoming` if you want to see what would have been added
4520 4527 by a pull at the time you issued this command. If you then decide
4521 4528 to add those changes to the repository, you should use :hg:`pull
4522 4529 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
4523 4530
4524 4531 If SOURCE is omitted, the 'default' path will be used.
4525 4532 See :hg:`help urls` for more information.
4526 4533
4527 4534 Returns 0 on success, 1 if an update had unresolved files.
4528 4535 """
4529 4536 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
4530 4537 other = hg.peer(repo, opts, source)
4531 4538 ui.status(_('pulling from %s\n') % util.hidepassword(source))
4532 4539 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
4533 4540
4534 4541 remotebookmarks = other.listkeys('bookmarks')
4535 4542
4536 4543 if opts.get('bookmark'):
4537 4544 if not revs:
4538 4545 revs = []
4539 4546 for b in opts['bookmark']:
4540 4547 if b not in remotebookmarks:
4541 4548 raise util.Abort(_('remote bookmark %s not found!') % b)
4542 4549 revs.append(remotebookmarks[b])
4543 4550
4544 4551 if revs:
4545 4552 try:
4546 4553 revs = [other.lookup(rev) for rev in revs]
4547 4554 except error.CapabilityError:
4548 4555 err = _("other repository doesn't support revision lookup, "
4549 4556 "so a rev cannot be specified.")
4550 4557 raise util.Abort(err)
4551 4558
4552 4559 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
4553 4560 bookmarks.updatefromremote(ui, repo, remotebookmarks, source)
4554 4561 if checkout:
4555 4562 checkout = str(repo.changelog.rev(other.lookup(checkout)))
4556 4563 repo._subtoppath = source
4557 4564 try:
4558 4565 ret = postincoming(ui, repo, modheads, opts.get('update'), checkout)
4559 4566
4560 4567 finally:
4561 4568 del repo._subtoppath
4562 4569
4563 4570 # update specified bookmarks
4564 4571 if opts.get('bookmark'):
4565 4572 marks = repo._bookmarks
4566 4573 for b in opts['bookmark']:
4567 4574 # explicit pull overrides local bookmark if any
4568 4575 ui.status(_("importing bookmark %s\n") % b)
4569 4576 marks[b] = repo[remotebookmarks[b]].node()
4570 4577 marks.write()
4571 4578
4572 4579 return ret
4573 4580
4574 4581 @command('^push',
4575 4582 [('f', 'force', None, _('force push')),
4576 4583 ('r', 'rev', [],
4577 4584 _('a changeset intended to be included in the destination'),
4578 4585 _('REV')),
4579 4586 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
4580 4587 ('b', 'branch', [],
4581 4588 _('a specific branch you would like to push'), _('BRANCH')),
4582 4589 ('', 'new-branch', False, _('allow pushing a new branch')),
4583 4590 ] + remoteopts,
4584 4591 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
4585 4592 def push(ui, repo, dest=None, **opts):
4586 4593 """push changes to the specified destination
4587 4594
4588 4595 Push changesets from the local repository to the specified
4589 4596 destination.
4590 4597
4591 4598 This operation is symmetrical to pull: it is identical to a pull
4592 4599 in the destination repository from the current one.
4593 4600
4594 4601 By default, push will not allow creation of new heads at the
4595 4602 destination, since multiple heads would make it unclear which head
4596 4603 to use. In this situation, it is recommended to pull and merge
4597 4604 before pushing.
4598 4605
4599 4606 Use --new-branch if you want to allow push to create a new named
4600 4607 branch that is not present at the destination. This allows you to
4601 4608 only create a new branch without forcing other changes.
4602 4609
4603 4610 Use -f/--force to override the default behavior and push all
4604 4611 changesets on all branches.
4605 4612
4606 4613 If -r/--rev is used, the specified revision and all its ancestors
4607 4614 will be pushed to the remote repository.
4608 4615
4609 4616 If -B/--bookmark is used, the specified bookmarked revision, its
4610 4617 ancestors, and the bookmark will be pushed to the remote
4611 4618 repository.
4612 4619
4613 4620 Please see :hg:`help urls` for important details about ``ssh://``
4614 4621 URLs. If DESTINATION is omitted, a default path will be used.
4615 4622
4616 4623 Returns 0 if push was successful, 1 if nothing to push.
4617 4624 """
4618 4625
4619 4626 if opts.get('bookmark'):
4620 4627 for b in opts['bookmark']:
4621 4628 # translate -B options to -r so changesets get pushed
4622 4629 if b in repo._bookmarks:
4623 4630 opts.setdefault('rev', []).append(b)
4624 4631 else:
4625 4632 # if we try to push a deleted bookmark, translate it to null
4626 4633 # this lets simultaneous -r, -b options continue working
4627 4634 opts.setdefault('rev', []).append("null")
4628 4635
4629 4636 dest = ui.expandpath(dest or 'default-push', dest or 'default')
4630 4637 dest, branches = hg.parseurl(dest, opts.get('branch'))
4631 4638 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
4632 4639 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
4633 4640 other = hg.peer(repo, opts, dest)
4634 4641 if revs:
4635 4642 revs = [repo.lookup(r) for r in scmutil.revrange(repo, revs)]
4636 4643
4637 4644 repo._subtoppath = dest
4638 4645 try:
4639 4646 # push subrepos depth-first for coherent ordering
4640 4647 c = repo['']
4641 4648 subs = c.substate # only repos that are committed
4642 4649 for s in sorted(subs):
4643 4650 if c.sub(s).push(opts) == 0:
4644 4651 return False
4645 4652 finally:
4646 4653 del repo._subtoppath
4647 4654 result = repo.push(other, opts.get('force'), revs=revs,
4648 4655 newbranch=opts.get('new_branch'))
4649 4656
4650 4657 result = not result
4651 4658
4652 4659 if opts.get('bookmark'):
4653 4660 rb = other.listkeys('bookmarks')
4654 4661 for b in opts['bookmark']:
4655 4662 # explicit push overrides remote bookmark if any
4656 4663 if b in repo._bookmarks:
4657 4664 ui.status(_("exporting bookmark %s\n") % b)
4658 4665 new = repo[b].hex()
4659 4666 elif b in rb:
4660 4667 ui.status(_("deleting remote bookmark %s\n") % b)
4661 4668 new = '' # delete
4662 4669 else:
4663 4670 ui.warn(_('bookmark %s does not exist on the local '
4664 4671 'or remote repository!\n') % b)
4665 4672 return 2
4666 4673 old = rb.get(b, '')
4667 4674 r = other.pushkey('bookmarks', b, old, new)
4668 4675 if not r:
4669 4676 ui.warn(_('updating bookmark %s failed!\n') % b)
4670 4677 if not result:
4671 4678 result = 2
4672 4679
4673 4680 return result
4674 4681
4675 4682 @command('recover', [])
4676 4683 def recover(ui, repo):
4677 4684 """roll back an interrupted transaction
4678 4685
4679 4686 Recover from an interrupted commit or pull.
4680 4687
4681 4688 This command tries to fix the repository status after an
4682 4689 interrupted operation. It should only be necessary when Mercurial
4683 4690 suggests it.
4684 4691
4685 4692 Returns 0 if successful, 1 if nothing to recover or verify fails.
4686 4693 """
4687 4694 if repo.recover():
4688 4695 return hg.verify(repo)
4689 4696 return 1
4690 4697
4691 4698 @command('^remove|rm',
4692 4699 [('A', 'after', None, _('record delete for missing files')),
4693 4700 ('f', 'force', None,
4694 4701 _('remove (and delete) file even if added or modified')),
4695 4702 ] + walkopts,
4696 4703 _('[OPTION]... FILE...'))
4697 4704 def remove(ui, repo, *pats, **opts):
4698 4705 """remove the specified files on the next commit
4699 4706
4700 4707 Schedule the indicated files for removal from the current branch.
4701 4708
4702 4709 This command schedules the files to be removed at the next commit.
4703 4710 To undo a remove before that, see :hg:`revert`. To undo added
4704 4711 files, see :hg:`forget`.
4705 4712
4706 4713 .. container:: verbose
4707 4714
4708 4715 -A/--after can be used to remove only files that have already
4709 4716 been deleted, -f/--force can be used to force deletion, and -Af
4710 4717 can be used to remove files from the next revision without
4711 4718 deleting them from the working directory.
4712 4719
4713 4720 The following table details the behavior of remove for different
4714 4721 file states (columns) and option combinations (rows). The file
4715 4722 states are Added [A], Clean [C], Modified [M] and Missing [!]
4716 4723 (as reported by :hg:`status`). The actions are Warn, Remove
4717 4724 (from branch) and Delete (from disk):
4718 4725
4719 4726 ======= == == == ==
4720 4727 A C M !
4721 4728 ======= == == == ==
4722 4729 none W RD W R
4723 4730 -f R RD RD R
4724 4731 -A W W W R
4725 4732 -Af R R R R
4726 4733 ======= == == == ==
4727 4734
4728 4735 Note that remove never deletes files in Added [A] state from the
4729 4736 working directory, not even if option --force is specified.
4730 4737
4731 4738 Returns 0 on success, 1 if any warnings encountered.
4732 4739 """
4733 4740
4734 4741 ret = 0
4735 4742 after, force = opts.get('after'), opts.get('force')
4736 4743 if not pats and not after:
4737 4744 raise util.Abort(_('no files specified'))
4738 4745
4739 4746 m = scmutil.match(repo[None], pats, opts)
4740 4747 s = repo.status(match=m, clean=True)
4741 4748 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
4742 4749
4743 4750 # warn about failure to delete explicit files/dirs
4744 4751 wctx = repo[None]
4745 4752 for f in m.files():
4746 4753 if f in repo.dirstate or f in wctx.dirs():
4747 4754 continue
4748 4755 if os.path.exists(m.rel(f)):
4749 4756 if os.path.isdir(m.rel(f)):
4750 4757 ui.warn(_('not removing %s: no tracked files\n') % m.rel(f))
4751 4758 else:
4752 4759 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
4753 4760 # missing files will generate a warning elsewhere
4754 4761 ret = 1
4755 4762
4756 4763 if force:
4757 4764 list = modified + deleted + clean + added
4758 4765 elif after:
4759 4766 list = deleted
4760 4767 for f in modified + added + clean:
4761 4768 ui.warn(_('not removing %s: file still exists\n') % m.rel(f))
4762 4769 ret = 1
4763 4770 else:
4764 4771 list = deleted + clean
4765 4772 for f in modified:
4766 4773 ui.warn(_('not removing %s: file is modified (use -f'
4767 4774 ' to force removal)\n') % m.rel(f))
4768 4775 ret = 1
4769 4776 for f in added:
4770 4777 ui.warn(_('not removing %s: file has been marked for add'
4771 4778 ' (use forget to undo)\n') % m.rel(f))
4772 4779 ret = 1
4773 4780
4774 4781 for f in sorted(list):
4775 4782 if ui.verbose or not m.exact(f):
4776 4783 ui.status(_('removing %s\n') % m.rel(f))
4777 4784
4778 4785 wlock = repo.wlock()
4779 4786 try:
4780 4787 if not after:
4781 4788 for f in list:
4782 4789 if f in added:
4783 4790 continue # we never unlink added files on remove
4784 4791 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
4785 4792 repo[None].forget(list)
4786 4793 finally:
4787 4794 wlock.release()
4788 4795
4789 4796 return ret
4790 4797
4791 4798 @command('rename|move|mv',
4792 4799 [('A', 'after', None, _('record a rename that has already occurred')),
4793 4800 ('f', 'force', None, _('forcibly copy over an existing managed file')),
4794 4801 ] + walkopts + dryrunopts,
4795 4802 _('[OPTION]... SOURCE... DEST'))
4796 4803 def rename(ui, repo, *pats, **opts):
4797 4804 """rename files; equivalent of copy + remove
4798 4805
4799 4806 Mark dest as copies of sources; mark sources for deletion. If dest
4800 4807 is a directory, copies are put in that directory. If dest is a
4801 4808 file, there can only be one source.
4802 4809
4803 4810 By default, this command copies the contents of files as they
4804 4811 exist in the working directory. If invoked with -A/--after, the
4805 4812 operation is recorded, but no copying is performed.
4806 4813
4807 4814 This command takes effect at the next commit. To undo a rename
4808 4815 before that, see :hg:`revert`.
4809 4816
4810 4817 Returns 0 on success, 1 if errors are encountered.
4811 4818 """
4812 4819 wlock = repo.wlock(False)
4813 4820 try:
4814 4821 return cmdutil.copy(ui, repo, pats, opts, rename=True)
4815 4822 finally:
4816 4823 wlock.release()
4817 4824
4818 4825 @command('resolve',
4819 4826 [('a', 'all', None, _('select all unresolved files')),
4820 4827 ('l', 'list', None, _('list state of files needing merge')),
4821 4828 ('m', 'mark', None, _('mark files as resolved')),
4822 4829 ('u', 'unmark', None, _('mark files as unresolved')),
4823 4830 ('n', 'no-status', None, _('hide status prefix'))]
4824 4831 + mergetoolopts + walkopts,
4825 4832 _('[OPTION]... [FILE]...'))
4826 4833 def resolve(ui, repo, *pats, **opts):
4827 4834 """redo merges or set/view the merge status of files
4828 4835
4829 4836 Merges with unresolved conflicts are often the result of
4830 4837 non-interactive merging using the ``internal:merge`` configuration
4831 4838 setting, or a command-line merge tool like ``diff3``. The resolve
4832 4839 command is used to manage the files involved in a merge, after
4833 4840 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
4834 4841 working directory must have two parents). See :hg:`help
4835 4842 merge-tools` for information on configuring merge tools.
4836 4843
4837 4844 The resolve command can be used in the following ways:
4838 4845
4839 4846 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
4840 4847 files, discarding any previous merge attempts. Re-merging is not
4841 4848 performed for files already marked as resolved. Use ``--all/-a``
4842 4849 to select all unresolved files. ``--tool`` can be used to specify
4843 4850 the merge tool used for the given files. It overrides the HGMERGE
4844 4851 environment variable and your configuration files. Previous file
4845 4852 contents are saved with a ``.orig`` suffix.
4846 4853
4847 4854 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
4848 4855 (e.g. after having manually fixed-up the files). The default is
4849 4856 to mark all unresolved files.
4850 4857
4851 4858 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
4852 4859 default is to mark all resolved files.
4853 4860
4854 4861 - :hg:`resolve -l`: list files which had or still have conflicts.
4855 4862 In the printed list, ``U`` = unresolved and ``R`` = resolved.
4856 4863
4857 4864 Note that Mercurial will not let you commit files with unresolved
4858 4865 merge conflicts. You must use :hg:`resolve -m ...` before you can
4859 4866 commit after a conflicting merge.
4860 4867
4861 4868 Returns 0 on success, 1 if any files fail a resolve attempt.
4862 4869 """
4863 4870
4864 4871 all, mark, unmark, show, nostatus = \
4865 4872 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
4866 4873
4867 4874 if (show and (mark or unmark)) or (mark and unmark):
4868 4875 raise util.Abort(_("too many options specified"))
4869 4876 if pats and all:
4870 4877 raise util.Abort(_("can't specify --all and patterns"))
4871 4878 if not (all or pats or show or mark or unmark):
4872 4879 raise util.Abort(_('no files or directories specified; '
4873 4880 'use --all to remerge all files'))
4874 4881
4875 4882 ms = mergemod.mergestate(repo)
4876 4883 m = scmutil.match(repo[None], pats, opts)
4877 4884 ret = 0
4878 4885
4879 4886 for f in ms:
4880 4887 if m(f):
4881 4888 if show:
4882 4889 if nostatus:
4883 4890 ui.write("%s\n" % f)
4884 4891 else:
4885 4892 ui.write("%s %s\n" % (ms[f].upper(), f),
4886 4893 label='resolve.' +
4887 4894 {'u': 'unresolved', 'r': 'resolved'}[ms[f]])
4888 4895 elif mark:
4889 4896 ms.mark(f, "r")
4890 4897 elif unmark:
4891 4898 ms.mark(f, "u")
4892 4899 else:
4893 4900 wctx = repo[None]
4894 4901 mctx = wctx.parents()[-1]
4895 4902
4896 4903 # backup pre-resolve (merge uses .orig for its own purposes)
4897 4904 a = repo.wjoin(f)
4898 4905 util.copyfile(a, a + ".resolve")
4899 4906
4900 4907 try:
4901 4908 # resolve file
4902 4909 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
4903 4910 if ms.resolve(f, wctx, mctx):
4904 4911 ret = 1
4905 4912 finally:
4906 4913 ui.setconfig('ui', 'forcemerge', '')
4907 4914 ms.commit()
4908 4915
4909 4916 # replace filemerge's .orig file with our resolve file
4910 4917 util.rename(a + ".resolve", a + ".orig")
4911 4918
4912 4919 ms.commit()
4913 4920 return ret
4914 4921
4915 4922 @command('revert',
4916 4923 [('a', 'all', None, _('revert all changes when no arguments given')),
4917 4924 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
4918 4925 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
4919 4926 ('C', 'no-backup', None, _('do not save backup copies of files')),
4920 4927 ] + walkopts + dryrunopts,
4921 4928 _('[OPTION]... [-r REV] [NAME]...'))
4922 4929 def revert(ui, repo, *pats, **opts):
4923 4930 """restore files to their checkout state
4924 4931
4925 4932 .. note::
4926 4933 To check out earlier revisions, you should use :hg:`update REV`.
4927 4934 To cancel an uncommitted merge (and lose your changes),
4928 4935 use :hg:`update --clean .`.
4929 4936
4930 4937 With no revision specified, revert the specified files or directories
4931 4938 to the contents they had in the parent of the working directory.
4932 4939 This restores the contents of files to an unmodified
4933 4940 state and unschedules adds, removes, copies, and renames. If the
4934 4941 working directory has two parents, you must explicitly specify a
4935 4942 revision.
4936 4943
4937 4944 Using the -r/--rev or -d/--date options, revert the given files or
4938 4945 directories to their states as of a specific revision. Because
4939 4946 revert does not change the working directory parents, this will
4940 4947 cause these files to appear modified. This can be helpful to "back
4941 4948 out" some or all of an earlier change. See :hg:`backout` for a
4942 4949 related method.
4943 4950
4944 4951 Modified files are saved with a .orig suffix before reverting.
4945 4952 To disable these backups, use --no-backup.
4946 4953
4947 4954 See :hg:`help dates` for a list of formats valid for -d/--date.
4948 4955
4949 4956 Returns 0 on success.
4950 4957 """
4951 4958
4952 4959 if opts.get("date"):
4953 4960 if opts.get("rev"):
4954 4961 raise util.Abort(_("you can't specify a revision and a date"))
4955 4962 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
4956 4963
4957 4964 parent, p2 = repo.dirstate.parents()
4958 4965 if not opts.get('rev') and p2 != nullid:
4959 4966 # revert after merge is a trap for new users (issue2915)
4960 4967 raise util.Abort(_('uncommitted merge with no revision specified'),
4961 4968 hint=_('use "hg update" or see "hg help revert"'))
4962 4969
4963 4970 ctx = scmutil.revsingle(repo, opts.get('rev'))
4964 4971
4965 4972 if not pats and not opts.get('all'):
4966 4973 msg = _("no files or directories specified")
4967 4974 if p2 != nullid:
4968 4975 hint = _("uncommitted merge, use --all to discard all changes,"
4969 4976 " or 'hg update -C .' to abort the merge")
4970 4977 raise util.Abort(msg, hint=hint)
4971 4978 dirty = util.any(repo.status())
4972 4979 node = ctx.node()
4973 4980 if node != parent:
4974 4981 if dirty:
4975 4982 hint = _("uncommitted changes, use --all to discard all"
4976 4983 " changes, or 'hg update %s' to update") % ctx.rev()
4977 4984 else:
4978 4985 hint = _("use --all to revert all files,"
4979 4986 " or 'hg update %s' to update") % ctx.rev()
4980 4987 elif dirty:
4981 4988 hint = _("uncommitted changes, use --all to discard all changes")
4982 4989 else:
4983 4990 hint = _("use --all to revert all files")
4984 4991 raise util.Abort(msg, hint=hint)
4985 4992
4986 4993 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats, **opts)
4987 4994
4988 4995 @command('rollback', dryrunopts +
4989 4996 [('f', 'force', False, _('ignore safety measures'))])
4990 4997 def rollback(ui, repo, **opts):
4991 4998 """roll back the last transaction (dangerous)
4992 4999
4993 5000 This command should be used with care. There is only one level of
4994 5001 rollback, and there is no way to undo a rollback. It will also
4995 5002 restore the dirstate at the time of the last transaction, losing
4996 5003 any dirstate changes since that time. This command does not alter
4997 5004 the working directory.
4998 5005
4999 5006 Transactions are used to encapsulate the effects of all commands
5000 5007 that create new changesets or propagate existing changesets into a
5001 5008 repository.
5002 5009
5003 5010 .. container:: verbose
5004 5011
5005 5012 For example, the following commands are transactional, and their
5006 5013 effects can be rolled back:
5007 5014
5008 5015 - commit
5009 5016 - import
5010 5017 - pull
5011 5018 - push (with this repository as the destination)
5012 5019 - unbundle
5013 5020
5014 5021 To avoid permanent data loss, rollback will refuse to rollback a
5015 5022 commit transaction if it isn't checked out. Use --force to
5016 5023 override this protection.
5017 5024
5018 5025 This command is not intended for use on public repositories. Once
5019 5026 changes are visible for pull by other users, rolling a transaction
5020 5027 back locally is ineffective (someone else may already have pulled
5021 5028 the changes). Furthermore, a race is possible with readers of the
5022 5029 repository; for example an in-progress pull from the repository
5023 5030 may fail if a rollback is performed.
5024 5031
5025 5032 Returns 0 on success, 1 if no rollback data is available.
5026 5033 """
5027 5034 return repo.rollback(dryrun=opts.get('dry_run'),
5028 5035 force=opts.get('force'))
5029 5036
5030 5037 @command('root', [])
5031 5038 def root(ui, repo):
5032 5039 """print the root (top) of the current working directory
5033 5040
5034 5041 Print the root directory of the current repository.
5035 5042
5036 5043 Returns 0 on success.
5037 5044 """
5038 5045 ui.write(repo.root + "\n")
5039 5046
5040 5047 @command('^serve',
5041 5048 [('A', 'accesslog', '', _('name of access log file to write to'),
5042 5049 _('FILE')),
5043 5050 ('d', 'daemon', None, _('run server in background')),
5044 5051 ('', 'daemon-pipefds', '', _('used internally by daemon mode'), _('NUM')),
5045 5052 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
5046 5053 # use string type, then we can check if something was passed
5047 5054 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
5048 5055 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
5049 5056 _('ADDR')),
5050 5057 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
5051 5058 _('PREFIX')),
5052 5059 ('n', 'name', '',
5053 5060 _('name to show in web pages (default: working directory)'), _('NAME')),
5054 5061 ('', 'web-conf', '',
5055 5062 _('name of the hgweb config file (see "hg help hgweb")'), _('FILE')),
5056 5063 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
5057 5064 _('FILE')),
5058 5065 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
5059 5066 ('', 'stdio', None, _('for remote clients')),
5060 5067 ('', 'cmdserver', '', _('for remote clients'), _('MODE')),
5061 5068 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
5062 5069 ('', 'style', '', _('template style to use'), _('STYLE')),
5063 5070 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
5064 5071 ('', 'certificate', '', _('SSL certificate file'), _('FILE'))],
5065 5072 _('[OPTION]...'))
5066 5073 def serve(ui, repo, **opts):
5067 5074 """start stand-alone webserver
5068 5075
5069 5076 Start a local HTTP repository browser and pull server. You can use
5070 5077 this for ad-hoc sharing and browsing of repositories. It is
5071 5078 recommended to use a real web server to serve a repository for
5072 5079 longer periods of time.
5073 5080
5074 5081 Please note that the server does not implement access control.
5075 5082 This means that, by default, anybody can read from the server and
5076 5083 nobody can write to it by default. Set the ``web.allow_push``
5077 5084 option to ``*`` to allow everybody to push to the server. You
5078 5085 should use a real web server if you need to authenticate users.
5079 5086
5080 5087 By default, the server logs accesses to stdout and errors to
5081 5088 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
5082 5089 files.
5083 5090
5084 5091 To have the server choose a free port number to listen on, specify
5085 5092 a port number of 0; in this case, the server will print the port
5086 5093 number it uses.
5087 5094
5088 5095 Returns 0 on success.
5089 5096 """
5090 5097
5091 5098 if opts["stdio"] and opts["cmdserver"]:
5092 5099 raise util.Abort(_("cannot use --stdio with --cmdserver"))
5093 5100
5094 5101 def checkrepo():
5095 5102 if repo is None:
5096 5103 raise error.RepoError(_("there is no Mercurial repository here"
5097 5104 " (.hg not found)"))
5098 5105
5099 5106 if opts["stdio"]:
5100 5107 checkrepo()
5101 5108 s = sshserver.sshserver(ui, repo)
5102 5109 s.serve_forever()
5103 5110
5104 5111 if opts["cmdserver"]:
5105 5112 checkrepo()
5106 5113 s = commandserver.server(ui, repo, opts["cmdserver"])
5107 5114 return s.serve()
5108 5115
5109 5116 # this way we can check if something was given in the command-line
5110 5117 if opts.get('port'):
5111 5118 opts['port'] = util.getport(opts.get('port'))
5112 5119
5113 5120 baseui = repo and repo.baseui or ui
5114 5121 optlist = ("name templates style address port prefix ipv6"
5115 5122 " accesslog errorlog certificate encoding")
5116 5123 for o in optlist.split():
5117 5124 val = opts.get(o, '')
5118 5125 if val in (None, ''): # should check against default options instead
5119 5126 continue
5120 5127 baseui.setconfig("web", o, val)
5121 5128 if repo and repo.ui != baseui:
5122 5129 repo.ui.setconfig("web", o, val)
5123 5130
5124 5131 o = opts.get('web_conf') or opts.get('webdir_conf')
5125 5132 if not o:
5126 5133 if not repo:
5127 5134 raise error.RepoError(_("there is no Mercurial repository"
5128 5135 " here (.hg not found)"))
5129 5136 o = repo
5130 5137
5131 5138 app = hgweb.hgweb(o, baseui=baseui)
5132 5139
5133 5140 class service(object):
5134 5141 def init(self):
5135 5142 util.setsignalhandler()
5136 5143 self.httpd = hgweb.server.create_server(ui, app)
5137 5144
5138 5145 if opts['port'] and not ui.verbose:
5139 5146 return
5140 5147
5141 5148 if self.httpd.prefix:
5142 5149 prefix = self.httpd.prefix.strip('/') + '/'
5143 5150 else:
5144 5151 prefix = ''
5145 5152
5146 5153 port = ':%d' % self.httpd.port
5147 5154 if port == ':80':
5148 5155 port = ''
5149 5156
5150 5157 bindaddr = self.httpd.addr
5151 5158 if bindaddr == '0.0.0.0':
5152 5159 bindaddr = '*'
5153 5160 elif ':' in bindaddr: # IPv6
5154 5161 bindaddr = '[%s]' % bindaddr
5155 5162
5156 5163 fqaddr = self.httpd.fqaddr
5157 5164 if ':' in fqaddr:
5158 5165 fqaddr = '[%s]' % fqaddr
5159 5166 if opts['port']:
5160 5167 write = ui.status
5161 5168 else:
5162 5169 write = ui.write
5163 5170 write(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
5164 5171 (fqaddr, port, prefix, bindaddr, self.httpd.port))
5165 5172
5166 5173 def run(self):
5167 5174 self.httpd.serve_forever()
5168 5175
5169 5176 service = service()
5170 5177
5171 5178 cmdutil.service(opts, initfn=service.init, runfn=service.run)
5172 5179
5173 5180 @command('showconfig|debugconfig',
5174 5181 [('u', 'untrusted', None, _('show untrusted configuration options'))],
5175 5182 _('[-u] [NAME]...'))
5176 5183 def showconfig(ui, repo, *values, **opts):
5177 5184 """show combined config settings from all hgrc files
5178 5185
5179 5186 With no arguments, print names and values of all config items.
5180 5187
5181 5188 With one argument of the form section.name, print just the value
5182 5189 of that config item.
5183 5190
5184 5191 With multiple arguments, print names and values of all config
5185 5192 items with matching section names.
5186 5193
5187 5194 With --debug, the source (filename and line number) is printed
5188 5195 for each config item.
5189 5196
5190 5197 Returns 0 on success.
5191 5198 """
5192 5199
5193 5200 for f in scmutil.rcpath():
5194 5201 ui.debug('read config from: %s\n' % f)
5195 5202 untrusted = bool(opts.get('untrusted'))
5196 5203 if values:
5197 5204 sections = [v for v in values if '.' not in v]
5198 5205 items = [v for v in values if '.' in v]
5199 5206 if len(items) > 1 or items and sections:
5200 5207 raise util.Abort(_('only one config item permitted'))
5201 5208 for section, name, value in ui.walkconfig(untrusted=untrusted):
5202 5209 value = str(value).replace('\n', '\\n')
5203 5210 sectname = section + '.' + name
5204 5211 if values:
5205 5212 for v in values:
5206 5213 if v == section:
5207 5214 ui.debug('%s: ' %
5208 5215 ui.configsource(section, name, untrusted))
5209 5216 ui.write('%s=%s\n' % (sectname, value))
5210 5217 elif v == sectname:
5211 5218 ui.debug('%s: ' %
5212 5219 ui.configsource(section, name, untrusted))
5213 5220 ui.write(value, '\n')
5214 5221 else:
5215 5222 ui.debug('%s: ' %
5216 5223 ui.configsource(section, name, untrusted))
5217 5224 ui.write('%s=%s\n' % (sectname, value))
5218 5225
5219 5226 @command('^status|st',
5220 5227 [('A', 'all', None, _('show status of all files')),
5221 5228 ('m', 'modified', None, _('show only modified files')),
5222 5229 ('a', 'added', None, _('show only added files')),
5223 5230 ('r', 'removed', None, _('show only removed files')),
5224 5231 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
5225 5232 ('c', 'clean', None, _('show only files without changes')),
5226 5233 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
5227 5234 ('i', 'ignored', None, _('show only ignored files')),
5228 5235 ('n', 'no-status', None, _('hide status prefix')),
5229 5236 ('C', 'copies', None, _('show source of copied files')),
5230 5237 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
5231 5238 ('', 'rev', [], _('show difference from revision'), _('REV')),
5232 5239 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
5233 5240 ] + walkopts + subrepoopts,
5234 5241 _('[OPTION]... [FILE]...'))
5235 5242 def status(ui, repo, *pats, **opts):
5236 5243 """show changed files in the working directory
5237 5244
5238 5245 Show status of files in the repository. If names are given, only
5239 5246 files that match are shown. Files that are clean or ignored or
5240 5247 the source of a copy/move operation, are not listed unless
5241 5248 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
5242 5249 Unless options described with "show only ..." are given, the
5243 5250 options -mardu are used.
5244 5251
5245 5252 Option -q/--quiet hides untracked (unknown and ignored) files
5246 5253 unless explicitly requested with -u/--unknown or -i/--ignored.
5247 5254
5248 5255 .. note::
5249 5256 status may appear to disagree with diff if permissions have
5250 5257 changed or a merge has occurred. The standard diff format does
5251 5258 not report permission changes and diff only reports changes
5252 5259 relative to one merge parent.
5253 5260
5254 5261 If one revision is given, it is used as the base revision.
5255 5262 If two revisions are given, the differences between them are
5256 5263 shown. The --change option can also be used as a shortcut to list
5257 5264 the changed files of a revision from its first parent.
5258 5265
5259 5266 The codes used to show the status of files are::
5260 5267
5261 5268 M = modified
5262 5269 A = added
5263 5270 R = removed
5264 5271 C = clean
5265 5272 ! = missing (deleted by non-hg command, but still tracked)
5266 5273 ? = not tracked
5267 5274 I = ignored
5268 5275 = origin of the previous file listed as A (added)
5269 5276
5270 5277 .. container:: verbose
5271 5278
5272 5279 Examples:
5273 5280
5274 5281 - show changes in the working directory relative to a
5275 5282 changeset::
5276 5283
5277 5284 hg status --rev 9353
5278 5285
5279 5286 - show all changes including copies in an existing changeset::
5280 5287
5281 5288 hg status --copies --change 9353
5282 5289
5283 5290 - get a NUL separated list of added files, suitable for xargs::
5284 5291
5285 5292 hg status -an0
5286 5293
5287 5294 Returns 0 on success.
5288 5295 """
5289 5296
5290 5297 revs = opts.get('rev')
5291 5298 change = opts.get('change')
5292 5299
5293 5300 if revs and change:
5294 5301 msg = _('cannot specify --rev and --change at the same time')
5295 5302 raise util.Abort(msg)
5296 5303 elif change:
5297 5304 node2 = scmutil.revsingle(repo, change, None).node()
5298 5305 node1 = repo[node2].p1().node()
5299 5306 else:
5300 5307 node1, node2 = scmutil.revpair(repo, revs)
5301 5308
5302 5309 cwd = (pats and repo.getcwd()) or ''
5303 5310 end = opts.get('print0') and '\0' or '\n'
5304 5311 copy = {}
5305 5312 states = 'modified added removed deleted unknown ignored clean'.split()
5306 5313 show = [k for k in states if opts.get(k)]
5307 5314 if opts.get('all'):
5308 5315 show += ui.quiet and (states[:4] + ['clean']) or states
5309 5316 if not show:
5310 5317 show = ui.quiet and states[:4] or states[:5]
5311 5318
5312 5319 stat = repo.status(node1, node2, scmutil.match(repo[node2], pats, opts),
5313 5320 'ignored' in show, 'clean' in show, 'unknown' in show,
5314 5321 opts.get('subrepos'))
5315 5322 changestates = zip(states, 'MAR!?IC', stat)
5316 5323
5317 5324 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
5318 5325 copy = copies.pathcopies(repo[node1], repo[node2])
5319 5326
5320 5327 fm = ui.formatter('status', opts)
5321 5328 fmt = '%s' + end
5322 5329 showchar = not opts.get('no_status')
5323 5330
5324 5331 for state, char, files in changestates:
5325 5332 if state in show:
5326 5333 label = 'status.' + state
5327 5334 for f in files:
5328 5335 fm.startitem()
5329 5336 fm.condwrite(showchar, 'status', '%s ', char, label=label)
5330 5337 fm.write('path', fmt, repo.pathto(f, cwd), label=label)
5331 5338 if f in copy:
5332 5339 fm.write("copy", ' %s' + end, repo.pathto(copy[f], cwd),
5333 5340 label='status.copied')
5334 5341 fm.end()
5335 5342
5336 5343 @command('^summary|sum',
5337 5344 [('', 'remote', None, _('check for push and pull'))], '[--remote]')
5338 5345 def summary(ui, repo, **opts):
5339 5346 """summarize working directory state
5340 5347
5341 5348 This generates a brief summary of the working directory state,
5342 5349 including parents, branch, commit status, and available updates.
5343 5350
5344 5351 With the --remote option, this will check the default paths for
5345 5352 incoming and outgoing changes. This can be time-consuming.
5346 5353
5347 5354 Returns 0 on success.
5348 5355 """
5349 5356
5350 5357 ctx = repo[None]
5351 5358 parents = ctx.parents()
5352 5359 pnode = parents[0].node()
5353 5360 marks = []
5354 5361
5355 5362 for p in parents:
5356 5363 # label with log.changeset (instead of log.parent) since this
5357 5364 # shows a working directory parent *changeset*:
5358 5365 # i18n: column positioning for "hg summary"
5359 5366 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
5360 5367 label='log.changeset changeset.%s' % p.phasestr())
5361 5368 ui.write(' '.join(p.tags()), label='log.tag')
5362 5369 if p.bookmarks():
5363 5370 marks.extend(p.bookmarks())
5364 5371 if p.rev() == -1:
5365 5372 if not len(repo):
5366 5373 ui.write(_(' (empty repository)'))
5367 5374 else:
5368 5375 ui.write(_(' (no revision checked out)'))
5369 5376 ui.write('\n')
5370 5377 if p.description():
5371 5378 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
5372 5379 label='log.summary')
5373 5380
5374 5381 branch = ctx.branch()
5375 5382 bheads = repo.branchheads(branch)
5376 5383 # i18n: column positioning for "hg summary"
5377 5384 m = _('branch: %s\n') % branch
5378 5385 if branch != 'default':
5379 5386 ui.write(m, label='log.branch')
5380 5387 else:
5381 5388 ui.status(m, label='log.branch')
5382 5389
5383 5390 if marks:
5384 5391 current = repo._bookmarkcurrent
5385 5392 # i18n: column positioning for "hg summary"
5386 5393 ui.write(_('bookmarks:'), label='log.bookmark')
5387 5394 if current is not None:
5388 5395 if current in marks:
5389 5396 ui.write(' *' + current, label='bookmarks.current')
5390 5397 marks.remove(current)
5391 5398 else:
5392 5399 ui.write(' [%s]' % current, label='bookmarks.current')
5393 5400 for m in marks:
5394 5401 ui.write(' ' + m, label='log.bookmark')
5395 5402 ui.write('\n', label='log.bookmark')
5396 5403
5397 5404 st = list(repo.status(unknown=True))[:6]
5398 5405
5399 5406 c = repo.dirstate.copies()
5400 5407 copied, renamed = [], []
5401 5408 for d, s in c.iteritems():
5402 5409 if s in st[2]:
5403 5410 st[2].remove(s)
5404 5411 renamed.append(d)
5405 5412 else:
5406 5413 copied.append(d)
5407 5414 if d in st[1]:
5408 5415 st[1].remove(d)
5409 5416 st.insert(3, renamed)
5410 5417 st.insert(4, copied)
5411 5418
5412 5419 ms = mergemod.mergestate(repo)
5413 5420 st.append([f for f in ms if ms[f] == 'u'])
5414 5421
5415 5422 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
5416 5423 st.append(subs)
5417 5424
5418 5425 labels = [ui.label(_('%d modified'), 'status.modified'),
5419 5426 ui.label(_('%d added'), 'status.added'),
5420 5427 ui.label(_('%d removed'), 'status.removed'),
5421 5428 ui.label(_('%d renamed'), 'status.copied'),
5422 5429 ui.label(_('%d copied'), 'status.copied'),
5423 5430 ui.label(_('%d deleted'), 'status.deleted'),
5424 5431 ui.label(_('%d unknown'), 'status.unknown'),
5425 5432 ui.label(_('%d ignored'), 'status.ignored'),
5426 5433 ui.label(_('%d unresolved'), 'resolve.unresolved'),
5427 5434 ui.label(_('%d subrepos'), 'status.modified')]
5428 5435 t = []
5429 5436 for s, l in zip(st, labels):
5430 5437 if s:
5431 5438 t.append(l % len(s))
5432 5439
5433 5440 t = ', '.join(t)
5434 5441 cleanworkdir = False
5435 5442
5436 5443 if len(parents) > 1:
5437 5444 t += _(' (merge)')
5438 5445 elif branch != parents[0].branch():
5439 5446 t += _(' (new branch)')
5440 5447 elif (parents[0].closesbranch() and
5441 5448 pnode in repo.branchheads(branch, closed=True)):
5442 5449 t += _(' (head closed)')
5443 5450 elif not (st[0] or st[1] or st[2] or st[3] or st[4] or st[9]):
5444 5451 t += _(' (clean)')
5445 5452 cleanworkdir = True
5446 5453 elif pnode not in bheads:
5447 5454 t += _(' (new branch head)')
5448 5455
5449 5456 if cleanworkdir:
5450 5457 # i18n: column positioning for "hg summary"
5451 5458 ui.status(_('commit: %s\n') % t.strip())
5452 5459 else:
5453 5460 # i18n: column positioning for "hg summary"
5454 5461 ui.write(_('commit: %s\n') % t.strip())
5455 5462
5456 5463 # all ancestors of branch heads - all ancestors of parent = new csets
5457 5464 new = [0] * len(repo)
5458 5465 cl = repo.changelog
5459 5466 for a in [cl.rev(n) for n in bheads]:
5460 5467 new[a] = 1
5461 5468 for a in cl.ancestors([cl.rev(n) for n in bheads]):
5462 5469 new[a] = 1
5463 5470 for a in [p.rev() for p in parents]:
5464 5471 if a >= 0:
5465 5472 new[a] = 0
5466 5473 for a in cl.ancestors([p.rev() for p in parents]):
5467 5474 new[a] = 0
5468 5475 new = sum(new)
5469 5476
5470 5477 if new == 0:
5471 5478 # i18n: column positioning for "hg summary"
5472 5479 ui.status(_('update: (current)\n'))
5473 5480 elif pnode not in bheads:
5474 5481 # i18n: column positioning for "hg summary"
5475 5482 ui.write(_('update: %d new changesets (update)\n') % new)
5476 5483 else:
5477 5484 # i18n: column positioning for "hg summary"
5478 5485 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
5479 5486 (new, len(bheads)))
5480 5487
5481 5488 if opts.get('remote'):
5482 5489 t = []
5483 5490 source, branches = hg.parseurl(ui.expandpath('default'))
5484 5491 sbranch = branches[0]
5485 5492 other = hg.peer(repo, {}, source)
5486 5493 revs, checkout = hg.addbranchrevs(repo, other, branches,
5487 5494 opts.get('rev'))
5488 5495 if revs:
5489 5496 revs = [other.lookup(rev) for rev in revs]
5490 5497 ui.debug('comparing with %s\n' % util.hidepassword(source))
5491 5498 repo.ui.pushbuffer()
5492 5499 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
5493 5500 _common, incoming, _rheads = commoninc
5494 5501 repo.ui.popbuffer()
5495 5502 if incoming:
5496 5503 t.append(_('1 or more incoming'))
5497 5504
5498 5505 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
5499 5506 dbranch = branches[0]
5500 5507 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
5501 5508 if source != dest:
5502 5509 other = hg.peer(repo, {}, dest)
5503 5510 ui.debug('comparing with %s\n' % util.hidepassword(dest))
5504 5511 if (source != dest or (sbranch is not None and sbranch != dbranch)):
5505 5512 commoninc = None
5506 5513 if revs:
5507 5514 revs = [repo.lookup(rev) for rev in revs]
5508 5515 repo.ui.pushbuffer()
5509 5516 outgoing = discovery.findcommonoutgoing(repo, other, onlyheads=revs,
5510 5517 commoninc=commoninc)
5511 5518 repo.ui.popbuffer()
5512 5519 o = outgoing.missing
5513 5520 if o:
5514 5521 t.append(_('%d outgoing') % len(o))
5515 5522 if 'bookmarks' in other.listkeys('namespaces'):
5516 5523 lmarks = repo.listkeys('bookmarks')
5517 5524 rmarks = other.listkeys('bookmarks')
5518 5525 diff = set(rmarks) - set(lmarks)
5519 5526 if len(diff) > 0:
5520 5527 t.append(_('%d incoming bookmarks') % len(diff))
5521 5528 diff = set(lmarks) - set(rmarks)
5522 5529 if len(diff) > 0:
5523 5530 t.append(_('%d outgoing bookmarks') % len(diff))
5524 5531
5525 5532 if t:
5526 5533 # i18n: column positioning for "hg summary"
5527 5534 ui.write(_('remote: %s\n') % (', '.join(t)))
5528 5535 else:
5529 5536 # i18n: column positioning for "hg summary"
5530 5537 ui.status(_('remote: (synced)\n'))
5531 5538
5532 5539 @command('tag',
5533 5540 [('f', 'force', None, _('force tag')),
5534 5541 ('l', 'local', None, _('make the tag local')),
5535 5542 ('r', 'rev', '', _('revision to tag'), _('REV')),
5536 5543 ('', 'remove', None, _('remove a tag')),
5537 5544 # -l/--local is already there, commitopts cannot be used
5538 5545 ('e', 'edit', None, _('edit commit message')),
5539 5546 ('m', 'message', '', _('use <text> as commit message'), _('TEXT')),
5540 5547 ] + commitopts2,
5541 5548 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
5542 5549 def tag(ui, repo, name1, *names, **opts):
5543 5550 """add one or more tags for the current or given revision
5544 5551
5545 5552 Name a particular revision using <name>.
5546 5553
5547 5554 Tags are used to name particular revisions of the repository and are
5548 5555 very useful to compare different revisions, to go back to significant
5549 5556 earlier versions or to mark branch points as releases, etc. Changing
5550 5557 an existing tag is normally disallowed; use -f/--force to override.
5551 5558
5552 5559 If no revision is given, the parent of the working directory is
5553 5560 used, or tip if no revision is checked out.
5554 5561
5555 5562 To facilitate version control, distribution, and merging of tags,
5556 5563 they are stored as a file named ".hgtags" which is managed similarly
5557 5564 to other project files and can be hand-edited if necessary. This
5558 5565 also means that tagging creates a new commit. The file
5559 5566 ".hg/localtags" is used for local tags (not shared among
5560 5567 repositories).
5561 5568
5562 5569 Tag commits are usually made at the head of a branch. If the parent
5563 5570 of the working directory is not a branch head, :hg:`tag` aborts; use
5564 5571 -f/--force to force the tag commit to be based on a non-head
5565 5572 changeset.
5566 5573
5567 5574 See :hg:`help dates` for a list of formats valid for -d/--date.
5568 5575
5569 5576 Since tag names have priority over branch names during revision
5570 5577 lookup, using an existing branch name as a tag name is discouraged.
5571 5578
5572 5579 Returns 0 on success.
5573 5580 """
5574 5581 wlock = lock = None
5575 5582 try:
5576 5583 wlock = repo.wlock()
5577 5584 lock = repo.lock()
5578 5585 rev_ = "."
5579 5586 names = [t.strip() for t in (name1,) + names]
5580 5587 if len(names) != len(set(names)):
5581 5588 raise util.Abort(_('tag names must be unique'))
5582 5589 for n in names:
5583 5590 scmutil.checknewlabel(repo, n, 'tag')
5584 5591 if not n:
5585 5592 raise util.Abort(_('tag names cannot consist entirely of '
5586 5593 'whitespace'))
5587 5594 if opts.get('rev') and opts.get('remove'):
5588 5595 raise util.Abort(_("--rev and --remove are incompatible"))
5589 5596 if opts.get('rev'):
5590 5597 rev_ = opts['rev']
5591 5598 message = opts.get('message')
5592 5599 if opts.get('remove'):
5593 5600 expectedtype = opts.get('local') and 'local' or 'global'
5594 5601 for n in names:
5595 5602 if not repo.tagtype(n):
5596 5603 raise util.Abort(_("tag '%s' does not exist") % n)
5597 5604 if repo.tagtype(n) != expectedtype:
5598 5605 if expectedtype == 'global':
5599 5606 raise util.Abort(_("tag '%s' is not a global tag") % n)
5600 5607 else:
5601 5608 raise util.Abort(_("tag '%s' is not a local tag") % n)
5602 5609 rev_ = nullid
5603 5610 if not message:
5604 5611 # we don't translate commit messages
5605 5612 message = 'Removed tag %s' % ', '.join(names)
5606 5613 elif not opts.get('force'):
5607 5614 for n in names:
5608 5615 if n in repo.tags():
5609 5616 raise util.Abort(_("tag '%s' already exists "
5610 5617 "(use -f to force)") % n)
5611 5618 if not opts.get('local'):
5612 5619 p1, p2 = repo.dirstate.parents()
5613 5620 if p2 != nullid:
5614 5621 raise util.Abort(_('uncommitted merge'))
5615 5622 bheads = repo.branchheads()
5616 5623 if not opts.get('force') and bheads and p1 not in bheads:
5617 5624 raise util.Abort(_('not at a branch head (use -f to force)'))
5618 5625 r = scmutil.revsingle(repo, rev_).node()
5619 5626
5620 5627 if not message:
5621 5628 # we don't translate commit messages
5622 5629 message = ('Added tag %s for changeset %s' %
5623 5630 (', '.join(names), short(r)))
5624 5631
5625 5632 date = opts.get('date')
5626 5633 if date:
5627 5634 date = util.parsedate(date)
5628 5635
5629 5636 if opts.get('edit'):
5630 5637 message = ui.edit(message, ui.username())
5631 5638
5632 5639 # don't allow tagging the null rev
5633 5640 if (not opts.get('remove') and
5634 5641 scmutil.revsingle(repo, rev_).rev() == nullrev):
5635 5642 raise util.Abort(_("cannot tag null revision"))
5636 5643
5637 5644 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
5638 5645 finally:
5639 5646 release(lock, wlock)
5640 5647
5641 5648 @command('tags', [], '')
5642 5649 def tags(ui, repo, **opts):
5643 5650 """list repository tags
5644 5651
5645 5652 This lists both regular and local tags. When the -v/--verbose
5646 5653 switch is used, a third column "local" is printed for local tags.
5647 5654
5648 5655 Returns 0 on success.
5649 5656 """
5650 5657
5651 5658 fm = ui.formatter('tags', opts)
5652 5659 hexfunc = ui.debugflag and hex or short
5653 5660 tagtype = ""
5654 5661
5655 5662 for t, n in reversed(repo.tagslist()):
5656 5663 hn = hexfunc(n)
5657 5664 label = 'tags.normal'
5658 5665 tagtype = ''
5659 5666 if repo.tagtype(t) == 'local':
5660 5667 label = 'tags.local'
5661 5668 tagtype = 'local'
5662 5669
5663 5670 fm.startitem()
5664 5671 fm.write('tag', '%s', t, label=label)
5665 5672 fmt = " " * (30 - encoding.colwidth(t)) + ' %5d:%s'
5666 5673 fm.condwrite(not ui.quiet, 'rev id', fmt,
5667 5674 repo.changelog.rev(n), hn, label=label)
5668 5675 fm.condwrite(ui.verbose and tagtype, 'type', ' %s',
5669 5676 tagtype, label=label)
5670 5677 fm.plain('\n')
5671 5678 fm.end()
5672 5679
5673 5680 @command('tip',
5674 5681 [('p', 'patch', None, _('show patch')),
5675 5682 ('g', 'git', None, _('use git extended diff format')),
5676 5683 ] + templateopts,
5677 5684 _('[-p] [-g]'))
5678 5685 def tip(ui, repo, **opts):
5679 5686 """show the tip revision
5680 5687
5681 5688 The tip revision (usually just called the tip) is the changeset
5682 5689 most recently added to the repository (and therefore the most
5683 5690 recently changed head).
5684 5691
5685 5692 If you have just made a commit, that commit will be the tip. If
5686 5693 you have just pulled changes from another repository, the tip of
5687 5694 that repository becomes the current tip. The "tip" tag is special
5688 5695 and cannot be renamed or assigned to a different changeset.
5689 5696
5690 5697 Returns 0 on success.
5691 5698 """
5692 5699 displayer = cmdutil.show_changeset(ui, repo, opts)
5693 5700 displayer.show(repo['tip'])
5694 5701 displayer.close()
5695 5702
5696 5703 @command('unbundle',
5697 5704 [('u', 'update', None,
5698 5705 _('update to new branch head if changesets were unbundled'))],
5699 5706 _('[-u] FILE...'))
5700 5707 def unbundle(ui, repo, fname1, *fnames, **opts):
5701 5708 """apply one or more changegroup files
5702 5709
5703 5710 Apply one or more compressed changegroup files generated by the
5704 5711 bundle command.
5705 5712
5706 5713 Returns 0 on success, 1 if an update has unresolved files.
5707 5714 """
5708 5715 fnames = (fname1,) + fnames
5709 5716
5710 5717 lock = repo.lock()
5711 5718 wc = repo['.']
5712 5719 try:
5713 5720 for fname in fnames:
5714 5721 f = hg.openpath(ui, fname)
5715 5722 gen = changegroup.readbundle(f, fname)
5716 5723 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
5717 5724 finally:
5718 5725 lock.release()
5719 5726 bookmarks.updatecurrentbookmark(repo, wc.node(), wc.branch())
5720 5727 return postincoming(ui, repo, modheads, opts.get('update'), None)
5721 5728
5722 5729 @command('^update|up|checkout|co',
5723 5730 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
5724 5731 ('c', 'check', None,
5725 5732 _('update across branches if no uncommitted changes')),
5726 5733 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5727 5734 ('r', 'rev', '', _('revision'), _('REV'))],
5728 5735 _('[-c] [-C] [-d DATE] [[-r] REV]'))
5729 5736 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
5730 5737 """update working directory (or switch revisions)
5731 5738
5732 5739 Update the repository's working directory to the specified
5733 5740 changeset. If no changeset is specified, update to the tip of the
5734 5741 current named branch and move the current bookmark (see :hg:`help
5735 5742 bookmarks`).
5736 5743
5737 5744 Update sets the working directory's parent revision to the specified
5738 5745 changeset (see :hg:`help parents`).
5739 5746
5740 5747 If the changeset is not a descendant or ancestor of the working
5741 5748 directory's parent, the update is aborted. With the -c/--check
5742 5749 option, the working directory is checked for uncommitted changes; if
5743 5750 none are found, the working directory is updated to the specified
5744 5751 changeset.
5745 5752
5746 5753 .. container:: verbose
5747 5754
5748 5755 The following rules apply when the working directory contains
5749 5756 uncommitted changes:
5750 5757
5751 5758 1. If neither -c/--check nor -C/--clean is specified, and if
5752 5759 the requested changeset is an ancestor or descendant of
5753 5760 the working directory's parent, the uncommitted changes
5754 5761 are merged into the requested changeset and the merged
5755 5762 result is left uncommitted. If the requested changeset is
5756 5763 not an ancestor or descendant (that is, it is on another
5757 5764 branch), the update is aborted and the uncommitted changes
5758 5765 are preserved.
5759 5766
5760 5767 2. With the -c/--check option, the update is aborted and the
5761 5768 uncommitted changes are preserved.
5762 5769
5763 5770 3. With the -C/--clean option, uncommitted changes are discarded and
5764 5771 the working directory is updated to the requested changeset.
5765 5772
5766 5773 To cancel an uncommitted merge (and lose your changes), use
5767 5774 :hg:`update --clean .`.
5768 5775
5769 5776 Use null as the changeset to remove the working directory (like
5770 5777 :hg:`clone -U`).
5771 5778
5772 5779 If you want to revert just one file to an older revision, use
5773 5780 :hg:`revert [-r REV] NAME`.
5774 5781
5775 5782 See :hg:`help dates` for a list of formats valid for -d/--date.
5776 5783
5777 5784 Returns 0 on success, 1 if there are unresolved files.
5778 5785 """
5779 5786 if rev and node:
5780 5787 raise util.Abort(_("please specify just one revision"))
5781 5788
5782 5789 if rev is None or rev == '':
5783 5790 rev = node
5784 5791
5785 5792 # with no argument, we also move the current bookmark, if any
5786 5793 movemarkfrom = None
5787 5794 if rev is None:
5788 5795 curmark = repo._bookmarkcurrent
5789 5796 if bookmarks.iscurrent(repo):
5790 5797 movemarkfrom = repo['.'].node()
5791 5798 elif curmark:
5792 5799 ui.status(_("updating to active bookmark %s\n") % curmark)
5793 5800 rev = curmark
5794 5801
5795 5802 # if we defined a bookmark, we have to remember the original bookmark name
5796 5803 brev = rev
5797 5804 rev = scmutil.revsingle(repo, rev, rev).rev()
5798 5805
5799 5806 if check and clean:
5800 5807 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
5801 5808
5802 5809 if date:
5803 5810 if rev is not None:
5804 5811 raise util.Abort(_("you can't specify a revision and a date"))
5805 5812 rev = cmdutil.finddate(ui, repo, date)
5806 5813
5807 5814 if check:
5808 5815 c = repo[None]
5809 5816 if c.dirty(merge=False, branch=False, missing=True):
5810 5817 raise util.Abort(_("uncommitted local changes"))
5811 5818 if rev is None:
5812 5819 rev = repo[repo[None].branch()].rev()
5813 5820 mergemod._checkunknown(repo, repo[None], repo[rev])
5814 5821
5815 5822 if clean:
5816 5823 ret = hg.clean(repo, rev)
5817 5824 else:
5818 5825 ret = hg.update(repo, rev)
5819 5826
5820 5827 if not ret and movemarkfrom:
5821 5828 if bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
5822 5829 ui.status(_("updating bookmark %s\n") % repo._bookmarkcurrent)
5823 5830 elif brev in repo._bookmarks:
5824 5831 bookmarks.setcurrent(repo, brev)
5825 5832 elif brev:
5826 5833 bookmarks.unsetcurrent(repo)
5827 5834
5828 5835 return ret
5829 5836
5830 5837 @command('verify', [])
5831 5838 def verify(ui, repo):
5832 5839 """verify the integrity of the repository
5833 5840
5834 5841 Verify the integrity of the current repository.
5835 5842
5836 5843 This will perform an extensive check of the repository's
5837 5844 integrity, validating the hashes and checksums of each entry in
5838 5845 the changelog, manifest, and tracked files, as well as the
5839 5846 integrity of their crosslinks and indices.
5840 5847
5841 5848 Please see http://mercurial.selenic.com/wiki/RepositoryCorruption
5842 5849 for more information about recovery from corruption of the
5843 5850 repository.
5844 5851
5845 5852 Returns 0 on success, 1 if errors are encountered.
5846 5853 """
5847 5854 return hg.verify(repo)
5848 5855
5849 5856 @command('version', [])
5850 5857 def version_(ui):
5851 5858 """output version and copyright information"""
5852 5859 ui.write(_("Mercurial Distributed SCM (version %s)\n")
5853 5860 % util.version())
5854 5861 ui.status(_(
5855 5862 "(see http://mercurial.selenic.com for more information)\n"
5856 5863 "\nCopyright (C) 2005-2012 Matt Mackall and others\n"
5857 5864 "This is free software; see the source for copying conditions. "
5858 5865 "There is NO\nwarranty; "
5859 5866 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
5860 5867 ))
5861 5868
5862 5869 norepo = ("clone init version help debugcommands debugcomplete"
5863 5870 " debugdate debuginstall debugfsinfo debugpushkey debugwireargs"
5864 5871 " debugknown debuggetbundle debugbundle")
5865 5872 optionalrepo = ("identify paths serve showconfig debugancestor debugdag"
5866 5873 " debugdata debugindex debugindexdot debugrevlog")
5867 5874 inferrepo = ("add addremove annotate cat commit diff grep forget log parents"
5868 5875 " remove resolve status debugwalk")
@@ -1,475 +1,473 b''
1 1 $ hg init a
2 2 $ cd a
3 3 $ echo 'root' >root
4 4 $ hg add root
5 5 $ hg commit -d '0 0' -m "Adding root node"
6 6
7 7 $ echo 'a' >a
8 8 $ hg add a
9 9 $ hg branch a
10 10 marked working directory as branch a
11 11 (branches are permanent and global, did you want a bookmark?)
12 12 $ hg commit -d '1 0' -m "Adding a branch"
13 13
14 14 $ hg branch q
15 15 marked working directory as branch q
16 16 (branches are permanent and global, did you want a bookmark?)
17 17 $ echo 'aa' >a
18 18 $ hg branch -C
19 19 reset working directory to branch a
20 20 $ hg commit -d '2 0' -m "Adding to a branch"
21 21
22 22 $ hg update -C 0
23 23 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
24 24 $ echo 'b' >b
25 25 $ hg add b
26 26 $ hg branch b
27 27 marked working directory as branch b
28 28 (branches are permanent and global, did you want a bookmark?)
29 29 $ hg commit -d '2 0' -m "Adding b branch"
30 30
31 31 $ echo 'bh1' >bh1
32 32 $ hg add bh1
33 33 $ hg commit -d '3 0' -m "Adding b branch head 1"
34 34
35 35 $ hg update -C 2
36 36 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
37 37 $ echo 'bh2' >bh2
38 38 $ hg add bh2
39 39 $ hg commit -d '4 0' -m "Adding b branch head 2"
40 40
41 41 $ echo 'c' >c
42 42 $ hg add c
43 43 $ hg branch c
44 44 marked working directory as branch c
45 45 (branches are permanent and global, did you want a bookmark?)
46 46 $ hg commit -d '5 0' -m "Adding c branch"
47 47
48 48 reserved names
49 49
50 50 $ hg branch tip
51 51 abort: the name 'tip' is reserved
52 52 [255]
53 53 $ hg branch null
54 54 abort: the name 'null' is reserved
55 55 [255]
56 56 $ hg branch .
57 57 abort: the name '.' is reserved
58 58 [255]
59 59
60 60 invalid characters
61 61
62 62 $ hg branch 'foo:bar'
63 63 abort: ':' cannot be used in a name
64 64 [255]
65 65
66 66 $ hg branch 'foo
67 67 > bar'
68 68 abort: '\n' cannot be used in a name
69 69 [255]
70 70
71 71 trailing or leading spaces should be stripped before testing duplicates
72 72
73 73 $ hg branch 'b '
74 74 abort: a branch of the same name already exists
75 75 (use 'hg update' to switch to it)
76 76 [255]
77 77
78 78 $ hg branch ' b'
79 79 abort: a branch of the same name already exists
80 80 (use 'hg update' to switch to it)
81 81 [255]
82 82
83 83 verify update will accept invalid legacy branch names
84 84
85 85 $ hg init test-invalid-branch-name
86 86 $ cd test-invalid-branch-name
87 87 $ hg pull -u "$TESTDIR"/bundles/test-invalid-branch-name.hg
88 88 pulling from *test-invalid-branch-name.hg (glob)
89 89 requesting all changes
90 90 adding changesets
91 91 adding manifests
92 92 adding file changes
93 93 added 3 changesets with 3 changes to 2 files
94 94 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
95 95
96 96 $ hg update '"colon:test"'
97 97 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
98 98 $ cd ..
99 99
100 100 $ echo 'd' >d
101 101 $ hg add d
102 102 $ hg branch 'a branch name much longer than the default justification used by branches'
103 103 marked working directory as branch a branch name much longer than the default justification used by branches
104 104 (branches are permanent and global, did you want a bookmark?)
105 105 $ hg commit -d '6 0' -m "Adding d branch"
106 106
107 107 $ hg branches
108 108 a branch name much longer than the default justification used by branches 7:10ff5895aa57
109 109 b 4:aee39cd168d0
110 110 c 6:589736a22561 (inactive)
111 111 a 5:d8cbc61dbaa6 (inactive)
112 112 default 0:19709c5a4e75 (inactive)
113 113
114 114 -------
115 115
116 116 $ hg branches -a
117 117 a branch name much longer than the default justification used by branches 7:10ff5895aa57
118 118 b 4:aee39cd168d0
119 119
120 120 --- Branch a
121 121
122 122 $ hg log -b a
123 123 changeset: 5:d8cbc61dbaa6
124 124 branch: a
125 125 parent: 2:881fe2b92ad0
126 126 user: test
127 127 date: Thu Jan 01 00:00:04 1970 +0000
128 128 summary: Adding b branch head 2
129 129
130 130 changeset: 2:881fe2b92ad0
131 131 branch: a
132 132 user: test
133 133 date: Thu Jan 01 00:00:02 1970 +0000
134 134 summary: Adding to a branch
135 135
136 136 changeset: 1:dd6b440dd85a
137 137 branch: a
138 138 user: test
139 139 date: Thu Jan 01 00:00:01 1970 +0000
140 140 summary: Adding a branch
141 141
142 142
143 143 ---- Branch b
144 144
145 145 $ hg log -b b
146 146 changeset: 4:aee39cd168d0
147 147 branch: b
148 148 user: test
149 149 date: Thu Jan 01 00:00:03 1970 +0000
150 150 summary: Adding b branch head 1
151 151
152 152 changeset: 3:ac22033332d1
153 153 branch: b
154 154 parent: 0:19709c5a4e75
155 155 user: test
156 156 date: Thu Jan 01 00:00:02 1970 +0000
157 157 summary: Adding b branch
158 158
159 159
160 160 ---- going to test branch closing
161 161
162 162 $ hg branches
163 163 a branch name much longer than the default justification used by branches 7:10ff5895aa57
164 164 b 4:aee39cd168d0
165 165 c 6:589736a22561 (inactive)
166 166 a 5:d8cbc61dbaa6 (inactive)
167 167 default 0:19709c5a4e75 (inactive)
168 168 $ hg up -C b
169 169 2 files updated, 0 files merged, 4 files removed, 0 files unresolved
170 170 $ echo 'xxx1' >> b
171 171 $ hg commit -d '7 0' -m 'adding cset to branch b'
172 172 $ hg up -C aee39cd168d0
173 173 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
174 174 $ echo 'xxx2' >> b
175 175 $ hg commit -d '8 0' -m 'adding head to branch b'
176 176 created new head
177 177 $ echo 'xxx3' >> b
178 178 $ hg commit -d '9 0' -m 'adding another cset to branch b'
179 179 $ hg branches
180 180 b 10:bfbe841b666e
181 181 a branch name much longer than the default justification used by branches 7:10ff5895aa57
182 182 c 6:589736a22561 (inactive)
183 183 a 5:d8cbc61dbaa6 (inactive)
184 184 default 0:19709c5a4e75 (inactive)
185 185 $ hg heads --closed
186 186 changeset: 10:bfbe841b666e
187 187 branch: b
188 188 tag: tip
189 189 user: test
190 190 date: Thu Jan 01 00:00:09 1970 +0000
191 191 summary: adding another cset to branch b
192 192
193 193 changeset: 8:eebb944467c9
194 194 branch: b
195 195 parent: 4:aee39cd168d0
196 196 user: test
197 197 date: Thu Jan 01 00:00:07 1970 +0000
198 198 summary: adding cset to branch b
199 199
200 200 changeset: 7:10ff5895aa57
201 201 branch: a branch name much longer than the default justification used by branches
202 202 user: test
203 203 date: Thu Jan 01 00:00:06 1970 +0000
204 204 summary: Adding d branch
205 205
206 206 changeset: 6:589736a22561
207 207 branch: c
208 208 user: test
209 209 date: Thu Jan 01 00:00:05 1970 +0000
210 210 summary: Adding c branch
211 211
212 212 changeset: 5:d8cbc61dbaa6
213 213 branch: a
214 214 parent: 2:881fe2b92ad0
215 215 user: test
216 216 date: Thu Jan 01 00:00:04 1970 +0000
217 217 summary: Adding b branch head 2
218 218
219 219 changeset: 0:19709c5a4e75
220 220 user: test
221 221 date: Thu Jan 01 00:00:00 1970 +0000
222 222 summary: Adding root node
223 223
224 224 $ hg heads
225 225 changeset: 10:bfbe841b666e
226 226 branch: b
227 227 tag: tip
228 228 user: test
229 229 date: Thu Jan 01 00:00:09 1970 +0000
230 230 summary: adding another cset to branch b
231 231
232 232 changeset: 8:eebb944467c9
233 233 branch: b
234 234 parent: 4:aee39cd168d0
235 235 user: test
236 236 date: Thu Jan 01 00:00:07 1970 +0000
237 237 summary: adding cset to branch b
238 238
239 239 changeset: 7:10ff5895aa57
240 240 branch: a branch name much longer than the default justification used by branches
241 241 user: test
242 242 date: Thu Jan 01 00:00:06 1970 +0000
243 243 summary: Adding d branch
244 244
245 245 changeset: 6:589736a22561
246 246 branch: c
247 247 user: test
248 248 date: Thu Jan 01 00:00:05 1970 +0000
249 249 summary: Adding c branch
250 250
251 251 changeset: 5:d8cbc61dbaa6
252 252 branch: a
253 253 parent: 2:881fe2b92ad0
254 254 user: test
255 255 date: Thu Jan 01 00:00:04 1970 +0000
256 256 summary: Adding b branch head 2
257 257
258 258 changeset: 0:19709c5a4e75
259 259 user: test
260 260 date: Thu Jan 01 00:00:00 1970 +0000
261 261 summary: Adding root node
262 262
263 263 $ hg commit -d '9 0' --close-branch -m 'prune bad branch'
264 264 $ hg branches -a
265 265 b 8:eebb944467c9
266 266 a branch name much longer than the default justification used by branches 7:10ff5895aa57
267 267 $ hg up -C b
268 268 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
269 269 $ hg commit -d '9 0' --close-branch -m 'close this part branch too'
270 $ hg commit -d '9 0' --close-branch -m 're-closing this branch'
271 abort: can only close branch heads
272 [255]
270 273
271 $ hg commit -d '9 0' --close-branch -m 're-closing this branch'
272 274 $ hg log -r tip --debug
273 changeset: 13:c2601d54b1427e99506bee25a566ef3a5963af0b
275 changeset: 12:e3d49c0575d8fc2cb1cd6859c747c14f5f6d499f
274 276 branch: b
275 277 tag: tip
276 278 phase: draft
277 parent: 12:e3d49c0575d8fc2cb1cd6859c747c14f5f6d499f
279 parent: 8:eebb944467c9fb9651ed232aeaf31b3c0a7fc6c1
278 280 parent: -1:0000000000000000000000000000000000000000
279 281 manifest: 8:6f9ed32d2b310e391a4f107d5f0f071df785bfee
280 282 user: test
281 283 date: Thu Jan 01 00:00:09 1970 +0000
282 284 extra: branch=b
283 285 extra: close=1
284 286 description:
285 re-closing this branch
286
287 close this part branch too
287 288
288 $ hg rollback
289 repository tip rolled back to revision 12 (undo commit)
290 working directory now based on revision 12
291 289
292 290 --- b branch should be inactive
293 291
294 292 $ hg branches
295 293 a branch name much longer than the default justification used by branches 7:10ff5895aa57
296 294 c 6:589736a22561 (inactive)
297 295 a 5:d8cbc61dbaa6 (inactive)
298 296 default 0:19709c5a4e75 (inactive)
299 297 $ hg branches -c
300 298 a branch name much longer than the default justification used by branches 7:10ff5895aa57
301 299 b 12:e3d49c0575d8 (closed)
302 300 c 6:589736a22561 (inactive)
303 301 a 5:d8cbc61dbaa6 (inactive)
304 302 default 0:19709c5a4e75 (inactive)
305 303 $ hg branches -a
306 304 a branch name much longer than the default justification used by branches 7:10ff5895aa57
307 305 $ hg branches -q
308 306 a branch name much longer than the default justification used by branches
309 307 c
310 308 a
311 309 default
312 310 $ hg heads b
313 311 no open branch heads found on branches b
314 312 [1]
315 313 $ hg heads --closed b
316 314 changeset: 12:e3d49c0575d8
317 315 branch: b
318 316 tag: tip
319 317 parent: 8:eebb944467c9
320 318 user: test
321 319 date: Thu Jan 01 00:00:09 1970 +0000
322 320 summary: close this part branch too
323 321
324 322 changeset: 11:d3f163457ebf
325 323 branch: b
326 324 user: test
327 325 date: Thu Jan 01 00:00:09 1970 +0000
328 326 summary: prune bad branch
329 327
330 328 $ echo 'xxx4' >> b
331 329 $ hg commit -d '9 0' -m 'reopen branch with a change'
332 330 reopening closed branch head 12
333 331
334 332 --- branch b is back in action
335 333
336 334 $ hg branches -a
337 335 b 13:e23b5505d1ad
338 336 a branch name much longer than the default justification used by branches 7:10ff5895aa57
339 337
340 338 ---- test heads listings
341 339
342 340 $ hg heads
343 341 changeset: 13:e23b5505d1ad
344 342 branch: b
345 343 tag: tip
346 344 user: test
347 345 date: Thu Jan 01 00:00:09 1970 +0000
348 346 summary: reopen branch with a change
349 347
350 348 changeset: 7:10ff5895aa57
351 349 branch: a branch name much longer than the default justification used by branches
352 350 user: test
353 351 date: Thu Jan 01 00:00:06 1970 +0000
354 352 summary: Adding d branch
355 353
356 354 changeset: 6:589736a22561
357 355 branch: c
358 356 user: test
359 357 date: Thu Jan 01 00:00:05 1970 +0000
360 358 summary: Adding c branch
361 359
362 360 changeset: 5:d8cbc61dbaa6
363 361 branch: a
364 362 parent: 2:881fe2b92ad0
365 363 user: test
366 364 date: Thu Jan 01 00:00:04 1970 +0000
367 365 summary: Adding b branch head 2
368 366
369 367 changeset: 0:19709c5a4e75
370 368 user: test
371 369 date: Thu Jan 01 00:00:00 1970 +0000
372 370 summary: Adding root node
373 371
374 372
375 373 branch default
376 374
377 375 $ hg heads default
378 376 changeset: 0:19709c5a4e75
379 377 user: test
380 378 date: Thu Jan 01 00:00:00 1970 +0000
381 379 summary: Adding root node
382 380
383 381
384 382 branch a
385 383
386 384 $ hg heads a
387 385 changeset: 5:d8cbc61dbaa6
388 386 branch: a
389 387 parent: 2:881fe2b92ad0
390 388 user: test
391 389 date: Thu Jan 01 00:00:04 1970 +0000
392 390 summary: Adding b branch head 2
393 391
394 392 $ hg heads --active a
395 393 no open branch heads found on branches a
396 394 [1]
397 395
398 396 branch b
399 397
400 398 $ hg heads b
401 399 changeset: 13:e23b5505d1ad
402 400 branch: b
403 401 tag: tip
404 402 user: test
405 403 date: Thu Jan 01 00:00:09 1970 +0000
406 404 summary: reopen branch with a change
407 405
408 406 $ hg heads --closed b
409 407 changeset: 13:e23b5505d1ad
410 408 branch: b
411 409 tag: tip
412 410 user: test
413 411 date: Thu Jan 01 00:00:09 1970 +0000
414 412 summary: reopen branch with a change
415 413
416 414 changeset: 11:d3f163457ebf
417 415 branch: b
418 416 user: test
419 417 date: Thu Jan 01 00:00:09 1970 +0000
420 418 summary: prune bad branch
421 419
422 420 default branch colors:
423 421
424 422 $ echo "[extensions]" >> $HGRCPATH
425 423 $ echo "color =" >> $HGRCPATH
426 424 $ echo "[color]" >> $HGRCPATH
427 425 $ echo "mode = ansi" >> $HGRCPATH
428 426
429 427 $ hg up -C c
430 428 3 files updated, 0 files merged, 2 files removed, 0 files unresolved
431 429 $ hg commit -d '9 0' --close-branch -m 'reclosing this branch'
432 430 $ hg up -C b
433 431 2 files updated, 0 files merged, 3 files removed, 0 files unresolved
434 432 $ hg branches --color=always
435 433 \x1b[0;32mb\x1b[0m \x1b[0;33m 13:e23b5505d1ad\x1b[0m (esc)
436 434 \x1b[0;0ma branch name much longer than the default justification used by branches\x1b[0m \x1b[0;33m7:10ff5895aa57\x1b[0m (esc)
437 435 \x1b[0;0ma\x1b[0m \x1b[0;33m 5:d8cbc61dbaa6\x1b[0m (inactive) (esc)
438 436 \x1b[0;0mdefault\x1b[0m \x1b[0;33m 0:19709c5a4e75\x1b[0m (inactive) (esc)
439 437
440 438 default closed branch color:
441 439
442 440 $ hg branches --color=always --closed
443 441 \x1b[0;32mb\x1b[0m \x1b[0;33m 13:e23b5505d1ad\x1b[0m (esc)
444 442 \x1b[0;0ma branch name much longer than the default justification used by branches\x1b[0m \x1b[0;33m7:10ff5895aa57\x1b[0m (esc)
445 443 \x1b[0;30;1mc\x1b[0m \x1b[0;33m 14:f894c25619d3\x1b[0m (closed) (esc)
446 444 \x1b[0;0ma\x1b[0m \x1b[0;33m 5:d8cbc61dbaa6\x1b[0m (inactive) (esc)
447 445 \x1b[0;0mdefault\x1b[0m \x1b[0;33m 0:19709c5a4e75\x1b[0m (inactive) (esc)
448 446
449 447 $ echo "[extensions]" >> $HGRCPATH
450 448 $ echo "color =" >> $HGRCPATH
451 449 $ echo "[color]" >> $HGRCPATH
452 450 $ echo "branches.active = green" >> $HGRCPATH
453 451 $ echo "branches.closed = blue" >> $HGRCPATH
454 452 $ echo "branches.current = red" >> $HGRCPATH
455 453 $ echo "branches.inactive = magenta" >> $HGRCPATH
456 454 $ echo "log.changeset = cyan" >> $HGRCPATH
457 455
458 456 custom branch colors:
459 457
460 458 $ hg branches --color=always
461 459 \x1b[0;31mb\x1b[0m \x1b[0;36m 13:e23b5505d1ad\x1b[0m (esc)
462 460 \x1b[0;32ma branch name much longer than the default justification used by branches\x1b[0m \x1b[0;36m7:10ff5895aa57\x1b[0m (esc)
463 461 \x1b[0;35ma\x1b[0m \x1b[0;36m 5:d8cbc61dbaa6\x1b[0m (inactive) (esc)
464 462 \x1b[0;35mdefault\x1b[0m \x1b[0;36m 0:19709c5a4e75\x1b[0m (inactive) (esc)
465 463
466 464 custom closed branch color:
467 465
468 466 $ hg branches --color=always --closed
469 467 \x1b[0;31mb\x1b[0m \x1b[0;36m 13:e23b5505d1ad\x1b[0m (esc)
470 468 \x1b[0;32ma branch name much longer than the default justification used by branches\x1b[0m \x1b[0;36m7:10ff5895aa57\x1b[0m (esc)
471 469 \x1b[0;34mc\x1b[0m \x1b[0;36m 14:f894c25619d3\x1b[0m (closed) (esc)
472 470 \x1b[0;35ma\x1b[0m \x1b[0;36m 5:d8cbc61dbaa6\x1b[0m (inactive) (esc)
473 471 \x1b[0;35mdefault\x1b[0m \x1b[0;36m 0:19709c5a4e75\x1b[0m (inactive) (esc)
474 472
475 473 $ cd ..
@@ -1,745 +1,769 b''
1 1 $ hg init
2 2
3 3 Setup:
4 4
5 5 $ echo a >> a
6 6 $ hg ci -Am 'base'
7 7 adding a
8 8
9 9 Refuse to amend public csets:
10 10
11 11 $ hg phase -r . -p
12 12 $ hg ci --amend
13 13 abort: cannot amend public changesets
14 14 [255]
15 15 $ hg phase -r . -f -d
16 16
17 17 $ echo a >> a
18 18 $ hg ci -Am 'base1'
19 19
20 20 Nothing to amend:
21 21
22 22 $ hg ci --amend
23 23 nothing changed
24 24 [1]
25 25
26 26 $ cat >> $HGRCPATH <<EOF
27 27 > [hooks]
28 28 > pretxncommit.foo = sh -c "echo \\"pretxncommit \$HG_NODE\\"; hg id -r \$HG_NODE"
29 29 > EOF
30 30
31 31 Amending changeset with changes in working dir:
32 32 (and check that --message does not trigger an editor)
33 33
34 34 $ echo a >> a
35 35 $ HGEDITOR="\"sh\" \"`pwd`/editor.sh\"" hg commit --amend -m 'amend base1'
36 36 pretxncommit 43f1ba15f28a50abf0aae529cf8a16bfced7b149
37 37 43f1ba15f28a tip
38 38 saved backup bundle to $TESTTMP/.hg/strip-backup/489edb5b847d-amend-backup.hg (glob)
39 39 $ echo 'pretxncommit.foo = ' >> $HGRCPATH
40 40 $ hg diff -c .
41 41 diff -r ad120869acf0 -r 43f1ba15f28a a
42 42 --- a/a Thu Jan 01 00:00:00 1970 +0000
43 43 +++ b/a Thu Jan 01 00:00:00 1970 +0000
44 44 @@ -1,1 +1,3 @@
45 45 a
46 46 +a
47 47 +a
48 48 $ hg log
49 49 changeset: 1:43f1ba15f28a
50 50 tag: tip
51 51 user: test
52 52 date: Thu Jan 01 00:00:00 1970 +0000
53 53 summary: amend base1
54 54
55 55 changeset: 0:ad120869acf0
56 56 user: test
57 57 date: Thu Jan 01 00:00:00 1970 +0000
58 58 summary: base
59 59
60 60
61 61 Check proper abort for empty message
62 62
63 63 $ cat > editor.sh << '__EOF__'
64 64 > #!/bin/sh
65 65 > echo "" > "$1"
66 66 > __EOF__
67 67 $ echo b > b
68 68 $ hg add b
69 69 $ hg summary
70 70 parent: 1:43f1ba15f28a tip
71 71 amend base1
72 72 branch: default
73 73 commit: 1 added, 1 unknown
74 74 update: (current)
75 75 $ HGEDITOR="\"sh\" \"`pwd`/editor.sh\"" hg commit --amend
76 76 transaction abort!
77 77 rollback completed
78 78 abort: empty commit message
79 79 [255]
80 80 $ hg summary
81 81 parent: 1:43f1ba15f28a tip
82 82 amend base1
83 83 branch: default
84 84 commit: 1 added, 1 unknown
85 85 update: (current)
86 86
87 87 Add new file:
88 88 $ hg ci --amend -m 'amend base1 new file'
89 89 saved backup bundle to $TESTTMP/.hg/strip-backup/43f1ba15f28a-amend-backup.hg (glob)
90 90
91 91 Remove file that was added in amended commit:
92 92 (and test logfile option)
93 93 (and test that logfile option do not trigger an editor)
94 94
95 95 $ hg rm b
96 96 $ echo 'amend base1 remove new file' > ../logfile
97 97 $ HGEDITOR="\"sh\" \"`pwd`/editor.sh\"" hg ci --amend --logfile ../logfile
98 98 saved backup bundle to $TESTTMP/.hg/strip-backup/b8e3cb2b3882-amend-backup.hg (glob)
99 99
100 100 $ hg cat b
101 101 b: no such file in rev 74609c7f506e
102 102 [1]
103 103
104 104 No changes, just a different message:
105 105
106 106 $ hg ci -v --amend -m 'no changes, new message'
107 107 amending changeset 74609c7f506e
108 108 copying changeset 74609c7f506e to ad120869acf0
109 109 a
110 110 stripping amended changeset 74609c7f506e
111 111 1 changesets found
112 112 saved backup bundle to $TESTTMP/.hg/strip-backup/74609c7f506e-amend-backup.hg (glob)
113 113 1 changesets found
114 114 adding branch
115 115 adding changesets
116 116 adding manifests
117 117 adding file changes
118 118 added 1 changesets with 1 changes to 1 files
119 119 committed changeset 1:1cd866679df8
120 120 $ hg diff -c .
121 121 diff -r ad120869acf0 -r 1cd866679df8 a
122 122 --- a/a Thu Jan 01 00:00:00 1970 +0000
123 123 +++ b/a Thu Jan 01 00:00:00 1970 +0000
124 124 @@ -1,1 +1,3 @@
125 125 a
126 126 +a
127 127 +a
128 128 $ hg log
129 129 changeset: 1:1cd866679df8
130 130 tag: tip
131 131 user: test
132 132 date: Thu Jan 01 00:00:00 1970 +0000
133 133 summary: no changes, new message
134 134
135 135 changeset: 0:ad120869acf0
136 136 user: test
137 137 date: Thu Jan 01 00:00:00 1970 +0000
138 138 summary: base
139 139
140 140
141 141 Disable default date on commit so when -d isn't given, the old date is preserved:
142 142
143 143 $ echo '[defaults]' >> $HGRCPATH
144 144 $ echo 'commit=' >> $HGRCPATH
145 145
146 146 Test -u/-d:
147 147
148 148 $ hg ci --amend -u foo -d '1 0'
149 149 saved backup bundle to $TESTTMP/.hg/strip-backup/1cd866679df8-amend-backup.hg (glob)
150 150 $ echo a >> a
151 151 $ hg ci --amend -u foo -d '1 0'
152 152 saved backup bundle to $TESTTMP/.hg/strip-backup/780e6f23e03d-amend-backup.hg (glob)
153 153 $ hg log -r .
154 154 changeset: 1:5f357c7560ab
155 155 tag: tip
156 156 user: foo
157 157 date: Thu Jan 01 00:00:01 1970 +0000
158 158 summary: no changes, new message
159 159
160 160
161 161 Open editor with old commit message if a message isn't given otherwise:
162 162
163 163 $ cat > editor.sh << '__EOF__'
164 164 > #!/bin/sh
165 165 > cat $1
166 166 > echo "another precious commit message" > "$1"
167 167 > __EOF__
168 168 $ HGEDITOR="\"sh\" \"`pwd`/editor.sh\"" hg commit --amend -v
169 169 amending changeset 5f357c7560ab
170 170 copying changeset 5f357c7560ab to ad120869acf0
171 171 no changes, new message
172 172
173 173
174 174 HG: Enter commit message. Lines beginning with 'HG:' are removed.
175 175 HG: Leave message empty to abort commit.
176 176 HG: --
177 177 HG: user: foo
178 178 HG: branch 'default'
179 179 HG: changed a
180 180 a
181 181 stripping amended changeset 5f357c7560ab
182 182 1 changesets found
183 183 saved backup bundle to $TESTTMP/.hg/strip-backup/5f357c7560ab-amend-backup.hg (glob)
184 184 1 changesets found
185 185 adding branch
186 186 adding changesets
187 187 adding manifests
188 188 adding file changes
189 189 added 1 changesets with 1 changes to 1 files
190 190 committed changeset 1:7ab3bf440b54
191 191
192 192 Same, but with changes in working dir (different code path):
193 193
194 194 $ echo a >> a
195 195 $ HGEDITOR="\"sh\" \"`pwd`/editor.sh\"" hg commit --amend -v
196 196 amending changeset 7ab3bf440b54
197 197 a
198 198 copying changeset a0ea9b1a4c8c to ad120869acf0
199 199 another precious commit message
200 200
201 201
202 202 HG: Enter commit message. Lines beginning with 'HG:' are removed.
203 203 HG: Leave message empty to abort commit.
204 204 HG: --
205 205 HG: user: foo
206 206 HG: branch 'default'
207 207 HG: changed a
208 208 a
209 209 stripping intermediate changeset a0ea9b1a4c8c
210 210 stripping amended changeset 7ab3bf440b54
211 211 2 changesets found
212 212 saved backup bundle to $TESTTMP/.hg/strip-backup/7ab3bf440b54-amend-backup.hg (glob)
213 213 1 changesets found
214 214 adding branch
215 215 adding changesets
216 216 adding manifests
217 217 adding file changes
218 218 added 1 changesets with 1 changes to 1 files
219 219 committed changeset 1:ea22a388757c
220 220
221 221 $ rm editor.sh
222 222 $ hg log -r .
223 223 changeset: 1:ea22a388757c
224 224 tag: tip
225 225 user: foo
226 226 date: Thu Jan 01 00:00:01 1970 +0000
227 227 summary: another precious commit message
228 228
229 229
230 230 Moving bookmarks, preserve active bookmark:
231 231
232 232 $ hg book book1
233 233 $ hg book book2
234 234 $ hg ci --amend -m 'move bookmarks'
235 235 saved backup bundle to $TESTTMP/.hg/strip-backup/ea22a388757c-amend-backup.hg (glob)
236 236 $ hg book
237 237 book1 1:6cec5aa930e2
238 238 * book2 1:6cec5aa930e2
239 239 $ echo a >> a
240 240 $ hg ci --amend -m 'move bookmarks'
241 241 saved backup bundle to $TESTTMP/.hg/strip-backup/6cec5aa930e2-amend-backup.hg (glob)
242 242 $ hg book
243 243 book1 1:48bb6e53a15f
244 244 * book2 1:48bb6e53a15f
245 245
246 246 abort does not loose bookmarks
247 247
248 248 $ cat > editor.sh << '__EOF__'
249 249 > #!/bin/sh
250 250 > echo "" > "$1"
251 251 > __EOF__
252 252 $ echo a >> a
253 253 $ HGEDITOR="\"sh\" \"`pwd`/editor.sh\"" hg commit --amend
254 254 transaction abort!
255 255 rollback completed
256 256 abort: empty commit message
257 257 [255]
258 258 $ hg book
259 259 book1 1:48bb6e53a15f
260 260 * book2 1:48bb6e53a15f
261 261 $ hg revert -Caq
262 262 $ rm editor.sh
263 263
264 264 $ echo '[defaults]' >> $HGRCPATH
265 265 $ echo "commit=-d '0 0'" >> $HGRCPATH
266 266
267 267 Moving branches:
268 268
269 269 $ hg branch foo
270 270 marked working directory as branch foo
271 271 (branches are permanent and global, did you want a bookmark?)
272 272 $ echo a >> a
273 273 $ hg ci -m 'branch foo'
274 274 $ hg branch default -f
275 275 marked working directory as branch default
276 276 (branches are permanent and global, did you want a bookmark?)
277 277 $ hg ci --amend -m 'back to default'
278 278 saved backup bundle to $TESTTMP/.hg/strip-backup/8ac881fbf49d-amend-backup.hg (glob)
279 279 $ hg branches
280 280 default 2:ce12b0b57d46
281 281
282 282 Close branch:
283 283
284 284 $ hg up -q 0
285 285 $ echo b >> b
286 286 $ hg branch foo
287 287 marked working directory as branch foo
288 288 (branches are permanent and global, did you want a bookmark?)
289 289 $ hg ci -Am 'fork'
290 290 adding b
291 291 $ echo b >> b
292 292 $ hg ci -mb
293 293 $ hg ci --amend --close-branch -m 'closing branch foo'
294 294 saved backup bundle to $TESTTMP/.hg/strip-backup/c962248fa264-amend-backup.hg (glob)
295 295
296 296 Same thing, different code path:
297 297
298 298 $ echo b >> b
299 299 $ hg ci -m 'reopen branch'
300 300 reopening closed branch head 4
301 301 $ echo b >> b
302 302 $ hg ci --amend --close-branch
303 303 saved backup bundle to $TESTTMP/.hg/strip-backup/027371728205-amend-backup.hg (glob)
304 304 $ hg branches
305 305 default 2:ce12b0b57d46
306 306
307 307 Refuse to amend during a merge:
308 308
309 309 $ hg up -q default
310 310 $ hg merge foo
311 311 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
312 312 (branch merge, don't forget to commit)
313 313 $ hg ci --amend
314 314 abort: cannot amend while merging
315 315 [255]
316 316 $ hg ci -m 'merge'
317 317
318 318 Follow copies/renames:
319 319
320 320 $ hg mv b c
321 321 $ hg ci -m 'b -> c'
322 322 $ hg mv c d
323 323 $ hg ci --amend -m 'b -> d'
324 324 saved backup bundle to $TESTTMP/.hg/strip-backup/b8c6eac7f12e-amend-backup.hg (glob)
325 325 $ hg st --rev '.^' --copies d
326 326 A d
327 327 b
328 328 $ hg cp d e
329 329 $ hg ci -m 'e = d'
330 330 $ hg cp e f
331 331 $ hg ci --amend -m 'f = d'
332 332 saved backup bundle to $TESTTMP/.hg/strip-backup/7f9761d65613-amend-backup.hg (glob)
333 333 $ hg st --rev '.^' --copies f
334 334 A f
335 335 d
336 336
337 337 $ mv f f.orig
338 338 $ hg rm -A f
339 339 $ hg ci -m removef
340 340 $ hg cp a f
341 341 $ mv f.orig f
342 342 $ hg ci --amend -m replacef
343 343 saved backup bundle to $TESTTMP/.hg/strip-backup/9e8c5f7e3d95-amend-backup.hg (glob)
344 344 $ hg st --change . --copies
345 345 $ hg log -r . --template "{file_copies}\n"
346 346
347 347
348 348 Move added file (issue3410):
349 349
350 350 $ echo g >> g
351 351 $ hg ci -Am g
352 352 adding g
353 353 $ hg mv g h
354 354 $ hg ci --amend
355 355 saved backup bundle to $TESTTMP/.hg/strip-backup/24aa8eacce2b-amend-backup.hg (glob)
356 356 $ hg st --change . --copies h
357 357 A h
358 358 $ hg log -r . --template "{file_copies}\n"
359 359
360 360
361 361 Can't rollback an amend:
362 362
363 363 $ hg rollback
364 364 no rollback information available
365 365 [1]
366 366
367 367 Preserve extra dict (issue3430):
368 368
369 369 $ hg branch a
370 370 marked working directory as branch a
371 371 (branches are permanent and global, did you want a bookmark?)
372 372 $ echo a >> a
373 373 $ hg ci -ma
374 374 $ hg ci --amend -m "a'"
375 375 saved backup bundle to $TESTTMP/.hg/strip-backup/3837aa2a2fdb-amend-backup.hg (glob)
376 376 $ hg log -r . --template "{branch}\n"
377 377 a
378 378 $ hg ci --amend -m "a''"
379 379 saved backup bundle to $TESTTMP/.hg/strip-backup/c05c06be7514-amend-backup.hg (glob)
380 380 $ hg log -r . --template "{branch}\n"
381 381 a
382 382
383 383 Also preserve other entries in the dict that are in the old commit,
384 384 first graft something so there's an additional entry:
385 385
386 386 $ hg up 0 -q
387 387 $ echo z > z
388 388 $ hg ci -Am 'fork'
389 389 adding z
390 390 created new head
391 391 $ hg up 11
392 392 5 files updated, 0 files merged, 1 files removed, 0 files unresolved
393 393 $ hg graft 12
394 394 grafting revision 12
395 395 $ hg ci --amend -m 'graft amend'
396 396 saved backup bundle to $TESTTMP/.hg/strip-backup/bd010aea3f39-amend-backup.hg (glob)
397 397 $ hg log -r . --debug | grep extra
398 398 extra: amend_source=bd010aea3f39f3fb2a2f884b9ccb0471cd77398e
399 399 extra: branch=a
400 400 extra: source=2647734878ef0236dda712fae9c1651cf694ea8a
401 401
402 402 Preserve phase
403 403
404 404 $ hg phase '.^::.'
405 405 11: draft
406 406 13: draft
407 407 $ hg phase --secret --force .
408 408 $ hg phase '.^::.'
409 409 11: draft
410 410 13: secret
411 411 $ hg commit --amend -m 'amend for phase' -q
412 412 $ hg phase '.^::.'
413 413 11: draft
414 414 13: secret
415 415
416 416 Test amend with obsolete
417 417 ---------------------------
418 418
419 419 Enable obsolete
420 420
421 421 $ cat > ${TESTTMP}/obs.py << EOF
422 422 > import mercurial.obsolete
423 423 > mercurial.obsolete._enabled = True
424 424 > EOF
425 425 $ echo '[extensions]' >> $HGRCPATH
426 426 $ echo "obs=${TESTTMP}/obs.py" >> $HGRCPATH
427 427
428 428
429 429 Amend with no files changes
430 430
431 431 $ hg id -n
432 432 13
433 433 $ hg ci --amend -m 'babar'
434 434 $ hg id -n
435 435 14
436 436 $ hg log -Gl 3 --style=compact
437 437 @ 14[tip]:11 b650e6ee8614 1970-01-01 00:00 +0000 test
438 438 | babar
439 439 |
440 440 | o 12:0 2647734878ef 1970-01-01 00:00 +0000 test
441 441 | | fork
442 442 | |
443 443 o | 11 3334b7925910 1970-01-01 00:00 +0000 test
444 444 | | a''
445 445 | |
446 446 $ hg log -Gl 4 --hidden --style=compact
447 447 @ 14[tip]:11 b650e6ee8614 1970-01-01 00:00 +0000 test
448 448 | babar
449 449 |
450 450 | x 13:11 68ff8ff97044 1970-01-01 00:00 +0000 test
451 451 |/ amend for phase
452 452 |
453 453 | o 12:0 2647734878ef 1970-01-01 00:00 +0000 test
454 454 | | fork
455 455 | |
456 456 o | 11 3334b7925910 1970-01-01 00:00 +0000 test
457 457 | | a''
458 458 | |
459 459
460 460 Amend with files changes
461 461
462 462 (note: the extra commit over 15 is a temporary junk I would be happy to get
463 463 ride of)
464 464
465 465 $ echo 'babar' >> a
466 466 $ hg commit --amend
467 467 $ hg log -Gl 6 --hidden --style=compact
468 468 @ 16[tip]:11 9f9e9bccf56c 1970-01-01 00:00 +0000 test
469 469 | babar
470 470 |
471 471 | x 15 90fef497c56f 1970-01-01 00:00 +0000 test
472 472 | | temporary amend commit for b650e6ee8614
473 473 | |
474 474 | x 14:11 b650e6ee8614 1970-01-01 00:00 +0000 test
475 475 |/ babar
476 476 |
477 477 | x 13:11 68ff8ff97044 1970-01-01 00:00 +0000 test
478 478 |/ amend for phase
479 479 |
480 480 | o 12:0 2647734878ef 1970-01-01 00:00 +0000 test
481 481 | | fork
482 482 | |
483 483 o | 11 3334b7925910 1970-01-01 00:00 +0000 test
484 484 | | a''
485 485 | |
486 486
487 487
488 488 Test that amend does not make it easy to create obsolescence cycle
489 489 ---------------------------------------------------------------------
490 490
491 491 $ hg id -r 14 --hidden
492 492 b650e6ee8614 (a)
493 493 $ hg revert -ar 14 --hidden
494 494 reverting a
495 495 $ hg commit --amend
496 496 $ hg id
497 497 b99e5df575f7 (a) tip
498 498
499 499 Test that rewriting leaving instability behind is allowed
500 500 ---------------------------------------------------------------------
501 501
502 502 $ hg up '.^'
503 503 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
504 504 $ echo 'b' >> a
505 505 $ hg log --style compact -r 'children(.)'
506 506 18[tip]:11 b99e5df575f7 1970-01-01 00:00 +0000 test
507 507 babar
508 508
509 509 $ hg commit --amend
510 510 $ hg log -r 'unstable()'
511 511 changeset: 18:b99e5df575f7
512 512 branch: a
513 513 parent: 11:3334b7925910
514 514 user: test
515 515 date: Thu Jan 01 00:00:00 1970 +0000
516 516 summary: babar
517 517
518 518
519 519 Amend a merge changeset (with renames and conflicts from the second parent):
520 520
521 521 $ hg up -q default
522 522 $ hg branch -q bar
523 523 $ hg cp a aa
524 524 $ hg mv z zz
525 525 $ echo cc > cc
526 526 $ hg add cc
527 527 $ hg ci -m aazzcc
528 528 $ hg up -q default
529 529 $ echo a >> a
530 530 $ echo dd > cc
531 531 $ hg add cc
532 532 $ hg ci -m aa
533 533 $ hg merge -q bar
534 534 warning: conflicts during merge.
535 535 merging cc incomplete! (edit conflicts, then use 'hg resolve --mark')
536 536 [1]
537 537 $ hg resolve -m cc
538 538 $ hg ci -m 'merge bar'
539 539 $ hg log --config diff.git=1 -pr .
540 540 changeset: 23:d51446492733
541 541 tag: tip
542 542 parent: 22:30d96aeaf27b
543 543 parent: 21:1aa437659d19
544 544 user: test
545 545 date: Thu Jan 01 00:00:00 1970 +0000
546 546 summary: merge bar
547 547
548 548 diff --git a/a b/aa
549 549 copy from a
550 550 copy to aa
551 551 diff --git a/cc b/cc
552 552 --- a/cc
553 553 +++ b/cc
554 554 @@ -1,1 +1,5 @@
555 555 +<<<<<<< local
556 556 dd
557 557 +=======
558 558 +cc
559 559 +>>>>>>> other
560 560 diff --git a/z b/zz
561 561 rename from z
562 562 rename to zz
563 563
564 564 $ hg debugrename aa
565 565 aa renamed from a:a80d06849b333b8a3d5c445f8ba3142010dcdc9e
566 566 $ hg debugrename zz
567 567 zz renamed from z:69a1b67522704ec122181c0890bd16e9d3e7516a
568 568 $ hg debugrename cc
569 569 cc not renamed
570 570 $ hg ci --amend -m 'merge bar (amend message)'
571 571 $ hg log --config diff.git=1 -pr .
572 572 changeset: 24:59de3dce7a79
573 573 tag: tip
574 574 parent: 22:30d96aeaf27b
575 575 parent: 21:1aa437659d19
576 576 user: test
577 577 date: Thu Jan 01 00:00:00 1970 +0000
578 578 summary: merge bar (amend message)
579 579
580 580 diff --git a/a b/aa
581 581 copy from a
582 582 copy to aa
583 583 diff --git a/cc b/cc
584 584 --- a/cc
585 585 +++ b/cc
586 586 @@ -1,1 +1,5 @@
587 587 +<<<<<<< local
588 588 dd
589 589 +=======
590 590 +cc
591 591 +>>>>>>> other
592 592 diff --git a/z b/zz
593 593 rename from z
594 594 rename to zz
595 595
596 596 $ hg debugrename aa
597 597 aa renamed from a:a80d06849b333b8a3d5c445f8ba3142010dcdc9e
598 598 $ hg debugrename zz
599 599 zz renamed from z:69a1b67522704ec122181c0890bd16e9d3e7516a
600 600 $ hg debugrename cc
601 601 cc not renamed
602 602 $ hg mv zz z
603 603 $ hg ci --amend -m 'merge bar (undo rename)'
604 604 $ hg log --config diff.git=1 -pr .
605 605 changeset: 26:7fb89c461f81
606 606 tag: tip
607 607 parent: 22:30d96aeaf27b
608 608 parent: 21:1aa437659d19
609 609 user: test
610 610 date: Thu Jan 01 00:00:00 1970 +0000
611 611 summary: merge bar (undo rename)
612 612
613 613 diff --git a/a b/aa
614 614 copy from a
615 615 copy to aa
616 616 diff --git a/cc b/cc
617 617 --- a/cc
618 618 +++ b/cc
619 619 @@ -1,1 +1,5 @@
620 620 +<<<<<<< local
621 621 dd
622 622 +=======
623 623 +cc
624 624 +>>>>>>> other
625 625
626 626 $ hg debugrename z
627 627 z not renamed
628 628
629 629 Amend a merge changeset (with renames during the merge):
630 630
631 631 $ hg up -q bar
632 632 $ echo x > x
633 633 $ hg add x
634 634 $ hg ci -m x
635 635 $ hg up -q default
636 636 $ hg merge -q bar
637 637 $ hg mv aa aaa
638 638 $ echo aa >> aaa
639 639 $ hg ci -m 'merge bar again'
640 640 $ hg log --config diff.git=1 -pr .
641 641 changeset: 28:982d7a34ffee
642 642 tag: tip
643 643 parent: 26:7fb89c461f81
644 644 parent: 27:4c94d5bc65f5
645 645 user: test
646 646 date: Thu Jan 01 00:00:00 1970 +0000
647 647 summary: merge bar again
648 648
649 649 diff --git a/aa b/aa
650 650 deleted file mode 100644
651 651 --- a/aa
652 652 +++ /dev/null
653 653 @@ -1,2 +0,0 @@
654 654 -a
655 655 -a
656 656 diff --git a/aaa b/aaa
657 657 new file mode 100644
658 658 --- /dev/null
659 659 +++ b/aaa
660 660 @@ -0,0 +1,3 @@
661 661 +a
662 662 +a
663 663 +aa
664 664 diff --git a/x b/x
665 665 new file mode 100644
666 666 --- /dev/null
667 667 +++ b/x
668 668 @@ -0,0 +1,1 @@
669 669 +x
670 670
671 671 $ hg debugrename aaa
672 672 aaa renamed from aa:37d9b5d994eab34eda9c16b195ace52c7b129980
673 673 $ hg mv aaa aa
674 674 $ hg ci --amend -m 'merge bar again (undo rename)'
675 675 $ hg log --config diff.git=1 -pr .
676 676 changeset: 30:522688c0e71b
677 677 tag: tip
678 678 parent: 26:7fb89c461f81
679 679 parent: 27:4c94d5bc65f5
680 680 user: test
681 681 date: Thu Jan 01 00:00:00 1970 +0000
682 682 summary: merge bar again (undo rename)
683 683
684 684 diff --git a/aa b/aa
685 685 --- a/aa
686 686 +++ b/aa
687 687 @@ -1,2 +1,3 @@
688 688 a
689 689 a
690 690 +aa
691 691 diff --git a/x b/x
692 692 new file mode 100644
693 693 --- /dev/null
694 694 +++ b/x
695 695 @@ -0,0 +1,1 @@
696 696 +x
697 697
698 698 $ hg debugrename aa
699 699 aa not renamed
700 700 $ hg debugrename -r '.^' aa
701 701 aa renamed from a:a80d06849b333b8a3d5c445f8ba3142010dcdc9e
702 702
703 703 Amend a merge changeset (with manifest-level conflicts):
704 704
705 705 $ hg up -q bar
706 706 $ hg rm aa
707 707 $ hg ci -m 'rm aa'
708 708 $ hg up -q default
709 709 $ echo aa >> aa
710 710 $ hg ci -m aa
711 711 $ hg merge -q bar
712 712 local changed aa which remote deleted
713 713 use (c)hanged version or (d)elete? c
714 714 $ hg ci -m 'merge bar (with conflicts)'
715 715 $ hg log --config diff.git=1 -pr .
716 716 changeset: 33:5f9904c491b8
717 717 tag: tip
718 718 parent: 32:01780b896f58
719 719 parent: 31:67db8847a540
720 720 user: test
721 721 date: Thu Jan 01 00:00:00 1970 +0000
722 722 summary: merge bar (with conflicts)
723 723
724 724
725 725 $ hg rm aa
726 726 $ hg ci --amend -m 'merge bar (with conflicts, amended)'
727 727 $ hg log --config diff.git=1 -pr .
728 728 changeset: 35:6ce0c89781a3
729 729 tag: tip
730 730 parent: 32:01780b896f58
731 731 parent: 31:67db8847a540
732 732 user: test
733 733 date: Thu Jan 01 00:00:00 1970 +0000
734 734 summary: merge bar (with conflicts, amended)
735 735
736 736 diff --git a/aa b/aa
737 737 deleted file mode 100644
738 738 --- a/aa
739 739 +++ /dev/null
740 740 @@ -1,4 +0,0 @@
741 741 -a
742 742 -a
743 743 -aa
744 744 -aa
745 745
746 Issue 3445: amending with --close-branch a commit that created a new head should fail
747 This shouldn't be possible:
748
749 $ hg up -q default
750 $ hg branch closewithamend
751 marked working directory as branch closewithamend
752 (branches are permanent and global, did you want a bookmark?)
753 $ hg ci -Am..
754 adding cc.orig
755 adding obs.py
756 adding obs.pyc
757 $ hg ci --amend --close-branch -m 'closing'
758 abort: can only close branch heads
759 [255]
760
761 This silliness fails:
762
763 $ hg branch silliness
764 marked working directory as branch silliness
765 (branches are permanent and global, did you want a bookmark?)
766 $ echo b >> b
767 $ hg ci --close-branch -m'open and close'
768 abort: can only close branch heads
769 [255]
General Comments 0
You need to be logged in to leave comments. Login now