##// END OF EJS Templates
merge with stable
Martin Geisler -
r13343:69e69b13 merge default
parent child Browse files
Show More
@@ -1,4537 +1,4533
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, nullid, nullrev, short
9 9 from lock import release
10 10 from i18n import _, gettext
11 11 import os, re, sys, difflib, time, tempfile
12 12 import hg, util, revlog, extensions, copies, error
13 13 import patch, help, mdiff, url, encoding, templatekw, discovery
14 14 import archival, changegroup, cmdutil, sshserver, hbisect, hgweb, hgweb.server
15 15 import merge as mergemod
16 16 import minirst, revset
17 17 import dagparser
18 18
19 19 # Commands start here, listed alphabetically
20 20
21 21 def add(ui, repo, *pats, **opts):
22 22 """add the specified files on the next commit
23 23
24 24 Schedule files to be version controlled and added to the
25 25 repository.
26 26
27 27 The files will be added to the repository at the next commit. To
28 28 undo an add before that, see :hg:`forget`.
29 29
30 30 If no names are given, add all files to the repository.
31 31
32 32 .. container:: verbose
33 33
34 34 An example showing how new (unknown) files are added
35 35 automatically by :hg:`add`::
36 36
37 37 $ ls
38 38 foo.c
39 39 $ hg status
40 40 ? foo.c
41 41 $ hg add
42 42 adding foo.c
43 43 $ hg status
44 44 A foo.c
45 45
46 46 Returns 0 if all files are successfully added.
47 47 """
48 48
49 49 m = cmdutil.match(repo, pats, opts)
50 50 rejected = cmdutil.add(ui, repo, m, opts.get('dry_run'),
51 51 opts.get('subrepos'), prefix="")
52 52 return rejected and 1 or 0
53 53
54 54 def addremove(ui, repo, *pats, **opts):
55 55 """add all new files, delete all missing files
56 56
57 57 Add all new files and remove all missing files from the
58 58 repository.
59 59
60 60 New files are ignored if they match any of the patterns in
61 61 .hgignore. As with add, these changes take effect at the next
62 62 commit.
63 63
64 64 Use the -s/--similarity option to detect renamed files. With a
65 65 parameter greater than 0, this compares every removed file with
66 66 every added file and records those similar enough as renames. This
67 67 option takes a percentage between 0 (disabled) and 100 (files must
68 68 be identical) as its parameter. Detecting renamed files this way
69 69 can be expensive. After using this option, :hg:`status -C` can be
70 70 used to check which files were identified as moved or renamed.
71 71
72 72 Returns 0 if all files are successfully added.
73 73 """
74 74 try:
75 75 sim = float(opts.get('similarity') or 100)
76 76 except ValueError:
77 77 raise util.Abort(_('similarity must be a number'))
78 78 if sim < 0 or sim > 100:
79 79 raise util.Abort(_('similarity must be between 0 and 100'))
80 80 return cmdutil.addremove(repo, pats, opts, similarity=sim / 100.0)
81 81
82 82 def annotate(ui, repo, *pats, **opts):
83 83 """show changeset information by line for each file
84 84
85 85 List changes in files, showing the revision id responsible for
86 86 each line
87 87
88 88 This command is useful for discovering when a change was made and
89 89 by whom.
90 90
91 91 Without the -a/--text option, annotate will avoid processing files
92 92 it detects as binary. With -a, annotate will annotate the file
93 93 anyway, although the results will probably be neither useful
94 94 nor desirable.
95 95
96 96 Returns 0 on success.
97 97 """
98 98 if opts.get('follow'):
99 99 # --follow is deprecated and now just an alias for -f/--file
100 100 # to mimic the behavior of Mercurial before version 1.5
101 101 opts['file'] = 1
102 102
103 103 datefunc = ui.quiet and util.shortdate or util.datestr
104 104 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
105 105
106 106 if not pats:
107 107 raise util.Abort(_('at least one filename or pattern is required'))
108 108
109 109 opmap = [('user', lambda x: ui.shortuser(x[0].user())),
110 110 ('number', lambda x: str(x[0].rev())),
111 111 ('changeset', lambda x: short(x[0].node())),
112 112 ('date', getdate),
113 113 ('file', lambda x: x[0].path()),
114 114 ]
115 115
116 116 if (not opts.get('user') and not opts.get('changeset')
117 117 and not opts.get('date') and not opts.get('file')):
118 118 opts['number'] = 1
119 119
120 120 linenumber = opts.get('line_number') is not None
121 121 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
122 122 raise util.Abort(_('at least one of -n/-c is required for -l'))
123 123
124 124 funcmap = [func for op, func in opmap if opts.get(op)]
125 125 if linenumber:
126 126 lastfunc = funcmap[-1]
127 127 funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])
128 128
129 129 ctx = cmdutil.revsingle(repo, opts.get('rev'))
130 130 m = cmdutil.match(repo, pats, opts)
131 131 follow = not opts.get('no_follow')
132 132 for abs in ctx.walk(m):
133 133 fctx = ctx[abs]
134 134 if not opts.get('text') and util.binary(fctx.data()):
135 135 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
136 136 continue
137 137
138 138 lines = fctx.annotate(follow=follow, linenumber=linenumber)
139 139 pieces = []
140 140
141 141 for f in funcmap:
142 142 l = [f(n) for n, dummy in lines]
143 143 if l:
144 144 sized = [(x, encoding.colwidth(x)) for x in l]
145 145 ml = max([w for x, w in sized])
146 146 pieces.append(["%s%s" % (' ' * (ml - w), x) for x, w in sized])
147 147
148 148 if pieces:
149 149 for p, l in zip(zip(*pieces), lines):
150 150 ui.write("%s: %s" % (" ".join(p), l[1]))
151 151
152 152 def archive(ui, repo, dest, **opts):
153 153 '''create an unversioned archive of a repository revision
154 154
155 155 By default, the revision used is the parent of the working
156 156 directory; use -r/--rev to specify a different revision.
157 157
158 158 The archive type is automatically detected based on file
159 159 extension (or override using -t/--type).
160 160
161 161 Valid types are:
162 162
163 163 :``files``: a directory full of files (default)
164 164 :``tar``: tar archive, uncompressed
165 165 :``tbz2``: tar archive, compressed using bzip2
166 166 :``tgz``: tar archive, compressed using gzip
167 167 :``uzip``: zip archive, uncompressed
168 168 :``zip``: zip archive, compressed using deflate
169 169
170 170 The exact name of the destination archive or directory is given
171 171 using a format string; see :hg:`help export` for details.
172 172
173 173 Each member added to an archive file has a directory prefix
174 174 prepended. Use -p/--prefix to specify a format string for the
175 175 prefix. The default is the basename of the archive, with suffixes
176 176 removed.
177 177
178 178 Returns 0 on success.
179 179 '''
180 180
181 181 ctx = cmdutil.revsingle(repo, opts.get('rev'))
182 182 if not ctx:
183 183 raise util.Abort(_('no working directory: please specify a revision'))
184 184 node = ctx.node()
185 185 dest = cmdutil.make_filename(repo, dest, node)
186 186 if os.path.realpath(dest) == repo.root:
187 187 raise util.Abort(_('repository root cannot be destination'))
188 188
189 189 kind = opts.get('type') or archival.guesskind(dest) or 'files'
190 190 prefix = opts.get('prefix')
191 191
192 192 if dest == '-':
193 193 if kind == 'files':
194 194 raise util.Abort(_('cannot archive plain files to stdout'))
195 195 dest = sys.stdout
196 196 if not prefix:
197 197 prefix = os.path.basename(repo.root) + '-%h'
198 198
199 199 prefix = cmdutil.make_filename(repo, prefix, node)
200 200 matchfn = cmdutil.match(repo, [], opts)
201 201 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
202 202 matchfn, prefix, subrepos=opts.get('subrepos'))
203 203
204 204 def backout(ui, repo, node=None, rev=None, **opts):
205 205 '''reverse effect of earlier changeset
206 206
207 The backout command merges the reverse effect of the reverted
208 changeset into the working directory.
209
210 With the --merge option, it first commits the reverted changes
211 as a new changeset. This new changeset is a child of the reverted
212 changeset.
213 The --merge option remembers the parent of the working directory
214 before starting the backout, then merges the new head with that
215 changeset afterwards.
216 This will result in an explicit merge in the history.
217
218 If you backout a changeset other than the original parent of the
219 working directory, the result of this merge is not committed,
220 as with a normal merge. Otherwise, no merge is needed and the
221 commit is automatic.
222
223 Note that the default behavior (without --merge) has changed in
224 version 1.7. To restore the previous default behavior, use
225 :hg:`backout --merge` and then :hg:`update --clean .` to get rid of
226 the ongoing merge.
207 Prepare a new changeset with the effect of REV undone in the
208 current working directory.
209
210 If REV is the parent of the working directory, then this changeset
211 is committed automatically. Otherwise, hg needs to merge the
212 changes and the merged result is left uncommitted.
213
214 By default, the pending changeset will have one parent,
215 maintaining a linear history. With --merge, the pending changeset
216 will instead have two parents: the old parent of the working
217 directory and a child of REV that simply undoes REV.
218
219 Before version 1.7, the default behavior was equivalent to
220 specifying --merge followed by :hg:`update --clean .` to cancel
221 the merge and leave the child of REV as a head to be merged
222 separately.
227 223
228 224 See :hg:`help dates` for a list of formats valid for -d/--date.
229 225
230 226 Returns 0 on success.
231 227 '''
232 228 if rev and node:
233 229 raise util.Abort(_("please specify just one revision"))
234 230
235 231 if not rev:
236 232 rev = node
237 233
238 234 if not rev:
239 235 raise util.Abort(_("please specify a revision to backout"))
240 236
241 237 date = opts.get('date')
242 238 if date:
243 239 opts['date'] = util.parsedate(date)
244 240
245 241 cmdutil.bail_if_changed(repo)
246 242 node = cmdutil.revsingle(repo, rev).node()
247 243
248 244 op1, op2 = repo.dirstate.parents()
249 245 a = repo.changelog.ancestor(op1, node)
250 246 if a != node:
251 247 raise util.Abort(_('cannot backout change on a different branch'))
252 248
253 249 p1, p2 = repo.changelog.parents(node)
254 250 if p1 == nullid:
255 251 raise util.Abort(_('cannot backout a change with no parents'))
256 252 if p2 != nullid:
257 253 if not opts.get('parent'):
258 254 raise util.Abort(_('cannot backout a merge changeset without '
259 255 '--parent'))
260 256 p = repo.lookup(opts['parent'])
261 257 if p not in (p1, p2):
262 258 raise util.Abort(_('%s is not a parent of %s') %
263 259 (short(p), short(node)))
264 260 parent = p
265 261 else:
266 262 if opts.get('parent'):
267 263 raise util.Abort(_('cannot use --parent on non-merge changeset'))
268 264 parent = p1
269 265
270 266 # the backout should appear on the same branch
271 267 branch = repo.dirstate.branch()
272 268 hg.clean(repo, node, show_stats=False)
273 269 repo.dirstate.setbranch(branch)
274 270 revert_opts = opts.copy()
275 271 revert_opts['date'] = None
276 272 revert_opts['all'] = True
277 273 revert_opts['rev'] = hex(parent)
278 274 revert_opts['no_backup'] = None
279 275 revert(ui, repo, **revert_opts)
280 276 if not opts.get('merge') and op1 != node:
281 277 try:
282 278 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
283 279 return hg.update(repo, op1)
284 280 finally:
285 281 ui.setconfig('ui', 'forcemerge', '')
286 282
287 283 commit_opts = opts.copy()
288 284 commit_opts['addremove'] = False
289 285 if not commit_opts['message'] and not commit_opts['logfile']:
290 286 # we don't translate commit messages
291 287 commit_opts['message'] = "Backed out changeset %s" % short(node)
292 288 commit_opts['force_editor'] = True
293 289 commit(ui, repo, **commit_opts)
294 290 def nice(node):
295 291 return '%d:%s' % (repo.changelog.rev(node), short(node))
296 292 ui.status(_('changeset %s backs out changeset %s\n') %
297 293 (nice(repo.changelog.tip()), nice(node)))
298 294 if opts.get('merge') and op1 != node:
299 295 hg.clean(repo, op1, show_stats=False)
300 296 ui.status(_('merging with changeset %s\n')
301 297 % nice(repo.changelog.tip()))
302 298 try:
303 299 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
304 300 return hg.merge(repo, hex(repo.changelog.tip()))
305 301 finally:
306 302 ui.setconfig('ui', 'forcemerge', '')
307 303 return 0
308 304
309 305 def bisect(ui, repo, rev=None, extra=None, command=None,
310 306 reset=None, good=None, bad=None, skip=None, noupdate=None):
311 307 """subdivision search of changesets
312 308
313 309 This command helps to find changesets which introduce problems. To
314 310 use, mark the earliest changeset you know exhibits the problem as
315 311 bad, then mark the latest changeset which is free from the problem
316 312 as good. Bisect will update your working directory to a revision
317 313 for testing (unless the -U/--noupdate option is specified). Once
318 314 you have performed tests, mark the working directory as good or
319 315 bad, and bisect will either update to another candidate changeset
320 316 or announce that it has found the bad revision.
321 317
322 318 As a shortcut, you can also use the revision argument to mark a
323 319 revision as good or bad without checking it out first.
324 320
325 321 If you supply a command, it will be used for automatic bisection.
326 322 Its exit status will be used to mark revisions as good or bad:
327 323 status 0 means good, 125 means to skip the revision, 127
328 324 (command not found) will abort the bisection, and any other
329 325 non-zero exit status means the revision is bad.
330 326
331 327 Returns 0 on success.
332 328 """
333 329 def print_result(nodes, good):
334 330 displayer = cmdutil.show_changeset(ui, repo, {})
335 331 if len(nodes) == 1:
336 332 # narrowed it down to a single revision
337 333 if good:
338 334 ui.write(_("The first good revision is:\n"))
339 335 else:
340 336 ui.write(_("The first bad revision is:\n"))
341 337 displayer.show(repo[nodes[0]])
342 338 parents = repo[nodes[0]].parents()
343 339 if len(parents) > 1:
344 340 side = good and state['bad'] or state['good']
345 341 num = len(set(i.node() for i in parents) & set(side))
346 342 if num == 1:
347 343 common = parents[0].ancestor(parents[1])
348 344 ui.write(_('Not all ancestors of this changeset have been'
349 345 ' checked.\nTo check the other ancestors, start'
350 346 ' from the common ancestor, %s.\n' % common))
351 347 else:
352 348 # multiple possible revisions
353 349 if good:
354 350 ui.write(_("Due to skipped revisions, the first "
355 351 "good revision could be any of:\n"))
356 352 else:
357 353 ui.write(_("Due to skipped revisions, the first "
358 354 "bad revision could be any of:\n"))
359 355 for n in nodes:
360 356 displayer.show(repo[n])
361 357 displayer.close()
362 358
363 359 def check_state(state, interactive=True):
364 360 if not state['good'] or not state['bad']:
365 361 if (good or bad or skip or reset) and interactive:
366 362 return
367 363 if not state['good']:
368 364 raise util.Abort(_('cannot bisect (no known good revisions)'))
369 365 else:
370 366 raise util.Abort(_('cannot bisect (no known bad revisions)'))
371 367 return True
372 368
373 369 # backward compatibility
374 370 if rev in "good bad reset init".split():
375 371 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
376 372 cmd, rev, extra = rev, extra, None
377 373 if cmd == "good":
378 374 good = True
379 375 elif cmd == "bad":
380 376 bad = True
381 377 else:
382 378 reset = True
383 379 elif extra or good + bad + skip + reset + bool(command) > 1:
384 380 raise util.Abort(_('incompatible arguments'))
385 381
386 382 if reset:
387 383 p = repo.join("bisect.state")
388 384 if os.path.exists(p):
389 385 os.unlink(p)
390 386 return
391 387
392 388 state = hbisect.load_state(repo)
393 389
394 390 if command:
395 391 changesets = 1
396 392 try:
397 393 while changesets:
398 394 # update state
399 395 status = util.system(command)
400 396 if status == 125:
401 397 transition = "skip"
402 398 elif status == 0:
403 399 transition = "good"
404 400 # status < 0 means process was killed
405 401 elif status == 127:
406 402 raise util.Abort(_("failed to execute %s") % command)
407 403 elif status < 0:
408 404 raise util.Abort(_("%s killed") % command)
409 405 else:
410 406 transition = "bad"
411 407 ctx = cmdutil.revsingle(repo, rev)
412 408 rev = None # clear for future iterations
413 409 state[transition].append(ctx.node())
414 410 ui.status(_('Changeset %d:%s: %s\n') % (ctx, ctx, transition))
415 411 check_state(state, interactive=False)
416 412 # bisect
417 413 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
418 414 # update to next check
419 415 cmdutil.bail_if_changed(repo)
420 416 hg.clean(repo, nodes[0], show_stats=False)
421 417 finally:
422 418 hbisect.save_state(repo, state)
423 419 print_result(nodes, good)
424 420 return
425 421
426 422 # update state
427 423
428 424 if rev:
429 425 nodes = [repo.lookup(i) for i in cmdutil.revrange(repo, [rev])]
430 426 else:
431 427 nodes = [repo.lookup('.')]
432 428
433 429 if good or bad or skip:
434 430 if good:
435 431 state['good'] += nodes
436 432 elif bad:
437 433 state['bad'] += nodes
438 434 elif skip:
439 435 state['skip'] += nodes
440 436 hbisect.save_state(repo, state)
441 437
442 438 if not check_state(state):
443 439 return
444 440
445 441 # actually bisect
446 442 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
447 443 if changesets == 0:
448 444 print_result(nodes, good)
449 445 else:
450 446 assert len(nodes) == 1 # only a single node can be tested next
451 447 node = nodes[0]
452 448 # compute the approximate number of remaining tests
453 449 tests, size = 0, 2
454 450 while size <= changesets:
455 451 tests, size = tests + 1, size * 2
456 452 rev = repo.changelog.rev(node)
457 453 ui.write(_("Testing changeset %d:%s "
458 454 "(%d changesets remaining, ~%d tests)\n")
459 455 % (rev, short(node), changesets, tests))
460 456 if not noupdate:
461 457 cmdutil.bail_if_changed(repo)
462 458 return hg.clean(repo, node)
463 459
464 460 def branch(ui, repo, label=None, **opts):
465 461 """set or show the current branch name
466 462
467 463 With no argument, show the current branch name. With one argument,
468 464 set the working directory branch name (the branch will not exist
469 465 in the repository until the next commit). Standard practice
470 466 recommends that primary development take place on the 'default'
471 467 branch.
472 468
473 469 Unless -f/--force is specified, branch will not let you set a
474 470 branch name that already exists, even if it's inactive.
475 471
476 472 Use -C/--clean to reset the working directory branch to that of
477 473 the parent of the working directory, negating a previous branch
478 474 change.
479 475
480 476 Use the command :hg:`update` to switch to an existing branch. Use
481 477 :hg:`commit --close-branch` to mark this branch as closed.
482 478
483 479 Returns 0 on success.
484 480 """
485 481
486 482 if opts.get('clean'):
487 483 label = repo[None].parents()[0].branch()
488 484 repo.dirstate.setbranch(label)
489 485 ui.status(_('reset working directory to branch %s\n') % label)
490 486 elif label:
491 487 if not opts.get('force') and label in repo.branchtags():
492 488 if label not in [p.branch() for p in repo.parents()]:
493 489 raise util.Abort(_('a branch of the same name already exists'
494 490 " (use 'hg update' to switch to it)"))
495 491 repo.dirstate.setbranch(label)
496 492 ui.status(_('marked working directory as branch %s\n') % label)
497 493 else:
498 494 ui.write("%s\n" % repo.dirstate.branch())
499 495
500 496 def branches(ui, repo, active=False, closed=False):
501 497 """list repository named branches
502 498
503 499 List the repository's named branches, indicating which ones are
504 500 inactive. If -c/--closed is specified, also list branches which have
505 501 been marked closed (see :hg:`commit --close-branch`).
506 502
507 503 If -a/--active is specified, only show active branches. A branch
508 504 is considered active if it contains repository heads.
509 505
510 506 Use the command :hg:`update` to switch to an existing branch.
511 507
512 508 Returns 0.
513 509 """
514 510
515 511 hexfunc = ui.debugflag and hex or short
516 512 activebranches = [repo[n].branch() for n in repo.heads()]
517 513 def testactive(tag, node):
518 514 realhead = tag in activebranches
519 515 open = node in repo.branchheads(tag, closed=False)
520 516 return realhead and open
521 517 branches = sorted([(testactive(tag, node), repo.changelog.rev(node), tag)
522 518 for tag, node in repo.branchtags().items()],
523 519 reverse=True)
524 520
525 521 for isactive, node, tag in branches:
526 522 if (not active) or isactive:
527 523 if ui.quiet:
528 524 ui.write("%s\n" % tag)
529 525 else:
530 526 hn = repo.lookup(node)
531 527 if isactive:
532 528 label = 'branches.active'
533 529 notice = ''
534 530 elif hn not in repo.branchheads(tag, closed=False):
535 531 if not closed:
536 532 continue
537 533 label = 'branches.closed'
538 534 notice = _(' (closed)')
539 535 else:
540 536 label = 'branches.inactive'
541 537 notice = _(' (inactive)')
542 538 if tag == repo.dirstate.branch():
543 539 label = 'branches.current'
544 540 rev = str(node).rjust(31 - encoding.colwidth(tag))
545 541 rev = ui.label('%s:%s' % (rev, hexfunc(hn)), 'log.changeset')
546 542 tag = ui.label(tag, label)
547 543 ui.write("%s %s%s\n" % (tag, rev, notice))
548 544
549 545 def bundle(ui, repo, fname, dest=None, **opts):
550 546 """create a changegroup file
551 547
552 548 Generate a compressed changegroup file collecting changesets not
553 549 known to be in another repository.
554 550
555 551 If you omit the destination repository, then hg assumes the
556 552 destination will have all the nodes you specify with --base
557 553 parameters. To create a bundle containing all changesets, use
558 554 -a/--all (or --base null).
559 555
560 556 You can change compression method with the -t/--type option.
561 557 The available compression methods are: none, bzip2, and
562 558 gzip (by default, bundles are compressed using bzip2).
563 559
564 560 The bundle file can then be transferred using conventional means
565 561 and applied to another repository with the unbundle or pull
566 562 command. This is useful when direct push and pull are not
567 563 available or when exporting an entire repository is undesirable.
568 564
569 565 Applying bundles preserves all changeset contents including
570 566 permissions, copy/rename information, and revision history.
571 567
572 568 Returns 0 on success, 1 if no changes found.
573 569 """
574 570 revs = None
575 571 if 'rev' in opts:
576 572 revs = cmdutil.revrange(repo, opts['rev'])
577 573
578 574 if opts.get('all'):
579 575 base = ['null']
580 576 else:
581 577 base = cmdutil.revrange(repo, opts.get('base'))
582 578 if base:
583 579 if dest:
584 580 raise util.Abort(_("--base is incompatible with specifying "
585 581 "a destination"))
586 582 base = [repo.lookup(rev) for rev in base]
587 583 # create the right base
588 584 # XXX: nodesbetween / changegroup* should be "fixed" instead
589 585 o = []
590 586 has = set((nullid,))
591 587 for n in base:
592 588 has.update(repo.changelog.reachable(n))
593 589 if revs:
594 590 revs = [repo.lookup(rev) for rev in revs]
595 591 visit = revs[:]
596 592 has.difference_update(visit)
597 593 else:
598 594 visit = repo.changelog.heads()
599 595 seen = {}
600 596 while visit:
601 597 n = visit.pop(0)
602 598 parents = [p for p in repo.changelog.parents(n) if p not in has]
603 599 if len(parents) == 0:
604 600 if n not in has:
605 601 o.append(n)
606 602 else:
607 603 for p in parents:
608 604 if p not in seen:
609 605 seen[p] = 1
610 606 visit.append(p)
611 607 else:
612 608 dest = ui.expandpath(dest or 'default-push', dest or 'default')
613 609 dest, branches = hg.parseurl(dest, opts.get('branch'))
614 610 other = hg.repository(hg.remoteui(repo, opts), dest)
615 611 revs, checkout = hg.addbranchrevs(repo, other, branches, revs)
616 612 if revs:
617 613 revs = [repo.lookup(rev) for rev in revs]
618 614 o = discovery.findoutgoing(repo, other, force=opts.get('force'))
619 615
620 616 if not o:
621 617 ui.status(_("no changes found\n"))
622 618 return 1
623 619
624 620 if revs:
625 621 cg = repo.changegroupsubset(o, revs, 'bundle')
626 622 else:
627 623 cg = repo.changegroup(o, 'bundle')
628 624
629 625 bundletype = opts.get('type', 'bzip2').lower()
630 626 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
631 627 bundletype = btypes.get(bundletype)
632 628 if bundletype not in changegroup.bundletypes:
633 629 raise util.Abort(_('unknown bundle type specified with --type'))
634 630
635 631 changegroup.writebundle(cg, fname, bundletype)
636 632
637 633 def cat(ui, repo, file1, *pats, **opts):
638 634 """output the current or given revision of files
639 635
640 636 Print the specified files as they were at the given revision. If
641 637 no revision is given, the parent of the working directory is used,
642 638 or tip if no revision is checked out.
643 639
644 640 Output may be to a file, in which case the name of the file is
645 641 given using a format string. The formatting rules are the same as
646 642 for the export command, with the following additions:
647 643
648 644 :``%s``: basename of file being printed
649 645 :``%d``: dirname of file being printed, or '.' if in repository root
650 646 :``%p``: root-relative path name of file being printed
651 647
652 648 Returns 0 on success.
653 649 """
654 650 ctx = cmdutil.revsingle(repo, opts.get('rev'))
655 651 err = 1
656 652 m = cmdutil.match(repo, (file1,) + pats, opts)
657 653 for abs in ctx.walk(m):
658 654 fp = cmdutil.make_file(repo, opts.get('output'), ctx.node(), pathname=abs)
659 655 data = ctx[abs].data()
660 656 if opts.get('decode'):
661 657 data = repo.wwritedata(abs, data)
662 658 fp.write(data)
663 659 err = 0
664 660 return err
665 661
666 662 def clone(ui, source, dest=None, **opts):
667 663 """make a copy of an existing repository
668 664
669 665 Create a copy of an existing repository in a new directory.
670 666
671 667 If no destination directory name is specified, it defaults to the
672 668 basename of the source.
673 669
674 670 The location of the source is added to the new repository's
675 671 .hg/hgrc file, as the default to be used for future pulls.
676 672
677 673 See :hg:`help urls` for valid source format details.
678 674
679 675 It is possible to specify an ``ssh://`` URL as the destination, but no
680 676 .hg/hgrc and working directory will be created on the remote side.
681 677 Please see :hg:`help urls` for important details about ``ssh://`` URLs.
682 678
683 679 A set of changesets (tags, or branch names) to pull may be specified
684 680 by listing each changeset (tag, or branch name) with -r/--rev.
685 681 If -r/--rev is used, the cloned repository will contain only a subset
686 682 of the changesets of the source repository. Only the set of changesets
687 683 defined by all -r/--rev options (including all their ancestors)
688 684 will be pulled into the destination repository.
689 685 No subsequent changesets (including subsequent tags) will be present
690 686 in the destination.
691 687
692 688 Using -r/--rev (or 'clone src#rev dest') implies --pull, even for
693 689 local source repositories.
694 690
695 691 For efficiency, hardlinks are used for cloning whenever the source
696 692 and destination are on the same filesystem (note this applies only
697 693 to the repository data, not to the working directory). Some
698 694 filesystems, such as AFS, implement hardlinking incorrectly, but
699 695 do not report errors. In these cases, use the --pull option to
700 696 avoid hardlinking.
701 697
702 698 In some cases, you can clone repositories and the working directory
703 699 using full hardlinks with ::
704 700
705 701 $ cp -al REPO REPOCLONE
706 702
707 703 This is the fastest way to clone, but it is not always safe. The
708 704 operation is not atomic (making sure REPO is not modified during
709 705 the operation is up to you) and you have to make sure your editor
710 706 breaks hardlinks (Emacs and most Linux Kernel tools do so). Also,
711 707 this is not compatible with certain extensions that place their
712 708 metadata under the .hg directory, such as mq.
713 709
714 710 Mercurial will update the working directory to the first applicable
715 711 revision from this list:
716 712
717 713 a) null if -U or the source repository has no changesets
718 714 b) if -u . and the source repository is local, the first parent of
719 715 the source repository's working directory
720 716 c) the changeset specified with -u (if a branch name, this means the
721 717 latest head of that branch)
722 718 d) the changeset specified with -r
723 719 e) the tipmost head specified with -b
724 720 f) the tipmost head specified with the url#branch source syntax
725 721 g) the tipmost head of the default branch
726 722 h) tip
727 723
728 724 Returns 0 on success.
729 725 """
730 726 if opts.get('noupdate') and opts.get('updaterev'):
731 727 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
732 728
733 729 r = hg.clone(hg.remoteui(ui, opts), source, dest,
734 730 pull=opts.get('pull'),
735 731 stream=opts.get('uncompressed'),
736 732 rev=opts.get('rev'),
737 733 update=opts.get('updaterev') or not opts.get('noupdate'),
738 734 branch=opts.get('branch'))
739 735
740 736 return r is None
741 737
742 738 def commit(ui, repo, *pats, **opts):
743 739 """commit the specified files or all outstanding changes
744 740
745 741 Commit changes to the given files into the repository. Unlike a
746 742 centralized SCM, this operation is a local operation. See
747 743 :hg:`push` for a way to actively distribute your changes.
748 744
749 745 If a list of files is omitted, all changes reported by :hg:`status`
750 746 will be committed.
751 747
752 748 If you are committing the result of a merge, do not provide any
753 749 filenames or -I/-X filters.
754 750
755 751 If no commit message is specified, Mercurial starts your
756 752 configured editor where you can enter a message. In case your
757 753 commit fails, you will find a backup of your message in
758 754 ``.hg/last-message.txt``.
759 755
760 756 See :hg:`help dates` for a list of formats valid for -d/--date.
761 757
762 758 Returns 0 on success, 1 if nothing changed.
763 759 """
764 760 extra = {}
765 761 if opts.get('close_branch'):
766 762 if repo['.'].node() not in repo.branchheads():
767 763 # The topo heads set is included in the branch heads set of the
768 764 # current branch, so it's sufficient to test branchheads
769 765 raise util.Abort(_('can only close branch heads'))
770 766 extra['close'] = 1
771 767 e = cmdutil.commiteditor
772 768 if opts.get('force_editor'):
773 769 e = cmdutil.commitforceeditor
774 770
775 771 def commitfunc(ui, repo, message, match, opts):
776 772 return repo.commit(message, opts.get('user'), opts.get('date'), match,
777 773 editor=e, extra=extra)
778 774
779 775 branch = repo[None].branch()
780 776 bheads = repo.branchheads(branch)
781 777
782 778 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
783 779 if not node:
784 780 ui.status(_("nothing changed\n"))
785 781 return 1
786 782
787 783 ctx = repo[node]
788 784 parents = ctx.parents()
789 785
790 786 if bheads and not [x for x in parents
791 787 if x.node() in bheads and x.branch() == branch]:
792 788 ui.status(_('created new head\n'))
793 789 # The message is not printed for initial roots. For the other
794 790 # changesets, it is printed in the following situations:
795 791 #
796 792 # Par column: for the 2 parents with ...
797 793 # N: null or no parent
798 794 # B: parent is on another named branch
799 795 # C: parent is a regular non head changeset
800 796 # H: parent was a branch head of the current branch
801 797 # Msg column: whether we print "created new head" message
802 798 # In the following, it is assumed that there already exists some
803 799 # initial branch heads of the current branch, otherwise nothing is
804 800 # printed anyway.
805 801 #
806 802 # Par Msg Comment
807 803 # NN y additional topo root
808 804 #
809 805 # BN y additional branch root
810 806 # CN y additional topo head
811 807 # HN n usual case
812 808 #
813 809 # BB y weird additional branch root
814 810 # CB y branch merge
815 811 # HB n merge with named branch
816 812 #
817 813 # CC y additional head from merge
818 814 # CH n merge with a head
819 815 #
820 816 # HH n head merge: head count decreases
821 817
822 818 if not opts.get('close_branch'):
823 819 for r in parents:
824 820 if r.extra().get('close') and r.branch() == branch:
825 821 ui.status(_('reopening closed branch head %d\n') % r)
826 822
827 823 if ui.debugflag:
828 824 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
829 825 elif ui.verbose:
830 826 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
831 827
832 828 def copy(ui, repo, *pats, **opts):
833 829 """mark files as copied for the next commit
834 830
835 831 Mark dest as having copies of source files. If dest is a
836 832 directory, copies are put in that directory. If dest is a file,
837 833 the source must be a single file.
838 834
839 835 By default, this command copies the contents of files as they
840 836 exist in the working directory. If invoked with -A/--after, the
841 837 operation is recorded, but no copying is performed.
842 838
843 839 This command takes effect with the next commit. To undo a copy
844 840 before that, see :hg:`revert`.
845 841
846 842 Returns 0 on success, 1 if errors are encountered.
847 843 """
848 844 wlock = repo.wlock(False)
849 845 try:
850 846 return cmdutil.copy(ui, repo, pats, opts)
851 847 finally:
852 848 wlock.release()
853 849
854 850 def debugancestor(ui, repo, *args):
855 851 """find the ancestor revision of two revisions in a given index"""
856 852 if len(args) == 3:
857 853 index, rev1, rev2 = args
858 854 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
859 855 lookup = r.lookup
860 856 elif len(args) == 2:
861 857 if not repo:
862 858 raise util.Abort(_("there is no Mercurial repository here "
863 859 "(.hg not found)"))
864 860 rev1, rev2 = args
865 861 r = repo.changelog
866 862 lookup = repo.lookup
867 863 else:
868 864 raise util.Abort(_('either two or three arguments required'))
869 865 a = r.ancestor(lookup(rev1), lookup(rev2))
870 866 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
871 867
872 868 def debugbuilddag(ui, repo, text,
873 869 mergeable_file=False,
874 870 appended_file=False,
875 871 overwritten_file=False,
876 872 new_file=False):
877 873 """builds a repo with a given dag from scratch in the current empty repo
878 874
879 875 Elements:
880 876
881 877 - "+n" is a linear run of n nodes based on the current default parent
882 878 - "." is a single node based on the current default parent
883 879 - "$" resets the default parent to null (implied at the start);
884 880 otherwise the default parent is always the last node created
885 881 - "<p" sets the default parent to the backref p
886 882 - "*p" is a fork at parent p, which is a backref
887 883 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
888 884 - "/p2" is a merge of the preceding node and p2
889 885 - ":tag" defines a local tag for the preceding node
890 886 - "@branch" sets the named branch for subsequent nodes
891 887 - "!command" runs the command using your shell
892 888 - "!!my command\\n" is like "!", but to the end of the line
893 889 - "#...\\n" is a comment up to the end of the line
894 890
895 891 Whitespace between the above elements is ignored.
896 892
897 893 A backref is either
898 894
899 895 - a number n, which references the node curr-n, where curr is the current
900 896 node, or
901 897 - the name of a local tag you placed earlier using ":tag", or
902 898 - empty to denote the default parent.
903 899
904 900 All string valued-elements are either strictly alphanumeric, or must
905 901 be enclosed in double quotes ("..."), with "\\" as escape character.
906 902
907 903 Note that the --overwritten-file and --appended-file options imply the
908 904 use of "HGMERGE=internal:local" during DAG buildup.
909 905 """
910 906
911 907 if not (mergeable_file or appended_file or overwritten_file or new_file):
912 908 raise util.Abort(_('need at least one of -m, -a, -o, -n'))
913 909
914 910 if len(repo.changelog) > 0:
915 911 raise util.Abort(_('repository is not empty'))
916 912
917 913 if overwritten_file or appended_file:
918 914 # we don't want to fail in merges during buildup
919 915 os.environ['HGMERGE'] = 'internal:local'
920 916
921 917 def writefile(fname, text, fmode="wb"):
922 918 f = open(fname, fmode)
923 919 try:
924 920 f.write(text)
925 921 finally:
926 922 f.close()
927 923
928 924 if mergeable_file:
929 925 linesperrev = 2
930 926 # determine number of revs in DAG
931 927 n = 0
932 928 for type, data in dagparser.parsedag(text):
933 929 if type == 'n':
934 930 n += 1
935 931 # make a file with k lines per rev
936 932 writefile("mf", "\n".join(str(i) for i in xrange(0, n * linesperrev))
937 933 + "\n")
938 934
939 935 at = -1
940 936 atbranch = 'default'
941 937 for type, data in dagparser.parsedag(text):
942 938 if type == 'n':
943 939 ui.status('node %s\n' % str(data))
944 940 id, ps = data
945 941 p1 = ps[0]
946 942 if p1 != at:
947 943 update(ui, repo, node=str(p1), clean=True)
948 944 at = p1
949 945 if repo.dirstate.branch() != atbranch:
950 946 branch(ui, repo, atbranch, force=True)
951 947 if len(ps) > 1:
952 948 p2 = ps[1]
953 949 merge(ui, repo, node=p2)
954 950
955 951 if mergeable_file:
956 952 f = open("mf", "rb+")
957 953 try:
958 954 lines = f.read().split("\n")
959 955 lines[id * linesperrev] += " r%i" % id
960 956 f.seek(0)
961 957 f.write("\n".join(lines))
962 958 finally:
963 959 f.close()
964 960
965 961 if appended_file:
966 962 writefile("af", "r%i\n" % id, "ab")
967 963
968 964 if overwritten_file:
969 965 writefile("of", "r%i\n" % id)
970 966
971 967 if new_file:
972 968 writefile("nf%i" % id, "r%i\n" % id)
973 969
974 970 commit(ui, repo, addremove=True, message="r%i" % id, date=(id, 0))
975 971 at = id
976 972 elif type == 'l':
977 973 id, name = data
978 974 ui.status('tag %s\n' % name)
979 975 tag(ui, repo, name, local=True)
980 976 elif type == 'a':
981 977 ui.status('branch %s\n' % data)
982 978 atbranch = data
983 979 elif type in 'cC':
984 980 r = util.system(data, cwd=repo.root)
985 981 if r:
986 982 desc, r = util.explain_exit(r)
987 983 raise util.Abort(_('%s command %s') % (data, desc))
988 984
989 985 def debugcommands(ui, cmd='', *args):
990 986 """list all available commands and options"""
991 987 for cmd, vals in sorted(table.iteritems()):
992 988 cmd = cmd.split('|')[0].strip('^')
993 989 opts = ', '.join([i[1] for i in vals[1]])
994 990 ui.write('%s: %s\n' % (cmd, opts))
995 991
996 992 def debugcomplete(ui, cmd='', **opts):
997 993 """returns the completion list associated with the given command"""
998 994
999 995 if opts.get('options'):
1000 996 options = []
1001 997 otables = [globalopts]
1002 998 if cmd:
1003 999 aliases, entry = cmdutil.findcmd(cmd, table, False)
1004 1000 otables.append(entry[1])
1005 1001 for t in otables:
1006 1002 for o in t:
1007 1003 if "(DEPRECATED)" in o[3]:
1008 1004 continue
1009 1005 if o[0]:
1010 1006 options.append('-%s' % o[0])
1011 1007 options.append('--%s' % o[1])
1012 1008 ui.write("%s\n" % "\n".join(options))
1013 1009 return
1014 1010
1015 1011 cmdlist = cmdutil.findpossible(cmd, table)
1016 1012 if ui.verbose:
1017 1013 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1018 1014 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1019 1015
1020 1016 def debugfsinfo(ui, path = "."):
1021 1017 """show information detected about current filesystem"""
1022 1018 open('.debugfsinfo', 'w').write('')
1023 1019 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
1024 1020 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
1025 1021 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
1026 1022 and 'yes' or 'no'))
1027 1023 os.unlink('.debugfsinfo')
1028 1024
1029 1025 def debugrebuildstate(ui, repo, rev="tip"):
1030 1026 """rebuild the dirstate as it would look like for the given revision"""
1031 1027 ctx = cmdutil.revsingle(repo, rev)
1032 1028 wlock = repo.wlock()
1033 1029 try:
1034 1030 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1035 1031 finally:
1036 1032 wlock.release()
1037 1033
1038 1034 def debugcheckstate(ui, repo):
1039 1035 """validate the correctness of the current dirstate"""
1040 1036 parent1, parent2 = repo.dirstate.parents()
1041 1037 m1 = repo[parent1].manifest()
1042 1038 m2 = repo[parent2].manifest()
1043 1039 errors = 0
1044 1040 for f in repo.dirstate:
1045 1041 state = repo.dirstate[f]
1046 1042 if state in "nr" and f not in m1:
1047 1043 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1048 1044 errors += 1
1049 1045 if state in "a" and f in m1:
1050 1046 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1051 1047 errors += 1
1052 1048 if state in "m" and f not in m1 and f not in m2:
1053 1049 ui.warn(_("%s in state %s, but not in either manifest\n") %
1054 1050 (f, state))
1055 1051 errors += 1
1056 1052 for f in m1:
1057 1053 state = repo.dirstate[f]
1058 1054 if state not in "nrm":
1059 1055 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1060 1056 errors += 1
1061 1057 if errors:
1062 1058 error = _(".hg/dirstate inconsistent with current parent's manifest")
1063 1059 raise util.Abort(error)
1064 1060
1065 1061 def showconfig(ui, repo, *values, **opts):
1066 1062 """show combined config settings from all hgrc files
1067 1063
1068 1064 With no arguments, print names and values of all config items.
1069 1065
1070 1066 With one argument of the form section.name, print just the value
1071 1067 of that config item.
1072 1068
1073 1069 With multiple arguments, print names and values of all config
1074 1070 items with matching section names.
1075 1071
1076 1072 With --debug, the source (filename and line number) is printed
1077 1073 for each config item.
1078 1074
1079 1075 Returns 0 on success.
1080 1076 """
1081 1077
1082 1078 for f in util.rcpath():
1083 1079 ui.debug(_('read config from: %s\n') % f)
1084 1080 untrusted = bool(opts.get('untrusted'))
1085 1081 if values:
1086 1082 sections = [v for v in values if '.' not in v]
1087 1083 items = [v for v in values if '.' in v]
1088 1084 if len(items) > 1 or items and sections:
1089 1085 raise util.Abort(_('only one config item permitted'))
1090 1086 for section, name, value in ui.walkconfig(untrusted=untrusted):
1091 1087 sectname = section + '.' + name
1092 1088 if values:
1093 1089 for v in values:
1094 1090 if v == section:
1095 1091 ui.debug('%s: ' %
1096 1092 ui.configsource(section, name, untrusted))
1097 1093 ui.write('%s=%s\n' % (sectname, value))
1098 1094 elif v == sectname:
1099 1095 ui.debug('%s: ' %
1100 1096 ui.configsource(section, name, untrusted))
1101 1097 ui.write(value, '\n')
1102 1098 else:
1103 1099 ui.debug('%s: ' %
1104 1100 ui.configsource(section, name, untrusted))
1105 1101 ui.write('%s=%s\n' % (sectname, value))
1106 1102
1107 1103 def debugpushkey(ui, repopath, namespace, *keyinfo):
1108 1104 '''access the pushkey key/value protocol
1109 1105
1110 1106 With two args, list the keys in the given namespace.
1111 1107
1112 1108 With five args, set a key to new if it currently is set to old.
1113 1109 Reports success or failure.
1114 1110 '''
1115 1111
1116 1112 target = hg.repository(ui, repopath)
1117 1113 if keyinfo:
1118 1114 key, old, new = keyinfo
1119 1115 r = target.pushkey(namespace, key, old, new)
1120 1116 ui.status(str(r) + '\n')
1121 1117 return not r
1122 1118 else:
1123 1119 for k, v in target.listkeys(namespace).iteritems():
1124 1120 ui.write("%s\t%s\n" % (k.encode('string-escape'),
1125 1121 v.encode('string-escape')))
1126 1122
1127 1123 def debugrevspec(ui, repo, expr):
1128 1124 '''parse and apply a revision specification'''
1129 1125 if ui.verbose:
1130 1126 tree = revset.parse(expr)
1131 1127 ui.note(tree, "\n")
1132 1128 func = revset.match(expr)
1133 1129 for c in func(repo, range(len(repo))):
1134 1130 ui.write("%s\n" % c)
1135 1131
1136 1132 def debugsetparents(ui, repo, rev1, rev2=None):
1137 1133 """manually set the parents of the current working directory
1138 1134
1139 1135 This is useful for writing repository conversion tools, but should
1140 1136 be used with care.
1141 1137
1142 1138 Returns 0 on success.
1143 1139 """
1144 1140
1145 1141 r1 = cmdutil.revsingle(repo, rev1).node()
1146 1142 r2 = cmdutil.revsingle(repo, rev2, 'null').node()
1147 1143
1148 1144 wlock = repo.wlock()
1149 1145 try:
1150 1146 repo.dirstate.setparents(r1, r2)
1151 1147 finally:
1152 1148 wlock.release()
1153 1149
1154 1150 def debugstate(ui, repo, nodates=None):
1155 1151 """show the contents of the current dirstate"""
1156 1152 timestr = ""
1157 1153 showdate = not nodates
1158 1154 for file_, ent in sorted(repo.dirstate._map.iteritems()):
1159 1155 if showdate:
1160 1156 if ent[3] == -1:
1161 1157 # Pad or slice to locale representation
1162 1158 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ",
1163 1159 time.localtime(0)))
1164 1160 timestr = 'unset'
1165 1161 timestr = (timestr[:locale_len] +
1166 1162 ' ' * (locale_len - len(timestr)))
1167 1163 else:
1168 1164 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
1169 1165 time.localtime(ent[3]))
1170 1166 if ent[1] & 020000:
1171 1167 mode = 'lnk'
1172 1168 else:
1173 1169 mode = '%3o' % (ent[1] & 0777)
1174 1170 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
1175 1171 for f in repo.dirstate.copies():
1176 1172 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
1177 1173
1178 1174 def debugsub(ui, repo, rev=None):
1179 1175 ctx = cmdutil.revsingle(repo, rev, None)
1180 1176 for k, v in sorted(ctx.substate.items()):
1181 1177 ui.write('path %s\n' % k)
1182 1178 ui.write(' source %s\n' % v[0])
1183 1179 ui.write(' revision %s\n' % v[1])
1184 1180
1185 1181 def debugdag(ui, repo, file_=None, *revs, **opts):
1186 1182 """format the changelog or an index DAG as a concise textual description
1187 1183
1188 1184 If you pass a revlog index, the revlog's DAG is emitted. If you list
1189 1185 revision numbers, they get labelled in the output as rN.
1190 1186
1191 1187 Otherwise, the changelog DAG of the current repo is emitted.
1192 1188 """
1193 1189 spaces = opts.get('spaces')
1194 1190 dots = opts.get('dots')
1195 1191 if file_:
1196 1192 rlog = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
1197 1193 revs = set((int(r) for r in revs))
1198 1194 def events():
1199 1195 for r in rlog:
1200 1196 yield 'n', (r, list(set(p for p in rlog.parentrevs(r) if p != -1)))
1201 1197 if r in revs:
1202 1198 yield 'l', (r, "r%i" % r)
1203 1199 elif repo:
1204 1200 cl = repo.changelog
1205 1201 tags = opts.get('tags')
1206 1202 branches = opts.get('branches')
1207 1203 if tags:
1208 1204 labels = {}
1209 1205 for l, n in repo.tags().items():
1210 1206 labels.setdefault(cl.rev(n), []).append(l)
1211 1207 def events():
1212 1208 b = "default"
1213 1209 for r in cl:
1214 1210 if branches:
1215 1211 newb = cl.read(cl.node(r))[5]['branch']
1216 1212 if newb != b:
1217 1213 yield 'a', newb
1218 1214 b = newb
1219 1215 yield 'n', (r, list(set(p for p in cl.parentrevs(r) if p != -1)))
1220 1216 if tags:
1221 1217 ls = labels.get(r)
1222 1218 if ls:
1223 1219 for l in ls:
1224 1220 yield 'l', (r, l)
1225 1221 else:
1226 1222 raise util.Abort(_('need repo for changelog dag'))
1227 1223
1228 1224 for line in dagparser.dagtextlines(events(),
1229 1225 addspaces=spaces,
1230 1226 wraplabels=True,
1231 1227 wrapannotations=True,
1232 1228 wrapnonlinear=dots,
1233 1229 usedots=dots,
1234 1230 maxlinewidth=70):
1235 1231 ui.write(line)
1236 1232 ui.write("\n")
1237 1233
1238 1234 def debugdata(ui, repo, file_, rev):
1239 1235 """dump the contents of a data file revision"""
1240 1236 r = None
1241 1237 if repo:
1242 1238 filelog = repo.file(file_)
1243 1239 if len(filelog):
1244 1240 r = filelog
1245 1241 if not r:
1246 1242 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
1247 1243 try:
1248 1244 ui.write(r.revision(r.lookup(rev)))
1249 1245 except KeyError:
1250 1246 raise util.Abort(_('invalid revision identifier %s') % rev)
1251 1247
1252 1248 def debugdate(ui, date, range=None, **opts):
1253 1249 """parse and display a date"""
1254 1250 if opts["extended"]:
1255 1251 d = util.parsedate(date, util.extendeddateformats)
1256 1252 else:
1257 1253 d = util.parsedate(date)
1258 1254 ui.write("internal: %s %s\n" % d)
1259 1255 ui.write("standard: %s\n" % util.datestr(d))
1260 1256 if range:
1261 1257 m = util.matchdate(range)
1262 1258 ui.write("match: %s\n" % m(d[0]))
1263 1259
1264 1260 def debugindex(ui, repo, file_, **opts):
1265 1261 """dump the contents of an index file"""
1266 1262 r = None
1267 1263 if repo:
1268 1264 filelog = repo.file(file_)
1269 1265 if len(filelog):
1270 1266 r = filelog
1271 1267
1272 1268 format = opts.get('format', 0)
1273 1269 if format not in (0, 1):
1274 1270 raise util.Abort("unknown format %d" % format)
1275 1271
1276 1272 if not r:
1277 1273 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
1278 1274
1279 1275 if format == 0:
1280 1276 ui.write(" rev offset length base linkrev"
1281 1277 " nodeid p1 p2\n")
1282 1278 elif format == 1:
1283 1279 ui.write(" rev flag offset length"
1284 1280 " size base link p1 p2 nodeid\n")
1285 1281
1286 1282 for i in r:
1287 1283 node = r.node(i)
1288 1284 if format == 0:
1289 1285 try:
1290 1286 pp = r.parents(node)
1291 1287 except:
1292 1288 pp = [nullid, nullid]
1293 1289 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1294 1290 i, r.start(i), r.length(i), r.base(i), r.linkrev(i),
1295 1291 short(node), short(pp[0]), short(pp[1])))
1296 1292 elif format == 1:
1297 1293 pr = r.parentrevs(i)
1298 1294 ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d % 6d %s\n" % (
1299 1295 i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
1300 1296 r.base(i), r.linkrev(i), pr[0], pr[1], short(node)))
1301 1297
1302 1298 def debugindexdot(ui, repo, file_):
1303 1299 """dump an index DAG as a graphviz dot file"""
1304 1300 r = None
1305 1301 if repo:
1306 1302 filelog = repo.file(file_)
1307 1303 if len(filelog):
1308 1304 r = filelog
1309 1305 if not r:
1310 1306 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
1311 1307 ui.write("digraph G {\n")
1312 1308 for i in r:
1313 1309 node = r.node(i)
1314 1310 pp = r.parents(node)
1315 1311 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1316 1312 if pp[1] != nullid:
1317 1313 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1318 1314 ui.write("}\n")
1319 1315
1320 1316 def debuginstall(ui):
1321 1317 '''test Mercurial installation
1322 1318
1323 1319 Returns 0 on success.
1324 1320 '''
1325 1321
1326 1322 def writetemp(contents):
1327 1323 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
1328 1324 f = os.fdopen(fd, "wb")
1329 1325 f.write(contents)
1330 1326 f.close()
1331 1327 return name
1332 1328
1333 1329 problems = 0
1334 1330
1335 1331 # encoding
1336 1332 ui.status(_("Checking encoding (%s)...\n") % encoding.encoding)
1337 1333 try:
1338 1334 encoding.fromlocal("test")
1339 1335 except util.Abort, inst:
1340 1336 ui.write(" %s\n" % inst)
1341 1337 ui.write(_(" (check that your locale is properly set)\n"))
1342 1338 problems += 1
1343 1339
1344 1340 # compiled modules
1345 1341 ui.status(_("Checking installed modules (%s)...\n")
1346 1342 % os.path.dirname(__file__))
1347 1343 try:
1348 1344 import bdiff, mpatch, base85, osutil
1349 1345 except Exception, inst:
1350 1346 ui.write(" %s\n" % inst)
1351 1347 ui.write(_(" One or more extensions could not be found"))
1352 1348 ui.write(_(" (check that you compiled the extensions)\n"))
1353 1349 problems += 1
1354 1350
1355 1351 # templates
1356 1352 ui.status(_("Checking templates...\n"))
1357 1353 try:
1358 1354 import templater
1359 1355 templater.templater(templater.templatepath("map-cmdline.default"))
1360 1356 except Exception, inst:
1361 1357 ui.write(" %s\n" % inst)
1362 1358 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
1363 1359 problems += 1
1364 1360
1365 1361 # patch
1366 1362 ui.status(_("Checking patch...\n"))
1367 1363 patchproblems = 0
1368 1364 a = "1\n2\n3\n4\n"
1369 1365 b = "1\n2\n3\ninsert\n4\n"
1370 1366 fa = writetemp(a)
1371 1367 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa),
1372 1368 os.path.basename(fa))
1373 1369 fd = writetemp(d)
1374 1370
1375 1371 files = {}
1376 1372 try:
1377 1373 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
1378 1374 except util.Abort, e:
1379 1375 ui.write(_(" patch call failed:\n"))
1380 1376 ui.write(" " + str(e) + "\n")
1381 1377 patchproblems += 1
1382 1378 else:
1383 1379 if list(files) != [os.path.basename(fa)]:
1384 1380 ui.write(_(" unexpected patch output!\n"))
1385 1381 patchproblems += 1
1386 1382 a = open(fa).read()
1387 1383 if a != b:
1388 1384 ui.write(_(" patch test failed!\n"))
1389 1385 patchproblems += 1
1390 1386
1391 1387 if patchproblems:
1392 1388 if ui.config('ui', 'patch'):
1393 1389 ui.write(_(" (Current patch tool may be incompatible with patch,"
1394 1390 " or misconfigured. Please check your configuration"
1395 1391 " file)\n"))
1396 1392 else:
1397 1393 ui.write(_(" Internal patcher failure, please report this error"
1398 1394 " to http://mercurial.selenic.com/wiki/BugTracker\n"))
1399 1395 problems += patchproblems
1400 1396
1401 1397 os.unlink(fa)
1402 1398 os.unlink(fd)
1403 1399
1404 1400 # editor
1405 1401 ui.status(_("Checking commit editor...\n"))
1406 1402 editor = ui.geteditor()
1407 1403 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
1408 1404 if not cmdpath:
1409 1405 if editor == 'vi':
1410 1406 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
1411 1407 ui.write(_(" (specify a commit editor in your configuration"
1412 1408 " file)\n"))
1413 1409 else:
1414 1410 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
1415 1411 ui.write(_(" (specify a commit editor in your configuration"
1416 1412 " file)\n"))
1417 1413 problems += 1
1418 1414
1419 1415 # check username
1420 1416 ui.status(_("Checking username...\n"))
1421 1417 try:
1422 1418 ui.username()
1423 1419 except util.Abort, e:
1424 1420 ui.write(" %s\n" % e)
1425 1421 ui.write(_(" (specify a username in your configuration file)\n"))
1426 1422 problems += 1
1427 1423
1428 1424 if not problems:
1429 1425 ui.status(_("No problems detected\n"))
1430 1426 else:
1431 1427 ui.write(_("%s problems detected,"
1432 1428 " please check your install!\n") % problems)
1433 1429
1434 1430 return problems
1435 1431
1436 1432 def debugrename(ui, repo, file1, *pats, **opts):
1437 1433 """dump rename information"""
1438 1434
1439 1435 ctx = cmdutil.revsingle(repo, opts.get('rev'))
1440 1436 m = cmdutil.match(repo, (file1,) + pats, opts)
1441 1437 for abs in ctx.walk(m):
1442 1438 fctx = ctx[abs]
1443 1439 o = fctx.filelog().renamed(fctx.filenode())
1444 1440 rel = m.rel(abs)
1445 1441 if o:
1446 1442 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1447 1443 else:
1448 1444 ui.write(_("%s not renamed\n") % rel)
1449 1445
1450 1446 def debugwalk(ui, repo, *pats, **opts):
1451 1447 """show how files match on given patterns"""
1452 1448 m = cmdutil.match(repo, pats, opts)
1453 1449 items = list(repo.walk(m))
1454 1450 if not items:
1455 1451 return
1456 1452 fmt = 'f %%-%ds %%-%ds %%s' % (
1457 1453 max([len(abs) for abs in items]),
1458 1454 max([len(m.rel(abs)) for abs in items]))
1459 1455 for abs in items:
1460 1456 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
1461 1457 ui.write("%s\n" % line.rstrip())
1462 1458
1463 1459 def diff(ui, repo, *pats, **opts):
1464 1460 """diff repository (or selected files)
1465 1461
1466 1462 Show differences between revisions for the specified files.
1467 1463
1468 1464 Differences between files are shown using the unified diff format.
1469 1465
1470 1466 .. note::
1471 1467 diff may generate unexpected results for merges, as it will
1472 1468 default to comparing against the working directory's first
1473 1469 parent changeset if no revisions are specified.
1474 1470
1475 1471 When two revision arguments are given, then changes are shown
1476 1472 between those revisions. If only one revision is specified then
1477 1473 that revision is compared to the working directory, and, when no
1478 1474 revisions are specified, the working directory files are compared
1479 1475 to its parent.
1480 1476
1481 1477 Alternatively you can specify -c/--change with a revision to see
1482 1478 the changes in that changeset relative to its first parent.
1483 1479
1484 1480 Without the -a/--text option, diff will avoid generating diffs of
1485 1481 files it detects as binary. With -a, diff will generate a diff
1486 1482 anyway, probably with undesirable results.
1487 1483
1488 1484 Use the -g/--git option to generate diffs in the git extended diff
1489 1485 format. For more information, read :hg:`help diffs`.
1490 1486
1491 1487 Returns 0 on success.
1492 1488 """
1493 1489
1494 1490 revs = opts.get('rev')
1495 1491 change = opts.get('change')
1496 1492 stat = opts.get('stat')
1497 1493 reverse = opts.get('reverse')
1498 1494
1499 1495 if revs and change:
1500 1496 msg = _('cannot specify --rev and --change at the same time')
1501 1497 raise util.Abort(msg)
1502 1498 elif change:
1503 1499 node2 = repo.lookup(change)
1504 1500 node1 = repo[node2].parents()[0].node()
1505 1501 else:
1506 1502 node1, node2 = cmdutil.revpair(repo, revs)
1507 1503
1508 1504 if reverse:
1509 1505 node1, node2 = node2, node1
1510 1506
1511 1507 diffopts = patch.diffopts(ui, opts)
1512 1508 m = cmdutil.match(repo, pats, opts)
1513 1509 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
1514 1510 listsubrepos=opts.get('subrepos'))
1515 1511
1516 1512 def export(ui, repo, *changesets, **opts):
1517 1513 """dump the header and diffs for one or more changesets
1518 1514
1519 1515 Print the changeset header and diffs for one or more revisions.
1520 1516
1521 1517 The information shown in the changeset header is: author, date,
1522 1518 branch name (if non-default), changeset hash, parent(s) and commit
1523 1519 comment.
1524 1520
1525 1521 .. note::
1526 1522 export may generate unexpected diff output for merge
1527 1523 changesets, as it will compare the merge changeset against its
1528 1524 first parent only.
1529 1525
1530 1526 Output may be to a file, in which case the name of the file is
1531 1527 given using a format string. The formatting rules are as follows:
1532 1528
1533 1529 :``%%``: literal "%" character
1534 1530 :``%H``: changeset hash (40 hexadecimal digits)
1535 1531 :``%N``: number of patches being generated
1536 1532 :``%R``: changeset revision number
1537 1533 :``%b``: basename of the exporting repository
1538 1534 :``%h``: short-form changeset hash (12 hexadecimal digits)
1539 1535 :``%n``: zero-padded sequence number, starting at 1
1540 1536 :``%r``: zero-padded changeset revision number
1541 1537
1542 1538 Without the -a/--text option, export will avoid generating diffs
1543 1539 of files it detects as binary. With -a, export will generate a
1544 1540 diff anyway, probably with undesirable results.
1545 1541
1546 1542 Use the -g/--git option to generate diffs in the git extended diff
1547 1543 format. See :hg:`help diffs` for more information.
1548 1544
1549 1545 With the --switch-parent option, the diff will be against the
1550 1546 second parent. It can be useful to review a merge.
1551 1547
1552 1548 Returns 0 on success.
1553 1549 """
1554 1550 changesets += tuple(opts.get('rev', []))
1555 1551 if not changesets:
1556 1552 raise util.Abort(_("export requires at least one changeset"))
1557 1553 revs = cmdutil.revrange(repo, changesets)
1558 1554 if len(revs) > 1:
1559 1555 ui.note(_('exporting patches:\n'))
1560 1556 else:
1561 1557 ui.note(_('exporting patch:\n'))
1562 1558 cmdutil.export(repo, revs, template=opts.get('output'),
1563 1559 switch_parent=opts.get('switch_parent'),
1564 1560 opts=patch.diffopts(ui, opts))
1565 1561
1566 1562 def forget(ui, repo, *pats, **opts):
1567 1563 """forget the specified files on the next commit
1568 1564
1569 1565 Mark the specified files so they will no longer be tracked
1570 1566 after the next commit.
1571 1567
1572 1568 This only removes files from the current branch, not from the
1573 1569 entire project history, and it does not delete them from the
1574 1570 working directory.
1575 1571
1576 1572 To undo a forget before the next commit, see :hg:`add`.
1577 1573
1578 1574 Returns 0 on success.
1579 1575 """
1580 1576
1581 1577 if not pats:
1582 1578 raise util.Abort(_('no files specified'))
1583 1579
1584 1580 m = cmdutil.match(repo, pats, opts)
1585 1581 s = repo.status(match=m, clean=True)
1586 1582 forget = sorted(s[0] + s[1] + s[3] + s[6])
1587 1583 errs = 0
1588 1584
1589 1585 for f in m.files():
1590 1586 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
1591 1587 ui.warn(_('not removing %s: file is already untracked\n')
1592 1588 % m.rel(f))
1593 1589 errs = 1
1594 1590
1595 1591 for f in forget:
1596 1592 if ui.verbose or not m.exact(f):
1597 1593 ui.status(_('removing %s\n') % m.rel(f))
1598 1594
1599 1595 repo[None].remove(forget, unlink=False)
1600 1596 return errs
1601 1597
1602 1598 def grep(ui, repo, pattern, *pats, **opts):
1603 1599 """search for a pattern in specified files and revisions
1604 1600
1605 1601 Search revisions of files for a regular expression.
1606 1602
1607 1603 This command behaves differently than Unix grep. It only accepts
1608 1604 Python/Perl regexps. It searches repository history, not the
1609 1605 working directory. It always prints the revision number in which a
1610 1606 match appears.
1611 1607
1612 1608 By default, grep only prints output for the first revision of a
1613 1609 file in which it finds a match. To get it to print every revision
1614 1610 that contains a change in match status ("-" for a match that
1615 1611 becomes a non-match, or "+" for a non-match that becomes a match),
1616 1612 use the --all flag.
1617 1613
1618 1614 Returns 0 if a match is found, 1 otherwise.
1619 1615 """
1620 1616 reflags = 0
1621 1617 if opts.get('ignore_case'):
1622 1618 reflags |= re.I
1623 1619 try:
1624 1620 regexp = re.compile(pattern, reflags)
1625 1621 except re.error, inst:
1626 1622 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
1627 1623 return 1
1628 1624 sep, eol = ':', '\n'
1629 1625 if opts.get('print0'):
1630 1626 sep = eol = '\0'
1631 1627
1632 1628 getfile = util.lrucachefunc(repo.file)
1633 1629
1634 1630 def matchlines(body):
1635 1631 begin = 0
1636 1632 linenum = 0
1637 1633 while True:
1638 1634 match = regexp.search(body, begin)
1639 1635 if not match:
1640 1636 break
1641 1637 mstart, mend = match.span()
1642 1638 linenum += body.count('\n', begin, mstart) + 1
1643 1639 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1644 1640 begin = body.find('\n', mend) + 1 or len(body)
1645 1641 lend = begin - 1
1646 1642 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1647 1643
1648 1644 class linestate(object):
1649 1645 def __init__(self, line, linenum, colstart, colend):
1650 1646 self.line = line
1651 1647 self.linenum = linenum
1652 1648 self.colstart = colstart
1653 1649 self.colend = colend
1654 1650
1655 1651 def __hash__(self):
1656 1652 return hash((self.linenum, self.line))
1657 1653
1658 1654 def __eq__(self, other):
1659 1655 return self.line == other.line
1660 1656
1661 1657 matches = {}
1662 1658 copies = {}
1663 1659 def grepbody(fn, rev, body):
1664 1660 matches[rev].setdefault(fn, [])
1665 1661 m = matches[rev][fn]
1666 1662 for lnum, cstart, cend, line in matchlines(body):
1667 1663 s = linestate(line, lnum, cstart, cend)
1668 1664 m.append(s)
1669 1665
1670 1666 def difflinestates(a, b):
1671 1667 sm = difflib.SequenceMatcher(None, a, b)
1672 1668 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1673 1669 if tag == 'insert':
1674 1670 for i in xrange(blo, bhi):
1675 1671 yield ('+', b[i])
1676 1672 elif tag == 'delete':
1677 1673 for i in xrange(alo, ahi):
1678 1674 yield ('-', a[i])
1679 1675 elif tag == 'replace':
1680 1676 for i in xrange(alo, ahi):
1681 1677 yield ('-', a[i])
1682 1678 for i in xrange(blo, bhi):
1683 1679 yield ('+', b[i])
1684 1680
1685 1681 def display(fn, ctx, pstates, states):
1686 1682 rev = ctx.rev()
1687 1683 datefunc = ui.quiet and util.shortdate or util.datestr
1688 1684 found = False
1689 1685 filerevmatches = {}
1690 1686 if opts.get('all'):
1691 1687 iter = difflinestates(pstates, states)
1692 1688 else:
1693 1689 iter = [('', l) for l in states]
1694 1690 for change, l in iter:
1695 1691 cols = [fn, str(rev)]
1696 1692 before, match, after = None, None, None
1697 1693 if opts.get('line_number'):
1698 1694 cols.append(str(l.linenum))
1699 1695 if opts.get('all'):
1700 1696 cols.append(change)
1701 1697 if opts.get('user'):
1702 1698 cols.append(ui.shortuser(ctx.user()))
1703 1699 if opts.get('date'):
1704 1700 cols.append(datefunc(ctx.date()))
1705 1701 if opts.get('files_with_matches'):
1706 1702 c = (fn, rev)
1707 1703 if c in filerevmatches:
1708 1704 continue
1709 1705 filerevmatches[c] = 1
1710 1706 else:
1711 1707 before = l.line[:l.colstart]
1712 1708 match = l.line[l.colstart:l.colend]
1713 1709 after = l.line[l.colend:]
1714 1710 ui.write(sep.join(cols))
1715 1711 if before is not None:
1716 1712 ui.write(sep + before)
1717 1713 ui.write(match, label='grep.match')
1718 1714 ui.write(after)
1719 1715 ui.write(eol)
1720 1716 found = True
1721 1717 return found
1722 1718
1723 1719 skip = {}
1724 1720 revfiles = {}
1725 1721 matchfn = cmdutil.match(repo, pats, opts)
1726 1722 found = False
1727 1723 follow = opts.get('follow')
1728 1724
1729 1725 def prep(ctx, fns):
1730 1726 rev = ctx.rev()
1731 1727 pctx = ctx.parents()[0]
1732 1728 parent = pctx.rev()
1733 1729 matches.setdefault(rev, {})
1734 1730 matches.setdefault(parent, {})
1735 1731 files = revfiles.setdefault(rev, [])
1736 1732 for fn in fns:
1737 1733 flog = getfile(fn)
1738 1734 try:
1739 1735 fnode = ctx.filenode(fn)
1740 1736 except error.LookupError:
1741 1737 continue
1742 1738
1743 1739 copied = flog.renamed(fnode)
1744 1740 copy = follow and copied and copied[0]
1745 1741 if copy:
1746 1742 copies.setdefault(rev, {})[fn] = copy
1747 1743 if fn in skip:
1748 1744 if copy:
1749 1745 skip[copy] = True
1750 1746 continue
1751 1747 files.append(fn)
1752 1748
1753 1749 if fn not in matches[rev]:
1754 1750 grepbody(fn, rev, flog.read(fnode))
1755 1751
1756 1752 pfn = copy or fn
1757 1753 if pfn not in matches[parent]:
1758 1754 try:
1759 1755 fnode = pctx.filenode(pfn)
1760 1756 grepbody(pfn, parent, flog.read(fnode))
1761 1757 except error.LookupError:
1762 1758 pass
1763 1759
1764 1760 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
1765 1761 rev = ctx.rev()
1766 1762 parent = ctx.parents()[0].rev()
1767 1763 for fn in sorted(revfiles.get(rev, [])):
1768 1764 states = matches[rev][fn]
1769 1765 copy = copies.get(rev, {}).get(fn)
1770 1766 if fn in skip:
1771 1767 if copy:
1772 1768 skip[copy] = True
1773 1769 continue
1774 1770 pstates = matches.get(parent, {}).get(copy or fn, [])
1775 1771 if pstates or states:
1776 1772 r = display(fn, ctx, pstates, states)
1777 1773 found = found or r
1778 1774 if r and not opts.get('all'):
1779 1775 skip[fn] = True
1780 1776 if copy:
1781 1777 skip[copy] = True
1782 1778 del matches[rev]
1783 1779 del revfiles[rev]
1784 1780
1785 1781 return not found
1786 1782
1787 1783 def heads(ui, repo, *branchrevs, **opts):
1788 1784 """show current repository heads or show branch heads
1789 1785
1790 1786 With no arguments, show all repository branch heads.
1791 1787
1792 1788 Repository "heads" are changesets with no child changesets. They are
1793 1789 where development generally takes place and are the usual targets
1794 1790 for update and merge operations. Branch heads are changesets that have
1795 1791 no child changeset on the same branch.
1796 1792
1797 1793 If one or more REVs are given, only branch heads on the branches
1798 1794 associated with the specified changesets are shown.
1799 1795
1800 1796 If -c/--closed is specified, also show branch heads marked closed
1801 1797 (see :hg:`commit --close-branch`).
1802 1798
1803 1799 If STARTREV is specified, only those heads that are descendants of
1804 1800 STARTREV will be displayed.
1805 1801
1806 1802 If -t/--topo is specified, named branch mechanics will be ignored and only
1807 1803 changesets without children will be shown.
1808 1804
1809 1805 Returns 0 if matching heads are found, 1 if not.
1810 1806 """
1811 1807
1812 1808 start = None
1813 1809 if 'rev' in opts:
1814 1810 start = cmdutil.revsingle(repo, opts['rev'], None).node()
1815 1811
1816 1812 if opts.get('topo'):
1817 1813 heads = [repo[h] for h in repo.heads(start)]
1818 1814 else:
1819 1815 heads = []
1820 1816 for b, ls in repo.branchmap().iteritems():
1821 1817 if start is None:
1822 1818 heads += [repo[h] for h in ls]
1823 1819 continue
1824 1820 startrev = repo.changelog.rev(start)
1825 1821 descendants = set(repo.changelog.descendants(startrev))
1826 1822 descendants.add(startrev)
1827 1823 rev = repo.changelog.rev
1828 1824 heads += [repo[h] for h in ls if rev(h) in descendants]
1829 1825
1830 1826 if branchrevs:
1831 1827 branches = set(repo[br].branch() for br in branchrevs)
1832 1828 heads = [h for h in heads if h.branch() in branches]
1833 1829
1834 1830 if not opts.get('closed'):
1835 1831 heads = [h for h in heads if not h.extra().get('close')]
1836 1832
1837 1833 if opts.get('active') and branchrevs:
1838 1834 dagheads = repo.heads(start)
1839 1835 heads = [h for h in heads if h.node() in dagheads]
1840 1836
1841 1837 if branchrevs:
1842 1838 haveheads = set(h.branch() for h in heads)
1843 1839 if branches - haveheads:
1844 1840 headless = ', '.join(b for b in branches - haveheads)
1845 1841 msg = _('no open branch heads found on branches %s')
1846 1842 if opts.get('rev'):
1847 1843 msg += _(' (started at %s)' % opts['rev'])
1848 1844 ui.warn((msg + '\n') % headless)
1849 1845
1850 1846 if not heads:
1851 1847 return 1
1852 1848
1853 1849 heads = sorted(heads, key=lambda x: -x.rev())
1854 1850 displayer = cmdutil.show_changeset(ui, repo, opts)
1855 1851 for ctx in heads:
1856 1852 displayer.show(ctx)
1857 1853 displayer.close()
1858 1854
1859 1855 def help_(ui, name=None, with_version=False, unknowncmd=False):
1860 1856 """show help for a given topic or a help overview
1861 1857
1862 1858 With no arguments, print a list of commands with short help messages.
1863 1859
1864 1860 Given a topic, extension, or command name, print help for that
1865 1861 topic.
1866 1862
1867 1863 Returns 0 if successful.
1868 1864 """
1869 1865 option_lists = []
1870 1866 textwidth = ui.termwidth() - 2
1871 1867
1872 1868 def addglobalopts(aliases):
1873 1869 if ui.verbose:
1874 1870 option_lists.append((_("global options:"), globalopts))
1875 1871 if name == 'shortlist':
1876 1872 option_lists.append((_('use "hg help" for the full list '
1877 1873 'of commands'), ()))
1878 1874 else:
1879 1875 if name == 'shortlist':
1880 1876 msg = _('use "hg help" for the full list of commands '
1881 1877 'or "hg -v" for details')
1882 1878 elif aliases:
1883 1879 msg = _('use "hg -v help%s" to show builtin aliases and '
1884 1880 'global options') % (name and " " + name or "")
1885 1881 else:
1886 1882 msg = _('use "hg -v help %s" to show global options') % name
1887 1883 option_lists.append((msg, ()))
1888 1884
1889 1885 def helpcmd(name):
1890 1886 if with_version:
1891 1887 version_(ui)
1892 1888 ui.write('\n')
1893 1889
1894 1890 try:
1895 1891 aliases, entry = cmdutil.findcmd(name, table, strict=unknowncmd)
1896 1892 except error.AmbiguousCommand, inst:
1897 1893 # py3k fix: except vars can't be used outside the scope of the
1898 1894 # except block, nor can be used inside a lambda. python issue4617
1899 1895 prefix = inst.args[0]
1900 1896 select = lambda c: c.lstrip('^').startswith(prefix)
1901 1897 helplist(_('list of commands:\n\n'), select)
1902 1898 return
1903 1899
1904 1900 # check if it's an invalid alias and display its error if it is
1905 1901 if getattr(entry[0], 'badalias', False):
1906 1902 if not unknowncmd:
1907 1903 entry[0](ui)
1908 1904 return
1909 1905
1910 1906 # synopsis
1911 1907 if len(entry) > 2:
1912 1908 if entry[2].startswith('hg'):
1913 1909 ui.write("%s\n" % entry[2])
1914 1910 else:
1915 1911 ui.write('hg %s %s\n' % (aliases[0], entry[2]))
1916 1912 else:
1917 1913 ui.write('hg %s\n' % aliases[0])
1918 1914
1919 1915 # aliases
1920 1916 if not ui.quiet and len(aliases) > 1:
1921 1917 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1922 1918
1923 1919 # description
1924 1920 doc = gettext(entry[0].__doc__)
1925 1921 if not doc:
1926 1922 doc = _("(no help text available)")
1927 1923 if hasattr(entry[0], 'definition'): # aliased command
1928 1924 if entry[0].definition.startswith('!'): # shell alias
1929 1925 doc = _('shell alias for::\n\n %s') % entry[0].definition[1:]
1930 1926 else:
1931 1927 doc = _('alias for: hg %s\n\n%s') % (entry[0].definition, doc)
1932 1928 if ui.quiet:
1933 1929 doc = doc.splitlines()[0]
1934 1930 keep = ui.verbose and ['verbose'] or []
1935 1931 formatted, pruned = minirst.format(doc, textwidth, keep=keep)
1936 1932 ui.write("\n%s\n" % formatted)
1937 1933 if pruned:
1938 1934 ui.write(_('\nuse "hg -v help %s" to show verbose help\n') % name)
1939 1935
1940 1936 if not ui.quiet:
1941 1937 # options
1942 1938 if entry[1]:
1943 1939 option_lists.append((_("options:\n"), entry[1]))
1944 1940
1945 1941 addglobalopts(False)
1946 1942
1947 1943 def helplist(header, select=None):
1948 1944 h = {}
1949 1945 cmds = {}
1950 1946 for c, e in table.iteritems():
1951 1947 f = c.split("|", 1)[0]
1952 1948 if select and not select(f):
1953 1949 continue
1954 1950 if (not select and name != 'shortlist' and
1955 1951 e[0].__module__ != __name__):
1956 1952 continue
1957 1953 if name == "shortlist" and not f.startswith("^"):
1958 1954 continue
1959 1955 f = f.lstrip("^")
1960 1956 if not ui.debugflag and f.startswith("debug"):
1961 1957 continue
1962 1958 doc = e[0].__doc__
1963 1959 if doc and 'DEPRECATED' in doc and not ui.verbose:
1964 1960 continue
1965 1961 doc = gettext(doc)
1966 1962 if not doc:
1967 1963 doc = _("(no help text available)")
1968 1964 h[f] = doc.splitlines()[0].rstrip()
1969 1965 cmds[f] = c.lstrip("^")
1970 1966
1971 1967 if not h:
1972 1968 ui.status(_('no commands defined\n'))
1973 1969 return
1974 1970
1975 1971 ui.status(header)
1976 1972 fns = sorted(h)
1977 1973 m = max(map(len, fns))
1978 1974 for f in fns:
1979 1975 if ui.verbose:
1980 1976 commands = cmds[f].replace("|",", ")
1981 1977 ui.write(" %s:\n %s\n"%(commands, h[f]))
1982 1978 else:
1983 1979 ui.write('%s\n' % (util.wrap(h[f], textwidth,
1984 1980 initindent=' %-*s ' % (m, f),
1985 1981 hangindent=' ' * (m + 4))))
1986 1982
1987 1983 if not ui.quiet:
1988 1984 addglobalopts(True)
1989 1985
1990 1986 def helptopic(name):
1991 1987 for names, header, doc in help.helptable:
1992 1988 if name in names:
1993 1989 break
1994 1990 else:
1995 1991 raise error.UnknownCommand(name)
1996 1992
1997 1993 # description
1998 1994 if not doc:
1999 1995 doc = _("(no help text available)")
2000 1996 if hasattr(doc, '__call__'):
2001 1997 doc = doc()
2002 1998
2003 1999 ui.write("%s\n\n" % header)
2004 2000 ui.write("%s\n" % minirst.format(doc, textwidth, indent=4))
2005 2001
2006 2002 def helpext(name):
2007 2003 try:
2008 2004 mod = extensions.find(name)
2009 2005 doc = gettext(mod.__doc__) or _('no help text available')
2010 2006 except KeyError:
2011 2007 mod = None
2012 2008 doc = extensions.disabledext(name)
2013 2009 if not doc:
2014 2010 raise error.UnknownCommand(name)
2015 2011
2016 2012 if '\n' not in doc:
2017 2013 head, tail = doc, ""
2018 2014 else:
2019 2015 head, tail = doc.split('\n', 1)
2020 2016 ui.write(_('%s extension - %s\n\n') % (name.split('.')[-1], head))
2021 2017 if tail:
2022 2018 ui.write(minirst.format(tail, textwidth))
2023 2019 ui.status('\n\n')
2024 2020
2025 2021 if mod:
2026 2022 try:
2027 2023 ct = mod.cmdtable
2028 2024 except AttributeError:
2029 2025 ct = {}
2030 2026 modcmds = set([c.split('|', 1)[0] for c in ct])
2031 2027 helplist(_('list of commands:\n\n'), modcmds.__contains__)
2032 2028 else:
2033 2029 ui.write(_('use "hg help extensions" for information on enabling '
2034 2030 'extensions\n'))
2035 2031
2036 2032 def helpextcmd(name):
2037 2033 cmd, ext, mod = extensions.disabledcmd(ui, name, ui.config('ui', 'strict'))
2038 2034 doc = gettext(mod.__doc__).splitlines()[0]
2039 2035
2040 2036 msg = help.listexts(_("'%s' is provided by the following "
2041 2037 "extension:") % cmd, {ext: doc}, len(ext),
2042 2038 indent=4)
2043 2039 ui.write(minirst.format(msg, textwidth))
2044 2040 ui.write('\n\n')
2045 2041 ui.write(_('use "hg help extensions" for information on enabling '
2046 2042 'extensions\n'))
2047 2043
2048 2044 help.addtopichook('revsets', revset.makedoc)
2049 2045
2050 2046 if name and name != 'shortlist':
2051 2047 i = None
2052 2048 if unknowncmd:
2053 2049 queries = (helpextcmd,)
2054 2050 else:
2055 2051 queries = (helptopic, helpcmd, helpext, helpextcmd)
2056 2052 for f in queries:
2057 2053 try:
2058 2054 f(name)
2059 2055 i = None
2060 2056 break
2061 2057 except error.UnknownCommand, inst:
2062 2058 i = inst
2063 2059 if i:
2064 2060 raise i
2065 2061
2066 2062 else:
2067 2063 # program name
2068 2064 if ui.verbose or with_version:
2069 2065 version_(ui)
2070 2066 else:
2071 2067 ui.status(_("Mercurial Distributed SCM\n"))
2072 2068 ui.status('\n')
2073 2069
2074 2070 # list of commands
2075 2071 if name == "shortlist":
2076 2072 header = _('basic commands:\n\n')
2077 2073 else:
2078 2074 header = _('list of commands:\n\n')
2079 2075
2080 2076 helplist(header)
2081 2077 if name != 'shortlist':
2082 2078 exts, maxlength = extensions.enabled()
2083 2079 text = help.listexts(_('enabled extensions:'), exts, maxlength)
2084 2080 if text:
2085 2081 ui.write("\n%s\n" % minirst.format(text, textwidth))
2086 2082
2087 2083 # list all option lists
2088 2084 opt_output = []
2089 2085 multioccur = False
2090 2086 for title, options in option_lists:
2091 2087 opt_output.append(("\n%s" % title, None))
2092 2088 for option in options:
2093 2089 if len(option) == 5:
2094 2090 shortopt, longopt, default, desc, optlabel = option
2095 2091 else:
2096 2092 shortopt, longopt, default, desc = option
2097 2093 optlabel = _("VALUE") # default label
2098 2094
2099 2095 if _("DEPRECATED") in desc and not ui.verbose:
2100 2096 continue
2101 2097 if isinstance(default, list):
2102 2098 numqualifier = " %s [+]" % optlabel
2103 2099 multioccur = True
2104 2100 elif (default is not None) and not isinstance(default, bool):
2105 2101 numqualifier = " %s" % optlabel
2106 2102 else:
2107 2103 numqualifier = ""
2108 2104 opt_output.append(("%2s%s" %
2109 2105 (shortopt and "-%s" % shortopt,
2110 2106 longopt and " --%s%s" %
2111 2107 (longopt, numqualifier)),
2112 2108 "%s%s" % (desc,
2113 2109 default
2114 2110 and _(" (default: %s)") % default
2115 2111 or "")))
2116 2112 if multioccur:
2117 2113 msg = _("\n[+] marked option can be specified multiple times")
2118 2114 if ui.verbose and name != 'shortlist':
2119 2115 opt_output.append((msg, None))
2120 2116 else:
2121 2117 opt_output.insert(-1, (msg, None))
2122 2118
2123 2119 if not name:
2124 2120 ui.write(_("\nadditional help topics:\n\n"))
2125 2121 topics = []
2126 2122 for names, header, doc in help.helptable:
2127 2123 topics.append((sorted(names, key=len, reverse=True)[0], header))
2128 2124 topics_len = max([len(s[0]) for s in topics])
2129 2125 for t, desc in topics:
2130 2126 ui.write(" %-*s %s\n" % (topics_len, t, desc))
2131 2127
2132 2128 if opt_output:
2133 2129 colwidth = encoding.colwidth
2134 2130 # normalize: (opt or message, desc or None, width of opt)
2135 2131 entries = [desc and (opt, desc, colwidth(opt)) or (opt, None, 0)
2136 2132 for opt, desc in opt_output]
2137 2133 hanging = max([e[2] for e in entries])
2138 2134 for opt, desc, width in entries:
2139 2135 if desc:
2140 2136 initindent = ' %s%s ' % (opt, ' ' * (hanging - width))
2141 2137 hangindent = ' ' * (hanging + 3)
2142 2138 ui.write('%s\n' % (util.wrap(desc, textwidth,
2143 2139 initindent=initindent,
2144 2140 hangindent=hangindent)))
2145 2141 else:
2146 2142 ui.write("%s\n" % opt)
2147 2143
2148 2144 def identify(ui, repo, source=None,
2149 2145 rev=None, num=None, id=None, branch=None, tags=None):
2150 2146 """identify the working copy or specified revision
2151 2147
2152 2148 With no revision, print a summary of the current state of the
2153 2149 repository.
2154 2150
2155 2151 Specifying a path to a repository root or Mercurial bundle will
2156 2152 cause lookup to operate on that repository/bundle.
2157 2153
2158 2154 This summary identifies the repository state using one or two
2159 2155 parent hash identifiers, followed by a "+" if there are
2160 2156 uncommitted changes in the working directory, a list of tags for
2161 2157 this revision and a branch name for non-default branches.
2162 2158
2163 2159 Returns 0 if successful.
2164 2160 """
2165 2161
2166 2162 if not repo and not source:
2167 2163 raise util.Abort(_("there is no Mercurial repository here "
2168 2164 "(.hg not found)"))
2169 2165
2170 2166 hexfunc = ui.debugflag and hex or short
2171 2167 default = not (num or id or branch or tags)
2172 2168 output = []
2173 2169
2174 2170 revs = []
2175 2171 if source:
2176 2172 source, branches = hg.parseurl(ui.expandpath(source))
2177 2173 repo = hg.repository(ui, source)
2178 2174 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
2179 2175
2180 2176 if not repo.local():
2181 2177 if not rev and revs:
2182 2178 rev = revs[0]
2183 2179 if not rev:
2184 2180 rev = "tip"
2185 2181 if num or branch or tags:
2186 2182 raise util.Abort(
2187 2183 "can't query remote revision number, branch, or tags")
2188 2184 output = [hexfunc(repo.lookup(rev))]
2189 2185 elif not rev:
2190 2186 ctx = repo[None]
2191 2187 parents = ctx.parents()
2192 2188 changed = False
2193 2189 if default or id or num:
2194 2190 changed = util.any(repo.status())
2195 2191 if default or id:
2196 2192 output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]),
2197 2193 (changed) and "+" or "")]
2198 2194 if num:
2199 2195 output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
2200 2196 (changed) and "+" or ""))
2201 2197 else:
2202 2198 ctx = cmdutil.revsingle(repo, rev)
2203 2199 if default or id:
2204 2200 output = [hexfunc(ctx.node())]
2205 2201 if num:
2206 2202 output.append(str(ctx.rev()))
2207 2203
2208 2204 if repo.local() and default and not ui.quiet:
2209 2205 b = ctx.branch()
2210 2206 if b != 'default':
2211 2207 output.append("(%s)" % b)
2212 2208
2213 2209 # multiple tags for a single parent separated by '/'
2214 2210 t = "/".join(ctx.tags())
2215 2211 if t:
2216 2212 output.append(t)
2217 2213
2218 2214 if branch:
2219 2215 output.append(ctx.branch())
2220 2216
2221 2217 if tags:
2222 2218 output.extend(ctx.tags())
2223 2219
2224 2220 ui.write("%s\n" % ' '.join(output))
2225 2221
2226 2222 def import_(ui, repo, patch1, *patches, **opts):
2227 2223 """import an ordered set of patches
2228 2224
2229 2225 Import a list of patches and commit them individually (unless
2230 2226 --no-commit is specified).
2231 2227
2232 2228 If there are outstanding changes in the working directory, import
2233 2229 will abort unless given the -f/--force flag.
2234 2230
2235 2231 You can import a patch straight from a mail message. Even patches
2236 2232 as attachments work (to use the body part, it must have type
2237 2233 text/plain or text/x-patch). From and Subject headers of email
2238 2234 message are used as default committer and commit message. All
2239 2235 text/plain body parts before first diff are added to commit
2240 2236 message.
2241 2237
2242 2238 If the imported patch was generated by :hg:`export`, user and
2243 2239 description from patch override values from message headers and
2244 2240 body. Values given on command line with -m/--message and -u/--user
2245 2241 override these.
2246 2242
2247 2243 If --exact is specified, import will set the working directory to
2248 2244 the parent of each patch before applying it, and will abort if the
2249 2245 resulting changeset has a different ID than the one recorded in
2250 2246 the patch. This may happen due to character set problems or other
2251 2247 deficiencies in the text patch format.
2252 2248
2253 2249 With -s/--similarity, hg will attempt to discover renames and
2254 2250 copies in the patch in the same way as 'addremove'.
2255 2251
2256 2252 To read a patch from standard input, use "-" as the patch name. If
2257 2253 a URL is specified, the patch will be downloaded from it.
2258 2254 See :hg:`help dates` for a list of formats valid for -d/--date.
2259 2255
2260 2256 Returns 0 on success.
2261 2257 """
2262 2258 patches = (patch1,) + patches
2263 2259
2264 2260 date = opts.get('date')
2265 2261 if date:
2266 2262 opts['date'] = util.parsedate(date)
2267 2263
2268 2264 try:
2269 2265 sim = float(opts.get('similarity') or 0)
2270 2266 except ValueError:
2271 2267 raise util.Abort(_('similarity must be a number'))
2272 2268 if sim < 0 or sim > 100:
2273 2269 raise util.Abort(_('similarity must be between 0 and 100'))
2274 2270
2275 2271 if opts.get('exact') or not opts.get('force'):
2276 2272 cmdutil.bail_if_changed(repo)
2277 2273
2278 2274 d = opts["base"]
2279 2275 strip = opts["strip"]
2280 2276 wlock = lock = None
2281 2277 msgs = []
2282 2278
2283 2279 def tryone(ui, hunk):
2284 2280 tmpname, message, user, date, branch, nodeid, p1, p2 = \
2285 2281 patch.extract(ui, hunk)
2286 2282
2287 2283 if not tmpname:
2288 2284 return None
2289 2285 commitid = _('to working directory')
2290 2286
2291 2287 try:
2292 2288 cmdline_message = cmdutil.logmessage(opts)
2293 2289 if cmdline_message:
2294 2290 # pickup the cmdline msg
2295 2291 message = cmdline_message
2296 2292 elif message:
2297 2293 # pickup the patch msg
2298 2294 message = message.strip()
2299 2295 else:
2300 2296 # launch the editor
2301 2297 message = None
2302 2298 ui.debug('message:\n%s\n' % message)
2303 2299
2304 2300 wp = repo.parents()
2305 2301 if opts.get('exact'):
2306 2302 if not nodeid or not p1:
2307 2303 raise util.Abort(_('not a Mercurial patch'))
2308 2304 p1 = repo.lookup(p1)
2309 2305 p2 = repo.lookup(p2 or hex(nullid))
2310 2306
2311 2307 if p1 != wp[0].node():
2312 2308 hg.clean(repo, p1)
2313 2309 repo.dirstate.setparents(p1, p2)
2314 2310 elif p2:
2315 2311 try:
2316 2312 p1 = repo.lookup(p1)
2317 2313 p2 = repo.lookup(p2)
2318 2314 if p1 == wp[0].node():
2319 2315 repo.dirstate.setparents(p1, p2)
2320 2316 except error.RepoError:
2321 2317 pass
2322 2318 if opts.get('exact') or opts.get('import_branch'):
2323 2319 repo.dirstate.setbranch(branch or 'default')
2324 2320
2325 2321 files = {}
2326 2322 try:
2327 2323 patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
2328 2324 files=files, eolmode=None)
2329 2325 finally:
2330 2326 files = cmdutil.updatedir(ui, repo, files,
2331 2327 similarity=sim / 100.0)
2332 2328 if opts.get('no_commit'):
2333 2329 if message:
2334 2330 msgs.append(message)
2335 2331 else:
2336 2332 if opts.get('exact'):
2337 2333 m = None
2338 2334 else:
2339 2335 m = cmdutil.matchfiles(repo, files or [])
2340 2336 n = repo.commit(message, opts.get('user') or user,
2341 2337 opts.get('date') or date, match=m,
2342 2338 editor=cmdutil.commiteditor)
2343 2339 if opts.get('exact'):
2344 2340 if hex(n) != nodeid:
2345 2341 repo.rollback()
2346 2342 raise util.Abort(_('patch is damaged'
2347 2343 ' or loses information'))
2348 2344 # Force a dirstate write so that the next transaction
2349 2345 # backups an up-do-date file.
2350 2346 repo.dirstate.write()
2351 2347 if n:
2352 2348 commitid = short(n)
2353 2349
2354 2350 return commitid
2355 2351 finally:
2356 2352 os.unlink(tmpname)
2357 2353
2358 2354 try:
2359 2355 wlock = repo.wlock()
2360 2356 lock = repo.lock()
2361 2357 lastcommit = None
2362 2358 for p in patches:
2363 2359 pf = os.path.join(d, p)
2364 2360
2365 2361 if pf == '-':
2366 2362 ui.status(_("applying patch from stdin\n"))
2367 2363 pf = sys.stdin
2368 2364 else:
2369 2365 ui.status(_("applying %s\n") % p)
2370 2366 pf = url.open(ui, pf)
2371 2367
2372 2368 haspatch = False
2373 2369 for hunk in patch.split(pf):
2374 2370 commitid = tryone(ui, hunk)
2375 2371 if commitid:
2376 2372 haspatch = True
2377 2373 if lastcommit:
2378 2374 ui.status(_('applied %s\n') % lastcommit)
2379 2375 lastcommit = commitid
2380 2376
2381 2377 if not haspatch:
2382 2378 raise util.Abort(_('no diffs found'))
2383 2379
2384 2380 if msgs:
2385 2381 repo.opener('last-message.txt', 'wb').write('\n* * *\n'.join(msgs))
2386 2382 finally:
2387 2383 release(lock, wlock)
2388 2384
2389 2385 def incoming(ui, repo, source="default", **opts):
2390 2386 """show new changesets found in source
2391 2387
2392 2388 Show new changesets found in the specified path/URL or the default
2393 2389 pull location. These are the changesets that would have been pulled
2394 2390 if a pull at the time you issued this command.
2395 2391
2396 2392 For remote repository, using --bundle avoids downloading the
2397 2393 changesets twice if the incoming is followed by a pull.
2398 2394
2399 2395 See pull for valid source format details.
2400 2396
2401 2397 Returns 0 if there are incoming changes, 1 otherwise.
2402 2398 """
2403 2399 if opts.get('bundle') and opts.get('subrepos'):
2404 2400 raise util.Abort(_('cannot combine --bundle and --subrepos'))
2405 2401
2406 2402 ret = hg.incoming(ui, repo, source, opts)
2407 2403 return ret
2408 2404
2409 2405 def init(ui, dest=".", **opts):
2410 2406 """create a new repository in the given directory
2411 2407
2412 2408 Initialize a new repository in the given directory. If the given
2413 2409 directory does not exist, it will be created.
2414 2410
2415 2411 If no directory is given, the current directory is used.
2416 2412
2417 2413 It is possible to specify an ``ssh://`` URL as the destination.
2418 2414 See :hg:`help urls` for more information.
2419 2415
2420 2416 Returns 0 on success.
2421 2417 """
2422 2418 hg.repository(hg.remoteui(ui, opts), ui.expandpath(dest), create=1)
2423 2419
2424 2420 def locate(ui, repo, *pats, **opts):
2425 2421 """locate files matching specific patterns
2426 2422
2427 2423 Print files under Mercurial control in the working directory whose
2428 2424 names match the given patterns.
2429 2425
2430 2426 By default, this command searches all directories in the working
2431 2427 directory. To search just the current directory and its
2432 2428 subdirectories, use "--include .".
2433 2429
2434 2430 If no patterns are given to match, this command prints the names
2435 2431 of all files under Mercurial control in the working directory.
2436 2432
2437 2433 If you want to feed the output of this command into the "xargs"
2438 2434 command, use the -0 option to both this command and "xargs". This
2439 2435 will avoid the problem of "xargs" treating single filenames that
2440 2436 contain whitespace as multiple filenames.
2441 2437
2442 2438 Returns 0 if a match is found, 1 otherwise.
2443 2439 """
2444 2440 end = opts.get('print0') and '\0' or '\n'
2445 2441 rev = cmdutil.revsingle(repo, opts.get('rev'), None).node()
2446 2442
2447 2443 ret = 1
2448 2444 m = cmdutil.match(repo, pats, opts, default='relglob')
2449 2445 m.bad = lambda x, y: False
2450 2446 for abs in repo[rev].walk(m):
2451 2447 if not rev and abs not in repo.dirstate:
2452 2448 continue
2453 2449 if opts.get('fullpath'):
2454 2450 ui.write(repo.wjoin(abs), end)
2455 2451 else:
2456 2452 ui.write(((pats and m.rel(abs)) or abs), end)
2457 2453 ret = 0
2458 2454
2459 2455 return ret
2460 2456
2461 2457 def log(ui, repo, *pats, **opts):
2462 2458 """show revision history of entire repository or files
2463 2459
2464 2460 Print the revision history of the specified files or the entire
2465 2461 project.
2466 2462
2467 2463 File history is shown without following rename or copy history of
2468 2464 files. Use -f/--follow with a filename to follow history across
2469 2465 renames and copies. --follow without a filename will only show
2470 2466 ancestors or descendants of the starting revision. --follow-first
2471 2467 only follows the first parent of merge revisions.
2472 2468
2473 2469 If no revision range is specified, the default is ``tip:0`` unless
2474 2470 --follow is set, in which case the working directory parent is
2475 2471 used as the starting revision. You can specify a revision set for
2476 2472 log, see :hg:`help revsets` for more information.
2477 2473
2478 2474 See :hg:`help dates` for a list of formats valid for -d/--date.
2479 2475
2480 2476 By default this command prints revision number and changeset id,
2481 2477 tags, non-trivial parents, user, date and time, and a summary for
2482 2478 each commit. When the -v/--verbose switch is used, the list of
2483 2479 changed files and full commit message are shown.
2484 2480
2485 2481 .. note::
2486 2482 log -p/--patch may generate unexpected diff output for merge
2487 2483 changesets, as it will only compare the merge changeset against
2488 2484 its first parent. Also, only files different from BOTH parents
2489 2485 will appear in files:.
2490 2486
2491 2487 Returns 0 on success.
2492 2488 """
2493 2489
2494 2490 matchfn = cmdutil.match(repo, pats, opts)
2495 2491 limit = cmdutil.loglimit(opts)
2496 2492 count = 0
2497 2493
2498 2494 endrev = None
2499 2495 if opts.get('copies') and opts.get('rev'):
2500 2496 endrev = max(cmdutil.revrange(repo, opts.get('rev'))) + 1
2501 2497
2502 2498 df = False
2503 2499 if opts["date"]:
2504 2500 df = util.matchdate(opts["date"])
2505 2501
2506 2502 branches = opts.get('branch', []) + opts.get('only_branch', [])
2507 2503 opts['branch'] = [repo.lookupbranch(b) for b in branches]
2508 2504
2509 2505 displayer = cmdutil.show_changeset(ui, repo, opts, True)
2510 2506 def prep(ctx, fns):
2511 2507 rev = ctx.rev()
2512 2508 parents = [p for p in repo.changelog.parentrevs(rev)
2513 2509 if p != nullrev]
2514 2510 if opts.get('no_merges') and len(parents) == 2:
2515 2511 return
2516 2512 if opts.get('only_merges') and len(parents) != 2:
2517 2513 return
2518 2514 if opts.get('branch') and ctx.branch() not in opts['branch']:
2519 2515 return
2520 2516 if df and not df(ctx.date()[0]):
2521 2517 return
2522 2518 if opts['user'] and not [k for k in opts['user']
2523 2519 if k.lower() in ctx.user().lower()]:
2524 2520 return
2525 2521 if opts.get('keyword'):
2526 2522 for k in [kw.lower() for kw in opts['keyword']]:
2527 2523 if (k in ctx.user().lower() or
2528 2524 k in ctx.description().lower() or
2529 2525 k in " ".join(ctx.files()).lower()):
2530 2526 break
2531 2527 else:
2532 2528 return
2533 2529
2534 2530 copies = None
2535 2531 if opts.get('copies') and rev:
2536 2532 copies = []
2537 2533 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
2538 2534 for fn in ctx.files():
2539 2535 rename = getrenamed(fn, rev)
2540 2536 if rename:
2541 2537 copies.append((fn, rename[0]))
2542 2538
2543 2539 revmatchfn = None
2544 2540 if opts.get('patch') or opts.get('stat'):
2545 2541 if opts.get('follow') or opts.get('follow_first'):
2546 2542 # note: this might be wrong when following through merges
2547 2543 revmatchfn = cmdutil.match(repo, fns, default='path')
2548 2544 else:
2549 2545 revmatchfn = matchfn
2550 2546
2551 2547 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
2552 2548
2553 2549 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
2554 2550 if count == limit:
2555 2551 break
2556 2552 if displayer.flush(ctx.rev()):
2557 2553 count += 1
2558 2554 displayer.close()
2559 2555
2560 2556 def manifest(ui, repo, node=None, rev=None):
2561 2557 """output the current or given revision of the project manifest
2562 2558
2563 2559 Print a list of version controlled files for the given revision.
2564 2560 If no revision is given, the first parent of the working directory
2565 2561 is used, or the null revision if no revision is checked out.
2566 2562
2567 2563 With -v, print file permissions, symlink and executable bits.
2568 2564 With --debug, print file revision hashes.
2569 2565
2570 2566 Returns 0 on success.
2571 2567 """
2572 2568
2573 2569 if rev and node:
2574 2570 raise util.Abort(_("please specify just one revision"))
2575 2571
2576 2572 if not node:
2577 2573 node = rev
2578 2574
2579 2575 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
2580 2576 ctx = cmdutil.revsingle(repo, node)
2581 2577 for f in ctx:
2582 2578 if ui.debugflag:
2583 2579 ui.write("%40s " % hex(ctx.manifest()[f]))
2584 2580 if ui.verbose:
2585 2581 ui.write(decor[ctx.flags(f)])
2586 2582 ui.write("%s\n" % f)
2587 2583
2588 2584 def merge(ui, repo, node=None, **opts):
2589 2585 """merge working directory with another revision
2590 2586
2591 2587 The current working directory is updated with all changes made in
2592 2588 the requested revision since the last common predecessor revision.
2593 2589
2594 2590 Files that changed between either parent are marked as changed for
2595 2591 the next commit and a commit must be performed before any further
2596 2592 updates to the repository are allowed. The next commit will have
2597 2593 two parents.
2598 2594
2599 2595 ``--tool`` can be used to specify the merge tool used for file
2600 2596 merges. It overrides the HGMERGE environment variable and your
2601 2597 configuration files.
2602 2598
2603 2599 If no revision is specified, the working directory's parent is a
2604 2600 head revision, and the current branch contains exactly one other
2605 2601 head, the other head is merged with by default. Otherwise, an
2606 2602 explicit revision with which to merge with must be provided.
2607 2603
2608 2604 :hg:`resolve` must be used to resolve unresolved files.
2609 2605
2610 2606 To undo an uncommitted merge, use :hg:`update --clean .` which
2611 2607 will check out a clean copy of the original merge parent, losing
2612 2608 all changes.
2613 2609
2614 2610 Returns 0 on success, 1 if there are unresolved files.
2615 2611 """
2616 2612
2617 2613 if opts.get('rev') and node:
2618 2614 raise util.Abort(_("please specify just one revision"))
2619 2615 if not node:
2620 2616 node = opts.get('rev')
2621 2617
2622 2618 if not node:
2623 2619 branch = repo[None].branch()
2624 2620 bheads = repo.branchheads(branch)
2625 2621 if len(bheads) > 2:
2626 2622 raise util.Abort(_(
2627 2623 'branch \'%s\' has %d heads - '
2628 2624 'please merge with an explicit rev\n'
2629 2625 '(run \'hg heads .\' to see heads)')
2630 2626 % (branch, len(bheads)))
2631 2627
2632 2628 parent = repo.dirstate.parents()[0]
2633 2629 if len(bheads) == 1:
2634 2630 if len(repo.heads()) > 1:
2635 2631 raise util.Abort(_(
2636 2632 'branch \'%s\' has one head - '
2637 2633 'please merge with an explicit rev\n'
2638 2634 '(run \'hg heads\' to see all heads)')
2639 2635 % branch)
2640 2636 msg = _('there is nothing to merge')
2641 2637 if parent != repo.lookup(repo[None].branch()):
2642 2638 msg = _('%s - use "hg update" instead') % msg
2643 2639 raise util.Abort(msg)
2644 2640
2645 2641 if parent not in bheads:
2646 2642 raise util.Abort(_('working dir not at a head rev - '
2647 2643 'use "hg update" or merge with an explicit rev'))
2648 2644 node = parent == bheads[0] and bheads[-1] or bheads[0]
2649 2645 else:
2650 2646 node = cmdutil.revsingle(repo, node).node()
2651 2647
2652 2648 if opts.get('preview'):
2653 2649 # find nodes that are ancestors of p2 but not of p1
2654 2650 p1 = repo.lookup('.')
2655 2651 p2 = repo.lookup(node)
2656 2652 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
2657 2653
2658 2654 displayer = cmdutil.show_changeset(ui, repo, opts)
2659 2655 for node in nodes:
2660 2656 displayer.show(repo[node])
2661 2657 displayer.close()
2662 2658 return 0
2663 2659
2664 2660 try:
2665 2661 # ui.forcemerge is an internal variable, do not document
2666 2662 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
2667 2663 return hg.merge(repo, node, force=opts.get('force'))
2668 2664 finally:
2669 2665 ui.setconfig('ui', 'forcemerge', '')
2670 2666
2671 2667 def outgoing(ui, repo, dest=None, **opts):
2672 2668 """show changesets not found in the destination
2673 2669
2674 2670 Show changesets not found in the specified destination repository
2675 2671 or the default push location. These are the changesets that would
2676 2672 be pushed if a push was requested.
2677 2673
2678 2674 See pull for details of valid destination formats.
2679 2675
2680 2676 Returns 0 if there are outgoing changes, 1 otherwise.
2681 2677 """
2682 2678 ret = hg.outgoing(ui, repo, dest, opts)
2683 2679 return ret
2684 2680
2685 2681 def parents(ui, repo, file_=None, **opts):
2686 2682 """show the parents of the working directory or revision
2687 2683
2688 2684 Print the working directory's parent revisions. If a revision is
2689 2685 given via -r/--rev, the parent of that revision will be printed.
2690 2686 If a file argument is given, the revision in which the file was
2691 2687 last changed (before the working directory revision or the
2692 2688 argument to --rev if given) is printed.
2693 2689
2694 2690 Returns 0 on success.
2695 2691 """
2696 2692
2697 2693 ctx = cmdutil.revsingle(repo, opts.get('rev'), None)
2698 2694
2699 2695 if file_:
2700 2696 m = cmdutil.match(repo, (file_,), opts)
2701 2697 if m.anypats() or len(m.files()) != 1:
2702 2698 raise util.Abort(_('can only specify an explicit filename'))
2703 2699 file_ = m.files()[0]
2704 2700 filenodes = []
2705 2701 for cp in ctx.parents():
2706 2702 if not cp:
2707 2703 continue
2708 2704 try:
2709 2705 filenodes.append(cp.filenode(file_))
2710 2706 except error.LookupError:
2711 2707 pass
2712 2708 if not filenodes:
2713 2709 raise util.Abort(_("'%s' not found in manifest!") % file_)
2714 2710 fl = repo.file(file_)
2715 2711 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
2716 2712 else:
2717 2713 p = [cp.node() for cp in ctx.parents()]
2718 2714
2719 2715 displayer = cmdutil.show_changeset(ui, repo, opts)
2720 2716 for n in p:
2721 2717 if n != nullid:
2722 2718 displayer.show(repo[n])
2723 2719 displayer.close()
2724 2720
2725 2721 def paths(ui, repo, search=None):
2726 2722 """show aliases for remote repositories
2727 2723
2728 2724 Show definition of symbolic path name NAME. If no name is given,
2729 2725 show definition of all available names.
2730 2726
2731 2727 Path names are defined in the [paths] section of your
2732 2728 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
2733 2729 repository, ``.hg/hgrc`` is used, too.
2734 2730
2735 2731 The path names ``default`` and ``default-push`` have a special
2736 2732 meaning. When performing a push or pull operation, they are used
2737 2733 as fallbacks if no location is specified on the command-line.
2738 2734 When ``default-push`` is set, it will be used for push and
2739 2735 ``default`` will be used for pull; otherwise ``default`` is used
2740 2736 as the fallback for both. When cloning a repository, the clone
2741 2737 source is written as ``default`` in ``.hg/hgrc``. Note that
2742 2738 ``default`` and ``default-push`` apply to all inbound (e.g.
2743 2739 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email` and
2744 2740 :hg:`bundle`) operations.
2745 2741
2746 2742 See :hg:`help urls` for more information.
2747 2743
2748 2744 Returns 0 on success.
2749 2745 """
2750 2746 if search:
2751 2747 for name, path in ui.configitems("paths"):
2752 2748 if name == search:
2753 2749 ui.write("%s\n" % url.hidepassword(path))
2754 2750 return
2755 2751 ui.warn(_("not found!\n"))
2756 2752 return 1
2757 2753 else:
2758 2754 for name, path in ui.configitems("paths"):
2759 2755 ui.write("%s = %s\n" % (name, url.hidepassword(path)))
2760 2756
2761 2757 def postincoming(ui, repo, modheads, optupdate, checkout):
2762 2758 if modheads == 0:
2763 2759 return
2764 2760 if optupdate:
2765 2761 if (modheads <= 1 or len(repo.branchheads()) == 1) or checkout:
2766 2762 return hg.update(repo, checkout)
2767 2763 else:
2768 2764 ui.status(_("not updating, since new heads added\n"))
2769 2765 if modheads > 1:
2770 2766 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2771 2767 else:
2772 2768 ui.status(_("(run 'hg update' to get a working copy)\n"))
2773 2769
2774 2770 def pull(ui, repo, source="default", **opts):
2775 2771 """pull changes from the specified source
2776 2772
2777 2773 Pull changes from a remote repository to a local one.
2778 2774
2779 2775 This finds all changes from the repository at the specified path
2780 2776 or URL and adds them to a local repository (the current one unless
2781 2777 -R is specified). By default, this does not update the copy of the
2782 2778 project in the working directory.
2783 2779
2784 2780 Use :hg:`incoming` if you want to see what would have been added
2785 2781 by a pull at the time you issued this command. If you then decide
2786 2782 to add those changes to the repository, you should use :hg:`pull
2787 2783 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
2788 2784
2789 2785 If SOURCE is omitted, the 'default' path will be used.
2790 2786 See :hg:`help urls` for more information.
2791 2787
2792 2788 Returns 0 on success, 1 if an update had unresolved files.
2793 2789 """
2794 2790 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
2795 2791 other = hg.repository(hg.remoteui(repo, opts), source)
2796 2792 ui.status(_('pulling from %s\n') % url.hidepassword(source))
2797 2793 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
2798 2794 if revs:
2799 2795 try:
2800 2796 revs = [other.lookup(rev) for rev in revs]
2801 2797 except error.CapabilityError:
2802 2798 err = _("other repository doesn't support revision lookup, "
2803 2799 "so a rev cannot be specified.")
2804 2800 raise util.Abort(err)
2805 2801
2806 2802 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
2807 2803 if checkout:
2808 2804 checkout = str(repo.changelog.rev(other.lookup(checkout)))
2809 2805 repo._subtoppath = source
2810 2806 try:
2811 2807 return postincoming(ui, repo, modheads, opts.get('update'), checkout)
2812 2808 finally:
2813 2809 del repo._subtoppath
2814 2810
2815 2811 def push(ui, repo, dest=None, **opts):
2816 2812 """push changes to the specified destination
2817 2813
2818 2814 Push changesets from the local repository to the specified
2819 2815 destination.
2820 2816
2821 2817 This operation is symmetrical to pull: it is identical to a pull
2822 2818 in the destination repository from the current one.
2823 2819
2824 2820 By default, push will not allow creation of new heads at the
2825 2821 destination, since multiple heads would make it unclear which head
2826 2822 to use. In this situation, it is recommended to pull and merge
2827 2823 before pushing.
2828 2824
2829 2825 Use --new-branch if you want to allow push to create a new named
2830 2826 branch that is not present at the destination. This allows you to
2831 2827 only create a new branch without forcing other changes.
2832 2828
2833 2829 Use -f/--force to override the default behavior and push all
2834 2830 changesets on all branches.
2835 2831
2836 2832 If -r/--rev is used, the specified revision and all its ancestors
2837 2833 will be pushed to the remote repository.
2838 2834
2839 2835 Please see :hg:`help urls` for important details about ``ssh://``
2840 2836 URLs. If DESTINATION is omitted, a default path will be used.
2841 2837
2842 2838 Returns 0 if push was successful, 1 if nothing to push.
2843 2839 """
2844 2840 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2845 2841 dest, branches = hg.parseurl(dest, opts.get('branch'))
2846 2842 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
2847 2843 other = hg.repository(hg.remoteui(repo, opts), dest)
2848 2844 ui.status(_('pushing to %s\n') % url.hidepassword(dest))
2849 2845 if revs:
2850 2846 revs = [repo.lookup(rev) for rev in revs]
2851 2847
2852 2848 repo._subtoppath = dest
2853 2849 try:
2854 2850 # push subrepos depth-first for coherent ordering
2855 2851 c = repo['']
2856 2852 subs = c.substate # only repos that are committed
2857 2853 for s in sorted(subs):
2858 2854 if not c.sub(s).push(opts.get('force')):
2859 2855 return False
2860 2856 finally:
2861 2857 del repo._subtoppath
2862 2858 r = repo.push(other, opts.get('force'), revs=revs,
2863 2859 newbranch=opts.get('new_branch'))
2864 2860 return r == 0
2865 2861
2866 2862 def recover(ui, repo):
2867 2863 """roll back an interrupted transaction
2868 2864
2869 2865 Recover from an interrupted commit or pull.
2870 2866
2871 2867 This command tries to fix the repository status after an
2872 2868 interrupted operation. It should only be necessary when Mercurial
2873 2869 suggests it.
2874 2870
2875 2871 Returns 0 if successful, 1 if nothing to recover or verify fails.
2876 2872 """
2877 2873 if repo.recover():
2878 2874 return hg.verify(repo)
2879 2875 return 1
2880 2876
2881 2877 def remove(ui, repo, *pats, **opts):
2882 2878 """remove the specified files on the next commit
2883 2879
2884 2880 Schedule the indicated files for removal from the repository.
2885 2881
2886 2882 This only removes files from the current branch, not from the
2887 2883 entire project history. -A/--after can be used to remove only
2888 2884 files that have already been deleted, -f/--force can be used to
2889 2885 force deletion, and -Af can be used to remove files from the next
2890 2886 revision without deleting them from the working directory.
2891 2887
2892 2888 The following table details the behavior of remove for different
2893 2889 file states (columns) and option combinations (rows). The file
2894 2890 states are Added [A], Clean [C], Modified [M] and Missing [!] (as
2895 2891 reported by :hg:`status`). The actions are Warn, Remove (from
2896 2892 branch) and Delete (from disk)::
2897 2893
2898 2894 A C M !
2899 2895 none W RD W R
2900 2896 -f R RD RD R
2901 2897 -A W W W R
2902 2898 -Af R R R R
2903 2899
2904 2900 This command schedules the files to be removed at the next commit.
2905 2901 To undo a remove before that, see :hg:`revert`.
2906 2902
2907 2903 Returns 0 on success, 1 if any warnings encountered.
2908 2904 """
2909 2905
2910 2906 ret = 0
2911 2907 after, force = opts.get('after'), opts.get('force')
2912 2908 if not pats and not after:
2913 2909 raise util.Abort(_('no files specified'))
2914 2910
2915 2911 m = cmdutil.match(repo, pats, opts)
2916 2912 s = repo.status(match=m, clean=True)
2917 2913 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2918 2914
2919 2915 for f in m.files():
2920 2916 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
2921 2917 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
2922 2918 ret = 1
2923 2919
2924 2920 if force:
2925 2921 remove, forget = modified + deleted + clean, added
2926 2922 elif after:
2927 2923 remove, forget = deleted, []
2928 2924 for f in modified + added + clean:
2929 2925 ui.warn(_('not removing %s: file still exists (use -f'
2930 2926 ' to force removal)\n') % m.rel(f))
2931 2927 ret = 1
2932 2928 else:
2933 2929 remove, forget = deleted + clean, []
2934 2930 for f in modified:
2935 2931 ui.warn(_('not removing %s: file is modified (use -f'
2936 2932 ' to force removal)\n') % m.rel(f))
2937 2933 ret = 1
2938 2934 for f in added:
2939 2935 ui.warn(_('not removing %s: file has been marked for add (use -f'
2940 2936 ' to force removal)\n') % m.rel(f))
2941 2937 ret = 1
2942 2938
2943 2939 for f in sorted(remove + forget):
2944 2940 if ui.verbose or not m.exact(f):
2945 2941 ui.status(_('removing %s\n') % m.rel(f))
2946 2942
2947 2943 repo[None].forget(forget)
2948 2944 repo[None].remove(remove, unlink=not after)
2949 2945 return ret
2950 2946
2951 2947 def rename(ui, repo, *pats, **opts):
2952 2948 """rename files; equivalent of copy + remove
2953 2949
2954 2950 Mark dest as copies of sources; mark sources for deletion. If dest
2955 2951 is a directory, copies are put in that directory. If dest is a
2956 2952 file, there can only be one source.
2957 2953
2958 2954 By default, this command copies the contents of files as they
2959 2955 exist in the working directory. If invoked with -A/--after, the
2960 2956 operation is recorded, but no copying is performed.
2961 2957
2962 2958 This command takes effect at the next commit. To undo a rename
2963 2959 before that, see :hg:`revert`.
2964 2960
2965 2961 Returns 0 on success, 1 if errors are encountered.
2966 2962 """
2967 2963 wlock = repo.wlock(False)
2968 2964 try:
2969 2965 return cmdutil.copy(ui, repo, pats, opts, rename=True)
2970 2966 finally:
2971 2967 wlock.release()
2972 2968
2973 2969 def resolve(ui, repo, *pats, **opts):
2974 2970 """redo merges or set/view the merge status of files
2975 2971
2976 2972 Merges with unresolved conflicts are often the result of
2977 2973 non-interactive merging using the ``internal:merge`` configuration
2978 2974 setting, or a command-line merge tool like ``diff3``. The resolve
2979 2975 command is used to manage the files involved in a merge, after
2980 2976 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
2981 2977 working directory must have two parents).
2982 2978
2983 2979 The resolve command can be used in the following ways:
2984 2980
2985 2981 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
2986 2982 files, discarding any previous merge attempts. Re-merging is not
2987 2983 performed for files already marked as resolved. Use ``--all/-a``
2988 2984 to selects all unresolved files. ``--tool`` can be used to specify
2989 2985 the merge tool used for the given files. It overrides the HGMERGE
2990 2986 environment variable and your configuration files.
2991 2987
2992 2988 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
2993 2989 (e.g. after having manually fixed-up the files). The default is
2994 2990 to mark all unresolved files.
2995 2991
2996 2992 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
2997 2993 default is to mark all resolved files.
2998 2994
2999 2995 - :hg:`resolve -l`: list files which had or still have conflicts.
3000 2996 In the printed list, ``U`` = unresolved and ``R`` = resolved.
3001 2997
3002 2998 Note that Mercurial will not let you commit files with unresolved
3003 2999 merge conflicts. You must use :hg:`resolve -m ...` before you can
3004 3000 commit after a conflicting merge.
3005 3001
3006 3002 Returns 0 on success, 1 if any files fail a resolve attempt.
3007 3003 """
3008 3004
3009 3005 all, mark, unmark, show, nostatus = \
3010 3006 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
3011 3007
3012 3008 if (show and (mark or unmark)) or (mark and unmark):
3013 3009 raise util.Abort(_("too many options specified"))
3014 3010 if pats and all:
3015 3011 raise util.Abort(_("can't specify --all and patterns"))
3016 3012 if not (all or pats or show or mark or unmark):
3017 3013 raise util.Abort(_('no files or directories specified; '
3018 3014 'use --all to remerge all files'))
3019 3015
3020 3016 ms = mergemod.mergestate(repo)
3021 3017 m = cmdutil.match(repo, pats, opts)
3022 3018 ret = 0
3023 3019
3024 3020 for f in ms:
3025 3021 if m(f):
3026 3022 if show:
3027 3023 if nostatus:
3028 3024 ui.write("%s\n" % f)
3029 3025 else:
3030 3026 ui.write("%s %s\n" % (ms[f].upper(), f),
3031 3027 label='resolve.' +
3032 3028 {'u': 'unresolved', 'r': 'resolved'}[ms[f]])
3033 3029 elif mark:
3034 3030 ms.mark(f, "r")
3035 3031 elif unmark:
3036 3032 ms.mark(f, "u")
3037 3033 else:
3038 3034 wctx = repo[None]
3039 3035 mctx = wctx.parents()[-1]
3040 3036
3041 3037 # backup pre-resolve (merge uses .orig for its own purposes)
3042 3038 a = repo.wjoin(f)
3043 3039 util.copyfile(a, a + ".resolve")
3044 3040
3045 3041 try:
3046 3042 # resolve file
3047 3043 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
3048 3044 if ms.resolve(f, wctx, mctx):
3049 3045 ret = 1
3050 3046 finally:
3051 3047 ui.setconfig('ui', 'forcemerge', '')
3052 3048
3053 3049 # replace filemerge's .orig file with our resolve file
3054 3050 util.rename(a + ".resolve", a + ".orig")
3055 3051
3056 3052 ms.commit()
3057 3053 return ret
3058 3054
3059 3055 def revert(ui, repo, *pats, **opts):
3060 3056 """restore individual files or directories to an earlier state
3061 3057
3062 3058 .. note::
3063 3059 This command is most likely not what you are looking for.
3064 3060 Revert will partially overwrite content in the working
3065 3061 directory without changing the working directory parents. Use
3066 3062 :hg:`update -r rev` to check out earlier revisions, or
3067 3063 :hg:`update --clean .` to undo a merge which has added another
3068 3064 parent.
3069 3065
3070 3066 With no revision specified, revert the named files or directories
3071 3067 to the contents they had in the parent of the working directory.
3072 3068 This restores the contents of the affected files to an unmodified
3073 3069 state and unschedules adds, removes, copies, and renames. If the
3074 3070 working directory has two parents, you must explicitly specify a
3075 3071 revision.
3076 3072
3077 3073 Using the -r/--rev option, revert the given files or directories
3078 3074 to their contents as of a specific revision. This can be helpful
3079 3075 to "roll back" some or all of an earlier change. See :hg:`help
3080 3076 dates` for a list of formats valid for -d/--date.
3081 3077
3082 3078 Revert modifies the working directory. It does not commit any
3083 3079 changes, or change the parent of the working directory. If you
3084 3080 revert to a revision other than the parent of the working
3085 3081 directory, the reverted files will thus appear modified
3086 3082 afterwards.
3087 3083
3088 3084 If a file has been deleted, it is restored. If the executable mode
3089 3085 of a file was changed, it is reset.
3090 3086
3091 3087 If names are given, all files matching the names are reverted.
3092 3088 If no arguments are given, no files are reverted.
3093 3089
3094 3090 Modified files are saved with a .orig suffix before reverting.
3095 3091 To disable these backups, use --no-backup.
3096 3092
3097 3093 Returns 0 on success.
3098 3094 """
3099 3095
3100 3096 if opts.get("date"):
3101 3097 if opts.get("rev"):
3102 3098 raise util.Abort(_("you can't specify a revision and a date"))
3103 3099 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
3104 3100
3105 3101 parent, p2 = repo.dirstate.parents()
3106 3102 if not opts.get('rev') and p2 != nullid:
3107 3103 raise util.Abort(_('uncommitted merge - '
3108 3104 'use "hg update", see "hg help revert"'))
3109 3105
3110 3106 if not pats and not opts.get('all'):
3111 3107 raise util.Abort(_('no files or directories specified; '
3112 3108 'use --all to revert the whole repo'))
3113 3109
3114 3110 ctx = cmdutil.revsingle(repo, opts.get('rev'))
3115 3111 node = ctx.node()
3116 3112 mf = ctx.manifest()
3117 3113 if node == parent:
3118 3114 pmf = mf
3119 3115 else:
3120 3116 pmf = None
3121 3117
3122 3118 # need all matching names in dirstate and manifest of target rev,
3123 3119 # so have to walk both. do not print errors if files exist in one
3124 3120 # but not other.
3125 3121
3126 3122 names = {}
3127 3123
3128 3124 wlock = repo.wlock()
3129 3125 try:
3130 3126 # walk dirstate.
3131 3127
3132 3128 m = cmdutil.match(repo, pats, opts)
3133 3129 m.bad = lambda x, y: False
3134 3130 for abs in repo.walk(m):
3135 3131 names[abs] = m.rel(abs), m.exact(abs)
3136 3132
3137 3133 # walk target manifest.
3138 3134
3139 3135 def badfn(path, msg):
3140 3136 if path in names:
3141 3137 return
3142 3138 path_ = path + '/'
3143 3139 for f in names:
3144 3140 if f.startswith(path_):
3145 3141 return
3146 3142 ui.warn("%s: %s\n" % (m.rel(path), msg))
3147 3143
3148 3144 m = cmdutil.match(repo, pats, opts)
3149 3145 m.bad = badfn
3150 3146 for abs in repo[node].walk(m):
3151 3147 if abs not in names:
3152 3148 names[abs] = m.rel(abs), m.exact(abs)
3153 3149
3154 3150 m = cmdutil.matchfiles(repo, names)
3155 3151 changes = repo.status(match=m)[:4]
3156 3152 modified, added, removed, deleted = map(set, changes)
3157 3153
3158 3154 # if f is a rename, also revert the source
3159 3155 cwd = repo.getcwd()
3160 3156 for f in added:
3161 3157 src = repo.dirstate.copied(f)
3162 3158 if src and src not in names and repo.dirstate[src] == 'r':
3163 3159 removed.add(src)
3164 3160 names[src] = (repo.pathto(src, cwd), True)
3165 3161
3166 3162 def removeforget(abs):
3167 3163 if repo.dirstate[abs] == 'a':
3168 3164 return _('forgetting %s\n')
3169 3165 return _('removing %s\n')
3170 3166
3171 3167 revert = ([], _('reverting %s\n'))
3172 3168 add = ([], _('adding %s\n'))
3173 3169 remove = ([], removeforget)
3174 3170 undelete = ([], _('undeleting %s\n'))
3175 3171
3176 3172 disptable = (
3177 3173 # dispatch table:
3178 3174 # file state
3179 3175 # action if in target manifest
3180 3176 # action if not in target manifest
3181 3177 # make backup if in target manifest
3182 3178 # make backup if not in target manifest
3183 3179 (modified, revert, remove, True, True),
3184 3180 (added, revert, remove, True, False),
3185 3181 (removed, undelete, None, False, False),
3186 3182 (deleted, revert, remove, False, False),
3187 3183 )
3188 3184
3189 3185 for abs, (rel, exact) in sorted(names.items()):
3190 3186 mfentry = mf.get(abs)
3191 3187 target = repo.wjoin(abs)
3192 3188 def handle(xlist, dobackup):
3193 3189 xlist[0].append(abs)
3194 3190 if (dobackup and not opts.get('no_backup') and
3195 3191 os.path.lexists(target)):
3196 3192 bakname = "%s.orig" % rel
3197 3193 ui.note(_('saving current version of %s as %s\n') %
3198 3194 (rel, bakname))
3199 3195 if not opts.get('dry_run'):
3200 3196 util.rename(target, bakname)
3201 3197 if ui.verbose or not exact:
3202 3198 msg = xlist[1]
3203 3199 if not isinstance(msg, basestring):
3204 3200 msg = msg(abs)
3205 3201 ui.status(msg % rel)
3206 3202 for table, hitlist, misslist, backuphit, backupmiss in disptable:
3207 3203 if abs not in table:
3208 3204 continue
3209 3205 # file has changed in dirstate
3210 3206 if mfentry:
3211 3207 handle(hitlist, backuphit)
3212 3208 elif misslist is not None:
3213 3209 handle(misslist, backupmiss)
3214 3210 break
3215 3211 else:
3216 3212 if abs not in repo.dirstate:
3217 3213 if mfentry:
3218 3214 handle(add, True)
3219 3215 elif exact:
3220 3216 ui.warn(_('file not managed: %s\n') % rel)
3221 3217 continue
3222 3218 # file has not changed in dirstate
3223 3219 if node == parent:
3224 3220 if exact:
3225 3221 ui.warn(_('no changes needed to %s\n') % rel)
3226 3222 continue
3227 3223 if pmf is None:
3228 3224 # only need parent manifest in this unlikely case,
3229 3225 # so do not read by default
3230 3226 pmf = repo[parent].manifest()
3231 3227 if abs in pmf:
3232 3228 if mfentry:
3233 3229 # if version of file is same in parent and target
3234 3230 # manifests, do nothing
3235 3231 if (pmf[abs] != mfentry or
3236 3232 pmf.flags(abs) != mf.flags(abs)):
3237 3233 handle(revert, False)
3238 3234 else:
3239 3235 handle(remove, False)
3240 3236
3241 3237 if not opts.get('dry_run'):
3242 3238 def checkout(f):
3243 3239 fc = ctx[f]
3244 3240 repo.wwrite(f, fc.data(), fc.flags())
3245 3241
3246 3242 audit_path = util.path_auditor(repo.root)
3247 3243 for f in remove[0]:
3248 3244 if repo.dirstate[f] == 'a':
3249 3245 repo.dirstate.forget(f)
3250 3246 continue
3251 3247 audit_path(f)
3252 3248 try:
3253 3249 util.unlinkpath(repo.wjoin(f))
3254 3250 except OSError:
3255 3251 pass
3256 3252 repo.dirstate.remove(f)
3257 3253
3258 3254 normal = None
3259 3255 if node == parent:
3260 3256 # We're reverting to our parent. If possible, we'd like status
3261 3257 # to report the file as clean. We have to use normallookup for
3262 3258 # merges to avoid losing information about merged/dirty files.
3263 3259 if p2 != nullid:
3264 3260 normal = repo.dirstate.normallookup
3265 3261 else:
3266 3262 normal = repo.dirstate.normal
3267 3263 for f in revert[0]:
3268 3264 checkout(f)
3269 3265 if normal:
3270 3266 normal(f)
3271 3267
3272 3268 for f in add[0]:
3273 3269 checkout(f)
3274 3270 repo.dirstate.add(f)
3275 3271
3276 3272 normal = repo.dirstate.normallookup
3277 3273 if node == parent and p2 == nullid:
3278 3274 normal = repo.dirstate.normal
3279 3275 for f in undelete[0]:
3280 3276 checkout(f)
3281 3277 normal(f)
3282 3278
3283 3279 finally:
3284 3280 wlock.release()
3285 3281
3286 3282 def rollback(ui, repo, **opts):
3287 3283 """roll back the last transaction (dangerous)
3288 3284
3289 3285 This command should be used with care. There is only one level of
3290 3286 rollback, and there is no way to undo a rollback. It will also
3291 3287 restore the dirstate at the time of the last transaction, losing
3292 3288 any dirstate changes since that time. This command does not alter
3293 3289 the working directory.
3294 3290
3295 3291 Transactions are used to encapsulate the effects of all commands
3296 3292 that create new changesets or propagate existing changesets into a
3297 3293 repository. For example, the following commands are transactional,
3298 3294 and their effects can be rolled back:
3299 3295
3300 3296 - commit
3301 3297 - import
3302 3298 - pull
3303 3299 - push (with this repository as the destination)
3304 3300 - unbundle
3305 3301
3306 3302 This command is not intended for use on public repositories. Once
3307 3303 changes are visible for pull by other users, rolling a transaction
3308 3304 back locally is ineffective (someone else may already have pulled
3309 3305 the changes). Furthermore, a race is possible with readers of the
3310 3306 repository; for example an in-progress pull from the repository
3311 3307 may fail if a rollback is performed.
3312 3308
3313 3309 Returns 0 on success, 1 if no rollback data is available.
3314 3310 """
3315 3311 return repo.rollback(opts.get('dry_run'))
3316 3312
3317 3313 def root(ui, repo):
3318 3314 """print the root (top) of the current working directory
3319 3315
3320 3316 Print the root directory of the current repository.
3321 3317
3322 3318 Returns 0 on success.
3323 3319 """
3324 3320 ui.write(repo.root + "\n")
3325 3321
3326 3322 def serve(ui, repo, **opts):
3327 3323 """start stand-alone webserver
3328 3324
3329 3325 Start a local HTTP repository browser and pull server. You can use
3330 3326 this for ad-hoc sharing and browsing of repositories. It is
3331 3327 recommended to use a real web server to serve a repository for
3332 3328 longer periods of time.
3333 3329
3334 3330 Please note that the server does not implement access control.
3335 3331 This means that, by default, anybody can read from the server and
3336 3332 nobody can write to it by default. Set the ``web.allow_push``
3337 3333 option to ``*`` to allow everybody to push to the server. You
3338 3334 should use a real web server if you need to authenticate users.
3339 3335
3340 3336 By default, the server logs accesses to stdout and errors to
3341 3337 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
3342 3338 files.
3343 3339
3344 3340 To have the server choose a free port number to listen on, specify
3345 3341 a port number of 0; in this case, the server will print the port
3346 3342 number it uses.
3347 3343
3348 3344 Returns 0 on success.
3349 3345 """
3350 3346
3351 3347 if opts["stdio"]:
3352 3348 if repo is None:
3353 3349 raise error.RepoError(_("There is no Mercurial repository here"
3354 3350 " (.hg not found)"))
3355 3351 s = sshserver.sshserver(ui, repo)
3356 3352 s.serve_forever()
3357 3353
3358 3354 # this way we can check if something was given in the command-line
3359 3355 if opts.get('port'):
3360 3356 opts['port'] = util.getport(opts.get('port'))
3361 3357
3362 3358 baseui = repo and repo.baseui or ui
3363 3359 optlist = ("name templates style address port prefix ipv6"
3364 3360 " accesslog errorlog certificate encoding")
3365 3361 for o in optlist.split():
3366 3362 val = opts.get(o, '')
3367 3363 if val in (None, ''): # should check against default options instead
3368 3364 continue
3369 3365 baseui.setconfig("web", o, val)
3370 3366 if repo and repo.ui != baseui:
3371 3367 repo.ui.setconfig("web", o, val)
3372 3368
3373 3369 o = opts.get('web_conf') or opts.get('webdir_conf')
3374 3370 if not o:
3375 3371 if not repo:
3376 3372 raise error.RepoError(_("There is no Mercurial repository"
3377 3373 " here (.hg not found)"))
3378 3374 o = repo.root
3379 3375
3380 3376 app = hgweb.hgweb(o, baseui=ui)
3381 3377
3382 3378 class service(object):
3383 3379 def init(self):
3384 3380 util.set_signal_handler()
3385 3381 self.httpd = hgweb.server.create_server(ui, app)
3386 3382
3387 3383 if opts['port'] and not ui.verbose:
3388 3384 return
3389 3385
3390 3386 if self.httpd.prefix:
3391 3387 prefix = self.httpd.prefix.strip('/') + '/'
3392 3388 else:
3393 3389 prefix = ''
3394 3390
3395 3391 port = ':%d' % self.httpd.port
3396 3392 if port == ':80':
3397 3393 port = ''
3398 3394
3399 3395 bindaddr = self.httpd.addr
3400 3396 if bindaddr == '0.0.0.0':
3401 3397 bindaddr = '*'
3402 3398 elif ':' in bindaddr: # IPv6
3403 3399 bindaddr = '[%s]' % bindaddr
3404 3400
3405 3401 fqaddr = self.httpd.fqaddr
3406 3402 if ':' in fqaddr:
3407 3403 fqaddr = '[%s]' % fqaddr
3408 3404 if opts['port']:
3409 3405 write = ui.status
3410 3406 else:
3411 3407 write = ui.write
3412 3408 write(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
3413 3409 (fqaddr, port, prefix, bindaddr, self.httpd.port))
3414 3410
3415 3411 def run(self):
3416 3412 self.httpd.serve_forever()
3417 3413
3418 3414 service = service()
3419 3415
3420 3416 cmdutil.service(opts, initfn=service.init, runfn=service.run)
3421 3417
3422 3418 def status(ui, repo, *pats, **opts):
3423 3419 """show changed files in the working directory
3424 3420
3425 3421 Show status of files in the repository. If names are given, only
3426 3422 files that match are shown. Files that are clean or ignored or
3427 3423 the source of a copy/move operation, are not listed unless
3428 3424 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
3429 3425 Unless options described with "show only ..." are given, the
3430 3426 options -mardu are used.
3431 3427
3432 3428 Option -q/--quiet hides untracked (unknown and ignored) files
3433 3429 unless explicitly requested with -u/--unknown or -i/--ignored.
3434 3430
3435 3431 .. note::
3436 3432 status may appear to disagree with diff if permissions have
3437 3433 changed or a merge has occurred. The standard diff format does
3438 3434 not report permission changes and diff only reports changes
3439 3435 relative to one merge parent.
3440 3436
3441 3437 If one revision is given, it is used as the base revision.
3442 3438 If two revisions are given, the differences between them are
3443 3439 shown. The --change option can also be used as a shortcut to list
3444 3440 the changed files of a revision from its first parent.
3445 3441
3446 3442 The codes used to show the status of files are::
3447 3443
3448 3444 M = modified
3449 3445 A = added
3450 3446 R = removed
3451 3447 C = clean
3452 3448 ! = missing (deleted by non-hg command, but still tracked)
3453 3449 ? = not tracked
3454 3450 I = ignored
3455 3451 = origin of the previous file listed as A (added)
3456 3452
3457 3453 Returns 0 on success.
3458 3454 """
3459 3455
3460 3456 revs = opts.get('rev')
3461 3457 change = opts.get('change')
3462 3458
3463 3459 if revs and change:
3464 3460 msg = _('cannot specify --rev and --change at the same time')
3465 3461 raise util.Abort(msg)
3466 3462 elif change:
3467 3463 node2 = repo.lookup(change)
3468 3464 node1 = repo[node2].parents()[0].node()
3469 3465 else:
3470 3466 node1, node2 = cmdutil.revpair(repo, revs)
3471 3467
3472 3468 cwd = (pats and repo.getcwd()) or ''
3473 3469 end = opts.get('print0') and '\0' or '\n'
3474 3470 copy = {}
3475 3471 states = 'modified added removed deleted unknown ignored clean'.split()
3476 3472 show = [k for k in states if opts.get(k)]
3477 3473 if opts.get('all'):
3478 3474 show += ui.quiet and (states[:4] + ['clean']) or states
3479 3475 if not show:
3480 3476 show = ui.quiet and states[:4] or states[:5]
3481 3477
3482 3478 stat = repo.status(node1, node2, cmdutil.match(repo, pats, opts),
3483 3479 'ignored' in show, 'clean' in show, 'unknown' in show,
3484 3480 opts.get('subrepos'))
3485 3481 changestates = zip(states, 'MAR!?IC', stat)
3486 3482
3487 3483 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
3488 3484 ctxn = repo[nullid]
3489 3485 ctx1 = repo[node1]
3490 3486 ctx2 = repo[node2]
3491 3487 added = stat[1]
3492 3488 if node2 is None:
3493 3489 added = stat[0] + stat[1] # merged?
3494 3490
3495 3491 for k, v in copies.copies(repo, ctx1, ctx2, ctxn)[0].iteritems():
3496 3492 if k in added:
3497 3493 copy[k] = v
3498 3494 elif v in added:
3499 3495 copy[v] = k
3500 3496
3501 3497 for state, char, files in changestates:
3502 3498 if state in show:
3503 3499 format = "%s %%s%s" % (char, end)
3504 3500 if opts.get('no_status'):
3505 3501 format = "%%s%s" % end
3506 3502
3507 3503 for f in files:
3508 3504 ui.write(format % repo.pathto(f, cwd),
3509 3505 label='status.' + state)
3510 3506 if f in copy:
3511 3507 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end),
3512 3508 label='status.copied')
3513 3509
3514 3510 def summary(ui, repo, **opts):
3515 3511 """summarize working directory state
3516 3512
3517 3513 This generates a brief summary of the working directory state,
3518 3514 including parents, branch, commit status, and available updates.
3519 3515
3520 3516 With the --remote option, this will check the default paths for
3521 3517 incoming and outgoing changes. This can be time-consuming.
3522 3518
3523 3519 Returns 0 on success.
3524 3520 """
3525 3521
3526 3522 ctx = repo[None]
3527 3523 parents = ctx.parents()
3528 3524 pnode = parents[0].node()
3529 3525
3530 3526 for p in parents:
3531 3527 # label with log.changeset (instead of log.parent) since this
3532 3528 # shows a working directory parent *changeset*:
3533 3529 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
3534 3530 label='log.changeset')
3535 3531 ui.write(' '.join(p.tags()), label='log.tag')
3536 3532 if p.rev() == -1:
3537 3533 if not len(repo):
3538 3534 ui.write(_(' (empty repository)'))
3539 3535 else:
3540 3536 ui.write(_(' (no revision checked out)'))
3541 3537 ui.write('\n')
3542 3538 if p.description():
3543 3539 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
3544 3540 label='log.summary')
3545 3541
3546 3542 branch = ctx.branch()
3547 3543 bheads = repo.branchheads(branch)
3548 3544 m = _('branch: %s\n') % branch
3549 3545 if branch != 'default':
3550 3546 ui.write(m, label='log.branch')
3551 3547 else:
3552 3548 ui.status(m, label='log.branch')
3553 3549
3554 3550 st = list(repo.status(unknown=True))[:6]
3555 3551
3556 3552 c = repo.dirstate.copies()
3557 3553 copied, renamed = [], []
3558 3554 for d, s in c.iteritems():
3559 3555 if s in st[2]:
3560 3556 st[2].remove(s)
3561 3557 renamed.append(d)
3562 3558 else:
3563 3559 copied.append(d)
3564 3560 if d in st[1]:
3565 3561 st[1].remove(d)
3566 3562 st.insert(3, renamed)
3567 3563 st.insert(4, copied)
3568 3564
3569 3565 ms = mergemod.mergestate(repo)
3570 3566 st.append([f for f in ms if ms[f] == 'u'])
3571 3567
3572 3568 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
3573 3569 st.append(subs)
3574 3570
3575 3571 labels = [ui.label(_('%d modified'), 'status.modified'),
3576 3572 ui.label(_('%d added'), 'status.added'),
3577 3573 ui.label(_('%d removed'), 'status.removed'),
3578 3574 ui.label(_('%d renamed'), 'status.copied'),
3579 3575 ui.label(_('%d copied'), 'status.copied'),
3580 3576 ui.label(_('%d deleted'), 'status.deleted'),
3581 3577 ui.label(_('%d unknown'), 'status.unknown'),
3582 3578 ui.label(_('%d ignored'), 'status.ignored'),
3583 3579 ui.label(_('%d unresolved'), 'resolve.unresolved'),
3584 3580 ui.label(_('%d subrepos'), 'status.modified')]
3585 3581 t = []
3586 3582 for s, l in zip(st, labels):
3587 3583 if s:
3588 3584 t.append(l % len(s))
3589 3585
3590 3586 t = ', '.join(t)
3591 3587 cleanworkdir = False
3592 3588
3593 3589 if len(parents) > 1:
3594 3590 t += _(' (merge)')
3595 3591 elif branch != parents[0].branch():
3596 3592 t += _(' (new branch)')
3597 3593 elif (parents[0].extra().get('close') and
3598 3594 pnode in repo.branchheads(branch, closed=True)):
3599 3595 t += _(' (head closed)')
3600 3596 elif not (st[0] or st[1] or st[2] or st[3] or st[4] or st[9]):
3601 3597 t += _(' (clean)')
3602 3598 cleanworkdir = True
3603 3599 elif pnode not in bheads:
3604 3600 t += _(' (new branch head)')
3605 3601
3606 3602 if cleanworkdir:
3607 3603 ui.status(_('commit: %s\n') % t.strip())
3608 3604 else:
3609 3605 ui.write(_('commit: %s\n') % t.strip())
3610 3606
3611 3607 # all ancestors of branch heads - all ancestors of parent = new csets
3612 3608 new = [0] * len(repo)
3613 3609 cl = repo.changelog
3614 3610 for a in [cl.rev(n) for n in bheads]:
3615 3611 new[a] = 1
3616 3612 for a in cl.ancestors(*[cl.rev(n) for n in bheads]):
3617 3613 new[a] = 1
3618 3614 for a in [p.rev() for p in parents]:
3619 3615 if a >= 0:
3620 3616 new[a] = 0
3621 3617 for a in cl.ancestors(*[p.rev() for p in parents]):
3622 3618 new[a] = 0
3623 3619 new = sum(new)
3624 3620
3625 3621 if new == 0:
3626 3622 ui.status(_('update: (current)\n'))
3627 3623 elif pnode not in bheads:
3628 3624 ui.write(_('update: %d new changesets (update)\n') % new)
3629 3625 else:
3630 3626 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
3631 3627 (new, len(bheads)))
3632 3628
3633 3629 if opts.get('remote'):
3634 3630 t = []
3635 3631 source, branches = hg.parseurl(ui.expandpath('default'))
3636 3632 other = hg.repository(hg.remoteui(repo, {}), source)
3637 3633 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
3638 3634 ui.debug('comparing with %s\n' % url.hidepassword(source))
3639 3635 repo.ui.pushbuffer()
3640 3636 common, incoming, rheads = discovery.findcommonincoming(repo, other)
3641 3637 repo.ui.popbuffer()
3642 3638 if incoming:
3643 3639 t.append(_('1 or more incoming'))
3644 3640
3645 3641 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
3646 3642 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
3647 3643 other = hg.repository(hg.remoteui(repo, {}), dest)
3648 3644 ui.debug('comparing with %s\n' % url.hidepassword(dest))
3649 3645 repo.ui.pushbuffer()
3650 3646 o = discovery.findoutgoing(repo, other)
3651 3647 repo.ui.popbuffer()
3652 3648 o = repo.changelog.nodesbetween(o, None)[0]
3653 3649 if o:
3654 3650 t.append(_('%d outgoing') % len(o))
3655 3651
3656 3652 if t:
3657 3653 ui.write(_('remote: %s\n') % (', '.join(t)))
3658 3654 else:
3659 3655 ui.status(_('remote: (synced)\n'))
3660 3656
3661 3657 def tag(ui, repo, name1, *names, **opts):
3662 3658 """add one or more tags for the current or given revision
3663 3659
3664 3660 Name a particular revision using <name>.
3665 3661
3666 3662 Tags are used to name particular revisions of the repository and are
3667 3663 very useful to compare different revisions, to go back to significant
3668 3664 earlier versions or to mark branch points as releases, etc. Changing
3669 3665 an existing tag is normally disallowed; use -f/--force to override.
3670 3666
3671 3667 If no revision is given, the parent of the working directory is
3672 3668 used, or tip if no revision is checked out.
3673 3669
3674 3670 To facilitate version control, distribution, and merging of tags,
3675 3671 they are stored as a file named ".hgtags" which is managed similarly
3676 3672 to other project files and can be hand-edited if necessary. This
3677 3673 also means that tagging creates a new commit. The file
3678 3674 ".hg/localtags" is used for local tags (not shared among
3679 3675 repositories).
3680 3676
3681 3677 Tag commits are usually made at the head of a branch. If the parent
3682 3678 of the working directory is not a branch head, :hg:`tag` aborts; use
3683 3679 -f/--force to force the tag commit to be based on a non-head
3684 3680 changeset.
3685 3681
3686 3682 See :hg:`help dates` for a list of formats valid for -d/--date.
3687 3683
3688 3684 Since tag names have priority over branch names during revision
3689 3685 lookup, using an existing branch name as a tag name is discouraged.
3690 3686
3691 3687 Returns 0 on success.
3692 3688 """
3693 3689
3694 3690 rev_ = "."
3695 3691 names = [t.strip() for t in (name1,) + names]
3696 3692 if len(names) != len(set(names)):
3697 3693 raise util.Abort(_('tag names must be unique'))
3698 3694 for n in names:
3699 3695 if n in ['tip', '.', 'null']:
3700 3696 raise util.Abort(_('the name \'%s\' is reserved') % n)
3701 3697 if not n:
3702 3698 raise util.Abort(_('tag names cannot consist entirely of whitespace'))
3703 3699 if opts.get('rev') and opts.get('remove'):
3704 3700 raise util.Abort(_("--rev and --remove are incompatible"))
3705 3701 if opts.get('rev'):
3706 3702 rev_ = opts['rev']
3707 3703 message = opts.get('message')
3708 3704 if opts.get('remove'):
3709 3705 expectedtype = opts.get('local') and 'local' or 'global'
3710 3706 for n in names:
3711 3707 if not repo.tagtype(n):
3712 3708 raise util.Abort(_('tag \'%s\' does not exist') % n)
3713 3709 if repo.tagtype(n) != expectedtype:
3714 3710 if expectedtype == 'global':
3715 3711 raise util.Abort(_('tag \'%s\' is not a global tag') % n)
3716 3712 else:
3717 3713 raise util.Abort(_('tag \'%s\' is not a local tag') % n)
3718 3714 rev_ = nullid
3719 3715 if not message:
3720 3716 # we don't translate commit messages
3721 3717 message = 'Removed tag %s' % ', '.join(names)
3722 3718 elif not opts.get('force'):
3723 3719 for n in names:
3724 3720 if n in repo.tags():
3725 3721 raise util.Abort(_('tag \'%s\' already exists '
3726 3722 '(use -f to force)') % n)
3727 3723 if not opts.get('local'):
3728 3724 p1, p2 = repo.dirstate.parents()
3729 3725 if p2 != nullid:
3730 3726 raise util.Abort(_('uncommitted merge'))
3731 3727 bheads = repo.branchheads()
3732 3728 if not opts.get('force') and bheads and p1 not in bheads:
3733 3729 raise util.Abort(_('not at a branch head (use -f to force)'))
3734 3730 r = cmdutil.revsingle(repo, rev_).node()
3735 3731
3736 3732 if not message:
3737 3733 # we don't translate commit messages
3738 3734 message = ('Added tag %s for changeset %s' %
3739 3735 (', '.join(names), short(r)))
3740 3736
3741 3737 date = opts.get('date')
3742 3738 if date:
3743 3739 date = util.parsedate(date)
3744 3740
3745 3741 if opts.get('edit'):
3746 3742 message = ui.edit(message, ui.username())
3747 3743
3748 3744 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
3749 3745
3750 3746 def tags(ui, repo):
3751 3747 """list repository tags
3752 3748
3753 3749 This lists both regular and local tags. When the -v/--verbose
3754 3750 switch is used, a third column "local" is printed for local tags.
3755 3751
3756 3752 Returns 0 on success.
3757 3753 """
3758 3754
3759 3755 hexfunc = ui.debugflag and hex or short
3760 3756 tagtype = ""
3761 3757
3762 3758 for t, n in reversed(repo.tagslist()):
3763 3759 if ui.quiet:
3764 3760 ui.write("%s\n" % t)
3765 3761 continue
3766 3762
3767 3763 try:
3768 3764 hn = hexfunc(n)
3769 3765 r = "%5d:%s" % (repo.changelog.rev(n), hn)
3770 3766 except error.LookupError:
3771 3767 r = " ?:%s" % hn
3772 3768 else:
3773 3769 spaces = " " * (30 - encoding.colwidth(t))
3774 3770 if ui.verbose:
3775 3771 if repo.tagtype(t) == 'local':
3776 3772 tagtype = " local"
3777 3773 else:
3778 3774 tagtype = ""
3779 3775 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
3780 3776
3781 3777 def tip(ui, repo, **opts):
3782 3778 """show the tip revision
3783 3779
3784 3780 The tip revision (usually just called the tip) is the changeset
3785 3781 most recently added to the repository (and therefore the most
3786 3782 recently changed head).
3787 3783
3788 3784 If you have just made a commit, that commit will be the tip. If
3789 3785 you have just pulled changes from another repository, the tip of
3790 3786 that repository becomes the current tip. The "tip" tag is special
3791 3787 and cannot be renamed or assigned to a different changeset.
3792 3788
3793 3789 Returns 0 on success.
3794 3790 """
3795 3791 displayer = cmdutil.show_changeset(ui, repo, opts)
3796 3792 displayer.show(repo[len(repo) - 1])
3797 3793 displayer.close()
3798 3794
3799 3795 def unbundle(ui, repo, fname1, *fnames, **opts):
3800 3796 """apply one or more changegroup files
3801 3797
3802 3798 Apply one or more compressed changegroup files generated by the
3803 3799 bundle command.
3804 3800
3805 3801 Returns 0 on success, 1 if an update has unresolved files.
3806 3802 """
3807 3803 fnames = (fname1,) + fnames
3808 3804
3809 3805 lock = repo.lock()
3810 3806 try:
3811 3807 for fname in fnames:
3812 3808 f = url.open(ui, fname)
3813 3809 gen = changegroup.readbundle(f, fname)
3814 3810 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname,
3815 3811 lock=lock)
3816 3812 finally:
3817 3813 lock.release()
3818 3814
3819 3815 return postincoming(ui, repo, modheads, opts.get('update'), None)
3820 3816
3821 3817 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
3822 3818 """update working directory (or switch revisions)
3823 3819
3824 3820 Update the repository's working directory to the specified
3825 3821 changeset. If no changeset is specified, update to the tip of the
3826 3822 current named branch.
3827 3823
3828 3824 If the changeset is not a descendant of the working directory's
3829 3825 parent, the update is aborted. With the -c/--check option, the
3830 3826 working directory is checked for uncommitted changes; if none are
3831 3827 found, the working directory is updated to the specified
3832 3828 changeset.
3833 3829
3834 3830 The following rules apply when the working directory contains
3835 3831 uncommitted changes:
3836 3832
3837 3833 1. If neither -c/--check nor -C/--clean is specified, and if
3838 3834 the requested changeset is an ancestor or descendant of
3839 3835 the working directory's parent, the uncommitted changes
3840 3836 are merged into the requested changeset and the merged
3841 3837 result is left uncommitted. If the requested changeset is
3842 3838 not an ancestor or descendant (that is, it is on another
3843 3839 branch), the update is aborted and the uncommitted changes
3844 3840 are preserved.
3845 3841
3846 3842 2. With the -c/--check option, the update is aborted and the
3847 3843 uncommitted changes are preserved.
3848 3844
3849 3845 3. With the -C/--clean option, uncommitted changes are discarded and
3850 3846 the working directory is updated to the requested changeset.
3851 3847
3852 3848 Use null as the changeset to remove the working directory (like
3853 3849 :hg:`clone -U`).
3854 3850
3855 3851 If you want to update just one file to an older changeset, use
3856 3852 :hg:`revert`.
3857 3853
3858 3854 See :hg:`help dates` for a list of formats valid for -d/--date.
3859 3855
3860 3856 Returns 0 on success, 1 if there are unresolved files.
3861 3857 """
3862 3858 if rev and node:
3863 3859 raise util.Abort(_("please specify just one revision"))
3864 3860
3865 3861 if not rev:
3866 3862 rev = node
3867 3863
3868 3864 rev = cmdutil.revsingle(repo, rev, rev).rev()
3869 3865
3870 3866 if check and clean:
3871 3867 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
3872 3868
3873 3869 if check:
3874 3870 # we could use dirty() but we can ignore merge and branch trivia
3875 3871 c = repo[None]
3876 3872 if c.modified() or c.added() or c.removed():
3877 3873 raise util.Abort(_("uncommitted local changes"))
3878 3874
3879 3875 if date:
3880 3876 if rev:
3881 3877 raise util.Abort(_("you can't specify a revision and a date"))
3882 3878 rev = cmdutil.finddate(ui, repo, date)
3883 3879
3884 3880 if clean or check:
3885 3881 return hg.clean(repo, rev)
3886 3882 else:
3887 3883 return hg.update(repo, rev)
3888 3884
3889 3885 def verify(ui, repo):
3890 3886 """verify the integrity of the repository
3891 3887
3892 3888 Verify the integrity of the current repository.
3893 3889
3894 3890 This will perform an extensive check of the repository's
3895 3891 integrity, validating the hashes and checksums of each entry in
3896 3892 the changelog, manifest, and tracked files, as well as the
3897 3893 integrity of their crosslinks and indices.
3898 3894
3899 3895 Returns 0 on success, 1 if errors are encountered.
3900 3896 """
3901 3897 return hg.verify(repo)
3902 3898
3903 3899 def version_(ui):
3904 3900 """output version and copyright information"""
3905 3901 ui.write(_("Mercurial Distributed SCM (version %s)\n")
3906 3902 % util.version())
3907 3903 ui.status(_(
3908 3904 "(see http://mercurial.selenic.com for more information)\n"
3909 3905 "\nCopyright (C) 2005-2010 Matt Mackall and others\n"
3910 3906 "This is free software; see the source for copying conditions. "
3911 3907 "There is NO\nwarranty; "
3912 3908 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
3913 3909 ))
3914 3910
3915 3911 # Command options and aliases are listed here, alphabetically
3916 3912
3917 3913 globalopts = [
3918 3914 ('R', 'repository', '',
3919 3915 _('repository root directory or name of overlay bundle file'),
3920 3916 _('REPO')),
3921 3917 ('', 'cwd', '',
3922 3918 _('change working directory'), _('DIR')),
3923 3919 ('y', 'noninteractive', None,
3924 3920 _('do not prompt, assume \'yes\' for any required answers')),
3925 3921 ('q', 'quiet', None, _('suppress output')),
3926 3922 ('v', 'verbose', None, _('enable additional output')),
3927 3923 ('', 'config', [],
3928 3924 _('set/override config option (use \'section.name=value\')'),
3929 3925 _('CONFIG')),
3930 3926 ('', 'debug', None, _('enable debugging output')),
3931 3927 ('', 'debugger', None, _('start debugger')),
3932 3928 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
3933 3929 _('ENCODE')),
3934 3930 ('', 'encodingmode', encoding.encodingmode,
3935 3931 _('set the charset encoding mode'), _('MODE')),
3936 3932 ('', 'traceback', None, _('always print a traceback on exception')),
3937 3933 ('', 'time', None, _('time how long the command takes')),
3938 3934 ('', 'profile', None, _('print command execution profile')),
3939 3935 ('', 'version', None, _('output version information and exit')),
3940 3936 ('h', 'help', None, _('display help and exit')),
3941 3937 ]
3942 3938
3943 3939 dryrunopts = [('n', 'dry-run', None,
3944 3940 _('do not perform actions, just print output'))]
3945 3941
3946 3942 remoteopts = [
3947 3943 ('e', 'ssh', '',
3948 3944 _('specify ssh command to use'), _('CMD')),
3949 3945 ('', 'remotecmd', '',
3950 3946 _('specify hg command to run on the remote side'), _('CMD')),
3951 3947 ('', 'insecure', None,
3952 3948 _('do not verify server certificate (ignoring web.cacerts config)')),
3953 3949 ]
3954 3950
3955 3951 walkopts = [
3956 3952 ('I', 'include', [],
3957 3953 _('include names matching the given patterns'), _('PATTERN')),
3958 3954 ('X', 'exclude', [],
3959 3955 _('exclude names matching the given patterns'), _('PATTERN')),
3960 3956 ]
3961 3957
3962 3958 commitopts = [
3963 3959 ('m', 'message', '',
3964 3960 _('use text as commit message'), _('TEXT')),
3965 3961 ('l', 'logfile', '',
3966 3962 _('read commit message from file'), _('FILE')),
3967 3963 ]
3968 3964
3969 3965 commitopts2 = [
3970 3966 ('d', 'date', '',
3971 3967 _('record datecode as commit date'), _('DATE')),
3972 3968 ('u', 'user', '',
3973 3969 _('record the specified user as committer'), _('USER')),
3974 3970 ]
3975 3971
3976 3972 templateopts = [
3977 3973 ('', 'style', '',
3978 3974 _('display using template map file'), _('STYLE')),
3979 3975 ('', 'template', '',
3980 3976 _('display with template'), _('TEMPLATE')),
3981 3977 ]
3982 3978
3983 3979 logopts = [
3984 3980 ('p', 'patch', None, _('show patch')),
3985 3981 ('g', 'git', None, _('use git extended diff format')),
3986 3982 ('l', 'limit', '',
3987 3983 _('limit number of changes displayed'), _('NUM')),
3988 3984 ('M', 'no-merges', None, _('do not show merges')),
3989 3985 ('', 'stat', None, _('output diffstat-style summary of changes')),
3990 3986 ] + templateopts
3991 3987
3992 3988 diffopts = [
3993 3989 ('a', 'text', None, _('treat all files as text')),
3994 3990 ('g', 'git', None, _('use git extended diff format')),
3995 3991 ('', 'nodates', None, _('omit dates from diff headers'))
3996 3992 ]
3997 3993
3998 3994 diffopts2 = [
3999 3995 ('p', 'show-function', None, _('show which function each change is in')),
4000 3996 ('', 'reverse', None, _('produce a diff that undoes the changes')),
4001 3997 ('w', 'ignore-all-space', None,
4002 3998 _('ignore white space when comparing lines')),
4003 3999 ('b', 'ignore-space-change', None,
4004 4000 _('ignore changes in the amount of white space')),
4005 4001 ('B', 'ignore-blank-lines', None,
4006 4002 _('ignore changes whose lines are all blank')),
4007 4003 ('U', 'unified', '',
4008 4004 _('number of lines of context to show'), _('NUM')),
4009 4005 ('', 'stat', None, _('output diffstat-style summary of changes')),
4010 4006 ]
4011 4007
4012 4008 similarityopts = [
4013 4009 ('s', 'similarity', '',
4014 4010 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
4015 4011 ]
4016 4012
4017 4013 subrepoopts = [
4018 4014 ('S', 'subrepos', None,
4019 4015 _('recurse into subrepositories'))
4020 4016 ]
4021 4017
4022 4018 table = {
4023 4019 "^add": (add, walkopts + subrepoopts + dryrunopts,
4024 4020 _('[OPTION]... [FILE]...')),
4025 4021 "addremove":
4026 4022 (addremove, similarityopts + walkopts + dryrunopts,
4027 4023 _('[OPTION]... [FILE]...')),
4028 4024 "^annotate|blame":
4029 4025 (annotate,
4030 4026 [('r', 'rev', '',
4031 4027 _('annotate the specified revision'), _('REV')),
4032 4028 ('', 'follow', None,
4033 4029 _('follow copies/renames and list the filename (DEPRECATED)')),
4034 4030 ('', 'no-follow', None, _("don't follow copies and renames")),
4035 4031 ('a', 'text', None, _('treat all files as text')),
4036 4032 ('u', 'user', None, _('list the author (long with -v)')),
4037 4033 ('f', 'file', None, _('list the filename')),
4038 4034 ('d', 'date', None, _('list the date (short with -q)')),
4039 4035 ('n', 'number', None, _('list the revision number (default)')),
4040 4036 ('c', 'changeset', None, _('list the changeset')),
4041 4037 ('l', 'line-number', None,
4042 4038 _('show line number at the first appearance'))
4043 4039 ] + walkopts,
4044 4040 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
4045 4041 "archive":
4046 4042 (archive,
4047 4043 [('', 'no-decode', None, _('do not pass files through decoders')),
4048 4044 ('p', 'prefix', '',
4049 4045 _('directory prefix for files in archive'), _('PREFIX')),
4050 4046 ('r', 'rev', '',
4051 4047 _('revision to distribute'), _('REV')),
4052 4048 ('t', 'type', '',
4053 4049 _('type of distribution to create'), _('TYPE')),
4054 4050 ] + subrepoopts + walkopts,
4055 4051 _('[OPTION]... DEST')),
4056 4052 "backout":
4057 4053 (backout,
4058 4054 [('', 'merge', None,
4059 4055 _('merge with old dirstate parent after backout')),
4060 4056 ('', 'parent', '',
4061 4057 _('parent to choose when backing out merge'), _('REV')),
4062 4058 ('t', 'tool', '',
4063 4059 _('specify merge tool')),
4064 4060 ('r', 'rev', '',
4065 4061 _('revision to backout'), _('REV')),
4066 4062 ] + walkopts + commitopts + commitopts2,
4067 4063 _('[OPTION]... [-r] REV')),
4068 4064 "bisect":
4069 4065 (bisect,
4070 4066 [('r', 'reset', False, _('reset bisect state')),
4071 4067 ('g', 'good', False, _('mark changeset good')),
4072 4068 ('b', 'bad', False, _('mark changeset bad')),
4073 4069 ('s', 'skip', False, _('skip testing changeset')),
4074 4070 ('c', 'command', '',
4075 4071 _('use command to check changeset state'), _('CMD')),
4076 4072 ('U', 'noupdate', False, _('do not update to target'))],
4077 4073 _("[-gbsr] [-U] [-c CMD] [REV]")),
4078 4074 "branch":
4079 4075 (branch,
4080 4076 [('f', 'force', None,
4081 4077 _('set branch name even if it shadows an existing branch')),
4082 4078 ('C', 'clean', None, _('reset branch name to parent branch name'))],
4083 4079 _('[-fC] [NAME]')),
4084 4080 "branches":
4085 4081 (branches,
4086 4082 [('a', 'active', False,
4087 4083 _('show only branches that have unmerged heads')),
4088 4084 ('c', 'closed', False,
4089 4085 _('show normal and closed branches'))],
4090 4086 _('[-ac]')),
4091 4087 "bundle":
4092 4088 (bundle,
4093 4089 [('f', 'force', None,
4094 4090 _('run even when the destination is unrelated')),
4095 4091 ('r', 'rev', [],
4096 4092 _('a changeset intended to be added to the destination'),
4097 4093 _('REV')),
4098 4094 ('b', 'branch', [],
4099 4095 _('a specific branch you would like to bundle'),
4100 4096 _('BRANCH')),
4101 4097 ('', 'base', [],
4102 4098 _('a base changeset assumed to be available at the destination'),
4103 4099 _('REV')),
4104 4100 ('a', 'all', None, _('bundle all changesets in the repository')),
4105 4101 ('t', 'type', 'bzip2',
4106 4102 _('bundle compression type to use'), _('TYPE')),
4107 4103 ] + remoteopts,
4108 4104 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]')),
4109 4105 "cat":
4110 4106 (cat,
4111 4107 [('o', 'output', '',
4112 4108 _('print output to file with formatted name'), _('FORMAT')),
4113 4109 ('r', 'rev', '',
4114 4110 _('print the given revision'), _('REV')),
4115 4111 ('', 'decode', None, _('apply any matching decode filter')),
4116 4112 ] + walkopts,
4117 4113 _('[OPTION]... FILE...')),
4118 4114 "^clone":
4119 4115 (clone,
4120 4116 [('U', 'noupdate', None,
4121 4117 _('the clone will include an empty working copy (only a repository)')),
4122 4118 ('u', 'updaterev', '',
4123 4119 _('revision, tag or branch to check out'), _('REV')),
4124 4120 ('r', 'rev', [],
4125 4121 _('include the specified changeset'), _('REV')),
4126 4122 ('b', 'branch', [],
4127 4123 _('clone only the specified branch'), _('BRANCH')),
4128 4124 ('', 'pull', None, _('use pull protocol to copy metadata')),
4129 4125 ('', 'uncompressed', None,
4130 4126 _('use uncompressed transfer (fast over LAN)')),
4131 4127 ] + remoteopts,
4132 4128 _('[OPTION]... SOURCE [DEST]')),
4133 4129 "^commit|ci":
4134 4130 (commit,
4135 4131 [('A', 'addremove', None,
4136 4132 _('mark new/missing files as added/removed before committing')),
4137 4133 ('', 'close-branch', None,
4138 4134 _('mark a branch as closed, hiding it from the branch list')),
4139 4135 ] + walkopts + commitopts + commitopts2,
4140 4136 _('[OPTION]... [FILE]...')),
4141 4137 "copy|cp":
4142 4138 (copy,
4143 4139 [('A', 'after', None, _('record a copy that has already occurred')),
4144 4140 ('f', 'force', None,
4145 4141 _('forcibly copy over an existing managed file')),
4146 4142 ] + walkopts + dryrunopts,
4147 4143 _('[OPTION]... [SOURCE]... DEST')),
4148 4144 "debugancestor": (debugancestor, [], _('[INDEX] REV1 REV2')),
4149 4145 "debugbuilddag":
4150 4146 (debugbuilddag,
4151 4147 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
4152 4148 ('a', 'appended-file', None, _('add single file all revs append to')),
4153 4149 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
4154 4150 ('n', 'new-file', None, _('add new file at each rev')),
4155 4151 ],
4156 4152 _('[OPTION]... TEXT')),
4157 4153 "debugcheckstate": (debugcheckstate, [], ''),
4158 4154 "debugcommands": (debugcommands, [], _('[COMMAND]')),
4159 4155 "debugcomplete":
4160 4156 (debugcomplete,
4161 4157 [('o', 'options', None, _('show the command options'))],
4162 4158 _('[-o] CMD')),
4163 4159 "debugdag":
4164 4160 (debugdag,
4165 4161 [('t', 'tags', None, _('use tags as labels')),
4166 4162 ('b', 'branches', None, _('annotate with branch names')),
4167 4163 ('', 'dots', None, _('use dots for runs')),
4168 4164 ('s', 'spaces', None, _('separate elements by spaces')),
4169 4165 ],
4170 4166 _('[OPTION]... [FILE [REV]...]')),
4171 4167 "debugdate":
4172 4168 (debugdate,
4173 4169 [('e', 'extended', None, _('try extended date formats'))],
4174 4170 _('[-e] DATE [RANGE]')),
4175 4171 "debugdata": (debugdata, [], _('FILE REV')),
4176 4172 "debugfsinfo": (debugfsinfo, [], _('[PATH]')),
4177 4173 "debugindex": (debugindex,
4178 4174 [('f', 'format', 0, _('revlog format'), _('FORMAT'))],
4179 4175 _('FILE')),
4180 4176 "debugindexdot": (debugindexdot, [], _('FILE')),
4181 4177 "debuginstall": (debuginstall, [], ''),
4182 4178 "debugpushkey": (debugpushkey, [], _('REPO NAMESPACE [KEY OLD NEW]')),
4183 4179 "debugrebuildstate":
4184 4180 (debugrebuildstate,
4185 4181 [('r', 'rev', '',
4186 4182 _('revision to rebuild to'), _('REV'))],
4187 4183 _('[-r REV] [REV]')),
4188 4184 "debugrename":
4189 4185 (debugrename,
4190 4186 [('r', 'rev', '',
4191 4187 _('revision to debug'), _('REV'))],
4192 4188 _('[-r REV] FILE')),
4193 4189 "debugrevspec":
4194 4190 (debugrevspec, [], ('REVSPEC')),
4195 4191 "debugsetparents":
4196 4192 (debugsetparents, [], _('REV1 [REV2]')),
4197 4193 "debugstate":
4198 4194 (debugstate,
4199 4195 [('', 'nodates', None, _('do not display the saved mtime'))],
4200 4196 _('[OPTION]...')),
4201 4197 "debugsub":
4202 4198 (debugsub,
4203 4199 [('r', 'rev', '',
4204 4200 _('revision to check'), _('REV'))],
4205 4201 _('[-r REV] [REV]')),
4206 4202 "debugwalk": (debugwalk, walkopts, _('[OPTION]... [FILE]...')),
4207 4203 "^diff":
4208 4204 (diff,
4209 4205 [('r', 'rev', [],
4210 4206 _('revision'), _('REV')),
4211 4207 ('c', 'change', '',
4212 4208 _('change made by revision'), _('REV'))
4213 4209 ] + diffopts + diffopts2 + walkopts + subrepoopts,
4214 4210 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...')),
4215 4211 "^export":
4216 4212 (export,
4217 4213 [('o', 'output', '',
4218 4214 _('print output to file with formatted name'), _('FORMAT')),
4219 4215 ('', 'switch-parent', None, _('diff against the second parent')),
4220 4216 ('r', 'rev', [],
4221 4217 _('revisions to export'), _('REV')),
4222 4218 ] + diffopts,
4223 4219 _('[OPTION]... [-o OUTFILESPEC] REV...')),
4224 4220 "^forget":
4225 4221 (forget,
4226 4222 [] + walkopts,
4227 4223 _('[OPTION]... FILE...')),
4228 4224 "grep":
4229 4225 (grep,
4230 4226 [('0', 'print0', None, _('end fields with NUL')),
4231 4227 ('', 'all', None, _('print all revisions that match')),
4232 4228 ('f', 'follow', None,
4233 4229 _('follow changeset history,'
4234 4230 ' or file history across copies and renames')),
4235 4231 ('i', 'ignore-case', None, _('ignore case when matching')),
4236 4232 ('l', 'files-with-matches', None,
4237 4233 _('print only filenames and revisions that match')),
4238 4234 ('n', 'line-number', None, _('print matching line numbers')),
4239 4235 ('r', 'rev', [],
4240 4236 _('only search files changed within revision range'), _('REV')),
4241 4237 ('u', 'user', None, _('list the author (long with -v)')),
4242 4238 ('d', 'date', None, _('list the date (short with -q)')),
4243 4239 ] + walkopts,
4244 4240 _('[OPTION]... PATTERN [FILE]...')),
4245 4241 "heads":
4246 4242 (heads,
4247 4243 [('r', 'rev', '',
4248 4244 _('show only heads which are descendants of STARTREV'),
4249 4245 _('STARTREV')),
4250 4246 ('t', 'topo', False, _('show topological heads only')),
4251 4247 ('a', 'active', False,
4252 4248 _('show active branchheads only (DEPRECATED)')),
4253 4249 ('c', 'closed', False,
4254 4250 _('show normal and closed branch heads')),
4255 4251 ] + templateopts,
4256 4252 _('[-ac] [-r STARTREV] [REV]...')),
4257 4253 "help": (help_, [], _('[TOPIC]')),
4258 4254 "identify|id":
4259 4255 (identify,
4260 4256 [('r', 'rev', '',
4261 4257 _('identify the specified revision'), _('REV')),
4262 4258 ('n', 'num', None, _('show local revision number')),
4263 4259 ('i', 'id', None, _('show global revision id')),
4264 4260 ('b', 'branch', None, _('show branch')),
4265 4261 ('t', 'tags', None, _('show tags'))],
4266 4262 _('[-nibt] [-r REV] [SOURCE]')),
4267 4263 "import|patch":
4268 4264 (import_,
4269 4265 [('p', 'strip', 1,
4270 4266 _('directory strip option for patch. This has the same '
4271 4267 'meaning as the corresponding patch option'),
4272 4268 _('NUM')),
4273 4269 ('b', 'base', '',
4274 4270 _('base path'), _('PATH')),
4275 4271 ('f', 'force', None,
4276 4272 _('skip check for outstanding uncommitted changes')),
4277 4273 ('', 'no-commit', None,
4278 4274 _("don't commit, just update the working directory")),
4279 4275 ('', 'exact', None,
4280 4276 _('apply patch to the nodes from which it was generated')),
4281 4277 ('', 'import-branch', None,
4282 4278 _('use any branch information in patch (implied by --exact)'))] +
4283 4279 commitopts + commitopts2 + similarityopts,
4284 4280 _('[OPTION]... PATCH...')),
4285 4281 "incoming|in":
4286 4282 (incoming,
4287 4283 [('f', 'force', None,
4288 4284 _('run even if remote repository is unrelated')),
4289 4285 ('n', 'newest-first', None, _('show newest record first')),
4290 4286 ('', 'bundle', '',
4291 4287 _('file to store the bundles into'), _('FILE')),
4292 4288 ('r', 'rev', [],
4293 4289 _('a remote changeset intended to be added'), _('REV')),
4294 4290 ('b', 'branch', [],
4295 4291 _('a specific branch you would like to pull'), _('BRANCH')),
4296 4292 ] + logopts + remoteopts + subrepoopts,
4297 4293 _('[-p] [-n] [-M] [-f] [-r REV]...'
4298 4294 ' [--bundle FILENAME] [SOURCE]')),
4299 4295 "^init":
4300 4296 (init,
4301 4297 remoteopts,
4302 4298 _('[-e CMD] [--remotecmd CMD] [DEST]')),
4303 4299 "locate":
4304 4300 (locate,
4305 4301 [('r', 'rev', '',
4306 4302 _('search the repository as it is in REV'), _('REV')),
4307 4303 ('0', 'print0', None,
4308 4304 _('end filenames with NUL, for use with xargs')),
4309 4305 ('f', 'fullpath', None,
4310 4306 _('print complete paths from the filesystem root')),
4311 4307 ] + walkopts,
4312 4308 _('[OPTION]... [PATTERN]...')),
4313 4309 "^log|history":
4314 4310 (log,
4315 4311 [('f', 'follow', None,
4316 4312 _('follow changeset history,'
4317 4313 ' or file history across copies and renames')),
4318 4314 ('', 'follow-first', None,
4319 4315 _('only follow the first parent of merge changesets')),
4320 4316 ('d', 'date', '',
4321 4317 _('show revisions matching date spec'), _('DATE')),
4322 4318 ('C', 'copies', None, _('show copied files')),
4323 4319 ('k', 'keyword', [],
4324 4320 _('do case-insensitive search for a given text'), _('TEXT')),
4325 4321 ('r', 'rev', [],
4326 4322 _('show the specified revision or range'), _('REV')),
4327 4323 ('', 'removed', None, _('include revisions where files were removed')),
4328 4324 ('m', 'only-merges', None, _('show only merges')),
4329 4325 ('u', 'user', [],
4330 4326 _('revisions committed by user'), _('USER')),
4331 4327 ('', 'only-branch', [],
4332 4328 _('show only changesets within the given named branch (DEPRECATED)'),
4333 4329 _('BRANCH')),
4334 4330 ('b', 'branch', [],
4335 4331 _('show changesets within the given named branch'), _('BRANCH')),
4336 4332 ('P', 'prune', [],
4337 4333 _('do not display revision or any of its ancestors'), _('REV')),
4338 4334 ] + logopts + walkopts,
4339 4335 _('[OPTION]... [FILE]')),
4340 4336 "manifest":
4341 4337 (manifest,
4342 4338 [('r', 'rev', '',
4343 4339 _('revision to display'), _('REV'))],
4344 4340 _('[-r REV]')),
4345 4341 "^merge":
4346 4342 (merge,
4347 4343 [('f', 'force', None, _('force a merge with outstanding changes')),
4348 4344 ('t', 'tool', '', _('specify merge tool')),
4349 4345 ('r', 'rev', '',
4350 4346 _('revision to merge'), _('REV')),
4351 4347 ('P', 'preview', None,
4352 4348 _('review revisions to merge (no merge is performed)'))],
4353 4349 _('[-P] [-f] [[-r] REV]')),
4354 4350 "outgoing|out":
4355 4351 (outgoing,
4356 4352 [('f', 'force', None,
4357 4353 _('run even when the destination is unrelated')),
4358 4354 ('r', 'rev', [],
4359 4355 _('a changeset intended to be included in the destination'),
4360 4356 _('REV')),
4361 4357 ('n', 'newest-first', None, _('show newest record first')),
4362 4358 ('b', 'branch', [],
4363 4359 _('a specific branch you would like to push'), _('BRANCH')),
4364 4360 ] + logopts + remoteopts + subrepoopts,
4365 4361 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
4366 4362 "parents":
4367 4363 (parents,
4368 4364 [('r', 'rev', '',
4369 4365 _('show parents of the specified revision'), _('REV')),
4370 4366 ] + templateopts,
4371 4367 _('[-r REV] [FILE]')),
4372 4368 "paths": (paths, [], _('[NAME]')),
4373 4369 "^pull":
4374 4370 (pull,
4375 4371 [('u', 'update', None,
4376 4372 _('update to new branch head if changesets were pulled')),
4377 4373 ('f', 'force', None,
4378 4374 _('run even when remote repository is unrelated')),
4379 4375 ('r', 'rev', [],
4380 4376 _('a remote changeset intended to be added'), _('REV')),
4381 4377 ('b', 'branch', [],
4382 4378 _('a specific branch you would like to pull'), _('BRANCH')),
4383 4379 ] + remoteopts,
4384 4380 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
4385 4381 "^push":
4386 4382 (push,
4387 4383 [('f', 'force', None, _('force push')),
4388 4384 ('r', 'rev', [],
4389 4385 _('a changeset intended to be included in the destination'),
4390 4386 _('REV')),
4391 4387 ('b', 'branch', [],
4392 4388 _('a specific branch you would like to push'), _('BRANCH')),
4393 4389 ('', 'new-branch', False, _('allow pushing a new branch')),
4394 4390 ] + remoteopts,
4395 4391 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
4396 4392 "recover": (recover, []),
4397 4393 "^remove|rm":
4398 4394 (remove,
4399 4395 [('A', 'after', None, _('record delete for missing files')),
4400 4396 ('f', 'force', None,
4401 4397 _('remove (and delete) file even if added or modified')),
4402 4398 ] + walkopts,
4403 4399 _('[OPTION]... FILE...')),
4404 4400 "rename|move|mv":
4405 4401 (rename,
4406 4402 [('A', 'after', None, _('record a rename that has already occurred')),
4407 4403 ('f', 'force', None,
4408 4404 _('forcibly copy over an existing managed file')),
4409 4405 ] + walkopts + dryrunopts,
4410 4406 _('[OPTION]... SOURCE... DEST')),
4411 4407 "resolve":
4412 4408 (resolve,
4413 4409 [('a', 'all', None, _('select all unresolved files')),
4414 4410 ('l', 'list', None, _('list state of files needing merge')),
4415 4411 ('m', 'mark', None, _('mark files as resolved')),
4416 4412 ('u', 'unmark', None, _('mark files as unresolved')),
4417 4413 ('t', 'tool', '', _('specify merge tool')),
4418 4414 ('n', 'no-status', None, _('hide status prefix'))]
4419 4415 + walkopts,
4420 4416 _('[OPTION]... [FILE]...')),
4421 4417 "revert":
4422 4418 (revert,
4423 4419 [('a', 'all', None, _('revert all changes when no arguments given')),
4424 4420 ('d', 'date', '',
4425 4421 _('tipmost revision matching date'), _('DATE')),
4426 4422 ('r', 'rev', '',
4427 4423 _('revert to the specified revision'), _('REV')),
4428 4424 ('', 'no-backup', None, _('do not save backup copies of files')),
4429 4425 ] + walkopts + dryrunopts,
4430 4426 _('[OPTION]... [-r REV] [NAME]...')),
4431 4427 "rollback": (rollback, dryrunopts),
4432 4428 "root": (root, []),
4433 4429 "^serve":
4434 4430 (serve,
4435 4431 [('A', 'accesslog', '',
4436 4432 _('name of access log file to write to'), _('FILE')),
4437 4433 ('d', 'daemon', None, _('run server in background')),
4438 4434 ('', 'daemon-pipefds', '',
4439 4435 _('used internally by daemon mode'), _('NUM')),
4440 4436 ('E', 'errorlog', '',
4441 4437 _('name of error log file to write to'), _('FILE')),
4442 4438 # use string type, then we can check if something was passed
4443 4439 ('p', 'port', '',
4444 4440 _('port to listen on (default: 8000)'), _('PORT')),
4445 4441 ('a', 'address', '',
4446 4442 _('address to listen on (default: all interfaces)'), _('ADDR')),
4447 4443 ('', 'prefix', '',
4448 4444 _('prefix path to serve from (default: server root)'), _('PREFIX')),
4449 4445 ('n', 'name', '',
4450 4446 _('name to show in web pages (default: working directory)'),
4451 4447 _('NAME')),
4452 4448 ('', 'web-conf', '',
4453 4449 _('name of the hgweb config file (see "hg help hgweb")'),
4454 4450 _('FILE')),
4455 4451 ('', 'webdir-conf', '',
4456 4452 _('name of the hgweb config file (DEPRECATED)'), _('FILE')),
4457 4453 ('', 'pid-file', '',
4458 4454 _('name of file to write process ID to'), _('FILE')),
4459 4455 ('', 'stdio', None, _('for remote clients')),
4460 4456 ('t', 'templates', '',
4461 4457 _('web templates to use'), _('TEMPLATE')),
4462 4458 ('', 'style', '',
4463 4459 _('template style to use'), _('STYLE')),
4464 4460 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
4465 4461 ('', 'certificate', '',
4466 4462 _('SSL certificate file'), _('FILE'))],
4467 4463 _('[OPTION]...')),
4468 4464 "showconfig|debugconfig":
4469 4465 (showconfig,
4470 4466 [('u', 'untrusted', None, _('show untrusted configuration options'))],
4471 4467 _('[-u] [NAME]...')),
4472 4468 "^summary|sum":
4473 4469 (summary,
4474 4470 [('', 'remote', None, _('check for push and pull'))], '[--remote]'),
4475 4471 "^status|st":
4476 4472 (status,
4477 4473 [('A', 'all', None, _('show status of all files')),
4478 4474 ('m', 'modified', None, _('show only modified files')),
4479 4475 ('a', 'added', None, _('show only added files')),
4480 4476 ('r', 'removed', None, _('show only removed files')),
4481 4477 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
4482 4478 ('c', 'clean', None, _('show only files without changes')),
4483 4479 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
4484 4480 ('i', 'ignored', None, _('show only ignored files')),
4485 4481 ('n', 'no-status', None, _('hide status prefix')),
4486 4482 ('C', 'copies', None, _('show source of copied files')),
4487 4483 ('0', 'print0', None,
4488 4484 _('end filenames with NUL, for use with xargs')),
4489 4485 ('', 'rev', [],
4490 4486 _('show difference from revision'), _('REV')),
4491 4487 ('', 'change', '',
4492 4488 _('list the changed files of a revision'), _('REV')),
4493 4489 ] + walkopts + subrepoopts,
4494 4490 _('[OPTION]... [FILE]...')),
4495 4491 "tag":
4496 4492 (tag,
4497 4493 [('f', 'force', None, _('force tag')),
4498 4494 ('l', 'local', None, _('make the tag local')),
4499 4495 ('r', 'rev', '',
4500 4496 _('revision to tag'), _('REV')),
4501 4497 ('', 'remove', None, _('remove a tag')),
4502 4498 # -l/--local is already there, commitopts cannot be used
4503 4499 ('e', 'edit', None, _('edit commit message')),
4504 4500 ('m', 'message', '',
4505 4501 _('use <text> as commit message'), _('TEXT')),
4506 4502 ] + commitopts2,
4507 4503 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')),
4508 4504 "tags": (tags, [], ''),
4509 4505 "tip":
4510 4506 (tip,
4511 4507 [('p', 'patch', None, _('show patch')),
4512 4508 ('g', 'git', None, _('use git extended diff format')),
4513 4509 ] + templateopts,
4514 4510 _('[-p] [-g]')),
4515 4511 "unbundle":
4516 4512 (unbundle,
4517 4513 [('u', 'update', None,
4518 4514 _('update to new branch head if changesets were unbundled'))],
4519 4515 _('[-u] FILE...')),
4520 4516 "^update|up|checkout|co":
4521 4517 (update,
4522 4518 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
4523 4519 ('c', 'check', None,
4524 4520 _('update across branches if no uncommitted changes')),
4525 4521 ('d', 'date', '',
4526 4522 _('tipmost revision matching date'), _('DATE')),
4527 4523 ('r', 'rev', '',
4528 4524 _('revision'), _('REV'))],
4529 4525 _('[-c] [-C] [-d DATE] [[-r] REV]')),
4530 4526 "verify": (verify, []),
4531 4527 "version": (version_, []),
4532 4528 }
4533 4529
4534 4530 norepo = ("clone init version help debugcommands debugcomplete"
4535 4531 " debugdate debuginstall debugfsinfo debugpushkey")
4536 4532 optionalrepo = ("identify paths serve showconfig debugancestor debugdag"
4537 4533 " debugdata debugindex debugindexdot")
@@ -1,683 +1,683
1 1 # dirstate.py - working directory tracking 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 nullid
9 9 from i18n import _
10 10 import util, ignore, osutil, parsers, encoding
11 11 import struct, os, stat, errno
12 12 import cStringIO
13 13
14 14 _format = ">cllll"
15 15 propertycache = util.propertycache
16 16
17 17 def _finddirs(path):
18 18 pos = path.rfind('/')
19 19 while pos != -1:
20 20 yield path[:pos]
21 21 pos = path.rfind('/', 0, pos)
22 22
23 23 def _incdirs(dirs, path):
24 24 for base in _finddirs(path):
25 25 if base in dirs:
26 26 dirs[base] += 1
27 27 return
28 28 dirs[base] = 1
29 29
30 30 def _decdirs(dirs, path):
31 31 for base in _finddirs(path):
32 32 if dirs[base] > 1:
33 33 dirs[base] -= 1
34 34 return
35 35 del dirs[base]
36 36
37 37 class dirstate(object):
38 38
39 39 def __init__(self, opener, ui, root, validate):
40 40 '''Create a new dirstate object.
41 41
42 42 opener is an open()-like callable that can be used to open the
43 43 dirstate file; root is the root of the directory tracked by
44 44 the dirstate.
45 45 '''
46 46 self._opener = opener
47 47 self._validate = validate
48 48 self._root = root
49 49 self._rootdir = os.path.join(root, '')
50 50 self._dirty = False
51 51 self._dirtypl = False
52 52 self._ui = ui
53 53
54 54 @propertycache
55 55 def _map(self):
56 56 '''Return the dirstate contents as a map from filename to
57 57 (state, mode, size, time).'''
58 58 self._read()
59 59 return self._map
60 60
61 61 @propertycache
62 62 def _copymap(self):
63 63 self._read()
64 64 return self._copymap
65 65
66 66 @propertycache
67 67 def _foldmap(self):
68 68 f = {}
69 69 for name in self._map:
70 70 f[os.path.normcase(name)] = name
71 71 return f
72 72
73 73 @propertycache
74 74 def _branch(self):
75 75 try:
76 76 return self._opener("branch").read().strip() or "default"
77 77 except IOError:
78 78 return "default"
79 79
80 80 @propertycache
81 81 def _pl(self):
82 82 try:
83 83 st = self._opener("dirstate").read(40)
84 84 l = len(st)
85 85 if l == 40:
86 86 return st[:20], st[20:40]
87 87 elif l > 0 and l < 40:
88 88 raise util.Abort(_('working directory state appears damaged!'))
89 89 except IOError, err:
90 90 if err.errno != errno.ENOENT:
91 91 raise
92 92 return [nullid, nullid]
93 93
94 94 @propertycache
95 95 def _dirs(self):
96 96 dirs = {}
97 97 for f, s in self._map.iteritems():
98 98 if s[0] != 'r':
99 99 _incdirs(dirs, f)
100 100 return dirs
101 101
102 102 @propertycache
103 103 def _ignore(self):
104 104 files = [self._join('.hgignore')]
105 105 for name, path in self._ui.configitems("ui"):
106 106 if name == 'ignore' or name.startswith('ignore.'):
107 107 files.append(util.expandpath(path))
108 108 return ignore.ignore(self._root, files, self._ui.warn)
109 109
110 110 @propertycache
111 111 def _slash(self):
112 112 return self._ui.configbool('ui', 'slash') and os.sep != '/'
113 113
114 114 @propertycache
115 115 def _checklink(self):
116 116 return util.checklink(self._root)
117 117
118 118 @propertycache
119 119 def _checkexec(self):
120 120 return util.checkexec(self._root)
121 121
122 122 @propertycache
123 123 def _checkcase(self):
124 124 return not util.checkcase(self._join('.hg'))
125 125
126 126 def _join(self, f):
127 127 # much faster than os.path.join()
128 128 # it's safe because f is always a relative path
129 129 return self._rootdir + f
130 130
131 131 def flagfunc(self, fallback):
132 132 if self._checklink:
133 133 if self._checkexec:
134 134 def f(x):
135 135 p = self._join(x)
136 136 if os.path.islink(p):
137 137 return 'l'
138 138 if util.is_exec(p):
139 139 return 'x'
140 140 return ''
141 141 return f
142 142 def f(x):
143 143 if os.path.islink(self._join(x)):
144 144 return 'l'
145 145 if 'x' in fallback(x):
146 146 return 'x'
147 147 return ''
148 148 return f
149 149 if self._checkexec:
150 150 def f(x):
151 151 if 'l' in fallback(x):
152 152 return 'l'
153 153 if util.is_exec(self._join(x)):
154 154 return 'x'
155 155 return ''
156 156 return f
157 157 return fallback
158 158
159 159 def getcwd(self):
160 160 cwd = os.getcwd()
161 161 if cwd == self._root:
162 162 return ''
163 163 # self._root ends with a path separator if self._root is '/' or 'C:\'
164 164 rootsep = self._root
165 165 if not util.endswithsep(rootsep):
166 166 rootsep += os.sep
167 167 if cwd.startswith(rootsep):
168 168 return cwd[len(rootsep):]
169 169 else:
170 170 # we're outside the repo. return an absolute path.
171 171 return cwd
172 172
173 173 def pathto(self, f, cwd=None):
174 174 if cwd is None:
175 175 cwd = self.getcwd()
176 176 path = util.pathto(self._root, cwd, f)
177 177 if self._slash:
178 178 return util.normpath(path)
179 179 return path
180 180
181 181 def __getitem__(self, key):
182 182 '''Return the current state of key (a filename) in the dirstate.
183 183
184 184 States are:
185 185 n normal
186 186 m needs merging
187 187 r marked for removal
188 188 a marked for addition
189 189 ? not tracked
190 190 '''
191 191 return self._map.get(key, ("?",))[0]
192 192
193 193 def __contains__(self, key):
194 194 return key in self._map
195 195
196 196 def __iter__(self):
197 197 for x in sorted(self._map):
198 198 yield x
199 199
200 200 def parents(self):
201 201 return [self._validate(p) for p in self._pl]
202 202
203 203 def branch(self):
204 204 return encoding.tolocal(self._branch)
205 205
206 206 def setparents(self, p1, p2=nullid):
207 207 self._dirty = self._dirtypl = True
208 208 self._pl = p1, p2
209 209
210 210 def setbranch(self, branch):
211 211 if branch in ['tip', '.', 'null']:
212 212 raise util.Abort(_('the name \'%s\' is reserved') % branch)
213 213 self._branch = encoding.fromlocal(branch)
214 214 self._opener("branch", "w").write(self._branch + '\n')
215 215
216 216 def _read(self):
217 217 self._map = {}
218 218 self._copymap = {}
219 219 try:
220 220 st = self._opener("dirstate").read()
221 221 except IOError, err:
222 222 if err.errno != errno.ENOENT:
223 223 raise
224 224 return
225 225 if not st:
226 226 return
227 227
228 228 p = parsers.parse_dirstate(self._map, self._copymap, st)
229 229 if not self._dirtypl:
230 230 self._pl = p
231 231
232 232 def invalidate(self):
233 233 for a in ("_map", "_copymap", "_foldmap", "_branch", "_pl", "_dirs",
234 234 "_ignore"):
235 235 if a in self.__dict__:
236 236 delattr(self, a)
237 237 self._dirty = False
238 238
239 239 def copy(self, source, dest):
240 240 """Mark dest as a copy of source. Unmark dest if source is None."""
241 241 if source == dest:
242 242 return
243 243 self._dirty = True
244 244 if source is not None:
245 245 self._copymap[dest] = source
246 246 elif dest in self._copymap:
247 247 del self._copymap[dest]
248 248
249 249 def copied(self, file):
250 250 return self._copymap.get(file, None)
251 251
252 252 def copies(self):
253 253 return self._copymap
254 254
255 255 def _droppath(self, f):
256 256 if self[f] not in "?r" and "_dirs" in self.__dict__:
257 257 _decdirs(self._dirs, f)
258 258
259 259 def _addpath(self, f, check=False):
260 260 oldstate = self[f]
261 261 if check or oldstate == "r":
262 262 if '\r' in f or '\n' in f:
263 263 raise util.Abort(
264 264 _("'\\n' and '\\r' disallowed in filenames: %r") % f)
265 265 if f in self._dirs:
266 266 raise util.Abort(_('directory %r already in dirstate') % f)
267 267 # shadows
268 268 for d in _finddirs(f):
269 269 if d in self._dirs:
270 270 break
271 271 if d in self._map and self[d] != 'r':
272 272 raise util.Abort(
273 273 _('file %r in dirstate clashes with %r') % (d, f))
274 274 if oldstate in "?r" and "_dirs" in self.__dict__:
275 275 _incdirs(self._dirs, f)
276 276
277 277 def normal(self, f):
278 278 '''Mark a file normal and clean.'''
279 279 self._dirty = True
280 280 self._addpath(f)
281 281 s = os.lstat(self._join(f))
282 282 self._map[f] = ('n', s.st_mode, s.st_size, int(s.st_mtime))
283 283 if f in self._copymap:
284 284 del self._copymap[f]
285 285
286 286 def normallookup(self, f):
287 287 '''Mark a file normal, but possibly dirty.'''
288 288 if self._pl[1] != nullid and f in self._map:
289 289 # if there is a merge going on and the file was either
290 290 # in state 'm' (-1) or coming from other parent (-2) before
291 291 # being removed, restore that state.
292 292 entry = self._map[f]
293 293 if entry[0] == 'r' and entry[2] in (-1, -2):
294 294 source = self._copymap.get(f)
295 295 if entry[2] == -1:
296 296 self.merge(f)
297 297 elif entry[2] == -2:
298 298 self.otherparent(f)
299 299 if source:
300 300 self.copy(source, f)
301 301 return
302 302 if entry[0] == 'm' or entry[0] == 'n' and entry[2] == -2:
303 303 return
304 304 self._dirty = True
305 305 self._addpath(f)
306 306 self._map[f] = ('n', 0, -1, -1)
307 307 if f in self._copymap:
308 308 del self._copymap[f]
309 309
310 310 def otherparent(self, f):
311 311 '''Mark as coming from the other parent, always dirty.'''
312 312 if self._pl[1] == nullid:
313 313 raise util.Abort(_("setting %r to other parent "
314 314 "only allowed in merges") % f)
315 315 self._dirty = True
316 316 self._addpath(f)
317 317 self._map[f] = ('n', 0, -2, -1)
318 318 if f in self._copymap:
319 319 del self._copymap[f]
320 320
321 321 def add(self, f):
322 322 '''Mark a file added.'''
323 323 self._dirty = True
324 324 self._addpath(f, True)
325 325 self._map[f] = ('a', 0, -1, -1)
326 326 if f in self._copymap:
327 327 del self._copymap[f]
328 328
329 329 def remove(self, f):
330 330 '''Mark a file removed.'''
331 331 self._dirty = True
332 332 self._droppath(f)
333 333 size = 0
334 334 if self._pl[1] != nullid and f in self._map:
335 335 # backup the previous state
336 336 entry = self._map[f]
337 337 if entry[0] == 'm': # merge
338 338 size = -1
339 339 elif entry[0] == 'n' and entry[2] == -2: # other parent
340 340 size = -2
341 341 self._map[f] = ('r', 0, size, 0)
342 342 if size == 0 and f in self._copymap:
343 343 del self._copymap[f]
344 344
345 345 def merge(self, f):
346 346 '''Mark a file merged.'''
347 347 self._dirty = True
348 348 s = os.lstat(self._join(f))
349 349 self._addpath(f)
350 350 self._map[f] = ('m', s.st_mode, s.st_size, int(s.st_mtime))
351 351 if f in self._copymap:
352 352 del self._copymap[f]
353 353
354 354 def forget(self, f):
355 355 '''Forget a file.'''
356 356 self._dirty = True
357 357 try:
358 358 self._droppath(f)
359 359 del self._map[f]
360 360 except KeyError:
361 361 self._ui.warn(_("not in dirstate: %s\n") % f)
362 362
363 363 def _normalize(self, path, knownpath):
364 364 norm_path = os.path.normcase(path)
365 365 fold_path = self._foldmap.get(norm_path, None)
366 366 if fold_path is None:
367 367 if knownpath or not os.path.lexists(os.path.join(self._root, path)):
368 368 fold_path = path
369 369 else:
370 370 fold_path = self._foldmap.setdefault(norm_path,
371 371 util.fspath(path, self._root))
372 372 return fold_path
373 373
374 374 def clear(self):
375 375 self._map = {}
376 376 if "_dirs" in self.__dict__:
377 377 delattr(self, "_dirs")
378 378 self._copymap = {}
379 379 self._pl = [nullid, nullid]
380 380 self._dirty = True
381 381
382 382 def rebuild(self, parent, files):
383 383 self.clear()
384 384 for f in files:
385 385 if 'x' in files.flags(f):
386 386 self._map[f] = ('n', 0777, -1, 0)
387 387 else:
388 388 self._map[f] = ('n', 0666, -1, 0)
389 389 self._pl = (parent, nullid)
390 390 self._dirty = True
391 391
392 392 def write(self):
393 393 if not self._dirty:
394 394 return
395 395 st = self._opener("dirstate", "w", atomictemp=True)
396 396
397 397 # use the modification time of the newly created temporary file as the
398 398 # filesystem's notion of 'now'
399 399 now = int(util.fstat(st).st_mtime)
400 400
401 401 cs = cStringIO.StringIO()
402 402 copymap = self._copymap
403 403 pack = struct.pack
404 404 write = cs.write
405 405 write("".join(self._pl))
406 406 for f, e in self._map.iteritems():
407 407 if e[0] == 'n' and e[3] == now:
408 408 # The file was last modified "simultaneously" with the current
409 409 # write to dirstate (i.e. within the same second for file-
410 410 # systems with a granularity of 1 sec). This commonly happens
411 411 # for at least a couple of files on 'update'.
412 412 # The user could change the file without changing its size
413 413 # within the same second. Invalidate the file's stat data in
414 414 # dirstate, forcing future 'status' calls to compare the
415 415 # contents of the file. This prevents mistakenly treating such
416 416 # files as clean.
417 417 e = (e[0], 0, -1, -1) # mark entry as 'unset'
418 418 self._map[f] = e
419 419
420 420 if f in copymap:
421 421 f = "%s\0%s" % (f, copymap[f])
422 422 e = pack(_format, e[0], e[1], e[2], e[3], len(f))
423 423 write(e)
424 424 write(f)
425 425 st.write(cs.getvalue())
426 426 st.rename()
427 427 self._dirty = self._dirtypl = False
428 428
429 429 def _dirignore(self, f):
430 430 if f == '.':
431 431 return False
432 432 if self._ignore(f):
433 433 return True
434 434 for p in _finddirs(f):
435 435 if self._ignore(p):
436 436 return True
437 437 return False
438 438
439 439 def walk(self, match, subrepos, unknown, ignored):
440 440 '''
441 441 Walk recursively through the directory tree, finding all files
442 442 matched by match.
443 443
444 444 Return a dict mapping filename to stat-like object (either
445 445 mercurial.osutil.stat instance or return value of os.stat()).
446 446 '''
447 447
448 448 def fwarn(f, msg):
449 449 self._ui.warn('%s: %s\n' % (self.pathto(f), msg))
450 450 return False
451 451
452 452 def badtype(mode):
453 453 kind = _('unknown')
454 454 if stat.S_ISCHR(mode):
455 455 kind = _('character device')
456 456 elif stat.S_ISBLK(mode):
457 457 kind = _('block device')
458 458 elif stat.S_ISFIFO(mode):
459 459 kind = _('fifo')
460 460 elif stat.S_ISSOCK(mode):
461 461 kind = _('socket')
462 462 elif stat.S_ISDIR(mode):
463 463 kind = _('directory')
464 464 return _('unsupported file type (type is %s)') % kind
465 465
466 466 ignore = self._ignore
467 467 dirignore = self._dirignore
468 468 if ignored:
469 469 ignore = util.never
470 470 dirignore = util.never
471 471 elif not unknown:
472 472 # if unknown and ignored are False, skip step 2
473 473 ignore = util.always
474 474 dirignore = util.always
475 475
476 476 matchfn = match.matchfn
477 477 badfn = match.bad
478 478 dmap = self._map
479 479 normpath = util.normpath
480 480 listdir = osutil.listdir
481 481 lstat = os.lstat
482 482 getkind = stat.S_IFMT
483 483 dirkind = stat.S_IFDIR
484 484 regkind = stat.S_IFREG
485 485 lnkkind = stat.S_IFLNK
486 486 join = self._join
487 487 work = []
488 488 wadd = work.append
489 489
490 490 exact = skipstep3 = False
491 491 if matchfn == match.exact: # match.exact
492 492 exact = True
493 493 dirignore = util.always # skip step 2
494 494 elif match.files() and not match.anypats(): # match.match, no patterns
495 495 skipstep3 = True
496 496
497 497 if self._checkcase:
498 498 normalize = self._normalize
499 499 skipstep3 = False
500 500 else:
501 501 normalize = lambda x, y: x
502 502
503 503 files = sorted(match.files())
504 504 subrepos.sort()
505 505 i, j = 0, 0
506 506 while i < len(files) and j < len(subrepos):
507 507 subpath = subrepos[j] + "/"
508 508 if files[i] < subpath:
509 509 i += 1
510 510 continue
511 while files and files[i].startswith(subpath):
511 while i < len(files) and files[i].startswith(subpath):
512 512 del files[i]
513 513 j += 1
514 514
515 515 if not files or '.' in files:
516 516 files = ['']
517 517 results = dict.fromkeys(subrepos)
518 518 results['.hg'] = None
519 519
520 520 # step 1: find all explicit files
521 521 for ff in files:
522 522 nf = normalize(normpath(ff), False)
523 523 if nf in results:
524 524 continue
525 525
526 526 try:
527 527 st = lstat(join(nf))
528 528 kind = getkind(st.st_mode)
529 529 if kind == dirkind:
530 530 skipstep3 = False
531 531 if nf in dmap:
532 532 #file deleted on disk but still in dirstate
533 533 results[nf] = None
534 534 match.dir(nf)
535 535 if not dirignore(nf):
536 536 wadd(nf)
537 537 elif kind == regkind or kind == lnkkind:
538 538 results[nf] = st
539 539 else:
540 540 badfn(ff, badtype(kind))
541 541 if nf in dmap:
542 542 results[nf] = None
543 543 except OSError, inst:
544 544 if nf in dmap: # does it exactly match a file?
545 545 results[nf] = None
546 546 else: # does it match a directory?
547 547 prefix = nf + "/"
548 548 for fn in dmap:
549 549 if fn.startswith(prefix):
550 550 match.dir(nf)
551 551 skipstep3 = False
552 552 break
553 553 else:
554 554 badfn(ff, inst.strerror)
555 555
556 556 # step 2: visit subdirectories
557 557 while work:
558 558 nd = work.pop()
559 559 skip = None
560 560 if nd == '.':
561 561 nd = ''
562 562 else:
563 563 skip = '.hg'
564 564 try:
565 565 entries = listdir(join(nd), stat=True, skip=skip)
566 566 except OSError, inst:
567 567 if inst.errno == errno.EACCES:
568 568 fwarn(nd, inst.strerror)
569 569 continue
570 570 raise
571 571 for f, kind, st in entries:
572 572 nf = normalize(nd and (nd + "/" + f) or f, True)
573 573 if nf not in results:
574 574 if kind == dirkind:
575 575 if not ignore(nf):
576 576 match.dir(nf)
577 577 wadd(nf)
578 578 if nf in dmap and matchfn(nf):
579 579 results[nf] = None
580 580 elif kind == regkind or kind == lnkkind:
581 581 if nf in dmap:
582 582 if matchfn(nf):
583 583 results[nf] = st
584 584 elif matchfn(nf) and not ignore(nf):
585 585 results[nf] = st
586 586 elif nf in dmap and matchfn(nf):
587 587 results[nf] = None
588 588
589 589 # step 3: report unseen items in the dmap hash
590 590 if not skipstep3 and not exact:
591 591 visit = sorted([f for f in dmap if f not in results and matchfn(f)])
592 592 for nf, st in zip(visit, util.statfiles([join(i) for i in visit])):
593 593 if not st is None and not getkind(st.st_mode) in (regkind, lnkkind):
594 594 st = None
595 595 results[nf] = st
596 596 for s in subrepos:
597 597 del results[s]
598 598 del results['.hg']
599 599 return results
600 600
601 601 def status(self, match, subrepos, ignored, clean, unknown):
602 602 '''Determine the status of the working copy relative to the
603 603 dirstate and return a tuple of lists (unsure, modified, added,
604 604 removed, deleted, unknown, ignored, clean), where:
605 605
606 606 unsure:
607 607 files that might have been modified since the dirstate was
608 608 written, but need to be read to be sure (size is the same
609 609 but mtime differs)
610 610 modified:
611 611 files that have definitely been modified since the dirstate
612 612 was written (different size or mode)
613 613 added:
614 614 files that have been explicitly added with hg add
615 615 removed:
616 616 files that have been explicitly removed with hg remove
617 617 deleted:
618 618 files that have been deleted through other means ("missing")
619 619 unknown:
620 620 files not in the dirstate that are not ignored
621 621 ignored:
622 622 files not in the dirstate that are ignored
623 623 (by _dirignore())
624 624 clean:
625 625 files that have definitely not been modified since the
626 626 dirstate was written
627 627 '''
628 628 listignored, listclean, listunknown = ignored, clean, unknown
629 629 lookup, modified, added, unknown, ignored = [], [], [], [], []
630 630 removed, deleted, clean = [], [], []
631 631
632 632 dmap = self._map
633 633 ladd = lookup.append # aka "unsure"
634 634 madd = modified.append
635 635 aadd = added.append
636 636 uadd = unknown.append
637 637 iadd = ignored.append
638 638 radd = removed.append
639 639 dadd = deleted.append
640 640 cadd = clean.append
641 641
642 642 lnkkind = stat.S_IFLNK
643 643
644 644 for fn, st in self.walk(match, subrepos, listunknown,
645 645 listignored).iteritems():
646 646 if fn not in dmap:
647 647 if (listignored or match.exact(fn)) and self._dirignore(fn):
648 648 if listignored:
649 649 iadd(fn)
650 650 elif listunknown:
651 651 uadd(fn)
652 652 continue
653 653
654 654 state, mode, size, time = dmap[fn]
655 655
656 656 if not st and state in "nma":
657 657 dadd(fn)
658 658 elif state == 'n':
659 659 # The "mode & lnkkind != lnkkind or self._checklink"
660 660 # lines are an expansion of "islink => checklink"
661 661 # where islink means "is this a link?" and checklink
662 662 # means "can we check links?".
663 663 if (size >= 0 and
664 664 (size != st.st_size
665 665 or ((mode ^ st.st_mode) & 0100 and self._checkexec))
666 666 and (mode & lnkkind != lnkkind or self._checklink)
667 667 or size == -2 # other parent
668 668 or fn in self._copymap):
669 669 madd(fn)
670 670 elif (time != int(st.st_mtime)
671 671 and (mode & lnkkind != lnkkind or self._checklink)):
672 672 ladd(fn)
673 673 elif listclean:
674 674 cadd(fn)
675 675 elif state == 'm':
676 676 madd(fn)
677 677 elif state == 'a':
678 678 aadd(fn)
679 679 elif state == 'r':
680 680 radd(fn)
681 681
682 682 return (lookup, modified, added, removed, deleted, unknown, ignored,
683 683 clean)
@@ -1,415 +1,427
1 1 Create test repository:
2 2
3 3 $ hg init repo
4 4 $ cd repo
5 5 $ echo x1 > x.txt
6 6
7 7 $ hg init foo
8 8 $ cd foo
9 9 $ echo y1 > y.txt
10 10
11 11 $ hg init bar
12 12 $ cd bar
13 13 $ echo z1 > z.txt
14 14
15 15 $ cd ..
16 16 $ echo 'bar = bar' > .hgsub
17 17
18 18 $ cd ..
19 19 $ echo 'foo = foo' > .hgsub
20 20
21 21 Add files --- .hgsub files must go first to trigger subrepos:
22 22
23 23 $ hg add -S .hgsub
24 24 $ hg add -S foo/.hgsub
25 25 $ hg add -S foo/bar
26 26 adding foo/bar/z.txt
27 27 $ hg add -S
28 28 adding x.txt
29 29 adding foo/y.txt
30 30
31 31 Test recursive status without committing anything:
32 32
33 33 $ hg status -S
34 34 A .hgsub
35 35 A foo/.hgsub
36 36 A foo/bar/z.txt
37 37 A foo/y.txt
38 38 A x.txt
39 39
40 40 Test recursive diff without committing anything:
41 41
42 42 $ hg diff --nodates -S foo
43 43 diff -r 000000000000 foo/.hgsub
44 44 --- /dev/null
45 45 +++ b/foo/.hgsub
46 46 @@ -0,0 +1,1 @@
47 47 +bar = bar
48 48 diff -r 000000000000 foo/y.txt
49 49 --- /dev/null
50 50 +++ b/foo/y.txt
51 51 @@ -0,0 +1,1 @@
52 52 +y1
53 53 diff -r 000000000000 foo/bar/z.txt
54 54 --- /dev/null
55 55 +++ b/foo/bar/z.txt
56 56 @@ -0,0 +1,1 @@
57 57 +z1
58 58
59 59 Commits:
60 60
61 61 $ hg commit -m 0-0-0
62 62 committing subrepository foo
63 63 committing subrepository foo/bar
64 64
65 65 $ cd foo
66 66 $ echo y2 >> y.txt
67 67 $ hg commit -m 0-1-0
68 68
69 69 $ cd bar
70 70 $ echo z2 >> z.txt
71 71 $ hg commit -m 0-1-1
72 72
73 73 $ cd ..
74 74 $ hg commit -m 0-2-1
75 75 committing subrepository bar
76 76
77 77 $ cd ..
78 78 $ hg commit -m 1-2-1
79 79 committing subrepository foo
80 80
81 81 Change working directory:
82 82
83 83 $ echo y3 >> foo/y.txt
84 84 $ echo z3 >> foo/bar/z.txt
85 85 $ hg status -S
86 86 M foo/bar/z.txt
87 87 M foo/y.txt
88 88 $ hg diff --nodates -S
89 89 diff -r d254738c5f5e foo/y.txt
90 90 --- a/foo/y.txt
91 91 +++ b/foo/y.txt
92 92 @@ -1,2 +1,3 @@
93 93 y1
94 94 y2
95 95 +y3
96 96 diff -r 9647f22de499 foo/bar/z.txt
97 97 --- a/foo/bar/z.txt
98 98 +++ b/foo/bar/z.txt
99 99 @@ -1,2 +1,3 @@
100 100 z1
101 101 z2
102 102 +z3
103 103
104 104 Status call crossing repository boundaries:
105 105
106 106 $ hg status -S foo/bar/z.txt
107 107 M foo/bar/z.txt
108 108 $ hg status -S -I 'foo/?.txt'
109 109 M foo/y.txt
110 110 $ hg status -S -I '**/?.txt'
111 111 M foo/bar/z.txt
112 112 M foo/y.txt
113 113 $ hg diff --nodates -S -I '**/?.txt'
114 114 diff -r d254738c5f5e foo/y.txt
115 115 --- a/foo/y.txt
116 116 +++ b/foo/y.txt
117 117 @@ -1,2 +1,3 @@
118 118 y1
119 119 y2
120 120 +y3
121 121 diff -r 9647f22de499 foo/bar/z.txt
122 122 --- a/foo/bar/z.txt
123 123 +++ b/foo/bar/z.txt
124 124 @@ -1,2 +1,3 @@
125 125 z1
126 126 z2
127 127 +z3
128 128
129 129 Status from within a subdirectory:
130 130
131 131 $ mkdir dir
132 132 $ cd dir
133 133 $ echo a1 > a.txt
134 134 $ hg status -S
135 135 M foo/bar/z.txt
136 136 M foo/y.txt
137 137 ? dir/a.txt
138 138 $ hg diff --nodates -S
139 139 diff -r d254738c5f5e foo/y.txt
140 140 --- a/foo/y.txt
141 141 +++ b/foo/y.txt
142 142 @@ -1,2 +1,3 @@
143 143 y1
144 144 y2
145 145 +y3
146 146 diff -r 9647f22de499 foo/bar/z.txt
147 147 --- a/foo/bar/z.txt
148 148 +++ b/foo/bar/z.txt
149 149 @@ -1,2 +1,3 @@
150 150 z1
151 151 z2
152 152 +z3
153 153
154 154 Status with relative path:
155 155
156 156 $ hg status -S ..
157 157 M ../foo/bar/z.txt
158 158 M ../foo/y.txt
159 159 ? a.txt
160 160 $ hg diff --nodates -S ..
161 161 diff -r d254738c5f5e foo/y.txt
162 162 --- a/foo/y.txt
163 163 +++ b/foo/y.txt
164 164 @@ -1,2 +1,3 @@
165 165 y1
166 166 y2
167 167 +y3
168 168 diff -r 9647f22de499 foo/bar/z.txt
169 169 --- a/foo/bar/z.txt
170 170 +++ b/foo/bar/z.txt
171 171 @@ -1,2 +1,3 @@
172 172 z1
173 173 z2
174 174 +z3
175 175 $ cd ..
176 176
177 177 Cleanup and final commit:
178 178
179 179 $ rm -r dir
180 180 $ hg commit -m 2-3-2
181 181 committing subrepository foo
182 182 committing subrepository foo/bar
183 183
184 184 Log with the relationships between repo and its subrepo:
185 185
186 186 $ hg log --template '{rev}:{node|short} {desc}\n'
187 187 2:1326fa26d0c0 2-3-2
188 188 1:4b3c9ff4f66b 1-2-1
189 189 0:23376cbba0d8 0-0-0
190 190
191 191 $ hg -R foo log --template '{rev}:{node|short} {desc}\n'
192 192 3:65903cebad86 2-3-2
193 193 2:d254738c5f5e 0-2-1
194 194 1:8629ce7dcc39 0-1-0
195 195 0:af048e97ade2 0-0-0
196 196
197 197 $ hg -R foo/bar log --template '{rev}:{node|short} {desc}\n'
198 198 2:31ecbdafd357 2-3-2
199 199 1:9647f22de499 0-1-1
200 200 0:4904098473f9 0-0-0
201 201
202 202 Status between revisions:
203 203
204 204 $ hg status -S
205 205 $ hg status -S --rev 0:1
206 206 M .hgsubstate
207 207 M foo/.hgsubstate
208 208 M foo/bar/z.txt
209 209 M foo/y.txt
210 210 $ hg diff --nodates -S -I '**/?.txt' --rev 0:1
211 211 diff -r af048e97ade2 -r d254738c5f5e foo/y.txt
212 212 --- a/foo/y.txt
213 213 +++ b/foo/y.txt
214 214 @@ -1,1 +1,2 @@
215 215 y1
216 216 +y2
217 217 diff -r 4904098473f9 -r 9647f22de499 foo/bar/z.txt
218 218 --- a/foo/bar/z.txt
219 219 +++ b/foo/bar/z.txt
220 220 @@ -1,1 +1,2 @@
221 221 z1
222 222 +z2
223 223
224 224 Enable progress extension for archive tests:
225 225
226 226 $ cp $HGRCPATH $HGRCPATH.no-progress
227 227 $ cat >> $HGRCPATH <<EOF
228 228 > [extensions]
229 229 > progress =
230 230 > [progress]
231 231 > assume-tty = 1
232 232 > delay = 0
233 233 > format = topic bar number
234 234 > refresh = 0
235 235 > width = 60
236 236 > EOF
237 237
238 238 Test archiving to a directory tree (the doubled lines in the output
239 239 only show up in the test output, not in real usage):
240 240
241 241 $ hg archive --subrepos ../archive 2>&1 | $TESTDIR/filtercr.py
242 242
243 243 archiving [ ] 0/3
244 244 archiving [ ] 0/3
245 245 archiving [=============> ] 1/3
246 246 archiving [=============> ] 1/3
247 247 archiving [===========================> ] 2/3
248 248 archiving [===========================> ] 2/3
249 249 archiving [==========================================>] 3/3
250 250 archiving [==========================================>] 3/3
251 251
252 252 archiving (foo) [ ] 0/3
253 253 archiving (foo) [ ] 0/3
254 254 archiving (foo) [===========> ] 1/3
255 255 archiving (foo) [===========> ] 1/3
256 256 archiving (foo) [=======================> ] 2/3
257 257 archiving (foo) [=======================> ] 2/3
258 258 archiving (foo) [====================================>] 3/3
259 259 archiving (foo) [====================================>] 3/3
260 260
261 261 archiving (foo/bar) [ ] 0/1
262 262 archiving (foo/bar) [ ] 0/1
263 263 archiving (foo/bar) [================================>] 1/1
264 264 archiving (foo/bar) [================================>] 1/1
265 265 \r (esc)
266 266 $ find ../archive | sort
267 267 ../archive
268 268 ../archive/.hg_archival.txt
269 269 ../archive/.hgsub
270 270 ../archive/.hgsubstate
271 271 ../archive/foo
272 272 ../archive/foo/.hgsub
273 273 ../archive/foo/.hgsubstate
274 274 ../archive/foo/bar
275 275 ../archive/foo/bar/z.txt
276 276 ../archive/foo/y.txt
277 277 ../archive/x.txt
278 278
279 279 Test archiving to zip file (unzip output is unstable):
280 280
281 281 $ hg archive --subrepos ../archive.zip 2>&1 | $TESTDIR/filtercr.py
282 282
283 283 archiving [ ] 0/3
284 284 archiving [ ] 0/3
285 285 archiving [=============> ] 1/3
286 286 archiving [=============> ] 1/3
287 287 archiving [===========================> ] 2/3
288 288 archiving [===========================> ] 2/3
289 289 archiving [==========================================>] 3/3
290 290 archiving [==========================================>] 3/3
291 291
292 292 archiving (foo) [ ] 0/3
293 293 archiving (foo) [ ] 0/3
294 294 archiving (foo) [===========> ] 1/3
295 295 archiving (foo) [===========> ] 1/3
296 296 archiving (foo) [=======================> ] 2/3
297 297 archiving (foo) [=======================> ] 2/3
298 298 archiving (foo) [====================================>] 3/3
299 299 archiving (foo) [====================================>] 3/3
300 300
301 301 archiving (foo/bar) [ ] 0/1
302 302 archiving (foo/bar) [ ] 0/1
303 303 archiving (foo/bar) [================================>] 1/1
304 304 archiving (foo/bar) [================================>] 1/1
305 305 \r (esc)
306 306
307 307 Disable progress extension and cleanup:
308 308
309 309 $ mv $HGRCPATH.no-progress $HGRCPATH
310 310
311 311 Clone and test outgoing:
312 312
313 313 $ cd ..
314 314 $ hg clone repo repo2
315 315 updating to branch default
316 316 pulling subrepo foo from $TESTTMP/repo/foo
317 317 requesting all changes
318 318 adding changesets
319 319 adding manifests
320 320 adding file changes
321 321 added 4 changesets with 7 changes to 3 files
322 322 pulling subrepo foo/bar from $TESTTMP/repo/foo/bar
323 323 requesting all changes
324 324 adding changesets
325 325 adding manifests
326 326 adding file changes
327 327 added 3 changesets with 3 changes to 1 files
328 328 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
329 329 $ cd repo2
330 330 $ hg outgoing -S
331 331 comparing with $TESTTMP/repo
332 332 searching for changes
333 333 no changes found
334 334 comparing with $TESTTMP/repo/foo
335 335 searching for changes
336 336 no changes found
337 337 comparing with $TESTTMP/repo/foo/bar
338 338 searching for changes
339 339 no changes found
340 340 [1]
341 341
342 342 Make nested change:
343 343
344 344 $ echo y4 >> foo/y.txt
345 345 $ hg diff --nodates -S
346 346 diff -r 65903cebad86 foo/y.txt
347 347 --- a/foo/y.txt
348 348 +++ b/foo/y.txt
349 349 @@ -1,3 +1,4 @@
350 350 y1
351 351 y2
352 352 y3
353 353 +y4
354 354 $ hg commit -m 3-4-2
355 355 committing subrepository foo
356 356 $ hg outgoing -S
357 357 comparing with $TESTTMP/repo
358 358 searching for changes
359 359 changeset: 3:2655b8ecc4ee
360 360 tag: tip
361 361 user: test
362 362 date: Thu Jan 01 00:00:00 1970 +0000
363 363 summary: 3-4-2
364 364
365 365 comparing with $TESTTMP/repo/foo
366 366 searching for changes
367 367 changeset: 4:e96193d6cb36
368 368 tag: tip
369 369 user: test
370 370 date: Thu Jan 01 00:00:00 1970 +0000
371 371 summary: 3-4-2
372 372
373 373 comparing with $TESTTMP/repo/foo/bar
374 374 searching for changes
375 375 no changes found
376 376
377 377
378 378 Switch to original repo and setup default path:
379 379
380 380 $ cd ../repo
381 381 $ echo '[paths]' >> .hg/hgrc
382 382 $ echo 'default = ../repo2' >> .hg/hgrc
383 383
384 384 Test incoming:
385 385
386 386 $ hg incoming -S
387 387 comparing with $TESTTMP/repo2
388 388 searching for changes
389 389 changeset: 3:2655b8ecc4ee
390 390 tag: tip
391 391 user: test
392 392 date: Thu Jan 01 00:00:00 1970 +0000
393 393 summary: 3-4-2
394 394
395 395 comparing with $TESTTMP/repo2/foo
396 396 searching for changes
397 397 changeset: 4:e96193d6cb36
398 398 tag: tip
399 399 user: test
400 400 date: Thu Jan 01 00:00:00 1970 +0000
401 401 summary: 3-4-2
402 402
403 403 comparing with $TESTTMP/repo2/foo/bar
404 404 searching for changes
405 405 no changes found
406 406
407 407 $ hg incoming -S --bundle incoming.hg
408 408 abort: cannot combine --bundle and --subrepos
409 409 [255]
410 410
411 411 Test missing subrepo:
412 412
413 413 $ rm -r foo
414 414 $ hg status -S
415 415 warning: error "unknown revision '65903cebad86f1a84bd4f1134f62fa7dcb7a1c98'" in subrepository "foo"
416
417 Issue2619: IndexError: list index out of range on hg add with subrepos
418 The subrepo must sorts after the explicit filename.
419
420 $ cd ..
421 $ hg init test
422 $ cd test
423 $ hg init x
424 $ echo "x = x" >> .hgsub
425 $ hg add .hgsub
426 $ touch a x/a
427 $ hg add a x/a
General Comments 0
You need to be logged in to leave comments. Login now