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