##// END OF EJS Templates
Merge with crew
Matt Mackall -
r8806:14a0bdd5 merge default
parent child Browse files
Show More
@@ -1,3483 +1,3485
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, incorporated herein by reference.
7 7
8 8 from node import hex, nullid, nullrev, short
9 9 from lock import release
10 10 from i18n import _, gettext
11 11 import os, re, sys, textwrap, subprocess, difflib, time
12 12 import hg, util, revlog, bundlerepo, extensions, copies, context, error
13 13 import patch, help, mdiff, tempfile, url, encoding
14 14 import archival, changegroup, cmdutil, sshserver, hbisect
15 15 from hgweb import server
16 16 import merge as merge_
17 17
18 18 # Commands start here, listed alphabetically
19 19
20 20 def add(ui, repo, *pats, **opts):
21 21 """add the specified files on the next commit
22 22
23 23 Schedule files to be version controlled and added to the
24 24 repository.
25 25
26 26 The files will be added to the repository at the next commit. To
27 27 undo an add before that, see hg revert.
28 28
29 29 If no names are given, add all files to the repository.
30 30 """
31 31
32 32 bad = []
33 33 exacts = {}
34 34 names = []
35 35 m = cmdutil.match(repo, pats, opts)
36 36 oldbad = m.bad
37 37 m.bad = lambda x,y: bad.append(x) or oldbad(x,y)
38 38
39 39 for f in repo.walk(m):
40 40 exact = m.exact(f)
41 41 if exact or f not in repo.dirstate:
42 42 names.append(f)
43 43 if ui.verbose or not exact:
44 44 ui.status(_('adding %s\n') % m.rel(f))
45 45 if not opts.get('dry_run'):
46 46 bad += [f for f in repo.add(names) if f in m.files()]
47 47 return bad and 1 or 0
48 48
49 49 def addremove(ui, repo, *pats, **opts):
50 50 """add all new files, delete all missing files
51 51
52 52 Add all new files and remove all missing files from the
53 53 repository.
54 54
55 55 New files are ignored if they match any of the patterns in
56 56 .hgignore. As with add, these changes take effect at the next
57 57 commit.
58 58
59 59 Use the -s/--similarity option to detect renamed files. With a
60 60 parameter > 0, this compares every removed file with every added
61 61 file and records those similar enough as renames. This option
62 62 takes a percentage between 0 (disabled) and 100 (files must be
63 63 identical) as its parameter. Detecting renamed files this way can
64 64 be expensive.
65 65 """
66 66 try:
67 67 sim = float(opts.get('similarity') or 0)
68 68 except ValueError:
69 69 raise util.Abort(_('similarity must be a number'))
70 70 if sim < 0 or sim > 100:
71 71 raise util.Abort(_('similarity must be between 0 and 100'))
72 72 return cmdutil.addremove(repo, pats, opts, similarity=sim/100.)
73 73
74 74 def annotate(ui, repo, *pats, **opts):
75 75 """show changeset information by line for each file
76 76
77 77 List changes in files, showing the revision id responsible for
78 78 each line
79 79
80 80 This command is useful for discovering when a change was made and
81 81 by whom.
82 82
83 83 Without the -a/--text option, annotate will avoid processing files
84 84 it detects as binary. With -a, annotate will annotate the file
85 85 anyway, although the results will probably be neither useful
86 86 nor desirable.
87 87 """
88 88 datefunc = ui.quiet and util.shortdate or util.datestr
89 89 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
90 90
91 91 if not pats:
92 92 raise util.Abort(_('at least one filename or pattern is required'))
93 93
94 94 opmap = [('user', lambda x: ui.shortuser(x[0].user())),
95 95 ('number', lambda x: str(x[0].rev())),
96 96 ('changeset', lambda x: short(x[0].node())),
97 97 ('date', getdate),
98 98 ('follow', lambda x: x[0].path()),
99 99 ]
100 100
101 101 if (not opts.get('user') and not opts.get('changeset') and not opts.get('date')
102 102 and not opts.get('follow')):
103 103 opts['number'] = 1
104 104
105 105 linenumber = opts.get('line_number') is not None
106 106 if (linenumber and (not opts.get('changeset')) and (not opts.get('number'))):
107 107 raise util.Abort(_('at least one of -n/-c is required for -l'))
108 108
109 109 funcmap = [func for op, func in opmap if opts.get(op)]
110 110 if linenumber:
111 111 lastfunc = funcmap[-1]
112 112 funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])
113 113
114 114 ctx = repo[opts.get('rev')]
115 115
116 116 m = cmdutil.match(repo, pats, opts)
117 117 for abs in ctx.walk(m):
118 118 fctx = ctx[abs]
119 119 if not opts.get('text') and util.binary(fctx.data()):
120 120 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
121 121 continue
122 122
123 123 lines = fctx.annotate(follow=opts.get('follow'),
124 124 linenumber=linenumber)
125 125 pieces = []
126 126
127 127 for f in funcmap:
128 128 l = [f(n) for n, dummy in lines]
129 129 if l:
130 130 ml = max(map(len, l))
131 131 pieces.append(["%*s" % (ml, x) for x in l])
132 132
133 133 if pieces:
134 134 for p, l in zip(zip(*pieces), lines):
135 135 ui.write("%s: %s" % (" ".join(p), l[1]))
136 136
137 137 def archive(ui, repo, dest, **opts):
138 138 '''create an unversioned archive of a repository revision
139 139
140 140 By default, the revision used is the parent of the working
141 141 directory; use -r/--rev to specify a different revision.
142 142
143 143 To specify the type of archive to create, use -t/--type. Valid
144 144 types are:
145 145
146 146 "files" (default): a directory full of files
147 147 "tar": tar archive, uncompressed
148 148 "tbz2": tar archive, compressed using bzip2
149 149 "tgz": tar archive, compressed using gzip
150 150 "uzip": zip archive, uncompressed
151 151 "zip": zip archive, compressed using deflate
152 152
153 153 The exact name of the destination archive or directory is given
154 154 using a format string; see 'hg help export' for details.
155 155
156 156 Each member added to an archive file has a directory prefix
157 157 prepended. Use -p/--prefix to specify a format string for the
158 158 prefix. The default is the basename of the archive, with suffixes
159 159 removed.
160 160 '''
161 161
162 162 ctx = repo[opts.get('rev')]
163 163 if not ctx:
164 164 raise util.Abort(_('no working directory: please specify a revision'))
165 165 node = ctx.node()
166 166 dest = cmdutil.make_filename(repo, dest, node)
167 167 if os.path.realpath(dest) == repo.root:
168 168 raise util.Abort(_('repository root cannot be destination'))
169 169 matchfn = cmdutil.match(repo, [], opts)
170 170 kind = opts.get('type') or 'files'
171 171 prefix = opts.get('prefix')
172 172 if dest == '-':
173 173 if kind == 'files':
174 174 raise util.Abort(_('cannot archive plain files to stdout'))
175 175 dest = sys.stdout
176 176 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
177 177 prefix = cmdutil.make_filename(repo, prefix, node)
178 178 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
179 179 matchfn, prefix)
180 180
181 181 def backout(ui, repo, node=None, rev=None, **opts):
182 182 '''reverse effect of earlier changeset
183 183
184 184 Commit the backed out changes as a new changeset. The new
185 185 changeset is a child of the backed out changeset.
186 186
187 187 If you backout a changeset other than the tip, a new head is
188 188 created. This head will be the new tip and you should merge this
189 189 backout changeset with another head.
190 190
191 191 The --merge option remembers the parent of the working directory
192 192 before starting the backout, then merges the new head with that
193 193 changeset afterwards. This saves you from doing the merge by hand.
194 194 The result of this merge is not committed, as with a normal merge.
195 195
196 196 See 'hg help dates' for a list of formats valid for -d/--date.
197 197 '''
198 198 if rev and node:
199 199 raise util.Abort(_("please specify just one revision"))
200 200
201 201 if not rev:
202 202 rev = node
203 203
204 204 if not rev:
205 205 raise util.Abort(_("please specify a revision to backout"))
206 206
207 207 date = opts.get('date')
208 208 if date:
209 209 opts['date'] = util.parsedate(date)
210 210
211 211 cmdutil.bail_if_changed(repo)
212 212 node = repo.lookup(rev)
213 213
214 214 op1, op2 = repo.dirstate.parents()
215 215 a = repo.changelog.ancestor(op1, node)
216 216 if a != node:
217 217 raise util.Abort(_('cannot backout change on a different branch'))
218 218
219 219 p1, p2 = repo.changelog.parents(node)
220 220 if p1 == nullid:
221 221 raise util.Abort(_('cannot backout a change with no parents'))
222 222 if p2 != nullid:
223 223 if not opts.get('parent'):
224 224 raise util.Abort(_('cannot backout a merge changeset without '
225 225 '--parent'))
226 226 p = repo.lookup(opts['parent'])
227 227 if p not in (p1, p2):
228 228 raise util.Abort(_('%s is not a parent of %s') %
229 229 (short(p), short(node)))
230 230 parent = p
231 231 else:
232 232 if opts.get('parent'):
233 233 raise util.Abort(_('cannot use --parent on non-merge changeset'))
234 234 parent = p1
235 235
236 236 # the backout should appear on the same branch
237 237 branch = repo.dirstate.branch()
238 238 hg.clean(repo, node, show_stats=False)
239 239 repo.dirstate.setbranch(branch)
240 240 revert_opts = opts.copy()
241 241 revert_opts['date'] = None
242 242 revert_opts['all'] = True
243 243 revert_opts['rev'] = hex(parent)
244 244 revert_opts['no_backup'] = None
245 245 revert(ui, repo, **revert_opts)
246 246 commit_opts = opts.copy()
247 247 commit_opts['addremove'] = False
248 248 if not commit_opts['message'] and not commit_opts['logfile']:
249 249 commit_opts['message'] = _("Backed out changeset %s") % (short(node))
250 250 commit_opts['force_editor'] = True
251 251 commit(ui, repo, **commit_opts)
252 252 def nice(node):
253 253 return '%d:%s' % (repo.changelog.rev(node), short(node))
254 254 ui.status(_('changeset %s backs out changeset %s\n') %
255 255 (nice(repo.changelog.tip()), nice(node)))
256 256 if op1 != node:
257 257 hg.clean(repo, op1, show_stats=False)
258 258 if opts.get('merge'):
259 259 ui.status(_('merging with changeset %s\n') % nice(repo.changelog.tip()))
260 260 hg.merge(repo, hex(repo.changelog.tip()))
261 261 else:
262 262 ui.status(_('the backout changeset is a new head - '
263 263 'do not forget to merge\n'))
264 264 ui.status(_('(use "backout --merge" '
265 265 'if you want to auto-merge)\n'))
266 266
267 267 def bisect(ui, repo, rev=None, extra=None, command=None,
268 268 reset=None, good=None, bad=None, skip=None, noupdate=None):
269 269 """subdivision search of changesets
270 270
271 271 This command helps to find changesets which introduce problems. To
272 272 use, mark the earliest changeset you know exhibits the problem as
273 273 bad, then mark the latest changeset which is free from the problem
274 274 as good. Bisect will update your working directory to a revision
275 275 for testing (unless the -U/--noupdate option is specified). Once
276 276 you have performed tests, mark the working directory as good or
277 277 bad, and bisect will either update to another candidate changeset
278 278 or announce that it has found the bad revision.
279 279
280 280 As a shortcut, you can also use the revision argument to mark a
281 281 revision as good or bad without checking it out first.
282 282
283 283 If you supply a command, it will be used for automatic bisection.
284 284 Its exit status will be used to mark revisions as good or bad:
285 285 status 0 means good, 125 means to skip the revision, 127
286 286 (command not found) will abort the bisection, and any other
287 287 non-zero exit status means the revision is bad.
288 288 """
289 289 def print_result(nodes, good):
290 290 displayer = cmdutil.show_changeset(ui, repo, {})
291 291 if len(nodes) == 1:
292 292 # narrowed it down to a single revision
293 293 if good:
294 294 ui.write(_("The first good revision is:\n"))
295 295 else:
296 296 ui.write(_("The first bad revision is:\n"))
297 297 displayer.show(repo[nodes[0]])
298 298 else:
299 299 # multiple possible revisions
300 300 if good:
301 301 ui.write(_("Due to skipped revisions, the first "
302 302 "good revision could be any of:\n"))
303 303 else:
304 304 ui.write(_("Due to skipped revisions, the first "
305 305 "bad revision could be any of:\n"))
306 306 for n in nodes:
307 307 displayer.show(repo[n])
308 308
309 309 def check_state(state, interactive=True):
310 310 if not state['good'] or not state['bad']:
311 311 if (good or bad or skip or reset) and interactive:
312 312 return
313 313 if not state['good']:
314 314 raise util.Abort(_('cannot bisect (no known good revisions)'))
315 315 else:
316 316 raise util.Abort(_('cannot bisect (no known bad revisions)'))
317 317 return True
318 318
319 319 # backward compatibility
320 320 if rev in "good bad reset init".split():
321 321 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
322 322 cmd, rev, extra = rev, extra, None
323 323 if cmd == "good":
324 324 good = True
325 325 elif cmd == "bad":
326 326 bad = True
327 327 else:
328 328 reset = True
329 329 elif extra or good + bad + skip + reset + bool(command) > 1:
330 330 raise util.Abort(_('incompatible arguments'))
331 331
332 332 if reset:
333 333 p = repo.join("bisect.state")
334 334 if os.path.exists(p):
335 335 os.unlink(p)
336 336 return
337 337
338 338 state = hbisect.load_state(repo)
339 339
340 340 if command:
341 341 commandpath = util.find_exe(command)
342 if commandpath is None:
343 raise util.Abort(_("cannot find executable: %s") % command)
342 344 changesets = 1
343 345 try:
344 346 while changesets:
345 347 # update state
346 348 status = subprocess.call([commandpath])
347 349 if status == 125:
348 350 transition = "skip"
349 351 elif status == 0:
350 352 transition = "good"
351 353 # status < 0 means process was killed
352 354 elif status == 127:
353 355 raise util.Abort(_("failed to execute %s") % command)
354 356 elif status < 0:
355 357 raise util.Abort(_("%s killed") % command)
356 358 else:
357 359 transition = "bad"
358 node = repo.lookup(rev or '.')
359 state[transition].append(node)
360 ui.note(_('Changeset %s: %s\n') % (short(node), transition))
360 ctx = repo[rev or '.']
361 state[transition].append(ctx.node())
362 ui.status(_('Changeset %d:%s: %s\n') % (ctx, ctx, transition))
361 363 check_state(state, interactive=False)
362 364 # bisect
363 365 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
364 366 # update to next check
365 367 cmdutil.bail_if_changed(repo)
366 368 hg.clean(repo, nodes[0], show_stats=False)
367 369 finally:
368 370 hbisect.save_state(repo, state)
369 371 return print_result(nodes, not status)
370 372
371 373 # update state
372 374 node = repo.lookup(rev or '.')
373 375 if good:
374 376 state['good'].append(node)
375 377 elif bad:
376 378 state['bad'].append(node)
377 379 elif skip:
378 380 state['skip'].append(node)
379 381
380 382 hbisect.save_state(repo, state)
381 383
382 384 if not check_state(state):
383 385 return
384 386
385 387 # actually bisect
386 388 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
387 389 if changesets == 0:
388 390 print_result(nodes, good)
389 391 else:
390 392 assert len(nodes) == 1 # only a single node can be tested next
391 393 node = nodes[0]
392 394 # compute the approximate number of remaining tests
393 395 tests, size = 0, 2
394 396 while size <= changesets:
395 397 tests, size = tests + 1, size * 2
396 398 rev = repo.changelog.rev(node)
397 399 ui.write(_("Testing changeset %s:%s "
398 400 "(%s changesets remaining, ~%s tests)\n")
399 401 % (rev, short(node), changesets, tests))
400 402 if not noupdate:
401 403 cmdutil.bail_if_changed(repo)
402 404 return hg.clean(repo, node)
403 405
404 406 def branch(ui, repo, label=None, **opts):
405 407 """set or show the current branch name
406 408
407 409 With no argument, show the current branch name. With one argument,
408 410 set the working directory branch name (the branch will not exist
409 411 in the repository until the next commit). Standard practice
410 412 recommends that primary development take place on the 'default'
411 413 branch.
412 414
413 415 Unless -f/--force is specified, branch will not let you set a
414 416 branch name that already exists, even if it's inactive.
415 417
416 418 Use -C/--clean to reset the working directory branch to that of
417 419 the parent of the working directory, negating a previous branch
418 420 change.
419 421
420 422 Use the command 'hg update' to switch to an existing branch.
421 423 """
422 424
423 425 if opts.get('clean'):
424 426 label = repo[None].parents()[0].branch()
425 427 repo.dirstate.setbranch(label)
426 428 ui.status(_('reset working directory to branch %s\n') % label)
427 429 elif label:
428 430 if not opts.get('force') and label in repo.branchtags():
429 431 if label not in [p.branch() for p in repo.parents()]:
430 432 raise util.Abort(_('a branch of the same name already exists'
431 433 ' (use --force to override)'))
432 434 repo.dirstate.setbranch(encoding.fromlocal(label))
433 435 ui.status(_('marked working directory as branch %s\n') % label)
434 436 else:
435 437 ui.write("%s\n" % encoding.tolocal(repo.dirstate.branch()))
436 438
437 439 def branches(ui, repo, active=False):
438 440 """list repository named branches
439 441
440 442 List the repository's named branches, indicating which ones are
441 443 inactive. If active is specified, only show active branches.
442 444
443 445 A branch is considered active if it contains repository heads.
444 446
445 447 Use the command 'hg update' to switch to an existing branch.
446 448 """
447 449 hexfunc = ui.debugflag and hex or short
448 450 activebranches = [encoding.tolocal(repo[n].branch())
449 451 for n in repo.heads()]
450 452 def testactive(tag, node):
451 453 realhead = tag in activebranches
452 454 open = node in repo.branchheads(tag, closed=False)
453 455 return realhead and open
454 456 branches = sorted([(testactive(tag, node), repo.changelog.rev(node), tag)
455 457 for tag, node in repo.branchtags().items()],
456 458 reverse=True)
457 459
458 460 for isactive, node, tag in branches:
459 461 if (not active) or isactive:
460 462 if ui.quiet:
461 463 ui.write("%s\n" % tag)
462 464 else:
463 465 hn = repo.lookup(node)
464 466 if isactive:
465 467 notice = ''
466 468 elif hn not in repo.branchheads(tag, closed=False):
467 469 notice = ' (closed)'
468 470 else:
469 471 notice = ' (inactive)'
470 472 rev = str(node).rjust(31 - encoding.colwidth(tag))
471 473 data = tag, rev, hexfunc(hn), notice
472 474 ui.write("%s %s:%s%s\n" % data)
473 475
474 476 def bundle(ui, repo, fname, dest=None, **opts):
475 477 """create a changegroup file
476 478
477 479 Generate a compressed changegroup file collecting changesets not
478 480 known to be in another repository.
479 481
480 482 If no destination repository is specified the destination is
481 483 assumed to have all the nodes specified by one or more --base
482 484 parameters. To create a bundle containing all changesets, use
483 485 -a/--all (or --base null). To change the compression method
484 486 applied, use the -t/--type option (by default, bundles are
485 487 compressed using bz2).
486 488
487 489 The bundle file can then be transferred using conventional means
488 490 and applied to another repository with the unbundle or pull
489 491 command. This is useful when direct push and pull are not
490 492 available or when exporting an entire repository is undesirable.
491 493
492 494 Applying bundles preserves all changeset contents including
493 495 permissions, copy/rename information, and revision history.
494 496 """
495 497 revs = opts.get('rev') or None
496 498 if revs:
497 499 revs = [repo.lookup(rev) for rev in revs]
498 500 if opts.get('all'):
499 501 base = ['null']
500 502 else:
501 503 base = opts.get('base')
502 504 if base:
503 505 if dest:
504 506 raise util.Abort(_("--base is incompatible with specifying "
505 507 "a destination"))
506 508 base = [repo.lookup(rev) for rev in base]
507 509 # create the right base
508 510 # XXX: nodesbetween / changegroup* should be "fixed" instead
509 511 o = []
510 512 has = set((nullid,))
511 513 for n in base:
512 514 has.update(repo.changelog.reachable(n))
513 515 if revs:
514 516 visit = list(revs)
515 517 else:
516 518 visit = repo.changelog.heads()
517 519 seen = {}
518 520 while visit:
519 521 n = visit.pop(0)
520 522 parents = [p for p in repo.changelog.parents(n) if p not in has]
521 523 if len(parents) == 0:
522 524 o.insert(0, n)
523 525 else:
524 526 for p in parents:
525 527 if p not in seen:
526 528 seen[p] = 1
527 529 visit.append(p)
528 530 else:
529 531 dest, revs, checkout = hg.parseurl(
530 532 ui.expandpath(dest or 'default-push', dest or 'default'), revs)
531 533 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
532 534 o = repo.findoutgoing(other, force=opts.get('force'))
533 535
534 536 if revs:
535 537 cg = repo.changegroupsubset(o, revs, 'bundle')
536 538 else:
537 539 cg = repo.changegroup(o, 'bundle')
538 540
539 541 bundletype = opts.get('type', 'bzip2').lower()
540 542 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
541 543 bundletype = btypes.get(bundletype)
542 544 if bundletype not in changegroup.bundletypes:
543 545 raise util.Abort(_('unknown bundle type specified with --type'))
544 546
545 547 changegroup.writebundle(cg, fname, bundletype)
546 548
547 549 def cat(ui, repo, file1, *pats, **opts):
548 550 """output the current or given revision of files
549 551
550 552 Print the specified files as they were at the given revision. If
551 553 no revision is given, the parent of the working directory is used,
552 554 or tip if no revision is checked out.
553 555
554 556 Output may be to a file, in which case the name of the file is
555 557 given using a format string. The formatting rules are the same as
556 558 for the export command, with the following additions:
557 559
558 560 %s basename of file being printed
559 561 %d dirname of file being printed, or '.' if in repository root
560 562 %p root-relative path name of file being printed
561 563 """
562 564 ctx = repo[opts.get('rev')]
563 565 err = 1
564 566 m = cmdutil.match(repo, (file1,) + pats, opts)
565 567 for abs in ctx.walk(m):
566 568 fp = cmdutil.make_file(repo, opts.get('output'), ctx.node(), pathname=abs)
567 569 data = ctx[abs].data()
568 570 if opts.get('decode'):
569 571 data = repo.wwritedata(abs, data)
570 572 fp.write(data)
571 573 err = 0
572 574 return err
573 575
574 576 def clone(ui, source, dest=None, **opts):
575 577 """make a copy of an existing repository
576 578
577 579 Create a copy of an existing repository in a new directory.
578 580
579 581 If no destination directory name is specified, it defaults to the
580 582 basename of the source.
581 583
582 584 The location of the source is added to the new repository's
583 585 .hg/hgrc file, as the default to be used for future pulls.
584 586
585 587 If you use the -r/--rev option to clone up to a specific revision,
586 588 no subsequent revisions (including subsequent tags) will be
587 589 present in the cloned repository. This option implies --pull, even
588 590 on local repositories.
589 591
590 592 By default, clone will check out the head of the 'default' branch.
591 593 If the -U/--noupdate option is used, the new clone will contain
592 594 only a repository (.hg) and no working copy (the working copy
593 595 parent is the null revision).
594 596
595 597 See 'hg help urls' for valid source format details.
596 598
597 599 It is possible to specify an ssh:// URL as the destination, but no
598 600 .hg/hgrc and working directory will be created on the remote side.
599 601 Please see 'hg help urls' for important details about ssh:// URLs.
600 602
601 603 For efficiency, hardlinks are used for cloning whenever the source
602 604 and destination are on the same filesystem (note this applies only
603 605 to the repository data, not to the checked out files). Some
604 606 filesystems, such as AFS, implement hardlinking incorrectly, but
605 607 do not report errors. In these cases, use the --pull option to
606 608 avoid hardlinking.
607 609
608 610 In some cases, you can clone repositories and checked out files
609 611 using full hardlinks with
610 612
611 613 $ cp -al REPO REPOCLONE
612 614
613 615 This is the fastest way to clone, but it is not always safe. The
614 616 operation is not atomic (making sure REPO is not modified during
615 617 the operation is up to you) and you have to make sure your editor
616 618 breaks hardlinks (Emacs and most Linux Kernel tools do so). Also,
617 619 this is not compatible with certain extensions that place their
618 620 metadata under the .hg directory, such as mq.
619 621
620 622 """
621 623 hg.clone(cmdutil.remoteui(ui, opts), source, dest,
622 624 pull=opts.get('pull'),
623 625 stream=opts.get('uncompressed'),
624 626 rev=opts.get('rev'),
625 627 update=not opts.get('noupdate'))
626 628
627 629 def commit(ui, repo, *pats, **opts):
628 630 """commit the specified files or all outstanding changes
629 631
630 632 Commit changes to the given files into the repository. Unlike a
631 633 centralized RCS, this operation is a local operation. See hg push
632 634 for a way to actively distribute your changes.
633 635
634 636 If a list of files is omitted, all changes reported by "hg status"
635 637 will be committed.
636 638
637 639 If you are committing the result of a merge, do not provide any
638 640 filenames or -I/-X filters.
639 641
640 642 If no commit message is specified, the configured editor is
641 643 started to prompt you for a message.
642 644
643 645 See 'hg help dates' for a list of formats valid for -d/--date.
644 646 """
645 647 extra = {}
646 648 if opts.get('close_branch'):
647 649 extra['close'] = 1
648 650 e = cmdutil.commiteditor
649 651 if opts.get('force_editor'):
650 652 e = cmdutil.commitforceeditor
651 653
652 654 def commitfunc(ui, repo, message, match, opts):
653 655 return repo.commit(message, opts.get('user'), opts.get('date'), match,
654 656 editor=e, extra=extra)
655 657
656 658 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
657 659 if not node:
658 660 return
659 661 cl = repo.changelog
660 662 rev = cl.rev(node)
661 663 parents = cl.parentrevs(rev)
662 664 if rev - 1 in parents:
663 665 # one of the parents was the old tip
664 666 pass
665 667 elif (parents == (nullrev, nullrev) or
666 668 len(cl.heads(cl.node(parents[0]))) > 1 and
667 669 (parents[1] == nullrev or len(cl.heads(cl.node(parents[1]))) > 1)):
668 670 ui.status(_('created new head\n'))
669 671
670 672 if ui.debugflag:
671 673 ui.write(_('committed changeset %d:%s\n') % (rev,hex(node)))
672 674 elif ui.verbose:
673 675 ui.write(_('committed changeset %d:%s\n') % (rev,short(node)))
674 676
675 677 def copy(ui, repo, *pats, **opts):
676 678 """mark files as copied for the next commit
677 679
678 680 Mark dest as having copies of source files. If dest is a
679 681 directory, copies are put in that directory. If dest is a file,
680 682 the source must be a single file.
681 683
682 684 By default, this command copies the contents of files as they
683 685 exist in the working directory. If invoked with -A/--after, the
684 686 operation is recorded, but no copying is performed.
685 687
686 688 This command takes effect with the next commit. To undo a copy
687 689 before that, see hg revert.
688 690 """
689 691 wlock = repo.wlock(False)
690 692 try:
691 693 return cmdutil.copy(ui, repo, pats, opts)
692 694 finally:
693 695 wlock.release()
694 696
695 697 def debugancestor(ui, repo, *args):
696 698 """find the ancestor revision of two revisions in a given index"""
697 699 if len(args) == 3:
698 700 index, rev1, rev2 = args
699 701 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
700 702 lookup = r.lookup
701 703 elif len(args) == 2:
702 704 if not repo:
703 705 raise util.Abort(_("There is no Mercurial repository here "
704 706 "(.hg not found)"))
705 707 rev1, rev2 = args
706 708 r = repo.changelog
707 709 lookup = repo.lookup
708 710 else:
709 711 raise util.Abort(_('either two or three arguments required'))
710 712 a = r.ancestor(lookup(rev1), lookup(rev2))
711 713 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
712 714
713 715 def debugcommands(ui, cmd='', *args):
714 716 for cmd, vals in sorted(table.iteritems()):
715 717 cmd = cmd.split('|')[0].strip('^')
716 718 opts = ', '.join([i[1] for i in vals[1]])
717 719 ui.write('%s: %s\n' % (cmd, opts))
718 720
719 721 def debugcomplete(ui, cmd='', **opts):
720 722 """returns the completion list associated with the given command"""
721 723
722 724 if opts.get('options'):
723 725 options = []
724 726 otables = [globalopts]
725 727 if cmd:
726 728 aliases, entry = cmdutil.findcmd(cmd, table, False)
727 729 otables.append(entry[1])
728 730 for t in otables:
729 731 for o in t:
730 732 if o[0]:
731 733 options.append('-%s' % o[0])
732 734 options.append('--%s' % o[1])
733 735 ui.write("%s\n" % "\n".join(options))
734 736 return
735 737
736 738 cmdlist = cmdutil.findpossible(cmd, table)
737 739 if ui.verbose:
738 740 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
739 741 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
740 742
741 743 def debugfsinfo(ui, path = "."):
742 744 file('.debugfsinfo', 'w').write('')
743 745 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
744 746 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
745 747 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
746 748 and 'yes' or 'no'))
747 749 os.unlink('.debugfsinfo')
748 750
749 751 def debugrebuildstate(ui, repo, rev="tip"):
750 752 """rebuild the dirstate as it would look like for the given revision"""
751 753 ctx = repo[rev]
752 754 wlock = repo.wlock()
753 755 try:
754 756 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
755 757 finally:
756 758 wlock.release()
757 759
758 760 def debugcheckstate(ui, repo):
759 761 """validate the correctness of the current dirstate"""
760 762 parent1, parent2 = repo.dirstate.parents()
761 763 m1 = repo[parent1].manifest()
762 764 m2 = repo[parent2].manifest()
763 765 errors = 0
764 766 for f in repo.dirstate:
765 767 state = repo.dirstate[f]
766 768 if state in "nr" and f not in m1:
767 769 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
768 770 errors += 1
769 771 if state in "a" and f in m1:
770 772 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
771 773 errors += 1
772 774 if state in "m" and f not in m1 and f not in m2:
773 775 ui.warn(_("%s in state %s, but not in either manifest\n") %
774 776 (f, state))
775 777 errors += 1
776 778 for f in m1:
777 779 state = repo.dirstate[f]
778 780 if state not in "nrm":
779 781 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
780 782 errors += 1
781 783 if errors:
782 784 error = _(".hg/dirstate inconsistent with current parent's manifest")
783 785 raise util.Abort(error)
784 786
785 787 def showconfig(ui, repo, *values, **opts):
786 788 """show combined config settings from all hgrc files
787 789
788 790 With no arguments, print names and values of all config items.
789 791
790 792 With one argument of the form section.name, print just the value
791 793 of that config item.
792 794
793 795 With multiple arguments, print names and values of all config
794 796 items with matching section names.
795 797
796 798 With --debug, the source (filename and line number) is printed
797 799 for each config item.
798 800 """
799 801
800 802 untrusted = bool(opts.get('untrusted'))
801 803 if values:
802 804 if len([v for v in values if '.' in v]) > 1:
803 805 raise util.Abort(_('only one config item permitted'))
804 806 for section, name, value in ui.walkconfig(untrusted=untrusted):
805 807 sectname = section + '.' + name
806 808 if values:
807 809 for v in values:
808 810 if v == section:
809 811 ui.debug('%s: ' %
810 812 ui.configsource(section, name, untrusted))
811 813 ui.write('%s=%s\n' % (sectname, value))
812 814 elif v == sectname:
813 815 ui.debug('%s: ' %
814 816 ui.configsource(section, name, untrusted))
815 817 ui.write(value, '\n')
816 818 else:
817 819 ui.debug('%s: ' %
818 820 ui.configsource(section, name, untrusted))
819 821 ui.write('%s=%s\n' % (sectname, value))
820 822
821 823 def debugsetparents(ui, repo, rev1, rev2=None):
822 824 """manually set the parents of the current working directory
823 825
824 826 This is useful for writing repository conversion tools, but should
825 827 be used with care.
826 828 """
827 829
828 830 if not rev2:
829 831 rev2 = hex(nullid)
830 832
831 833 wlock = repo.wlock()
832 834 try:
833 835 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
834 836 finally:
835 837 wlock.release()
836 838
837 839 def debugstate(ui, repo, nodates=None):
838 840 """show the contents of the current dirstate"""
839 841 timestr = ""
840 842 showdate = not nodates
841 843 for file_, ent in sorted(repo.dirstate._map.iteritems()):
842 844 if showdate:
843 845 if ent[3] == -1:
844 846 # Pad or slice to locale representation
845 847 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(0)))
846 848 timestr = 'unset'
847 849 timestr = timestr[:locale_len] + ' '*(locale_len - len(timestr))
848 850 else:
849 851 timestr = time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(ent[3]))
850 852 if ent[1] & 020000:
851 853 mode = 'lnk'
852 854 else:
853 855 mode = '%3o' % (ent[1] & 0777)
854 856 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
855 857 for f in repo.dirstate.copies():
856 858 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
857 859
858 860 def debugdata(ui, file_, rev):
859 861 """dump the contents of a data file revision"""
860 862 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
861 863 try:
862 864 ui.write(r.revision(r.lookup(rev)))
863 865 except KeyError:
864 866 raise util.Abort(_('invalid revision identifier %s') % rev)
865 867
866 868 def debugdate(ui, date, range=None, **opts):
867 869 """parse and display a date"""
868 870 if opts["extended"]:
869 871 d = util.parsedate(date, util.extendeddateformats)
870 872 else:
871 873 d = util.parsedate(date)
872 874 ui.write("internal: %s %s\n" % d)
873 875 ui.write("standard: %s\n" % util.datestr(d))
874 876 if range:
875 877 m = util.matchdate(range)
876 878 ui.write("match: %s\n" % m(d[0]))
877 879
878 880 def debugindex(ui, file_):
879 881 """dump the contents of an index file"""
880 882 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
881 883 ui.write(" rev offset length base linkrev"
882 884 " nodeid p1 p2\n")
883 885 for i in r:
884 886 node = r.node(i)
885 887 try:
886 888 pp = r.parents(node)
887 889 except:
888 890 pp = [nullid, nullid]
889 891 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
890 892 i, r.start(i), r.length(i), r.base(i), r.linkrev(i),
891 893 short(node), short(pp[0]), short(pp[1])))
892 894
893 895 def debugindexdot(ui, file_):
894 896 """dump an index DAG as a graphviz dot file"""
895 897 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
896 898 ui.write("digraph G {\n")
897 899 for i in r:
898 900 node = r.node(i)
899 901 pp = r.parents(node)
900 902 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
901 903 if pp[1] != nullid:
902 904 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
903 905 ui.write("}\n")
904 906
905 907 def debuginstall(ui):
906 908 '''test Mercurial installation'''
907 909
908 910 def writetemp(contents):
909 911 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
910 912 f = os.fdopen(fd, "wb")
911 913 f.write(contents)
912 914 f.close()
913 915 return name
914 916
915 917 problems = 0
916 918
917 919 # encoding
918 920 ui.status(_("Checking encoding (%s)...\n") % encoding.encoding)
919 921 try:
920 922 encoding.fromlocal("test")
921 923 except util.Abort, inst:
922 924 ui.write(" %s\n" % inst)
923 925 ui.write(_(" (check that your locale is properly set)\n"))
924 926 problems += 1
925 927
926 928 # compiled modules
927 929 ui.status(_("Checking extensions...\n"))
928 930 try:
929 931 import bdiff, mpatch, base85
930 932 except Exception, inst:
931 933 ui.write(" %s\n" % inst)
932 934 ui.write(_(" One or more extensions could not be found"))
933 935 ui.write(_(" (check that you compiled the extensions)\n"))
934 936 problems += 1
935 937
936 938 # templates
937 939 ui.status(_("Checking templates...\n"))
938 940 try:
939 941 import templater
940 942 templater.templater(templater.templatepath("map-cmdline.default"))
941 943 except Exception, inst:
942 944 ui.write(" %s\n" % inst)
943 945 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
944 946 problems += 1
945 947
946 948 # patch
947 949 ui.status(_("Checking patch...\n"))
948 950 patchproblems = 0
949 951 a = "1\n2\n3\n4\n"
950 952 b = "1\n2\n3\ninsert\n4\n"
951 953 fa = writetemp(a)
952 954 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa),
953 955 os.path.basename(fa))
954 956 fd = writetemp(d)
955 957
956 958 files = {}
957 959 try:
958 960 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
959 961 except util.Abort, e:
960 962 ui.write(_(" patch call failed:\n"))
961 963 ui.write(" " + str(e) + "\n")
962 964 patchproblems += 1
963 965 else:
964 966 if list(files) != [os.path.basename(fa)]:
965 967 ui.write(_(" unexpected patch output!\n"))
966 968 patchproblems += 1
967 969 a = file(fa).read()
968 970 if a != b:
969 971 ui.write(_(" patch test failed!\n"))
970 972 patchproblems += 1
971 973
972 974 if patchproblems:
973 975 if ui.config('ui', 'patch'):
974 976 ui.write(_(" (Current patch tool may be incompatible with patch,"
975 977 " or misconfigured. Please check your .hgrc file)\n"))
976 978 else:
977 979 ui.write(_(" Internal patcher failure, please report this error"
978 980 " to http://www.selenic.com/mercurial/bts\n"))
979 981 problems += patchproblems
980 982
981 983 os.unlink(fa)
982 984 os.unlink(fd)
983 985
984 986 # editor
985 987 ui.status(_("Checking commit editor...\n"))
986 988 editor = ui.geteditor()
987 989 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
988 990 if not cmdpath:
989 991 if editor == 'vi':
990 992 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
991 993 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
992 994 else:
993 995 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
994 996 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
995 997 problems += 1
996 998
997 999 # check username
998 1000 ui.status(_("Checking username...\n"))
999 1001 user = os.environ.get("HGUSER")
1000 1002 if user is None:
1001 1003 user = ui.config("ui", "username")
1002 1004 if user is None:
1003 1005 user = os.environ.get("EMAIL")
1004 1006 if not user:
1005 1007 ui.warn(" ")
1006 1008 ui.username()
1007 1009 ui.write(_(" (specify a username in your .hgrc file)\n"))
1008 1010
1009 1011 if not problems:
1010 1012 ui.status(_("No problems detected\n"))
1011 1013 else:
1012 1014 ui.write(_("%s problems detected,"
1013 1015 " please check your install!\n") % problems)
1014 1016
1015 1017 return problems
1016 1018
1017 1019 def debugrename(ui, repo, file1, *pats, **opts):
1018 1020 """dump rename information"""
1019 1021
1020 1022 ctx = repo[opts.get('rev')]
1021 1023 m = cmdutil.match(repo, (file1,) + pats, opts)
1022 1024 for abs in ctx.walk(m):
1023 1025 fctx = ctx[abs]
1024 1026 o = fctx.filelog().renamed(fctx.filenode())
1025 1027 rel = m.rel(abs)
1026 1028 if o:
1027 1029 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1028 1030 else:
1029 1031 ui.write(_("%s not renamed\n") % rel)
1030 1032
1031 1033 def debugwalk(ui, repo, *pats, **opts):
1032 1034 """show how files match on given patterns"""
1033 1035 m = cmdutil.match(repo, pats, opts)
1034 1036 items = list(repo.walk(m))
1035 1037 if not items:
1036 1038 return
1037 1039 fmt = 'f %%-%ds %%-%ds %%s' % (
1038 1040 max([len(abs) for abs in items]),
1039 1041 max([len(m.rel(abs)) for abs in items]))
1040 1042 for abs in items:
1041 1043 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
1042 1044 ui.write("%s\n" % line.rstrip())
1043 1045
1044 1046 def diff(ui, repo, *pats, **opts):
1045 1047 """diff repository (or selected files)
1046 1048
1047 1049 Show differences between revisions for the specified files.
1048 1050
1049 1051 Differences between files are shown using the unified diff format.
1050 1052
1051 1053 NOTE: diff may generate unexpected results for merges, as it will
1052 1054 default to comparing against the working directory's first parent
1053 1055 changeset if no revisions are specified.
1054 1056
1055 1057 When two revision arguments are given, then changes are shown
1056 1058 between those revisions. If only one revision is specified then
1057 1059 that revision is compared to the working directory, and, when no
1058 1060 revisions are specified, the working directory files are compared
1059 1061 to its parent.
1060 1062
1061 1063 Without the -a/--text option, diff will avoid generating diffs of
1062 1064 files it detects as binary. With -a, diff will generate a diff
1063 1065 anyway, probably with undesirable results.
1064 1066
1065 1067 Use the -g/--git option to generate diffs in the git extended diff
1066 1068 format. For more information, read 'hg help diffs'.
1067 1069 """
1068 1070
1069 1071 revs = opts.get('rev')
1070 1072 change = opts.get('change')
1071 1073
1072 1074 if revs and change:
1073 1075 msg = _('cannot specify --rev and --change at the same time')
1074 1076 raise util.Abort(msg)
1075 1077 elif change:
1076 1078 node2 = repo.lookup(change)
1077 1079 node1 = repo[node2].parents()[0].node()
1078 1080 else:
1079 1081 node1, node2 = cmdutil.revpair(repo, revs)
1080 1082
1081 1083 m = cmdutil.match(repo, pats, opts)
1082 1084 it = patch.diff(repo, node1, node2, match=m, opts=patch.diffopts(ui, opts))
1083 1085 for chunk in it:
1084 1086 ui.write(chunk)
1085 1087
1086 1088 def export(ui, repo, *changesets, **opts):
1087 1089 """dump the header and diffs for one or more changesets
1088 1090
1089 1091 Print the changeset header and diffs for one or more revisions.
1090 1092
1091 1093 The information shown in the changeset header is: author,
1092 1094 changeset hash, parent(s) and commit comment.
1093 1095
1094 1096 NOTE: export may generate unexpected diff output for merge
1095 1097 changesets, as it will compare the merge changeset against its
1096 1098 first parent only.
1097 1099
1098 1100 Output may be to a file, in which case the name of the file is
1099 1101 given using a format string. The formatting rules are as follows:
1100 1102
1101 1103 %% literal "%" character
1102 1104 %H changeset hash (40 bytes of hexadecimal)
1103 1105 %N number of patches being generated
1104 1106 %R changeset revision number
1105 1107 %b basename of the exporting repository
1106 1108 %h short-form changeset hash (12 bytes of hexadecimal)
1107 1109 %n zero-padded sequence number, starting at 1
1108 1110 %r zero-padded changeset revision number
1109 1111
1110 1112 Without the -a/--text option, export will avoid generating diffs
1111 1113 of files it detects as binary. With -a, export will generate a
1112 1114 diff anyway, probably with undesirable results.
1113 1115
1114 1116 Use the -g/--git option to generate diffs in the git extended diff
1115 1117 format. See 'hg help diffs' for more information.
1116 1118
1117 1119 With the --switch-parent option, the diff will be against the
1118 1120 second parent. It can be useful to review a merge.
1119 1121 """
1120 1122 if not changesets:
1121 1123 raise util.Abort(_("export requires at least one changeset"))
1122 1124 revs = cmdutil.revrange(repo, changesets)
1123 1125 if len(revs) > 1:
1124 1126 ui.note(_('exporting patches:\n'))
1125 1127 else:
1126 1128 ui.note(_('exporting patch:\n'))
1127 1129 patch.export(repo, revs, template=opts.get('output'),
1128 1130 switch_parent=opts.get('switch_parent'),
1129 1131 opts=patch.diffopts(ui, opts))
1130 1132
1131 1133 def grep(ui, repo, pattern, *pats, **opts):
1132 1134 """search for a pattern in specified files and revisions
1133 1135
1134 1136 Search revisions of files for a regular expression.
1135 1137
1136 1138 This command behaves differently than Unix grep. It only accepts
1137 1139 Python/Perl regexps. It searches repository history, not the
1138 1140 working directory. It always prints the revision number in which a
1139 1141 match appears.
1140 1142
1141 1143 By default, grep only prints output for the first revision of a
1142 1144 file in which it finds a match. To get it to print every revision
1143 1145 that contains a change in match status ("-" for a match that
1144 1146 becomes a non-match, or "+" for a non-match that becomes a match),
1145 1147 use the --all flag.
1146 1148 """
1147 1149 reflags = 0
1148 1150 if opts.get('ignore_case'):
1149 1151 reflags |= re.I
1150 1152 try:
1151 1153 regexp = re.compile(pattern, reflags)
1152 1154 except Exception, inst:
1153 1155 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
1154 1156 return None
1155 1157 sep, eol = ':', '\n'
1156 1158 if opts.get('print0'):
1157 1159 sep = eol = '\0'
1158 1160
1159 1161 fcache = {}
1160 1162 forder = []
1161 1163 def getfile(fn):
1162 1164 if fn not in fcache:
1163 1165 if len(fcache) > 20:
1164 1166 del fcache[forder.pop(0)]
1165 1167 fcache[fn] = repo.file(fn)
1166 1168 else:
1167 1169 forder.remove(fn)
1168 1170
1169 1171 forder.append(fn)
1170 1172 return fcache[fn]
1171 1173
1172 1174 def matchlines(body):
1173 1175 begin = 0
1174 1176 linenum = 0
1175 1177 while True:
1176 1178 match = regexp.search(body, begin)
1177 1179 if not match:
1178 1180 break
1179 1181 mstart, mend = match.span()
1180 1182 linenum += body.count('\n', begin, mstart) + 1
1181 1183 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1182 1184 begin = body.find('\n', mend) + 1 or len(body)
1183 1185 lend = begin - 1
1184 1186 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1185 1187
1186 1188 class linestate(object):
1187 1189 def __init__(self, line, linenum, colstart, colend):
1188 1190 self.line = line
1189 1191 self.linenum = linenum
1190 1192 self.colstart = colstart
1191 1193 self.colend = colend
1192 1194
1193 1195 def __hash__(self):
1194 1196 return hash((self.linenum, self.line))
1195 1197
1196 1198 def __eq__(self, other):
1197 1199 return self.line == other.line
1198 1200
1199 1201 matches = {}
1200 1202 copies = {}
1201 1203 def grepbody(fn, rev, body):
1202 1204 matches[rev].setdefault(fn, [])
1203 1205 m = matches[rev][fn]
1204 1206 for lnum, cstart, cend, line in matchlines(body):
1205 1207 s = linestate(line, lnum, cstart, cend)
1206 1208 m.append(s)
1207 1209
1208 1210 def difflinestates(a, b):
1209 1211 sm = difflib.SequenceMatcher(None, a, b)
1210 1212 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1211 1213 if tag == 'insert':
1212 1214 for i in xrange(blo, bhi):
1213 1215 yield ('+', b[i])
1214 1216 elif tag == 'delete':
1215 1217 for i in xrange(alo, ahi):
1216 1218 yield ('-', a[i])
1217 1219 elif tag == 'replace':
1218 1220 for i in xrange(alo, ahi):
1219 1221 yield ('-', a[i])
1220 1222 for i in xrange(blo, bhi):
1221 1223 yield ('+', b[i])
1222 1224
1223 1225 prev = {}
1224 1226 def display(fn, rev, states, prevstates):
1225 1227 datefunc = ui.quiet and util.shortdate or util.datestr
1226 1228 found = False
1227 1229 filerevmatches = {}
1228 1230 r = prev.get(fn, -1)
1229 1231 if opts.get('all'):
1230 1232 iter = difflinestates(states, prevstates)
1231 1233 else:
1232 1234 iter = [('', l) for l in prevstates]
1233 1235 for change, l in iter:
1234 1236 cols = [fn, str(r)]
1235 1237 if opts.get('line_number'):
1236 1238 cols.append(str(l.linenum))
1237 1239 if opts.get('all'):
1238 1240 cols.append(change)
1239 1241 if opts.get('user'):
1240 1242 cols.append(ui.shortuser(get(r)[1]))
1241 1243 if opts.get('date'):
1242 1244 cols.append(datefunc(get(r)[2]))
1243 1245 if opts.get('files_with_matches'):
1244 1246 c = (fn, r)
1245 1247 if c in filerevmatches:
1246 1248 continue
1247 1249 filerevmatches[c] = 1
1248 1250 else:
1249 1251 cols.append(l.line)
1250 1252 ui.write(sep.join(cols), eol)
1251 1253 found = True
1252 1254 return found
1253 1255
1254 1256 fstate = {}
1255 1257 skip = {}
1256 1258 get = util.cachefunc(lambda r: repo[r].changeset())
1257 1259 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1258 1260 found = False
1259 1261 follow = opts.get('follow')
1260 1262 for st, rev, fns in changeiter:
1261 1263 if st == 'window':
1262 1264 matches.clear()
1263 1265 elif st == 'add':
1264 1266 ctx = repo[rev]
1265 1267 matches[rev] = {}
1266 1268 for fn in fns:
1267 1269 if fn in skip:
1268 1270 continue
1269 1271 try:
1270 1272 grepbody(fn, rev, getfile(fn).read(ctx.filenode(fn)))
1271 1273 fstate.setdefault(fn, [])
1272 1274 if follow:
1273 1275 copied = getfile(fn).renamed(ctx.filenode(fn))
1274 1276 if copied:
1275 1277 copies.setdefault(rev, {})[fn] = copied[0]
1276 1278 except error.LookupError:
1277 1279 pass
1278 1280 elif st == 'iter':
1279 1281 for fn, m in sorted(matches[rev].items()):
1280 1282 copy = copies.get(rev, {}).get(fn)
1281 1283 if fn in skip:
1282 1284 if copy:
1283 1285 skip[copy] = True
1284 1286 continue
1285 1287 if fn in prev or fstate[fn]:
1286 1288 r = display(fn, rev, m, fstate[fn])
1287 1289 found = found or r
1288 1290 if r and not opts.get('all'):
1289 1291 skip[fn] = True
1290 1292 if copy:
1291 1293 skip[copy] = True
1292 1294 fstate[fn] = m
1293 1295 if copy:
1294 1296 fstate[copy] = m
1295 1297 prev[fn] = rev
1296 1298
1297 1299 for fn, state in sorted(fstate.items()):
1298 1300 if fn in skip:
1299 1301 continue
1300 1302 if fn not in copies.get(prev[fn], {}):
1301 1303 found = display(fn, rev, {}, state) or found
1302 1304 return (not found and 1) or 0
1303 1305
1304 1306 def heads(ui, repo, *branchrevs, **opts):
1305 1307 """show current repository heads or show branch heads
1306 1308
1307 1309 With no arguments, show all repository head changesets.
1308 1310
1309 1311 Repository "heads" are changesets that don't have child
1310 1312 changesets. They are where development generally takes place and
1311 1313 are the usual targets for update and merge operations.
1312 1314
1313 1315 If one or more REV is given, the "branch heads" will be shown for
1314 1316 the named branch associated with that revision. The name of the
1315 1317 branch is called the revision's branch tag.
1316 1318
1317 1319 Branch heads are revisions on a given named branch that do not have
1318 1320 any children on the same branch. A branch head could be a true head
1319 1321 or it could be the last changeset on a branch before a new branch
1320 1322 was created. If none of the branch heads are true heads, the branch
1321 1323 is considered inactive.
1322 1324
1323 1325 If STARTREV is specified only those heads (or branch heads) that
1324 1326 are descendants of STARTREV will be displayed.
1325 1327 """
1326 1328 if opts.get('rev'):
1327 1329 start = repo.lookup(opts['rev'])
1328 1330 else:
1329 1331 start = None
1330 1332 closed = opts.get('closed')
1331 1333 hideinactive, _heads = opts.get('active'), None
1332 1334 if not branchrevs:
1333 1335 # Assume we're looking repo-wide heads if no revs were specified.
1334 1336 heads = repo.heads(start)
1335 1337 else:
1336 1338 if hideinactive:
1337 1339 _heads = repo.heads(start)
1338 1340 heads = []
1339 1341 visitedset = set()
1340 1342 for branchrev in branchrevs:
1341 1343 branch = repo[branchrev].branch()
1342 1344 if branch in visitedset:
1343 1345 continue
1344 1346 visitedset.add(branch)
1345 1347 bheads = repo.branchheads(branch, start, closed=closed)
1346 1348 if not bheads:
1347 1349 if not opts.get('rev'):
1348 1350 ui.warn(_("no open branch heads on branch %s\n") % branch)
1349 1351 elif branch != branchrev:
1350 1352 ui.warn(_("no changes on branch %s containing %s are "
1351 1353 "reachable from %s\n")
1352 1354 % (branch, branchrev, opts.get('rev')))
1353 1355 else:
1354 1356 ui.warn(_("no changes on branch %s are reachable from %s\n")
1355 1357 % (branch, opts.get('rev')))
1356 1358 if hideinactive:
1357 1359 bheads = [bhead for bhead in bheads if bhead in _heads]
1358 1360 heads.extend(bheads)
1359 1361 if not heads:
1360 1362 return 1
1361 1363 displayer = cmdutil.show_changeset(ui, repo, opts)
1362 1364 for n in heads:
1363 1365 displayer.show(repo[n])
1364 1366
1365 1367 def help_(ui, name=None, with_version=False):
1366 1368 """show help for a given topic or a help overview
1367 1369
1368 1370 With no arguments, print a list of commands with short help messages.
1369 1371
1370 1372 Given a topic, extension, or command name, print help for that
1371 1373 topic."""
1372 1374 option_lists = []
1373 1375
1374 1376 def addglobalopts(aliases):
1375 1377 if ui.verbose:
1376 1378 option_lists.append((_("global options:"), globalopts))
1377 1379 if name == 'shortlist':
1378 1380 option_lists.append((_('use "hg help" for the full list '
1379 1381 'of commands'), ()))
1380 1382 else:
1381 1383 if name == 'shortlist':
1382 1384 msg = _('use "hg help" for the full list of commands '
1383 1385 'or "hg -v" for details')
1384 1386 elif aliases:
1385 1387 msg = _('use "hg -v help%s" to show aliases and '
1386 1388 'global options') % (name and " " + name or "")
1387 1389 else:
1388 1390 msg = _('use "hg -v help %s" to show global options') % name
1389 1391 option_lists.append((msg, ()))
1390 1392
1391 1393 def helpcmd(name):
1392 1394 if with_version:
1393 1395 version_(ui)
1394 1396 ui.write('\n')
1395 1397
1396 1398 try:
1397 1399 aliases, i = cmdutil.findcmd(name, table, False)
1398 1400 except error.AmbiguousCommand, inst:
1399 1401 select = lambda c: c.lstrip('^').startswith(inst.args[0])
1400 1402 helplist(_('list of commands:\n\n'), select)
1401 1403 return
1402 1404
1403 1405 # synopsis
1404 1406 if len(i) > 2:
1405 1407 if i[2].startswith('hg'):
1406 1408 ui.write("%s\n" % i[2])
1407 1409 else:
1408 1410 ui.write('hg %s %s\n' % (aliases[0], i[2]))
1409 1411 else:
1410 1412 ui.write('hg %s\n' % aliases[0])
1411 1413
1412 1414 # aliases
1413 1415 if not ui.quiet and len(aliases) > 1:
1414 1416 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1415 1417
1416 1418 # description
1417 1419 doc = gettext(i[0].__doc__)
1418 1420 if not doc:
1419 1421 doc = _("(no help text available)")
1420 1422 if ui.quiet:
1421 1423 doc = doc.splitlines(0)[0]
1422 1424 ui.write("\n%s\n" % doc.rstrip())
1423 1425
1424 1426 if not ui.quiet:
1425 1427 # options
1426 1428 if i[1]:
1427 1429 option_lists.append((_("options:\n"), i[1]))
1428 1430
1429 1431 addglobalopts(False)
1430 1432
1431 1433 def helplist(header, select=None):
1432 1434 h = {}
1433 1435 cmds = {}
1434 1436 for c, e in table.iteritems():
1435 1437 f = c.split("|", 1)[0]
1436 1438 if select and not select(f):
1437 1439 continue
1438 1440 if (not select and name != 'shortlist' and
1439 1441 e[0].__module__ != __name__):
1440 1442 continue
1441 1443 if name == "shortlist" and not f.startswith("^"):
1442 1444 continue
1443 1445 f = f.lstrip("^")
1444 1446 if not ui.debugflag and f.startswith("debug"):
1445 1447 continue
1446 1448 doc = gettext(e[0].__doc__)
1447 1449 if not doc:
1448 1450 doc = _("(no help text available)")
1449 1451 h[f] = doc.splitlines(0)[0].rstrip()
1450 1452 cmds[f] = c.lstrip("^")
1451 1453
1452 1454 if not h:
1453 1455 ui.status(_('no commands defined\n'))
1454 1456 return
1455 1457
1456 1458 ui.status(header)
1457 1459 fns = sorted(h)
1458 1460 m = max(map(len, fns))
1459 1461 for f in fns:
1460 1462 if ui.verbose:
1461 1463 commands = cmds[f].replace("|",", ")
1462 1464 ui.write(" %s:\n %s\n"%(commands, h[f]))
1463 1465 else:
1464 1466 ui.write(' %-*s %s\n' % (m, f, h[f]))
1465 1467
1466 1468 exts = list(extensions.extensions())
1467 1469 if exts and name != 'shortlist':
1468 1470 ui.write(_('\nenabled extensions:\n\n'))
1469 1471 maxlength = 0
1470 1472 exthelps = []
1471 1473 for ename, ext in exts:
1472 1474 doc = (gettext(ext.__doc__) or _('(no help text available)'))
1473 1475 ename = ename.split('.')[-1]
1474 1476 maxlength = max(len(ename), maxlength)
1475 1477 exthelps.append((ename, doc.splitlines(0)[0].strip()))
1476 1478 for ename, text in exthelps:
1477 1479 ui.write(_(' %s %s\n') % (ename.ljust(maxlength), text))
1478 1480
1479 1481 if not ui.quiet:
1480 1482 addglobalopts(True)
1481 1483
1482 1484 def helptopic(name):
1483 1485 for names, header, doc in help.helptable:
1484 1486 if name in names:
1485 1487 break
1486 1488 else:
1487 1489 raise error.UnknownCommand(name)
1488 1490
1489 1491 # description
1490 1492 if not doc:
1491 1493 doc = _("(no help text available)")
1492 1494 if hasattr(doc, '__call__'):
1493 1495 doc = doc()
1494 1496
1495 1497 ui.write("%s\n" % header)
1496 1498 ui.write("%s\n" % doc.rstrip())
1497 1499
1498 1500 def helpext(name):
1499 1501 try:
1500 1502 mod = extensions.find(name)
1501 1503 except KeyError:
1502 1504 raise error.UnknownCommand(name)
1503 1505
1504 1506 doc = gettext(mod.__doc__) or _('no help text available')
1505 1507 doc = doc.splitlines(0)
1506 1508 ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0]))
1507 1509 for d in doc[1:]:
1508 1510 ui.write(d, '\n')
1509 1511
1510 1512 ui.status('\n')
1511 1513
1512 1514 try:
1513 1515 ct = mod.cmdtable
1514 1516 except AttributeError:
1515 1517 ct = {}
1516 1518
1517 1519 modcmds = set([c.split('|', 1)[0] for c in ct])
1518 1520 helplist(_('list of commands:\n\n'), modcmds.__contains__)
1519 1521
1520 1522 if name and name != 'shortlist':
1521 1523 i = None
1522 1524 for f in (helptopic, helpcmd, helpext):
1523 1525 try:
1524 1526 f(name)
1525 1527 i = None
1526 1528 break
1527 1529 except error.UnknownCommand, inst:
1528 1530 i = inst
1529 1531 if i:
1530 1532 raise i
1531 1533
1532 1534 else:
1533 1535 # program name
1534 1536 if ui.verbose or with_version:
1535 1537 version_(ui)
1536 1538 else:
1537 1539 ui.status(_("Mercurial Distributed SCM\n"))
1538 1540 ui.status('\n')
1539 1541
1540 1542 # list of commands
1541 1543 if name == "shortlist":
1542 1544 header = _('basic commands:\n\n')
1543 1545 else:
1544 1546 header = _('list of commands:\n\n')
1545 1547
1546 1548 helplist(header)
1547 1549
1548 1550 # list all option lists
1549 1551 opt_output = []
1550 1552 for title, options in option_lists:
1551 1553 opt_output.append(("\n%s" % title, None))
1552 1554 for shortopt, longopt, default, desc in options:
1553 1555 if "DEPRECATED" in desc and not ui.verbose: continue
1554 1556 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1555 1557 longopt and " --%s" % longopt),
1556 1558 "%s%s" % (desc,
1557 1559 default
1558 1560 and _(" (default: %s)") % default
1559 1561 or "")))
1560 1562
1561 1563 if not name:
1562 1564 ui.write(_("\nadditional help topics:\n\n"))
1563 1565 topics = []
1564 1566 for names, header, doc in help.helptable:
1565 1567 names = [(-len(name), name) for name in names]
1566 1568 names.sort()
1567 1569 topics.append((names[0][1], header))
1568 1570 topics_len = max([len(s[0]) for s in topics])
1569 1571 for t, desc in topics:
1570 1572 ui.write(" %-*s %s\n" % (topics_len, t, desc))
1571 1573
1572 1574 if opt_output:
1573 1575 opts_len = max([len(line[0]) for line in opt_output if line[1]] or [0])
1574 1576 for first, second in opt_output:
1575 1577 if second:
1576 1578 # wrap descriptions at 70 characters, just like the
1577 1579 # main help texts
1578 1580 second = textwrap.wrap(second, width=70 - opts_len - 3)
1579 1581 pad = '\n' + ' ' * (opts_len + 3)
1580 1582 ui.write(" %-*s %s\n" % (opts_len, first, pad.join(second)))
1581 1583 else:
1582 1584 ui.write("%s\n" % first)
1583 1585
1584 1586 def identify(ui, repo, source=None,
1585 1587 rev=None, num=None, id=None, branch=None, tags=None):
1586 1588 """identify the working copy or specified revision
1587 1589
1588 1590 With no revision, print a summary of the current state of the
1589 1591 repository.
1590 1592
1591 1593 Specifying a path to a repository root or Mercurial bundle will
1592 1594 cause lookup to operate on that repository/bundle.
1593 1595
1594 1596 This summary identifies the repository state using one or two
1595 1597 parent hash identifiers, followed by a "+" if there are
1596 1598 uncommitted changes in the working directory, a list of tags for
1597 1599 this revision and a branch name for non-default branches.
1598 1600 """
1599 1601
1600 1602 if not repo and not source:
1601 1603 raise util.Abort(_("There is no Mercurial repository here "
1602 1604 "(.hg not found)"))
1603 1605
1604 1606 hexfunc = ui.debugflag and hex or short
1605 1607 default = not (num or id or branch or tags)
1606 1608 output = []
1607 1609
1608 1610 revs = []
1609 1611 if source:
1610 1612 source, revs, checkout = hg.parseurl(ui.expandpath(source), [])
1611 1613 repo = hg.repository(ui, source)
1612 1614
1613 1615 if not repo.local():
1614 1616 if not rev and revs:
1615 1617 rev = revs[0]
1616 1618 if not rev:
1617 1619 rev = "tip"
1618 1620 if num or branch or tags:
1619 1621 raise util.Abort(
1620 1622 "can't query remote revision number, branch, or tags")
1621 1623 output = [hexfunc(repo.lookup(rev))]
1622 1624 elif not rev:
1623 1625 ctx = repo[None]
1624 1626 parents = ctx.parents()
1625 1627 changed = False
1626 1628 if default or id or num:
1627 1629 changed = ctx.files() + ctx.deleted()
1628 1630 if default or id:
1629 1631 output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]),
1630 1632 (changed) and "+" or "")]
1631 1633 if num:
1632 1634 output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
1633 1635 (changed) and "+" or ""))
1634 1636 else:
1635 1637 ctx = repo[rev]
1636 1638 if default or id:
1637 1639 output = [hexfunc(ctx.node())]
1638 1640 if num:
1639 1641 output.append(str(ctx.rev()))
1640 1642
1641 1643 if repo.local() and default and not ui.quiet:
1642 1644 b = encoding.tolocal(ctx.branch())
1643 1645 if b != 'default':
1644 1646 output.append("(%s)" % b)
1645 1647
1646 1648 # multiple tags for a single parent separated by '/'
1647 1649 t = "/".join(ctx.tags())
1648 1650 if t:
1649 1651 output.append(t)
1650 1652
1651 1653 if branch:
1652 1654 output.append(encoding.tolocal(ctx.branch()))
1653 1655
1654 1656 if tags:
1655 1657 output.extend(ctx.tags())
1656 1658
1657 1659 ui.write("%s\n" % ' '.join(output))
1658 1660
1659 1661 def import_(ui, repo, patch1, *patches, **opts):
1660 1662 """import an ordered set of patches
1661 1663
1662 1664 Import a list of patches and commit them individually.
1663 1665
1664 1666 If there are outstanding changes in the working directory, import
1665 1667 will abort unless given the -f/--force flag.
1666 1668
1667 1669 You can import a patch straight from a mail message. Even patches
1668 1670 as attachments work (to use the body part, it must have type
1669 1671 text/plain or text/x-patch). From and Subject headers of email
1670 1672 message are used as default committer and commit message. All
1671 1673 text/plain body parts before first diff are added to commit
1672 1674 message.
1673 1675
1674 1676 If the imported patch was generated by hg export, user and
1675 1677 description from patch override values from message headers and
1676 1678 body. Values given on command line with -m/--message and -u/--user
1677 1679 override these.
1678 1680
1679 1681 If --exact is specified, import will set the working directory to
1680 1682 the parent of each patch before applying it, and will abort if the
1681 1683 resulting changeset has a different ID than the one recorded in
1682 1684 the patch. This may happen due to character set problems or other
1683 1685 deficiencies in the text patch format.
1684 1686
1685 1687 With -s/--similarity, hg will attempt to discover renames and
1686 1688 copies in the patch in the same way as 'addremove'.
1687 1689
1688 1690 To read a patch from standard input, use "-" as the patch name.
1689 1691 See 'hg help dates' for a list of formats valid for -d/--date.
1690 1692 """
1691 1693 patches = (patch1,) + patches
1692 1694
1693 1695 date = opts.get('date')
1694 1696 if date:
1695 1697 opts['date'] = util.parsedate(date)
1696 1698
1697 1699 try:
1698 1700 sim = float(opts.get('similarity') or 0)
1699 1701 except ValueError:
1700 1702 raise util.Abort(_('similarity must be a number'))
1701 1703 if sim < 0 or sim > 100:
1702 1704 raise util.Abort(_('similarity must be between 0 and 100'))
1703 1705
1704 1706 if opts.get('exact') or not opts.get('force'):
1705 1707 cmdutil.bail_if_changed(repo)
1706 1708
1707 1709 d = opts["base"]
1708 1710 strip = opts["strip"]
1709 1711 wlock = lock = None
1710 1712 try:
1711 1713 wlock = repo.wlock()
1712 1714 lock = repo.lock()
1713 1715 for p in patches:
1714 1716 pf = os.path.join(d, p)
1715 1717
1716 1718 if pf == '-':
1717 1719 ui.status(_("applying patch from stdin\n"))
1718 1720 pf = sys.stdin
1719 1721 else:
1720 1722 ui.status(_("applying %s\n") % p)
1721 1723 pf = url.open(ui, pf)
1722 1724 data = patch.extract(ui, pf)
1723 1725 tmpname, message, user, date, branch, nodeid, p1, p2 = data
1724 1726
1725 1727 if tmpname is None:
1726 1728 raise util.Abort(_('no diffs found'))
1727 1729
1728 1730 try:
1729 1731 cmdline_message = cmdutil.logmessage(opts)
1730 1732 if cmdline_message:
1731 1733 # pickup the cmdline msg
1732 1734 message = cmdline_message
1733 1735 elif message:
1734 1736 # pickup the patch msg
1735 1737 message = message.strip()
1736 1738 else:
1737 1739 # launch the editor
1738 1740 message = None
1739 1741 ui.debug(_('message:\n%s\n') % message)
1740 1742
1741 1743 wp = repo.parents()
1742 1744 if opts.get('exact'):
1743 1745 if not nodeid or not p1:
1744 1746 raise util.Abort(_('not a Mercurial patch'))
1745 1747 p1 = repo.lookup(p1)
1746 1748 p2 = repo.lookup(p2 or hex(nullid))
1747 1749
1748 1750 if p1 != wp[0].node():
1749 1751 hg.clean(repo, p1)
1750 1752 repo.dirstate.setparents(p1, p2)
1751 1753 elif p2:
1752 1754 try:
1753 1755 p1 = repo.lookup(p1)
1754 1756 p2 = repo.lookup(p2)
1755 1757 if p1 == wp[0].node():
1756 1758 repo.dirstate.setparents(p1, p2)
1757 1759 except error.RepoError:
1758 1760 pass
1759 1761 if opts.get('exact') or opts.get('import_branch'):
1760 1762 repo.dirstate.setbranch(branch or 'default')
1761 1763
1762 1764 files = {}
1763 1765 try:
1764 1766 patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1765 1767 files=files)
1766 1768 finally:
1767 1769 files = patch.updatedir(ui, repo, files, similarity=sim/100.)
1768 1770 if not opts.get('no_commit'):
1769 1771 m = cmdutil.matchfiles(repo, files or [])
1770 1772 n = repo.commit(message, opts.get('user') or user,
1771 1773 opts.get('date') or date, match=m,
1772 1774 editor=cmdutil.commiteditor)
1773 1775 if opts.get('exact'):
1774 1776 if hex(n) != nodeid:
1775 1777 repo.rollback()
1776 1778 raise util.Abort(_('patch is damaged'
1777 1779 ' or loses information'))
1778 1780 # Force a dirstate write so that the next transaction
1779 1781 # backups an up-do-date file.
1780 1782 repo.dirstate.write()
1781 1783 finally:
1782 1784 os.unlink(tmpname)
1783 1785 finally:
1784 1786 release(lock, wlock)
1785 1787
1786 1788 def incoming(ui, repo, source="default", **opts):
1787 1789 """show new changesets found in source
1788 1790
1789 1791 Show new changesets found in the specified path/URL or the default
1790 1792 pull location. These are the changesets that would have been pulled
1791 1793 if a pull at the time you issued this command.
1792 1794
1793 1795 For remote repository, using --bundle avoids downloading the
1794 1796 changesets twice if the incoming is followed by a pull.
1795 1797
1796 1798 See pull for valid source format details.
1797 1799 """
1798 1800 limit = cmdutil.loglimit(opts)
1799 1801 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts.get('rev'))
1800 1802 other = hg.repository(cmdutil.remoteui(repo, opts), source)
1801 1803 ui.status(_('comparing with %s\n') % url.hidepassword(source))
1802 1804 if revs:
1803 1805 revs = [other.lookup(rev) for rev in revs]
1804 1806 common, incoming, rheads = repo.findcommonincoming(other, heads=revs,
1805 1807 force=opts["force"])
1806 1808 if not incoming:
1807 1809 try:
1808 1810 os.unlink(opts["bundle"])
1809 1811 except:
1810 1812 pass
1811 1813 ui.status(_("no changes found\n"))
1812 1814 return 1
1813 1815
1814 1816 cleanup = None
1815 1817 try:
1816 1818 fname = opts["bundle"]
1817 1819 if fname or not other.local():
1818 1820 # create a bundle (uncompressed if other repo is not local)
1819 1821
1820 1822 if revs is None and other.capable('changegroupsubset'):
1821 1823 revs = rheads
1822 1824
1823 1825 if revs is None:
1824 1826 cg = other.changegroup(incoming, "incoming")
1825 1827 else:
1826 1828 cg = other.changegroupsubset(incoming, revs, 'incoming')
1827 1829 bundletype = other.local() and "HG10BZ" or "HG10UN"
1828 1830 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
1829 1831 # keep written bundle?
1830 1832 if opts["bundle"]:
1831 1833 cleanup = None
1832 1834 if not other.local():
1833 1835 # use the created uncompressed bundlerepo
1834 1836 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1835 1837
1836 1838 o = other.changelog.nodesbetween(incoming, revs)[0]
1837 1839 if opts.get('newest_first'):
1838 1840 o.reverse()
1839 1841 displayer = cmdutil.show_changeset(ui, other, opts)
1840 1842 count = 0
1841 1843 for n in o:
1842 1844 if count >= limit:
1843 1845 break
1844 1846 parents = [p for p in other.changelog.parents(n) if p != nullid]
1845 1847 if opts.get('no_merges') and len(parents) == 2:
1846 1848 continue
1847 1849 count += 1
1848 1850 displayer.show(other[n])
1849 1851 finally:
1850 1852 if hasattr(other, 'close'):
1851 1853 other.close()
1852 1854 if cleanup:
1853 1855 os.unlink(cleanup)
1854 1856
1855 1857 def init(ui, dest=".", **opts):
1856 1858 """create a new repository in the given directory
1857 1859
1858 1860 Initialize a new repository in the given directory. If the given
1859 1861 directory does not exist, it will be created.
1860 1862
1861 1863 If no directory is given, the current directory is used.
1862 1864
1863 1865 It is possible to specify an ssh:// URL as the destination.
1864 1866 See 'hg help urls' for more information.
1865 1867 """
1866 1868 hg.repository(cmdutil.remoteui(ui, opts), dest, create=1)
1867 1869
1868 1870 def locate(ui, repo, *pats, **opts):
1869 1871 """locate files matching specific patterns
1870 1872
1871 1873 Print files under Mercurial control in the working directory whose
1872 1874 names match the given patterns.
1873 1875
1874 1876 By default, this command searches all directories in the working
1875 1877 directory. To search just the current directory and its
1876 1878 subdirectories, use "--include .".
1877 1879
1878 1880 If no patterns are given to match, this command prints the names
1879 1881 of all files under Mercurial control in the working directory.
1880 1882
1881 1883 If you want to feed the output of this command into the "xargs"
1882 1884 command, use the -0 option to both this command and "xargs". This
1883 1885 will avoid the problem of "xargs" treating single filenames that
1884 1886 contain whitespace as multiple filenames.
1885 1887 """
1886 1888 end = opts.get('print0') and '\0' or '\n'
1887 1889 rev = opts.get('rev') or None
1888 1890
1889 1891 ret = 1
1890 1892 m = cmdutil.match(repo, pats, opts, default='relglob')
1891 1893 m.bad = lambda x,y: False
1892 1894 for abs in repo[rev].walk(m):
1893 1895 if not rev and abs not in repo.dirstate:
1894 1896 continue
1895 1897 if opts.get('fullpath'):
1896 1898 ui.write(repo.wjoin(abs), end)
1897 1899 else:
1898 1900 ui.write(((pats and m.rel(abs)) or abs), end)
1899 1901 ret = 0
1900 1902
1901 1903 return ret
1902 1904
1903 1905 def log(ui, repo, *pats, **opts):
1904 1906 """show revision history of entire repository or files
1905 1907
1906 1908 Print the revision history of the specified files or the entire
1907 1909 project.
1908 1910
1909 1911 File history is shown without following rename or copy history of
1910 1912 files. Use -f/--follow with a filename to follow history across
1911 1913 renames and copies. --follow without a filename will only show
1912 1914 ancestors or descendants of the starting revision. --follow-first
1913 1915 only follows the first parent of merge revisions.
1914 1916
1915 1917 If no revision range is specified, the default is tip:0 unless
1916 1918 --follow is set, in which case the working directory parent is
1917 1919 used as the starting revision.
1918 1920
1919 1921 See 'hg help dates' for a list of formats valid for -d/--date.
1920 1922
1921 1923 By default this command prints revision number and changeset id,
1922 1924 tags, non-trivial parents, user, date and time, and a summary for
1923 1925 each commit. When the -v/--verbose switch is used, the list of
1924 1926 changed files and full commit message are shown.
1925 1927
1926 1928 NOTE: log -p/--patch may generate unexpected diff output for merge
1927 1929 changesets, as it will only compare the merge changeset against
1928 1930 its first parent. Also, only files different from BOTH parents
1929 1931 will appear in files:.
1930 1932 """
1931 1933
1932 1934 get = util.cachefunc(lambda r: repo[r].changeset())
1933 1935 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1934 1936
1935 1937 limit = cmdutil.loglimit(opts)
1936 1938 count = 0
1937 1939
1938 1940 if opts.get('copies') and opts.get('rev'):
1939 1941 endrev = max(cmdutil.revrange(repo, opts.get('rev'))) + 1
1940 1942 else:
1941 1943 endrev = len(repo)
1942 1944 rcache = {}
1943 1945 ncache = {}
1944 1946 def getrenamed(fn, rev):
1945 1947 '''looks up all renames for a file (up to endrev) the first
1946 1948 time the file is given. It indexes on the changerev and only
1947 1949 parses the manifest if linkrev != changerev.
1948 1950 Returns rename info for fn at changerev rev.'''
1949 1951 if fn not in rcache:
1950 1952 rcache[fn] = {}
1951 1953 ncache[fn] = {}
1952 1954 fl = repo.file(fn)
1953 1955 for i in fl:
1954 1956 node = fl.node(i)
1955 1957 lr = fl.linkrev(i)
1956 1958 renamed = fl.renamed(node)
1957 1959 rcache[fn][lr] = renamed
1958 1960 if renamed:
1959 1961 ncache[fn][node] = renamed
1960 1962 if lr >= endrev:
1961 1963 break
1962 1964 if rev in rcache[fn]:
1963 1965 return rcache[fn][rev]
1964 1966
1965 1967 # If linkrev != rev (i.e. rev not found in rcache) fallback to
1966 1968 # filectx logic.
1967 1969
1968 1970 try:
1969 1971 return repo[rev][fn].renamed()
1970 1972 except error.LookupError:
1971 1973 pass
1972 1974 return None
1973 1975
1974 1976 df = False
1975 1977 if opts["date"]:
1976 1978 df = util.matchdate(opts["date"])
1977 1979
1978 1980 only_branches = opts.get('only_branch')
1979 1981
1980 1982 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
1981 1983 for st, rev, fns in changeiter:
1982 1984 if st == 'add':
1983 1985 parents = [p for p in repo.changelog.parentrevs(rev)
1984 1986 if p != nullrev]
1985 1987 if opts.get('no_merges') and len(parents) == 2:
1986 1988 continue
1987 1989 if opts.get('only_merges') and len(parents) != 2:
1988 1990 continue
1989 1991
1990 1992 if only_branches:
1991 1993 revbranch = get(rev)[5]['branch']
1992 1994 if revbranch not in only_branches:
1993 1995 continue
1994 1996
1995 1997 if df:
1996 1998 changes = get(rev)
1997 1999 if not df(changes[2][0]):
1998 2000 continue
1999 2001
2000 2002 if opts.get('keyword'):
2001 2003 changes = get(rev)
2002 2004 miss = 0
2003 2005 for k in [kw.lower() for kw in opts['keyword']]:
2004 2006 if not (k in changes[1].lower() or
2005 2007 k in changes[4].lower() or
2006 2008 k in " ".join(changes[3]).lower()):
2007 2009 miss = 1
2008 2010 break
2009 2011 if miss:
2010 2012 continue
2011 2013
2012 2014 if opts['user']:
2013 2015 changes = get(rev)
2014 2016 if not [k for k in opts['user'] if k in changes[1]]:
2015 2017 continue
2016 2018
2017 2019 copies = []
2018 2020 if opts.get('copies') and rev:
2019 2021 for fn in get(rev)[3]:
2020 2022 rename = getrenamed(fn, rev)
2021 2023 if rename:
2022 2024 copies.append((fn, rename[0]))
2023 2025 displayer.show(context.changectx(repo, rev), copies=copies)
2024 2026 elif st == 'iter':
2025 2027 if count == limit: break
2026 2028 if displayer.flush(rev):
2027 2029 count += 1
2028 2030
2029 2031 def manifest(ui, repo, node=None, rev=None):
2030 2032 """output the current or given revision of the project manifest
2031 2033
2032 2034 Print a list of version controlled files for the given revision.
2033 2035 If no revision is given, the first parent of the working directory
2034 2036 is used, or the null revision if no revision is checked out.
2035 2037
2036 2038 With -v, print file permissions, symlink and executable bits.
2037 2039 With --debug, print file revision hashes.
2038 2040 """
2039 2041
2040 2042 if rev and node:
2041 2043 raise util.Abort(_("please specify just one revision"))
2042 2044
2043 2045 if not node:
2044 2046 node = rev
2045 2047
2046 2048 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
2047 2049 ctx = repo[node]
2048 2050 for f in ctx:
2049 2051 if ui.debugflag:
2050 2052 ui.write("%40s " % hex(ctx.manifest()[f]))
2051 2053 if ui.verbose:
2052 2054 ui.write(decor[ctx.flags(f)])
2053 2055 ui.write("%s\n" % f)
2054 2056
2055 2057 def merge(ui, repo, node=None, **opts):
2056 2058 """merge working directory with another revision
2057 2059
2058 2060 The current working directory is updated with all changes made in
2059 2061 the requested revision since the last common predecessor revision.
2060 2062
2061 2063 Files that changed between either parent are marked as changed for
2062 2064 the next commit and a commit must be performed before any further
2063 2065 updates to the repository are allowed. The next commit will have
2064 2066 two parents.
2065 2067
2066 2068 If no revision is specified, the working directory's parent is a
2067 2069 head revision, and the current branch contains exactly one other
2068 2070 head, the other head is merged with by default. Otherwise, an
2069 2071 explicit revision with which to merge with must be provided.
2070 2072 """
2071 2073
2072 2074 if opts.get('rev') and node:
2073 2075 raise util.Abort(_("please specify just one revision"))
2074 2076 if not node:
2075 2077 node = opts.get('rev')
2076 2078
2077 2079 if not node:
2078 2080 branch = repo.changectx(None).branch()
2079 2081 bheads = repo.branchheads(branch)
2080 2082 if len(bheads) > 2:
2081 2083 raise util.Abort(_("branch '%s' has %d heads - "
2082 2084 "please merge with an explicit rev") %
2083 2085 (branch, len(bheads)))
2084 2086
2085 2087 parent = repo.dirstate.parents()[0]
2086 2088 if len(bheads) == 1:
2087 2089 if len(repo.heads()) > 1:
2088 2090 raise util.Abort(_("branch '%s' has one head - "
2089 2091 "please merge with an explicit rev") %
2090 2092 branch)
2091 2093 msg = _('there is nothing to merge')
2092 2094 if parent != repo.lookup(repo[None].branch()):
2093 2095 msg = _('%s - use "hg update" instead') % msg
2094 2096 raise util.Abort(msg)
2095 2097
2096 2098 if parent not in bheads:
2097 2099 raise util.Abort(_('working dir not at a head rev - '
2098 2100 'use "hg update" or merge with an explicit rev'))
2099 2101 node = parent == bheads[0] and bheads[-1] or bheads[0]
2100 2102
2101 2103 if opts.get('show'):
2102 2104 p1 = repo['.']
2103 2105 p2 = repo[node]
2104 2106 common = p1.ancestor(p2)
2105 2107 roots, heads = [common.node()], [p2.node()]
2106 2108 displayer = cmdutil.show_changeset(ui, repo, opts)
2107 2109 for node in repo.changelog.nodesbetween(roots=roots, heads=heads)[0]:
2108 2110 displayer.show(repo[node])
2109 2111 return 0
2110 2112
2111 2113 return hg.merge(repo, node, force=opts.get('force'))
2112 2114
2113 2115 def outgoing(ui, repo, dest=None, **opts):
2114 2116 """show changesets not found in destination
2115 2117
2116 2118 Show changesets not found in the specified destination repository
2117 2119 or the default push location. These are the changesets that would
2118 2120 be pushed if a push was requested.
2119 2121
2120 2122 See pull for valid destination format details.
2121 2123 """
2122 2124 limit = cmdutil.loglimit(opts)
2123 2125 dest, revs, checkout = hg.parseurl(
2124 2126 ui.expandpath(dest or 'default-push', dest or 'default'), opts.get('rev'))
2125 2127 if revs:
2126 2128 revs = [repo.lookup(rev) for rev in revs]
2127 2129
2128 2130 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
2129 2131 ui.status(_('comparing with %s\n') % url.hidepassword(dest))
2130 2132 o = repo.findoutgoing(other, force=opts.get('force'))
2131 2133 if not o:
2132 2134 ui.status(_("no changes found\n"))
2133 2135 return 1
2134 2136 o = repo.changelog.nodesbetween(o, revs)[0]
2135 2137 if opts.get('newest_first'):
2136 2138 o.reverse()
2137 2139 displayer = cmdutil.show_changeset(ui, repo, opts)
2138 2140 count = 0
2139 2141 for n in o:
2140 2142 if count >= limit:
2141 2143 break
2142 2144 parents = [p for p in repo.changelog.parents(n) if p != nullid]
2143 2145 if opts.get('no_merges') and len(parents) == 2:
2144 2146 continue
2145 2147 count += 1
2146 2148 displayer.show(repo[n])
2147 2149
2148 2150 def parents(ui, repo, file_=None, **opts):
2149 2151 """show the parents of the working directory or revision
2150 2152
2151 2153 Print the working directory's parent revisions. If a revision is
2152 2154 given via -r/--rev, the parent of that revision will be printed.
2153 2155 If a file argument is given, the revision in which the file was
2154 2156 last changed (before the working directory revision or the
2155 2157 argument to --rev if given) is printed.
2156 2158 """
2157 2159 rev = opts.get('rev')
2158 2160 if rev:
2159 2161 ctx = repo[rev]
2160 2162 else:
2161 2163 ctx = repo[None]
2162 2164
2163 2165 if file_:
2164 2166 m = cmdutil.match(repo, (file_,), opts)
2165 2167 if m.anypats() or len(m.files()) != 1:
2166 2168 raise util.Abort(_('can only specify an explicit filename'))
2167 2169 file_ = m.files()[0]
2168 2170 filenodes = []
2169 2171 for cp in ctx.parents():
2170 2172 if not cp:
2171 2173 continue
2172 2174 try:
2173 2175 filenodes.append(cp.filenode(file_))
2174 2176 except error.LookupError:
2175 2177 pass
2176 2178 if not filenodes:
2177 2179 raise util.Abort(_("'%s' not found in manifest!") % file_)
2178 2180 fl = repo.file(file_)
2179 2181 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
2180 2182 else:
2181 2183 p = [cp.node() for cp in ctx.parents()]
2182 2184
2183 2185 displayer = cmdutil.show_changeset(ui, repo, opts)
2184 2186 for n in p:
2185 2187 if n != nullid:
2186 2188 displayer.show(repo[n])
2187 2189
2188 2190 def paths(ui, repo, search=None):
2189 2191 """show aliases for remote repositories
2190 2192
2191 2193 Show definition of symbolic path name NAME. If no name is given,
2192 2194 show definition of all available names.
2193 2195
2194 2196 Path names are defined in the [paths] section of /etc/mercurial/hgrc
2195 2197 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
2196 2198
2197 2199 See 'hg help urls' for more information.
2198 2200 """
2199 2201 if search:
2200 2202 for name, path in ui.configitems("paths"):
2201 2203 if name == search:
2202 2204 ui.write("%s\n" % url.hidepassword(path))
2203 2205 return
2204 2206 ui.warn(_("not found!\n"))
2205 2207 return 1
2206 2208 else:
2207 2209 for name, path in ui.configitems("paths"):
2208 2210 ui.write("%s = %s\n" % (name, url.hidepassword(path)))
2209 2211
2210 2212 def postincoming(ui, repo, modheads, optupdate, checkout):
2211 2213 if modheads == 0:
2212 2214 return
2213 2215 if optupdate:
2214 2216 if (modheads <= 1 or len(repo.branchheads()) == 1) or checkout:
2215 2217 return hg.update(repo, checkout)
2216 2218 else:
2217 2219 ui.status(_("not updating, since new heads added\n"))
2218 2220 if modheads > 1:
2219 2221 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2220 2222 else:
2221 2223 ui.status(_("(run 'hg update' to get a working copy)\n"))
2222 2224
2223 2225 def pull(ui, repo, source="default", **opts):
2224 2226 """pull changes from the specified source
2225 2227
2226 2228 Pull changes from a remote repository to a local one.
2227 2229
2228 2230 This finds all changes from the repository at the specified path
2229 2231 or URL and adds them to a local repository (the current one unless
2230 2232 -R is specified). By default, this does not update the copy of the
2231 2233 project in the working directory.
2232 2234
2233 2235 Use hg incoming if you want to see what would have been added by a
2234 2236 pull at the time you issued this command. If you then decide to
2235 2237 added those changes to the repository, you should use pull -r X
2236 2238 where X is the last changeset listed by hg incoming.
2237 2239
2238 2240 If SOURCE is omitted, the 'default' path will be used.
2239 2241 See 'hg help urls' for more information.
2240 2242 """
2241 2243 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts.get('rev'))
2242 2244 other = hg.repository(cmdutil.remoteui(repo, opts), source)
2243 2245 ui.status(_('pulling from %s\n') % url.hidepassword(source))
2244 2246 if revs:
2245 2247 try:
2246 2248 revs = [other.lookup(rev) for rev in revs]
2247 2249 except error.CapabilityError:
2248 2250 err = _("Other repository doesn't support revision lookup, "
2249 2251 "so a rev cannot be specified.")
2250 2252 raise util.Abort(err)
2251 2253
2252 2254 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
2253 2255 return postincoming(ui, repo, modheads, opts.get('update'), checkout)
2254 2256
2255 2257 def push(ui, repo, dest=None, **opts):
2256 2258 """push changes to the specified destination
2257 2259
2258 2260 Push changes from the local repository to the given destination.
2259 2261
2260 2262 This is the symmetrical operation for pull. It moves changes from
2261 2263 the current repository to a different one. If the destination is
2262 2264 local this is identical to a pull in that directory from the
2263 2265 current one.
2264 2266
2265 2267 By default, push will refuse to run if it detects the result would
2266 2268 increase the number of remote heads. This generally indicates the
2267 2269 user forgot to pull and merge before pushing.
2268 2270
2269 2271 If -r/--rev is used, the named revision and all its ancestors will
2270 2272 be pushed to the remote repository.
2271 2273
2272 2274 Please see 'hg help urls' for important details about ssh://
2273 2275 URLs. If DESTINATION is omitted, a default path will be used.
2274 2276 See 'hg help urls' for more information.
2275 2277 """
2276 2278 dest, revs, checkout = hg.parseurl(
2277 2279 ui.expandpath(dest or 'default-push', dest or 'default'), opts.get('rev'))
2278 2280 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
2279 2281 ui.status(_('pushing to %s\n') % url.hidepassword(dest))
2280 2282 if revs:
2281 2283 revs = [repo.lookup(rev) for rev in revs]
2282 2284 r = repo.push(other, opts.get('force'), revs=revs)
2283 2285 return r == 0
2284 2286
2285 2287 def recover(ui, repo):
2286 2288 """roll back an interrupted transaction
2287 2289
2288 2290 Recover from an interrupted commit or pull.
2289 2291
2290 2292 This command tries to fix the repository status after an
2291 2293 interrupted operation. It should only be necessary when Mercurial
2292 2294 suggests it.
2293 2295 """
2294 2296 if repo.recover():
2295 2297 return hg.verify(repo)
2296 2298 return 1
2297 2299
2298 2300 def remove(ui, repo, *pats, **opts):
2299 2301 """remove the specified files on the next commit
2300 2302
2301 2303 Schedule the indicated files for removal from the repository.
2302 2304
2303 2305 This only removes files from the current branch, not from the
2304 2306 entire project history. -A/--after can be used to remove only
2305 2307 files that have already been deleted, -f/--force can be used to
2306 2308 force deletion, and -Af can be used to remove files from the next
2307 2309 revision without deleting them from the working directory.
2308 2310
2309 2311 The following table details the behavior of remove for different
2310 2312 file states (columns) and option combinations (rows). The file
2311 2313 states are Added [A], Clean [C], Modified [M] and Missing [!]
2312 2314 (as reported by hg status). The actions are Warn, Remove (from
2313 2315 branch) and Delete (from disk).
2314 2316
2315 2317 A C M !
2316 2318 none W RD W R
2317 2319 -f R RD RD R
2318 2320 -A W W W R
2319 2321 -Af R R R R
2320 2322
2321 2323 This command schedules the files to be removed at the next commit.
2322 2324 To undo a remove before that, see hg revert.
2323 2325 """
2324 2326
2325 2327 after, force = opts.get('after'), opts.get('force')
2326 2328 if not pats and not after:
2327 2329 raise util.Abort(_('no files specified'))
2328 2330
2329 2331 m = cmdutil.match(repo, pats, opts)
2330 2332 s = repo.status(match=m, clean=True)
2331 2333 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2332 2334
2333 2335 for f in m.files():
2334 2336 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
2335 2337 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
2336 2338
2337 2339 def warn(files, reason):
2338 2340 for f in files:
2339 2341 ui.warn(_('not removing %s: file %s (use -f to force removal)\n')
2340 2342 % (m.rel(f), reason))
2341 2343
2342 2344 if force:
2343 2345 remove, forget = modified + deleted + clean, added
2344 2346 elif after:
2345 2347 remove, forget = deleted, []
2346 2348 warn(modified + added + clean, _('still exists'))
2347 2349 else:
2348 2350 remove, forget = deleted + clean, []
2349 2351 warn(modified, _('is modified'))
2350 2352 warn(added, _('has been marked for add'))
2351 2353
2352 2354 for f in sorted(remove + forget):
2353 2355 if ui.verbose or not m.exact(f):
2354 2356 ui.status(_('removing %s\n') % m.rel(f))
2355 2357
2356 2358 repo.forget(forget)
2357 2359 repo.remove(remove, unlink=not after)
2358 2360
2359 2361 def rename(ui, repo, *pats, **opts):
2360 2362 """rename files; equivalent of copy + remove
2361 2363
2362 2364 Mark dest as copies of sources; mark sources for deletion. If dest
2363 2365 is a directory, copies are put in that directory. If dest is a
2364 2366 file, there can only be one source.
2365 2367
2366 2368 By default, this command copies the contents of files as they
2367 2369 exist in the working directory. If invoked with -A/--after, the
2368 2370 operation is recorded, but no copying is performed.
2369 2371
2370 2372 This command takes effect at the next commit. To undo a rename
2371 2373 before that, see hg revert.
2372 2374 """
2373 2375 wlock = repo.wlock(False)
2374 2376 try:
2375 2377 return cmdutil.copy(ui, repo, pats, opts, rename=True)
2376 2378 finally:
2377 2379 wlock.release()
2378 2380
2379 2381 def resolve(ui, repo, *pats, **opts):
2380 2382 """retry file merges from a merge or update
2381 2383
2382 2384 This command will cleanly retry unresolved file merges using file
2383 2385 revisions preserved from the last update or merge. To attempt to
2384 2386 resolve all unresolved files, use the -a/--all switch.
2385 2387
2386 2388 If a conflict is resolved manually, please note that the changes
2387 2389 will be overwritten if the merge is retried with resolve. The
2388 2390 -m/--mark switch should be used to mark the file as resolved.
2389 2391
2390 2392 This command also allows listing resolved files and manually
2391 2393 indicating whether or not files are resolved. All files must be
2392 2394 marked as resolved before a commit is permitted.
2393 2395
2394 2396 The codes used to show the status of files are:
2395 2397 U = unresolved
2396 2398 R = resolved
2397 2399 """
2398 2400
2399 2401 all, mark, unmark, show = [opts.get(o) for o in 'all mark unmark list'.split()]
2400 2402
2401 2403 if (show and (mark or unmark)) or (mark and unmark):
2402 2404 raise util.Abort(_("too many options specified"))
2403 2405 if pats and all:
2404 2406 raise util.Abort(_("can't specify --all and patterns"))
2405 2407 if not (all or pats or show or mark or unmark):
2406 2408 raise util.Abort(_('no files or directories specified; '
2407 2409 'use --all to remerge all files'))
2408 2410
2409 2411 ms = merge_.mergestate(repo)
2410 2412 m = cmdutil.match(repo, pats, opts)
2411 2413
2412 2414 for f in ms:
2413 2415 if m(f):
2414 2416 if show:
2415 2417 ui.write("%s %s\n" % (ms[f].upper(), f))
2416 2418 elif mark:
2417 2419 ms.mark(f, "r")
2418 2420 elif unmark:
2419 2421 ms.mark(f, "u")
2420 2422 else:
2421 2423 wctx = repo[None]
2422 2424 mctx = wctx.parents()[-1]
2423 2425
2424 2426 # backup pre-resolve (merge uses .orig for its own purposes)
2425 2427 a = repo.wjoin(f)
2426 2428 util.copyfile(a, a + ".resolve")
2427 2429
2428 2430 # resolve file
2429 2431 ms.resolve(f, wctx, mctx)
2430 2432
2431 2433 # replace filemerge's .orig file with our resolve file
2432 2434 util.rename(a + ".resolve", a + ".orig")
2433 2435
2434 2436 def revert(ui, repo, *pats, **opts):
2435 2437 """restore individual files or directories to an earlier state
2436 2438
2437 2439 (Use update -r to check out earlier revisions, revert does not
2438 2440 change the working directory parents.)
2439 2441
2440 2442 With no revision specified, revert the named files or directories
2441 2443 to the contents they had in the parent of the working directory.
2442 2444 This restores the contents of the affected files to an unmodified
2443 2445 state and unschedules adds, removes, copies, and renames. If the
2444 2446 working directory has two parents, you must explicitly specify the
2445 2447 revision to revert to.
2446 2448
2447 2449 Using the -r/--rev option, revert the given files or directories
2448 2450 to their contents as of a specific revision. This can be helpful
2449 2451 to "roll back" some or all of an earlier change. See 'hg help
2450 2452 dates' for a list of formats valid for -d/--date.
2451 2453
2452 2454 Revert modifies the working directory. It does not commit any
2453 2455 changes, or change the parent of the working directory. If you
2454 2456 revert to a revision other than the parent of the working
2455 2457 directory, the reverted files will thus appear modified
2456 2458 afterwards.
2457 2459
2458 2460 If a file has been deleted, it is restored. If the executable mode
2459 2461 of a file was changed, it is reset.
2460 2462
2461 2463 If names are given, all files matching the names are reverted.
2462 2464 If no arguments are given, no files are reverted.
2463 2465
2464 2466 Modified files are saved with a .orig suffix before reverting.
2465 2467 To disable these backups, use --no-backup.
2466 2468 """
2467 2469
2468 2470 if opts["date"]:
2469 2471 if opts["rev"]:
2470 2472 raise util.Abort(_("you can't specify a revision and a date"))
2471 2473 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2472 2474
2473 2475 if not pats and not opts.get('all'):
2474 2476 raise util.Abort(_('no files or directories specified; '
2475 2477 'use --all to revert the whole repo'))
2476 2478
2477 2479 parent, p2 = repo.dirstate.parents()
2478 2480 if not opts.get('rev') and p2 != nullid:
2479 2481 raise util.Abort(_('uncommitted merge - please provide a '
2480 2482 'specific revision'))
2481 2483 ctx = repo[opts.get('rev')]
2482 2484 node = ctx.node()
2483 2485 mf = ctx.manifest()
2484 2486 if node == parent:
2485 2487 pmf = mf
2486 2488 else:
2487 2489 pmf = None
2488 2490
2489 2491 # need all matching names in dirstate and manifest of target rev,
2490 2492 # so have to walk both. do not print errors if files exist in one
2491 2493 # but not other.
2492 2494
2493 2495 names = {}
2494 2496
2495 2497 wlock = repo.wlock()
2496 2498 try:
2497 2499 # walk dirstate.
2498 2500
2499 2501 m = cmdutil.match(repo, pats, opts)
2500 2502 m.bad = lambda x,y: False
2501 2503 for abs in repo.walk(m):
2502 2504 names[abs] = m.rel(abs), m.exact(abs)
2503 2505
2504 2506 # walk target manifest.
2505 2507
2506 2508 def badfn(path, msg):
2507 2509 if path in names:
2508 2510 return
2509 2511 path_ = path + '/'
2510 2512 for f in names:
2511 2513 if f.startswith(path_):
2512 2514 return
2513 2515 ui.warn("%s: %s\n" % (m.rel(path), msg))
2514 2516
2515 2517 m = cmdutil.match(repo, pats, opts)
2516 2518 m.bad = badfn
2517 2519 for abs in repo[node].walk(m):
2518 2520 if abs not in names:
2519 2521 names[abs] = m.rel(abs), m.exact(abs)
2520 2522
2521 2523 m = cmdutil.matchfiles(repo, names)
2522 2524 changes = repo.status(match=m)[:4]
2523 2525 modified, added, removed, deleted = map(set, changes)
2524 2526
2525 2527 # if f is a rename, also revert the source
2526 2528 cwd = repo.getcwd()
2527 2529 for f in added:
2528 2530 src = repo.dirstate.copied(f)
2529 2531 if src and src not in names and repo.dirstate[src] == 'r':
2530 2532 removed.add(src)
2531 2533 names[src] = (repo.pathto(src, cwd), True)
2532 2534
2533 2535 def removeforget(abs):
2534 2536 if repo.dirstate[abs] == 'a':
2535 2537 return _('forgetting %s\n')
2536 2538 return _('removing %s\n')
2537 2539
2538 2540 revert = ([], _('reverting %s\n'))
2539 2541 add = ([], _('adding %s\n'))
2540 2542 remove = ([], removeforget)
2541 2543 undelete = ([], _('undeleting %s\n'))
2542 2544
2543 2545 disptable = (
2544 2546 # dispatch table:
2545 2547 # file state
2546 2548 # action if in target manifest
2547 2549 # action if not in target manifest
2548 2550 # make backup if in target manifest
2549 2551 # make backup if not in target manifest
2550 2552 (modified, revert, remove, True, True),
2551 2553 (added, revert, remove, True, False),
2552 2554 (removed, undelete, None, False, False),
2553 2555 (deleted, revert, remove, False, False),
2554 2556 )
2555 2557
2556 2558 for abs, (rel, exact) in sorted(names.items()):
2557 2559 mfentry = mf.get(abs)
2558 2560 target = repo.wjoin(abs)
2559 2561 def handle(xlist, dobackup):
2560 2562 xlist[0].append(abs)
2561 2563 if dobackup and not opts.get('no_backup') and util.lexists(target):
2562 2564 bakname = "%s.orig" % rel
2563 2565 ui.note(_('saving current version of %s as %s\n') %
2564 2566 (rel, bakname))
2565 2567 if not opts.get('dry_run'):
2566 2568 util.copyfile(target, bakname)
2567 2569 if ui.verbose or not exact:
2568 2570 msg = xlist[1]
2569 2571 if not isinstance(msg, basestring):
2570 2572 msg = msg(abs)
2571 2573 ui.status(msg % rel)
2572 2574 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2573 2575 if abs not in table: continue
2574 2576 # file has changed in dirstate
2575 2577 if mfentry:
2576 2578 handle(hitlist, backuphit)
2577 2579 elif misslist is not None:
2578 2580 handle(misslist, backupmiss)
2579 2581 break
2580 2582 else:
2581 2583 if abs not in repo.dirstate:
2582 2584 if mfentry:
2583 2585 handle(add, True)
2584 2586 elif exact:
2585 2587 ui.warn(_('file not managed: %s\n') % rel)
2586 2588 continue
2587 2589 # file has not changed in dirstate
2588 2590 if node == parent:
2589 2591 if exact: ui.warn(_('no changes needed to %s\n') % rel)
2590 2592 continue
2591 2593 if pmf is None:
2592 2594 # only need parent manifest in this unlikely case,
2593 2595 # so do not read by default
2594 2596 pmf = repo[parent].manifest()
2595 2597 if abs in pmf:
2596 2598 if mfentry:
2597 2599 # if version of file is same in parent and target
2598 2600 # manifests, do nothing
2599 2601 if (pmf[abs] != mfentry or
2600 2602 pmf.flags(abs) != mf.flags(abs)):
2601 2603 handle(revert, False)
2602 2604 else:
2603 2605 handle(remove, False)
2604 2606
2605 2607 if not opts.get('dry_run'):
2606 2608 def checkout(f):
2607 2609 fc = ctx[f]
2608 2610 repo.wwrite(f, fc.data(), fc.flags())
2609 2611
2610 2612 audit_path = util.path_auditor(repo.root)
2611 2613 for f in remove[0]:
2612 2614 if repo.dirstate[f] == 'a':
2613 2615 repo.dirstate.forget(f)
2614 2616 continue
2615 2617 audit_path(f)
2616 2618 try:
2617 2619 util.unlink(repo.wjoin(f))
2618 2620 except OSError:
2619 2621 pass
2620 2622 repo.dirstate.remove(f)
2621 2623
2622 2624 normal = None
2623 2625 if node == parent:
2624 2626 # We're reverting to our parent. If possible, we'd like status
2625 2627 # to report the file as clean. We have to use normallookup for
2626 2628 # merges to avoid losing information about merged/dirty files.
2627 2629 if p2 != nullid:
2628 2630 normal = repo.dirstate.normallookup
2629 2631 else:
2630 2632 normal = repo.dirstate.normal
2631 2633 for f in revert[0]:
2632 2634 checkout(f)
2633 2635 if normal:
2634 2636 normal(f)
2635 2637
2636 2638 for f in add[0]:
2637 2639 checkout(f)
2638 2640 repo.dirstate.add(f)
2639 2641
2640 2642 normal = repo.dirstate.normallookup
2641 2643 if node == parent and p2 == nullid:
2642 2644 normal = repo.dirstate.normal
2643 2645 for f in undelete[0]:
2644 2646 checkout(f)
2645 2647 normal(f)
2646 2648
2647 2649 finally:
2648 2650 wlock.release()
2649 2651
2650 2652 def rollback(ui, repo):
2651 2653 """roll back the last transaction
2652 2654
2653 2655 This command should be used with care. There is only one level of
2654 2656 rollback, and there is no way to undo a rollback. It will also
2655 2657 restore the dirstate at the time of the last transaction, losing
2656 2658 any dirstate changes since that time.
2657 2659
2658 2660 Transactions are used to encapsulate the effects of all commands
2659 2661 that create new changesets or propagate existing changesets into a
2660 2662 repository. For example, the following commands are transactional,
2661 2663 and their effects can be rolled back:
2662 2664
2663 2665 commit
2664 2666 import
2665 2667 pull
2666 2668 push (with this repository as destination)
2667 2669 unbundle
2668 2670
2669 2671 This command is not intended for use on public repositories. Once
2670 2672 changes are visible for pull by other users, rolling a transaction
2671 2673 back locally is ineffective (someone else may already have pulled
2672 2674 the changes). Furthermore, a race is possible with readers of the
2673 2675 repository; for example an in-progress pull from the repository
2674 2676 may fail if a rollback is performed.
2675 2677 """
2676 2678 repo.rollback()
2677 2679
2678 2680 def root(ui, repo):
2679 2681 """print the root (top) of the current working directory
2680 2682
2681 2683 Print the root directory of the current repository.
2682 2684 """
2683 2685 ui.write(repo.root + "\n")
2684 2686
2685 2687 def serve(ui, repo, **opts):
2686 2688 """export the repository via HTTP
2687 2689
2688 2690 Start a local HTTP repository browser and pull server.
2689 2691
2690 2692 By default, the server logs accesses to stdout and errors to
2691 2693 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
2692 2694 files.
2693 2695 """
2694 2696
2695 2697 if opts["stdio"]:
2696 2698 if repo is None:
2697 2699 raise error.RepoError(_("There is no Mercurial repository here"
2698 2700 " (.hg not found)"))
2699 2701 s = sshserver.sshserver(ui, repo)
2700 2702 s.serve_forever()
2701 2703
2702 2704 baseui = repo and repo.baseui or ui
2703 2705 optlist = ("name templates style address port prefix ipv6"
2704 2706 " accesslog errorlog webdir_conf certificate")
2705 2707 for o in optlist.split():
2706 2708 if opts[o]:
2707 2709 baseui.setconfig("web", o, str(opts[o]))
2708 2710 if (repo is not None) and (repo.ui != baseui):
2709 2711 repo.ui.setconfig("web", o, str(opts[o]))
2710 2712
2711 2713 if repo is None and not ui.config("web", "webdir_conf"):
2712 2714 raise error.RepoError(_("There is no Mercurial repository here"
2713 2715 " (.hg not found)"))
2714 2716
2715 2717 class service(object):
2716 2718 def init(self):
2717 2719 util.set_signal_handler()
2718 2720 self.httpd = server.create_server(baseui, repo)
2719 2721
2720 2722 if not ui.verbose: return
2721 2723
2722 2724 if self.httpd.prefix:
2723 2725 prefix = self.httpd.prefix.strip('/') + '/'
2724 2726 else:
2725 2727 prefix = ''
2726 2728
2727 2729 port = ':%d' % self.httpd.port
2728 2730 if port == ':80':
2729 2731 port = ''
2730 2732
2731 2733 bindaddr = self.httpd.addr
2732 2734 if bindaddr == '0.0.0.0':
2733 2735 bindaddr = '*'
2734 2736 elif ':' in bindaddr: # IPv6
2735 2737 bindaddr = '[%s]' % bindaddr
2736 2738
2737 2739 fqaddr = self.httpd.fqaddr
2738 2740 if ':' in fqaddr:
2739 2741 fqaddr = '[%s]' % fqaddr
2740 2742 ui.status(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
2741 2743 (fqaddr, port, prefix, bindaddr, self.httpd.port))
2742 2744
2743 2745 def run(self):
2744 2746 self.httpd.serve_forever()
2745 2747
2746 2748 service = service()
2747 2749
2748 2750 cmdutil.service(opts, initfn=service.init, runfn=service.run)
2749 2751
2750 2752 def status(ui, repo, *pats, **opts):
2751 2753 """show changed files in the working directory
2752 2754
2753 2755 Show status of files in the repository. If names are given, only
2754 2756 files that match are shown. Files that are clean or ignored or
2755 2757 the source of a copy/move operation, are not listed unless
2756 2758 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
2757 2759 Unless options described with "show only ..." are given, the
2758 2760 options -mardu are used.
2759 2761
2760 2762 Option -q/--quiet hides untracked (unknown and ignored) files
2761 2763 unless explicitly requested with -u/--unknown or -i/--ignored.
2762 2764
2763 2765 NOTE: status may appear to disagree with diff if permissions have
2764 2766 changed or a merge has occurred. The standard diff format does not
2765 2767 report permission changes and diff only reports changes relative
2766 2768 to one merge parent.
2767 2769
2768 2770 If one revision is given, it is used as the base revision.
2769 2771 If two revisions are given, the differences between them are
2770 2772 shown.
2771 2773
2772 2774 The codes used to show the status of files are:
2773 2775 M = modified
2774 2776 A = added
2775 2777 R = removed
2776 2778 C = clean
2777 2779 ! = missing (deleted by non-hg command, but still tracked)
2778 2780 ? = not tracked
2779 2781 I = ignored
2780 2782 = origin of the previous file listed as A (added)
2781 2783 """
2782 2784
2783 2785 node1, node2 = cmdutil.revpair(repo, opts.get('rev'))
2784 2786 cwd = (pats and repo.getcwd()) or ''
2785 2787 end = opts.get('print0') and '\0' or '\n'
2786 2788 copy = {}
2787 2789 states = 'modified added removed deleted unknown ignored clean'.split()
2788 2790 show = [k for k in states if opts.get(k)]
2789 2791 if opts.get('all'):
2790 2792 show += ui.quiet and (states[:4] + ['clean']) or states
2791 2793 if not show:
2792 2794 show = ui.quiet and states[:4] or states[:5]
2793 2795
2794 2796 stat = repo.status(node1, node2, cmdutil.match(repo, pats, opts),
2795 2797 'ignored' in show, 'clean' in show, 'unknown' in show)
2796 2798 changestates = zip(states, 'MAR!?IC', stat)
2797 2799
2798 2800 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
2799 2801 ctxn = repo[nullid]
2800 2802 ctx1 = repo[node1]
2801 2803 ctx2 = repo[node2]
2802 2804 added = stat[1]
2803 2805 if node2 is None:
2804 2806 added = stat[0] + stat[1] # merged?
2805 2807
2806 2808 for k, v in copies.copies(repo, ctx1, ctx2, ctxn)[0].iteritems():
2807 2809 if k in added:
2808 2810 copy[k] = v
2809 2811 elif v in added:
2810 2812 copy[v] = k
2811 2813
2812 2814 for state, char, files in changestates:
2813 2815 if state in show:
2814 2816 format = "%s %%s%s" % (char, end)
2815 2817 if opts.get('no_status'):
2816 2818 format = "%%s%s" % end
2817 2819
2818 2820 for f in files:
2819 2821 ui.write(format % repo.pathto(f, cwd))
2820 2822 if f in copy:
2821 2823 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end))
2822 2824
2823 2825 def tag(ui, repo, name1, *names, **opts):
2824 2826 """add one or more tags for the current or given revision
2825 2827
2826 2828 Name a particular revision using <name>.
2827 2829
2828 2830 Tags are used to name particular revisions of the repository and are
2829 2831 very useful to compare different revisions, to go back to significant
2830 2832 earlier versions or to mark branch points as releases, etc.
2831 2833
2832 2834 If no revision is given, the parent of the working directory is
2833 2835 used, or tip if no revision is checked out.
2834 2836
2835 2837 To facilitate version control, distribution, and merging of tags,
2836 2838 they are stored as a file named ".hgtags" which is managed
2837 2839 similarly to other project files and can be hand-edited if
2838 2840 necessary. The file '.hg/localtags' is used for local tags (not
2839 2841 shared among repositories).
2840 2842
2841 2843 See 'hg help dates' for a list of formats valid for -d/--date.
2842 2844 """
2843 2845
2844 2846 rev_ = "."
2845 2847 names = (name1,) + names
2846 2848 if len(names) != len(set(names)):
2847 2849 raise util.Abort(_('tag names must be unique'))
2848 2850 for n in names:
2849 2851 if n in ['tip', '.', 'null']:
2850 2852 raise util.Abort(_('the name \'%s\' is reserved') % n)
2851 2853 if opts.get('rev') and opts.get('remove'):
2852 2854 raise util.Abort(_("--rev and --remove are incompatible"))
2853 2855 if opts.get('rev'):
2854 2856 rev_ = opts['rev']
2855 2857 message = opts.get('message')
2856 2858 if opts.get('remove'):
2857 2859 expectedtype = opts.get('local') and 'local' or 'global'
2858 2860 for n in names:
2859 2861 if not repo.tagtype(n):
2860 2862 raise util.Abort(_('tag \'%s\' does not exist') % n)
2861 2863 if repo.tagtype(n) != expectedtype:
2862 2864 if expectedtype == 'global':
2863 2865 raise util.Abort(_('tag \'%s\' is not a global tag') % n)
2864 2866 else:
2865 2867 raise util.Abort(_('tag \'%s\' is not a local tag') % n)
2866 2868 rev_ = nullid
2867 2869 if not message:
2868 2870 message = _('Removed tag %s') % ', '.join(names)
2869 2871 elif not opts.get('force'):
2870 2872 for n in names:
2871 2873 if n in repo.tags():
2872 2874 raise util.Abort(_('tag \'%s\' already exists '
2873 2875 '(use -f to force)') % n)
2874 2876 if not rev_ and repo.dirstate.parents()[1] != nullid:
2875 2877 raise util.Abort(_('uncommitted merge - please provide a '
2876 2878 'specific revision'))
2877 2879 r = repo[rev_].node()
2878 2880
2879 2881 if not message:
2880 2882 message = (_('Added tag %s for changeset %s') %
2881 2883 (', '.join(names), short(r)))
2882 2884
2883 2885 date = opts.get('date')
2884 2886 if date:
2885 2887 date = util.parsedate(date)
2886 2888
2887 2889 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
2888 2890
2889 2891 def tags(ui, repo):
2890 2892 """list repository tags
2891 2893
2892 2894 This lists both regular and local tags. When the -v/--verbose
2893 2895 switch is used, a third column "local" is printed for local tags.
2894 2896 """
2895 2897
2896 2898 hexfunc = ui.debugflag and hex or short
2897 2899 tagtype = ""
2898 2900
2899 2901 for t, n in reversed(repo.tagslist()):
2900 2902 if ui.quiet:
2901 2903 ui.write("%s\n" % t)
2902 2904 continue
2903 2905
2904 2906 try:
2905 2907 hn = hexfunc(n)
2906 2908 r = "%5d:%s" % (repo.changelog.rev(n), hn)
2907 2909 except error.LookupError:
2908 2910 r = " ?:%s" % hn
2909 2911 else:
2910 2912 spaces = " " * (30 - encoding.colwidth(t))
2911 2913 if ui.verbose:
2912 2914 if repo.tagtype(t) == 'local':
2913 2915 tagtype = " local"
2914 2916 else:
2915 2917 tagtype = ""
2916 2918 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
2917 2919
2918 2920 def tip(ui, repo, **opts):
2919 2921 """show the tip revision
2920 2922
2921 2923 The tip revision (usually just called the tip) is the changeset
2922 2924 most recently added to the repository (and therefore the most
2923 2925 recently changed head).
2924 2926
2925 2927 If you have just made a commit, that commit will be the tip. If
2926 2928 you have just pulled changes from another repository, the tip of
2927 2929 that repository becomes the current tip. The "tip" tag is special
2928 2930 and cannot be renamed or assigned to a different changeset.
2929 2931 """
2930 2932 cmdutil.show_changeset(ui, repo, opts).show(repo[len(repo) - 1])
2931 2933
2932 2934 def unbundle(ui, repo, fname1, *fnames, **opts):
2933 2935 """apply one or more changegroup files
2934 2936
2935 2937 Apply one or more compressed changegroup files generated by the
2936 2938 bundle command.
2937 2939 """
2938 2940 fnames = (fname1,) + fnames
2939 2941
2940 2942 lock = repo.lock()
2941 2943 try:
2942 2944 for fname in fnames:
2943 2945 f = url.open(ui, fname)
2944 2946 gen = changegroup.readbundle(f, fname)
2945 2947 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
2946 2948 finally:
2947 2949 lock.release()
2948 2950
2949 2951 return postincoming(ui, repo, modheads, opts.get('update'), None)
2950 2952
2951 2953 def update(ui, repo, node=None, rev=None, clean=False, date=None):
2952 2954 """update working directory
2953 2955
2954 2956 Update the repository's working directory to the specified
2955 2957 revision, or the tip of the current branch if none is specified.
2956 2958 Use null as the revision to remove the working copy (like 'hg
2957 2959 clone -U').
2958 2960
2959 2961 When the working directory contains no uncommitted changes, it
2960 2962 will be replaced by the state of the requested revision from the
2961 2963 repository. When the requested revision is on a different branch,
2962 2964 the working directory will additionally be switched to that
2963 2965 branch.
2964 2966
2965 2967 When there are uncommitted changes, use option -C/--clean to
2966 2968 discard them, forcibly replacing the state of the working
2967 2969 directory with the requested revision.
2968 2970
2969 2971 When there are uncommitted changes and option -C/--clean is not
2970 2972 used, and the parent revision and requested revision are on the
2971 2973 same branch, and one of them is an ancestor of the other, then the
2972 2974 new working directory will contain the requested revision merged
2973 2975 with the uncommitted changes. Otherwise, the update will fail with
2974 2976 a suggestion to use 'merge' or 'update -C' instead.
2975 2977
2976 2978 If you want to update just one file to an older revision, use
2977 2979 revert.
2978 2980
2979 2981 See 'hg help dates' for a list of formats valid for -d/--date.
2980 2982 """
2981 2983 if rev and node:
2982 2984 raise util.Abort(_("please specify just one revision"))
2983 2985
2984 2986 if not rev:
2985 2987 rev = node
2986 2988
2987 2989 if date:
2988 2990 if rev:
2989 2991 raise util.Abort(_("you can't specify a revision and a date"))
2990 2992 rev = cmdutil.finddate(ui, repo, date)
2991 2993
2992 2994 if clean:
2993 2995 return hg.clean(repo, rev)
2994 2996 else:
2995 2997 return hg.update(repo, rev)
2996 2998
2997 2999 def verify(ui, repo):
2998 3000 """verify the integrity of the repository
2999 3001
3000 3002 Verify the integrity of the current repository.
3001 3003
3002 3004 This will perform an extensive check of the repository's
3003 3005 integrity, validating the hashes and checksums of each entry in
3004 3006 the changelog, manifest, and tracked files, as well as the
3005 3007 integrity of their crosslinks and indices.
3006 3008 """
3007 3009 return hg.verify(repo)
3008 3010
3009 3011 def version_(ui):
3010 3012 """output version and copyright information"""
3011 3013 ui.write(_("Mercurial Distributed SCM (version %s)\n")
3012 3014 % util.version())
3013 3015 ui.status(_(
3014 3016 "\nCopyright (C) 2005-2009 Matt Mackall <mpm@selenic.com> and others\n"
3015 3017 "This is free software; see the source for copying conditions. "
3016 3018 "There is NO\nwarranty; "
3017 3019 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
3018 3020 ))
3019 3021
3020 3022 # Command options and aliases are listed here, alphabetically
3021 3023
3022 3024 globalopts = [
3023 3025 ('R', 'repository', '',
3024 3026 _('repository root directory or symbolic path name')),
3025 3027 ('', 'cwd', '', _('change working directory')),
3026 3028 ('y', 'noninteractive', None,
3027 3029 _('do not prompt, assume \'yes\' for any required answers')),
3028 3030 ('q', 'quiet', None, _('suppress output')),
3029 3031 ('v', 'verbose', None, _('enable additional output')),
3030 3032 ('', 'config', [], _('set/override config option')),
3031 3033 ('', 'debug', None, _('enable debugging output')),
3032 3034 ('', 'debugger', None, _('start debugger')),
3033 3035 ('', 'encoding', encoding.encoding, _('set the charset encoding')),
3034 3036 ('', 'encodingmode', encoding.encodingmode,
3035 3037 _('set the charset encoding mode')),
3036 3038 ('', 'traceback', None, _('print traceback on exception')),
3037 3039 ('', 'time', None, _('time how long the command takes')),
3038 3040 ('', 'profile', None, _('print command execution profile')),
3039 3041 ('', 'version', None, _('output version information and exit')),
3040 3042 ('h', 'help', None, _('display help and exit')),
3041 3043 ]
3042 3044
3043 3045 dryrunopts = [('n', 'dry-run', None,
3044 3046 _('do not perform actions, just print output'))]
3045 3047
3046 3048 remoteopts = [
3047 3049 ('e', 'ssh', '', _('specify ssh command to use')),
3048 3050 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
3049 3051 ]
3050 3052
3051 3053 walkopts = [
3052 3054 ('I', 'include', [], _('include names matching the given patterns')),
3053 3055 ('X', 'exclude', [], _('exclude names matching the given patterns')),
3054 3056 ]
3055 3057
3056 3058 commitopts = [
3057 3059 ('m', 'message', '', _('use <text> as commit message')),
3058 3060 ('l', 'logfile', '', _('read commit message from <file>')),
3059 3061 ]
3060 3062
3061 3063 commitopts2 = [
3062 3064 ('d', 'date', '', _('record datecode as commit date')),
3063 3065 ('u', 'user', '', _('record the specified user as committer')),
3064 3066 ]
3065 3067
3066 3068 templateopts = [
3067 3069 ('', 'style', '', _('display using template map file')),
3068 3070 ('', 'template', '', _('display with template')),
3069 3071 ]
3070 3072
3071 3073 logopts = [
3072 3074 ('p', 'patch', None, _('show patch')),
3073 3075 ('g', 'git', None, _('use git extended diff format')),
3074 3076 ('l', 'limit', '', _('limit number of changes displayed')),
3075 3077 ('M', 'no-merges', None, _('do not show merges')),
3076 3078 ] + templateopts
3077 3079
3078 3080 diffopts = [
3079 3081 ('a', 'text', None, _('treat all files as text')),
3080 3082 ('g', 'git', None, _('use git extended diff format')),
3081 3083 ('', 'nodates', None, _("don't include dates in diff headers"))
3082 3084 ]
3083 3085
3084 3086 diffopts2 = [
3085 3087 ('p', 'show-function', None, _('show which function each change is in')),
3086 3088 ('w', 'ignore-all-space', None,
3087 3089 _('ignore white space when comparing lines')),
3088 3090 ('b', 'ignore-space-change', None,
3089 3091 _('ignore changes in the amount of white space')),
3090 3092 ('B', 'ignore-blank-lines', None,
3091 3093 _('ignore changes whose lines are all blank')),
3092 3094 ('U', 'unified', '', _('number of lines of context to show'))
3093 3095 ]
3094 3096
3095 3097 similarityopts = [
3096 3098 ('s', 'similarity', '',
3097 3099 _('guess renamed files by similarity (0<=s<=100)'))
3098 3100 ]
3099 3101
3100 3102 table = {
3101 3103 "^add": (add, walkopts + dryrunopts, _('[OPTION]... [FILE]...')),
3102 3104 "addremove":
3103 3105 (addremove, similarityopts + walkopts + dryrunopts,
3104 3106 _('[OPTION]... [FILE]...')),
3105 3107 "^annotate|blame":
3106 3108 (annotate,
3107 3109 [('r', 'rev', '', _('annotate the specified revision')),
3108 3110 ('f', 'follow', None, _('follow file copies and renames')),
3109 3111 ('a', 'text', None, _('treat all files as text')),
3110 3112 ('u', 'user', None, _('list the author (long with -v)')),
3111 3113 ('d', 'date', None, _('list the date (short with -q)')),
3112 3114 ('n', 'number', None, _('list the revision number (default)')),
3113 3115 ('c', 'changeset', None, _('list the changeset')),
3114 3116 ('l', 'line-number', None,
3115 3117 _('show line number at the first appearance'))
3116 3118 ] + walkopts,
3117 3119 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
3118 3120 "archive":
3119 3121 (archive,
3120 3122 [('', 'no-decode', None, _('do not pass files through decoders')),
3121 3123 ('p', 'prefix', '', _('directory prefix for files in archive')),
3122 3124 ('r', 'rev', '', _('revision to distribute')),
3123 3125 ('t', 'type', '', _('type of distribution to create')),
3124 3126 ] + walkopts,
3125 3127 _('[OPTION]... DEST')),
3126 3128 "backout":
3127 3129 (backout,
3128 3130 [('', 'merge', None,
3129 3131 _('merge with old dirstate parent after backout')),
3130 3132 ('', 'parent', '', _('parent to choose when backing out merge')),
3131 3133 ('r', 'rev', '', _('revision to backout')),
3132 3134 ] + walkopts + commitopts + commitopts2,
3133 3135 _('[OPTION]... [-r] REV')),
3134 3136 "bisect":
3135 3137 (bisect,
3136 3138 [('r', 'reset', False, _('reset bisect state')),
3137 3139 ('g', 'good', False, _('mark changeset good')),
3138 3140 ('b', 'bad', False, _('mark changeset bad')),
3139 3141 ('s', 'skip', False, _('skip testing changeset')),
3140 3142 ('c', 'command', '', _('use command to check changeset state')),
3141 3143 ('U', 'noupdate', False, _('do not update to target'))],
3142 3144 _("[-gbsr] [-c CMD] [REV]")),
3143 3145 "branch":
3144 3146 (branch,
3145 3147 [('f', 'force', None,
3146 3148 _('set branch name even if it shadows an existing branch')),
3147 3149 ('C', 'clean', None, _('reset branch name to parent branch name'))],
3148 3150 _('[-fC] [NAME]')),
3149 3151 "branches":
3150 3152 (branches,
3151 3153 [('a', 'active', False,
3152 3154 _('show only branches that have unmerged heads'))],
3153 3155 _('[-a]')),
3154 3156 "bundle":
3155 3157 (bundle,
3156 3158 [('f', 'force', None,
3157 3159 _('run even when remote repository is unrelated')),
3158 3160 ('r', 'rev', [],
3159 3161 _('a changeset up to which you would like to bundle')),
3160 3162 ('', 'base', [],
3161 3163 _('a base changeset to specify instead of a destination')),
3162 3164 ('a', 'all', None, _('bundle all changesets in the repository')),
3163 3165 ('t', 'type', 'bzip2', _('bundle compression type to use')),
3164 3166 ] + remoteopts,
3165 3167 _('[-f] [-a] [-r REV]... [--base REV]... FILE [DEST]')),
3166 3168 "cat":
3167 3169 (cat,
3168 3170 [('o', 'output', '', _('print output to file with formatted name')),
3169 3171 ('r', 'rev', '', _('print the given revision')),
3170 3172 ('', 'decode', None, _('apply any matching decode filter')),
3171 3173 ] + walkopts,
3172 3174 _('[OPTION]... FILE...')),
3173 3175 "^clone":
3174 3176 (clone,
3175 3177 [('U', 'noupdate', None,
3176 3178 _('the clone will only contain a repository (no working copy)')),
3177 3179 ('r', 'rev', [],
3178 3180 _('a changeset you would like to have after cloning')),
3179 3181 ('', 'pull', None, _('use pull protocol to copy metadata')),
3180 3182 ('', 'uncompressed', None,
3181 3183 _('use uncompressed transfer (fast over LAN)')),
3182 3184 ] + remoteopts,
3183 3185 _('[OPTION]... SOURCE [DEST]')),
3184 3186 "^commit|ci":
3185 3187 (commit,
3186 3188 [('A', 'addremove', None,
3187 3189 _('mark new/missing files as added/removed before committing')),
3188 3190 ('', 'close-branch', None,
3189 3191 _('mark a branch as closed, hiding it from the branch list')),
3190 3192 ] + walkopts + commitopts + commitopts2,
3191 3193 _('[OPTION]... [FILE]...')),
3192 3194 "copy|cp":
3193 3195 (copy,
3194 3196 [('A', 'after', None, _('record a copy that has already occurred')),
3195 3197 ('f', 'force', None,
3196 3198 _('forcibly copy over an existing managed file')),
3197 3199 ] + walkopts + dryrunopts,
3198 3200 _('[OPTION]... [SOURCE]... DEST')),
3199 3201 "debugancestor": (debugancestor, [], _('[INDEX] REV1 REV2')),
3200 3202 "debugcheckstate": (debugcheckstate, []),
3201 3203 "debugcommands": (debugcommands, [], _('[COMMAND]')),
3202 3204 "debugcomplete":
3203 3205 (debugcomplete,
3204 3206 [('o', 'options', None, _('show the command options'))],
3205 3207 _('[-o] CMD')),
3206 3208 "debugdate":
3207 3209 (debugdate,
3208 3210 [('e', 'extended', None, _('try extended date formats'))],
3209 3211 _('[-e] DATE [RANGE]')),
3210 3212 "debugdata": (debugdata, [], _('FILE REV')),
3211 3213 "debugfsinfo": (debugfsinfo, [], _('[PATH]')),
3212 3214 "debugindex": (debugindex, [], _('FILE')),
3213 3215 "debugindexdot": (debugindexdot, [], _('FILE')),
3214 3216 "debuginstall": (debuginstall, []),
3215 3217 "debugrebuildstate":
3216 3218 (debugrebuildstate,
3217 3219 [('r', 'rev', '', _('revision to rebuild to'))],
3218 3220 _('[-r REV] [REV]')),
3219 3221 "debugrename":
3220 3222 (debugrename,
3221 3223 [('r', 'rev', '', _('revision to debug'))],
3222 3224 _('[-r REV] FILE')),
3223 3225 "debugsetparents":
3224 3226 (debugsetparents, [], _('REV1 [REV2]')),
3225 3227 "debugstate":
3226 3228 (debugstate,
3227 3229 [('', 'nodates', None, _('do not display the saved mtime'))],
3228 3230 _('[OPTION]...')),
3229 3231 "debugwalk": (debugwalk, walkopts, _('[OPTION]... [FILE]...')),
3230 3232 "^diff":
3231 3233 (diff,
3232 3234 [('r', 'rev', [], _('revision')),
3233 3235 ('c', 'change', '', _('change made by revision'))
3234 3236 ] + diffopts + diffopts2 + walkopts,
3235 3237 _('[OPTION]... [-r REV1 [-r REV2]] [FILE]...')),
3236 3238 "^export":
3237 3239 (export,
3238 3240 [('o', 'output', '', _('print output to file with formatted name')),
3239 3241 ('', 'switch-parent', None, _('diff against the second parent'))
3240 3242 ] + diffopts,
3241 3243 _('[OPTION]... [-o OUTFILESPEC] REV...')),
3242 3244 "grep":
3243 3245 (grep,
3244 3246 [('0', 'print0', None, _('end fields with NUL')),
3245 3247 ('', 'all', None, _('print all revisions that match')),
3246 3248 ('f', 'follow', None,
3247 3249 _('follow changeset history, or file history across copies and renames')),
3248 3250 ('i', 'ignore-case', None, _('ignore case when matching')),
3249 3251 ('l', 'files-with-matches', None,
3250 3252 _('print only filenames and revisions that match')),
3251 3253 ('n', 'line-number', None, _('print matching line numbers')),
3252 3254 ('r', 'rev', [], _('search in given revision range')),
3253 3255 ('u', 'user', None, _('list the author (long with -v)')),
3254 3256 ('d', 'date', None, _('list the date (short with -q)')),
3255 3257 ] + walkopts,
3256 3258 _('[OPTION]... PATTERN [FILE]...')),
3257 3259 "heads":
3258 3260 (heads,
3259 3261 [('r', 'rev', '', _('show only heads which are descendants of REV')),
3260 3262 ('a', 'active', False,
3261 3263 _('show only the active heads from open branches')),
3262 3264 ('c', 'closed', False,
3263 3265 _('show normal and closed heads')),
3264 3266 ] + templateopts,
3265 3267 _('[-r STARTREV] [REV]...')),
3266 3268 "help": (help_, [], _('[TOPIC]')),
3267 3269 "identify|id":
3268 3270 (identify,
3269 3271 [('r', 'rev', '', _('identify the specified revision')),
3270 3272 ('n', 'num', None, _('show local revision number')),
3271 3273 ('i', 'id', None, _('show global revision id')),
3272 3274 ('b', 'branch', None, _('show branch')),
3273 3275 ('t', 'tags', None, _('show tags'))],
3274 3276 _('[-nibt] [-r REV] [SOURCE]')),
3275 3277 "import|patch":
3276 3278 (import_,
3277 3279 [('p', 'strip', 1,
3278 3280 _('directory strip option for patch. This has the same '
3279 3281 'meaning as the corresponding patch option')),
3280 3282 ('b', 'base', '', _('base path')),
3281 3283 ('f', 'force', None,
3282 3284 _('skip check for outstanding uncommitted changes')),
3283 3285 ('', 'no-commit', None, _("don't commit, just update the working directory")),
3284 3286 ('', 'exact', None,
3285 3287 _('apply patch to the nodes from which it was generated')),
3286 3288 ('', 'import-branch', None,
3287 3289 _('use any branch information in patch (implied by --exact)'))] +
3288 3290 commitopts + commitopts2 + similarityopts,
3289 3291 _('[OPTION]... PATCH...')),
3290 3292 "incoming|in":
3291 3293 (incoming,
3292 3294 [('f', 'force', None,
3293 3295 _('run even when remote repository is unrelated')),
3294 3296 ('n', 'newest-first', None, _('show newest record first')),
3295 3297 ('', 'bundle', '', _('file to store the bundles into')),
3296 3298 ('r', 'rev', [],
3297 3299 _('a specific revision up to which you would like to pull')),
3298 3300 ] + logopts + remoteopts,
3299 3301 _('[-p] [-n] [-M] [-f] [-r REV]...'
3300 3302 ' [--bundle FILENAME] [SOURCE]')),
3301 3303 "^init":
3302 3304 (init,
3303 3305 remoteopts,
3304 3306 _('[-e CMD] [--remotecmd CMD] [DEST]')),
3305 3307 "locate":
3306 3308 (locate,
3307 3309 [('r', 'rev', '', _('search the repository as it stood at REV')),
3308 3310 ('0', 'print0', None,
3309 3311 _('end filenames with NUL, for use with xargs')),
3310 3312 ('f', 'fullpath', None,
3311 3313 _('print complete paths from the filesystem root')),
3312 3314 ] + walkopts,
3313 3315 _('[OPTION]... [PATTERN]...')),
3314 3316 "^log|history":
3315 3317 (log,
3316 3318 [('f', 'follow', None,
3317 3319 _('follow changeset history, or file history across copies and renames')),
3318 3320 ('', 'follow-first', None,
3319 3321 _('only follow the first parent of merge changesets')),
3320 3322 ('d', 'date', '', _('show revisions matching date spec')),
3321 3323 ('C', 'copies', None, _('show copied files')),
3322 3324 ('k', 'keyword', [], _('do case-insensitive search for a keyword')),
3323 3325 ('r', 'rev', [], _('show the specified revision or range')),
3324 3326 ('', 'removed', None, _('include revisions where files were removed')),
3325 3327 ('m', 'only-merges', None, _('show only merges')),
3326 3328 ('u', 'user', [], _('revisions committed by user')),
3327 3329 ('b', 'only-branch', [],
3328 3330 _('show only changesets within the given named branch')),
3329 3331 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
3330 3332 ] + logopts + walkopts,
3331 3333 _('[OPTION]... [FILE]')),
3332 3334 "manifest":
3333 3335 (manifest,
3334 3336 [('r', 'rev', '', _('revision to display'))],
3335 3337 _('[-r REV]')),
3336 3338 "^merge":
3337 3339 (merge,
3338 3340 [('f', 'force', None, _('force a merge with outstanding changes')),
3339 3341 ('r', 'rev', '', _('revision to merge')),
3340 3342 ('S', 'show', None,
3341 3343 _('review revisions to merge (no merge is performed)'))],
3342 3344 _('[-f] [[-r] REV]')),
3343 3345 "outgoing|out":
3344 3346 (outgoing,
3345 3347 [('f', 'force', None,
3346 3348 _('run even when remote repository is unrelated')),
3347 3349 ('r', 'rev', [],
3348 3350 _('a specific revision up to which you would like to push')),
3349 3351 ('n', 'newest-first', None, _('show newest record first')),
3350 3352 ] + logopts + remoteopts,
3351 3353 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
3352 3354 "^parents":
3353 3355 (parents,
3354 3356 [('r', 'rev', '', _('show parents from the specified revision')),
3355 3357 ] + templateopts,
3356 3358 _('[-r REV] [FILE]')),
3357 3359 "paths": (paths, [], _('[NAME]')),
3358 3360 "^pull":
3359 3361 (pull,
3360 3362 [('u', 'update', None,
3361 3363 _('update to new tip if changesets were pulled')),
3362 3364 ('f', 'force', None,
3363 3365 _('run even when remote repository is unrelated')),
3364 3366 ('r', 'rev', [],
3365 3367 _('a specific revision up to which you would like to pull')),
3366 3368 ] + remoteopts,
3367 3369 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
3368 3370 "^push":
3369 3371 (push,
3370 3372 [('f', 'force', None, _('force push')),
3371 3373 ('r', 'rev', [],
3372 3374 _('a specific revision up to which you would like to push')),
3373 3375 ] + remoteopts,
3374 3376 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
3375 3377 "recover": (recover, []),
3376 3378 "^remove|rm":
3377 3379 (remove,
3378 3380 [('A', 'after', None, _('record delete for missing files')),
3379 3381 ('f', 'force', None,
3380 3382 _('remove (and delete) file even if added or modified')),
3381 3383 ] + walkopts,
3382 3384 _('[OPTION]... FILE...')),
3383 3385 "rename|mv":
3384 3386 (rename,
3385 3387 [('A', 'after', None, _('record a rename that has already occurred')),
3386 3388 ('f', 'force', None,
3387 3389 _('forcibly copy over an existing managed file')),
3388 3390 ] + walkopts + dryrunopts,
3389 3391 _('[OPTION]... SOURCE... DEST')),
3390 3392 "resolve":
3391 3393 (resolve,
3392 3394 [('a', 'all', None, _('remerge all unresolved files')),
3393 3395 ('l', 'list', None, _('list state of files needing merge')),
3394 3396 ('m', 'mark', None, _('mark files as resolved')),
3395 3397 ('u', 'unmark', None, _('unmark files as resolved'))]
3396 3398 + walkopts,
3397 3399 _('[OPTION]... [FILE]...')),
3398 3400 "revert":
3399 3401 (revert,
3400 3402 [('a', 'all', None, _('revert all changes when no arguments given')),
3401 3403 ('d', 'date', '', _('tipmost revision matching date')),
3402 3404 ('r', 'rev', '', _('revision to revert to')),
3403 3405 ('', 'no-backup', None, _('do not save backup copies of files')),
3404 3406 ] + walkopts + dryrunopts,
3405 3407 _('[OPTION]... [-r REV] [NAME]...')),
3406 3408 "rollback": (rollback, []),
3407 3409 "root": (root, []),
3408 3410 "^serve":
3409 3411 (serve,
3410 3412 [('A', 'accesslog', '', _('name of access log file to write to')),
3411 3413 ('d', 'daemon', None, _('run server in background')),
3412 3414 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3413 3415 ('E', 'errorlog', '', _('name of error log file to write to')),
3414 3416 ('p', 'port', 0, _('port to listen on (default: 8000)')),
3415 3417 ('a', 'address', '', _('address to listen on (default: all interfaces)')),
3416 3418 ('', 'prefix', '', _('prefix path to serve from (default: server root)')),
3417 3419 ('n', 'name', '',
3418 3420 _('name to show in web pages (default: working directory)')),
3419 3421 ('', 'webdir-conf', '', _('name of the webdir config file'
3420 3422 ' (serve more than one repository)')),
3421 3423 ('', 'pid-file', '', _('name of file to write process ID to')),
3422 3424 ('', 'stdio', None, _('for remote clients')),
3423 3425 ('t', 'templates', '', _('web templates to use')),
3424 3426 ('', 'style', '', _('template style to use')),
3425 3427 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
3426 3428 ('', 'certificate', '', _('SSL certificate file'))],
3427 3429 _('[OPTION]...')),
3428 3430 "showconfig|debugconfig":
3429 3431 (showconfig,
3430 3432 [('u', 'untrusted', None, _('show untrusted configuration options'))],
3431 3433 _('[-u] [NAME]...')),
3432 3434 "^status|st":
3433 3435 (status,
3434 3436 [('A', 'all', None, _('show status of all files')),
3435 3437 ('m', 'modified', None, _('show only modified files')),
3436 3438 ('a', 'added', None, _('show only added files')),
3437 3439 ('r', 'removed', None, _('show only removed files')),
3438 3440 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3439 3441 ('c', 'clean', None, _('show only files without changes')),
3440 3442 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3441 3443 ('i', 'ignored', None, _('show only ignored files')),
3442 3444 ('n', 'no-status', None, _('hide status prefix')),
3443 3445 ('C', 'copies', None, _('show source of copied files')),
3444 3446 ('0', 'print0', None,
3445 3447 _('end filenames with NUL, for use with xargs')),
3446 3448 ('', 'rev', [], _('show difference from revision')),
3447 3449 ] + walkopts,
3448 3450 _('[OPTION]... [FILE]...')),
3449 3451 "tag":
3450 3452 (tag,
3451 3453 [('f', 'force', None, _('replace existing tag')),
3452 3454 ('l', 'local', None, _('make the tag local')),
3453 3455 ('r', 'rev', '', _('revision to tag')),
3454 3456 ('', 'remove', None, _('remove a tag')),
3455 3457 # -l/--local is already there, commitopts cannot be used
3456 3458 ('m', 'message', '', _('use <text> as commit message')),
3457 3459 ] + commitopts2,
3458 3460 _('[-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')),
3459 3461 "tags": (tags, []),
3460 3462 "tip":
3461 3463 (tip,
3462 3464 [('p', 'patch', None, _('show patch')),
3463 3465 ('g', 'git', None, _('use git extended diff format')),
3464 3466 ] + templateopts,
3465 3467 _('[-p]')),
3466 3468 "unbundle":
3467 3469 (unbundle,
3468 3470 [('u', 'update', None,
3469 3471 _('update to new tip if changesets were unbundled'))],
3470 3472 _('[-u] FILE...')),
3471 3473 "^update|up|checkout|co":
3472 3474 (update,
3473 3475 [('C', 'clean', None, _('overwrite locally modified files (no backup)')),
3474 3476 ('d', 'date', '', _('tipmost revision matching date')),
3475 3477 ('r', 'rev', '', _('revision'))],
3476 3478 _('[-C] [-d DATE] [[-r] REV]')),
3477 3479 "verify": (verify, []),
3478 3480 "version": (version_, []),
3479 3481 }
3480 3482
3481 3483 norepo = ("clone init version help debugcommands debugcomplete debugdata"
3482 3484 " debugindex debugindexdot debugdate debuginstall debugfsinfo")
3483 3485 optionalrepo = ("identify paths serve showconfig debugancestor")
@@ -1,601 +1,601
1 1 # dirstate.py - working directory tracking for mercurial
2 2 #
3 3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2, incorporated herein by reference.
7 7
8 8 from node import nullid
9 9 from i18n import _
10 10 import util, ignore, osutil, parsers
11 11 import struct, os, stat, errno
12 12 import cStringIO, sys
13 13
14 14 _unknown = ('?', 0, 0, 0)
15 15 _format = ">cllll"
16 16 propertycache = util.propertycache
17 17
18 18 def _finddirs(path):
19 19 pos = path.rfind('/')
20 20 while pos != -1:
21 21 yield path[:pos]
22 22 pos = path.rfind('/', 0, pos)
23 23
24 24 def _incdirs(dirs, path):
25 25 for base in _finddirs(path):
26 26 if base in dirs:
27 27 dirs[base] += 1
28 28 return
29 29 dirs[base] = 1
30 30
31 31 def _decdirs(dirs, path):
32 32 for base in _finddirs(path):
33 33 if dirs[base] > 1:
34 34 dirs[base] -= 1
35 35 return
36 36 del dirs[base]
37 37
38 38 class dirstate(object):
39 39
40 40 def __init__(self, opener, ui, root):
41 41 self._opener = opener
42 42 self._root = root
43 43 self._rootdir = os.path.join(root, '')
44 44 self._dirty = False
45 45 self._dirtypl = False
46 46 self._ui = ui
47 47
48 48 @propertycache
49 49 def _map(self):
50 50 self._read()
51 51 return self._map
52 52
53 53 @propertycache
54 54 def _copymap(self):
55 55 self._read()
56 56 return self._copymap
57 57
58 58 @propertycache
59 59 def _foldmap(self):
60 60 f = {}
61 61 for name in self._map:
62 62 f[os.path.normcase(name)] = name
63 63 return f
64 64
65 65 @propertycache
66 66 def _branch(self):
67 67 try:
68 68 return self._opener("branch").read().strip() or "default"
69 69 except IOError:
70 70 return "default"
71 71
72 72 @propertycache
73 73 def _pl(self):
74 74 try:
75 75 st = self._opener("dirstate").read(40)
76 76 l = len(st)
77 77 if l == 40:
78 78 return st[:20], st[20:40]
79 79 elif l > 0 and l < 40:
80 80 raise util.Abort(_('working directory state appears damaged!'))
81 81 except IOError, err:
82 82 if err.errno != errno.ENOENT: raise
83 83 return [nullid, nullid]
84 84
85 85 @propertycache
86 86 def _dirs(self):
87 87 dirs = {}
88 88 for f,s in self._map.iteritems():
89 89 if s[0] != 'r':
90 90 _incdirs(dirs, f)
91 91 return dirs
92 92
93 93 @propertycache
94 94 def _ignore(self):
95 95 files = [self._join('.hgignore')]
96 96 for name, path in self._ui.configitems("ui"):
97 97 if name == 'ignore' or name.startswith('ignore.'):
98 98 files.append(os.path.expanduser(path))
99 99 return ignore.ignore(self._root, files, self._ui.warn)
100 100
101 101 @propertycache
102 102 def _slash(self):
103 103 return self._ui.configbool('ui', 'slash') and os.sep != '/'
104 104
105 105 @propertycache
106 106 def _checklink(self):
107 107 return util.checklink(self._root)
108 108
109 109 @propertycache
110 110 def _checkexec(self):
111 111 return util.checkexec(self._root)
112 112
113 113 @propertycache
114 114 def _checkcase(self):
115 115 return not util.checkcase(self._join('.hg'))
116 116
117 117 def _join(self, f):
118 118 # much faster than os.path.join()
119 119 # it's safe because f is always a relative path
120 120 return self._rootdir + f
121 121
122 122 def flagfunc(self, fallback):
123 123 if self._checklink:
124 124 if self._checkexec:
125 125 def f(x):
126 126 p = self._join(x)
127 127 if os.path.islink(p):
128 128 return 'l'
129 129 if util.is_exec(p):
130 130 return 'x'
131 131 return ''
132 132 return f
133 133 def f(x):
134 134 if os.path.islink(self._join(x)):
135 135 return 'l'
136 136 if 'x' in fallback(x):
137 137 return 'x'
138 138 return ''
139 139 return f
140 140 if self._checkexec:
141 141 def f(x):
142 142 if 'l' in fallback(x):
143 143 return 'l'
144 144 if util.is_exec(self._join(x)):
145 145 return 'x'
146 146 return ''
147 147 return f
148 148 return fallback
149 149
150 150 def getcwd(self):
151 151 cwd = os.getcwd()
152 152 if cwd == self._root: return ''
153 153 # self._root ends with a path separator if self._root is '/' or 'C:\'
154 154 rootsep = self._root
155 155 if not util.endswithsep(rootsep):
156 156 rootsep += os.sep
157 157 if cwd.startswith(rootsep):
158 158 return cwd[len(rootsep):]
159 159 else:
160 160 # we're outside the repo. return an absolute path.
161 161 return cwd
162 162
163 163 def pathto(self, f, cwd=None):
164 164 if cwd is None:
165 165 cwd = self.getcwd()
166 166 path = util.pathto(self._root, cwd, f)
167 167 if self._slash:
168 168 return util.normpath(path)
169 169 return path
170 170
171 171 def __getitem__(self, key):
172 172 ''' current states:
173 173 n normal
174 174 m needs merging
175 175 r marked for removal
176 176 a marked for addition
177 177 ? not tracked'''
178 178 return self._map.get(key, ("?",))[0]
179 179
180 180 def __contains__(self, key):
181 181 return key in self._map
182 182
183 183 def __iter__(self):
184 184 for x in sorted(self._map):
185 185 yield x
186 186
187 187 def parents(self):
188 188 return self._pl
189 189
190 190 def branch(self):
191 191 return self._branch
192 192
193 193 def setparents(self, p1, p2=nullid):
194 194 self._dirty = self._dirtypl = True
195 195 self._pl = p1, p2
196 196
197 197 def setbranch(self, branch):
198 198 self._branch = branch
199 199 self._opener("branch", "w").write(branch + '\n')
200 200
201 201 def _read(self):
202 202 self._map = {}
203 203 self._copymap = {}
204 204 try:
205 205 st = self._opener("dirstate").read()
206 206 except IOError, err:
207 207 if err.errno != errno.ENOENT: raise
208 208 return
209 209 if not st:
210 210 return
211 211
212 212 p = parsers.parse_dirstate(self._map, self._copymap, st)
213 213 if not self._dirtypl:
214 214 self._pl = p
215 215
216 216 def invalidate(self):
217 217 for a in "_map _copymap _foldmap _branch _pl _dirs _ignore".split():
218 218 if a in self.__dict__:
219 219 delattr(self, a)
220 220 self._dirty = False
221 221
222 222 def copy(self, source, dest):
223 223 """Mark dest as a copy of source. Unmark dest if source is None.
224 224 """
225 225 if source == dest:
226 226 return
227 227 self._dirty = True
228 228 if source is not None:
229 229 self._copymap[dest] = source
230 230 elif dest in self._copymap:
231 231 del self._copymap[dest]
232 232
233 233 def copied(self, file):
234 234 return self._copymap.get(file, None)
235 235
236 236 def copies(self):
237 237 return self._copymap
238 238
239 239 def _droppath(self, f):
240 240 if self[f] not in "?r" and "_dirs" in self.__dict__:
241 241 _decdirs(self._dirs, f)
242 242
243 243 def _addpath(self, f, check=False):
244 244 oldstate = self[f]
245 245 if check or oldstate == "r":
246 246 if '\r' in f or '\n' in f:
247 247 raise util.Abort(
248 248 _("'\\n' and '\\r' disallowed in filenames: %r") % f)
249 249 if f in self._dirs:
250 250 raise util.Abort(_('directory %r already in dirstate') % f)
251 251 # shadows
252 252 for d in _finddirs(f):
253 253 if d in self._dirs:
254 254 break
255 255 if d in self._map and self[d] != 'r':
256 256 raise util.Abort(
257 257 _('file %r in dirstate clashes with %r') % (d, f))
258 258 if oldstate in "?r" and "_dirs" in self.__dict__:
259 259 _incdirs(self._dirs, f)
260 260
261 261 def normal(self, f):
262 262 'mark a file normal and clean'
263 263 self._dirty = True
264 264 self._addpath(f)
265 265 s = os.lstat(self._join(f))
266 266 self._map[f] = ('n', s.st_mode, s.st_size, int(s.st_mtime))
267 267 if f in self._copymap:
268 268 del self._copymap[f]
269 269
270 270 def normallookup(self, f):
271 271 'mark a file normal, but possibly dirty'
272 272 if self._pl[1] != nullid and f in self._map:
273 273 # if there is a merge going on and the file was either
274 274 # in state 'm' or dirty before being removed, restore that state.
275 275 entry = self._map[f]
276 276 if entry[0] == 'r' and entry[2] in (-1, -2):
277 277 source = self._copymap.get(f)
278 278 if entry[2] == -1:
279 279 self.merge(f)
280 280 elif entry[2] == -2:
281 281 self.normaldirty(f)
282 282 if source:
283 283 self.copy(source, f)
284 284 return
285 285 if entry[0] == 'm' or entry[0] == 'n' and entry[2] == -2:
286 286 return
287 287 self._dirty = True
288 288 self._addpath(f)
289 289 self._map[f] = ('n', 0, -1, -1)
290 290 if f in self._copymap:
291 291 del self._copymap[f]
292 292
293 293 def normaldirty(self, f):
294 294 'mark a file normal, but dirty'
295 295 self._dirty = True
296 296 self._addpath(f)
297 297 self._map[f] = ('n', 0, -2, -1)
298 298 if f in self._copymap:
299 299 del self._copymap[f]
300 300
301 301 def add(self, f):
302 302 'mark a file added'
303 303 self._dirty = True
304 304 self._addpath(f, True)
305 305 self._map[f] = ('a', 0, -1, -1)
306 306 if f in self._copymap:
307 307 del self._copymap[f]
308 308
309 309 def remove(self, f):
310 310 'mark a file removed'
311 311 self._dirty = True
312 312 self._droppath(f)
313 313 size = 0
314 314 if self._pl[1] != nullid and f in self._map:
315 315 entry = self._map[f]
316 316 if entry[0] == 'm':
317 317 size = -1
318 318 elif entry[0] == 'n' and entry[2] == -2:
319 319 size = -2
320 320 self._map[f] = ('r', 0, size, 0)
321 321 if size == 0 and f in self._copymap:
322 322 del self._copymap[f]
323 323
324 324 def merge(self, f):
325 325 'mark a file merged'
326 326 self._dirty = True
327 327 s = os.lstat(self._join(f))
328 328 self._addpath(f)
329 329 self._map[f] = ('m', s.st_mode, s.st_size, int(s.st_mtime))
330 330 if f in self._copymap:
331 331 del self._copymap[f]
332 332
333 333 def forget(self, f):
334 334 'forget a file'
335 335 self._dirty = True
336 336 try:
337 337 self._droppath(f)
338 338 del self._map[f]
339 339 except KeyError:
340 340 self._ui.warn(_("not in dirstate: %s\n") % f)
341 341
342 342 def _normalize(self, path, knownpath):
343 343 norm_path = os.path.normcase(path)
344 344 fold_path = self._foldmap.get(norm_path, None)
345 345 if fold_path is None:
346 346 if knownpath or not os.path.exists(os.path.join(self._root, path)):
347 347 fold_path = path
348 348 else:
349 349 fold_path = self._foldmap.setdefault(norm_path,
350 350 util.fspath(path, self._root))
351 351 return fold_path
352 352
353 353 def clear(self):
354 354 self._map = {}
355 355 if "_dirs" in self.__dict__:
356 356 delattr(self, "_dirs");
357 357 self._copymap = {}
358 358 self._pl = [nullid, nullid]
359 359 self._dirty = True
360 360
361 361 def rebuild(self, parent, files):
362 362 self.clear()
363 363 for f in files:
364 364 if 'x' in files.flags(f):
365 365 self._map[f] = ('n', 0777, -1, 0)
366 366 else:
367 367 self._map[f] = ('n', 0666, -1, 0)
368 368 self._pl = (parent, nullid)
369 369 self._dirty = True
370 370
371 371 def write(self):
372 372 if not self._dirty:
373 373 return
374 374 st = self._opener("dirstate", "w", atomictemp=True)
375 375
376 376 try:
377 377 gran = int(self._ui.config('dirstate', 'granularity', 1))
378 378 except ValueError:
379 379 gran = 1
380 380 limit = sys.maxint
381 381 if gran > 0:
382 382 limit = util.fstat(st).st_mtime - gran
383 383
384 384 cs = cStringIO.StringIO()
385 385 copymap = self._copymap
386 386 pack = struct.pack
387 387 write = cs.write
388 388 write("".join(self._pl))
389 389 for f, e in self._map.iteritems():
390 390 if f in copymap:
391 391 f = "%s\0%s" % (f, copymap[f])
392 392 if e[3] > limit and e[0] == 'n':
393 393 e = (e[0], 0, -1, -1)
394 394 e = pack(_format, e[0], e[1], e[2], e[3], len(f))
395 395 write(e)
396 396 write(f)
397 397 st.write(cs.getvalue())
398 398 st.rename()
399 399 self._dirty = self._dirtypl = False
400 400
401 401 def _dirignore(self, f):
402 402 if f == '.':
403 403 return False
404 404 if self._ignore(f):
405 405 return True
406 406 for p in _finddirs(f):
407 407 if self._ignore(p):
408 408 return True
409 409 return False
410 410
411 411 def walk(self, match, unknown, ignored):
412 412 '''
413 413 walk recursively through the directory tree, finding all files
414 414 matched by the match function
415 415
416 416 results are yielded in a tuple (filename, stat), where stat
417 417 and st is the stat result if the file was found in the directory.
418 418 '''
419 419
420 420 def fwarn(f, msg):
421 421 self._ui.warn('%s: %s\n' % (self.pathto(f), msg))
422 422 return False
423 423
424 424 def badtype(mode):
425 425 kind = _('unknown')
426 426 if stat.S_ISCHR(mode): kind = _('character device')
427 427 elif stat.S_ISBLK(mode): kind = _('block device')
428 428 elif stat.S_ISFIFO(mode): kind = _('fifo')
429 429 elif stat.S_ISSOCK(mode): kind = _('socket')
430 430 elif stat.S_ISDIR(mode): kind = _('directory')
431 431 return _('unsupported file type (type is %s)') % kind
432 432
433 433 ignore = self._ignore
434 434 dirignore = self._dirignore
435 435 if ignored:
436 436 ignore = util.never
437 437 dirignore = util.never
438 438 elif not unknown:
439 439 # if unknown and ignored are False, skip step 2
440 440 ignore = util.always
441 441 dirignore = util.always
442 442
443 443 matchfn = match.matchfn
444 444 badfn = match.bad
445 445 dmap = self._map
446 446 normpath = util.normpath
447 447 listdir = osutil.listdir
448 448 lstat = os.lstat
449 449 getkind = stat.S_IFMT
450 450 dirkind = stat.S_IFDIR
451 451 regkind = stat.S_IFREG
452 452 lnkkind = stat.S_IFLNK
453 453 join = self._join
454 454 work = []
455 455 wadd = work.append
456 456
457 457 if self._checkcase:
458 458 normalize = self._normalize
459 459 else:
460 460 normalize = lambda x, y: x
461 461
462 462 exact = skipstep3 = False
463 463 if matchfn == match.exact: # match.exact
464 464 exact = True
465 465 dirignore = util.always # skip step 2
466 466 elif match.files() and not match.anypats(): # match.match, no patterns
467 467 skipstep3 = True
468 468
469 469 files = set(match.files())
470 470 if not files or '.' in files:
471 471 files = ['']
472 472 results = {'.hg': None}
473 473
474 474 # step 1: find all explicit files
475 475 for ff in sorted(files):
476 nf = normalize(normpath(ff), True)
476 nf = normalize(normpath(ff), False)
477 477 if nf in results:
478 478 continue
479 479
480 480 try:
481 481 st = lstat(join(nf))
482 482 kind = getkind(st.st_mode)
483 483 if kind == dirkind:
484 484 skipstep3 = False
485 485 if nf in dmap:
486 486 #file deleted on disk but still in dirstate
487 487 results[nf] = None
488 488 match.dir(nf)
489 489 if not dirignore(nf):
490 490 wadd(nf)
491 491 elif kind == regkind or kind == lnkkind:
492 492 results[nf] = st
493 493 else:
494 494 badfn(ff, badtype(kind))
495 495 if nf in dmap:
496 496 results[nf] = None
497 497 except OSError, inst:
498 498 if nf in dmap: # does it exactly match a file?
499 499 results[nf] = None
500 500 else: # does it match a directory?
501 501 prefix = nf + "/"
502 502 for fn in dmap:
503 503 if fn.startswith(prefix):
504 504 match.dir(nf)
505 505 skipstep3 = False
506 506 break
507 507 else:
508 508 badfn(ff, inst.strerror)
509 509
510 510 # step 2: visit subdirectories
511 511 while work:
512 512 nd = work.pop()
513 513 skip = None
514 514 if nd == '.':
515 515 nd = ''
516 516 else:
517 517 skip = '.hg'
518 518 try:
519 519 entries = listdir(join(nd), stat=True, skip=skip)
520 520 except OSError, inst:
521 521 if inst.errno == errno.EACCES:
522 522 fwarn(nd, inst.strerror)
523 523 continue
524 524 raise
525 525 for f, kind, st in entries:
526 526 nf = normalize(nd and (nd + "/" + f) or f, True)
527 527 if nf not in results:
528 528 if kind == dirkind:
529 529 if not ignore(nf):
530 530 match.dir(nf)
531 531 wadd(nf)
532 532 if nf in dmap and matchfn(nf):
533 533 results[nf] = None
534 534 elif kind == regkind or kind == lnkkind:
535 535 if nf in dmap:
536 536 if matchfn(nf):
537 537 results[nf] = st
538 538 elif matchfn(nf) and not ignore(nf):
539 539 results[nf] = st
540 540 elif nf in dmap and matchfn(nf):
541 541 results[nf] = None
542 542
543 543 # step 3: report unseen items in the dmap hash
544 544 if not skipstep3 and not exact:
545 545 visit = sorted([f for f in dmap if f not in results and matchfn(f)])
546 546 for nf, st in zip(visit, util.statfiles([join(i) for i in visit])):
547 547 if not st is None and not getkind(st.st_mode) in (regkind, lnkkind):
548 548 st = None
549 549 results[nf] = st
550 550
551 551 del results['.hg']
552 552 return results
553 553
554 554 def status(self, match, ignored, clean, unknown):
555 555 listignored, listclean, listunknown = ignored, clean, unknown
556 556 lookup, modified, added, unknown, ignored = [], [], [], [], []
557 557 removed, deleted, clean = [], [], []
558 558
559 559 dmap = self._map
560 560 ladd = lookup.append
561 561 madd = modified.append
562 562 aadd = added.append
563 563 uadd = unknown.append
564 564 iadd = ignored.append
565 565 radd = removed.append
566 566 dadd = deleted.append
567 567 cadd = clean.append
568 568
569 569 for fn, st in self.walk(match, listunknown, listignored).iteritems():
570 570 if fn not in dmap:
571 571 if (listignored or match.exact(fn)) and self._dirignore(fn):
572 572 if listignored:
573 573 iadd(fn)
574 574 elif listunknown:
575 575 uadd(fn)
576 576 continue
577 577
578 578 state, mode, size, time = dmap[fn]
579 579
580 580 if not st and state in "nma":
581 581 dadd(fn)
582 582 elif state == 'n':
583 583 if (size >= 0 and
584 584 (size != st.st_size
585 585 or ((mode ^ st.st_mode) & 0100 and self._checkexec))
586 586 or size == -2
587 587 or fn in self._copymap):
588 588 madd(fn)
589 589 elif time != int(st.st_mtime):
590 590 ladd(fn)
591 591 elif listclean:
592 592 cadd(fn)
593 593 elif state == 'm':
594 594 madd(fn)
595 595 elif state == 'a':
596 596 aadd(fn)
597 597 elif state == 'r':
598 598 radd(fn)
599 599
600 600 return (lookup, modified, added, removed, deleted, unknown, ignored,
601 601 clean)
@@ -1,84 +1,106
1 1 #!/bin/sh
2 2
3 3 set -e
4 4
5 5 echo % init
6 6 hg init
7 7
8 8 echo % committing changes
9 9 count=0
10 10 echo > a
11 11 while test $count -lt 32 ; do
12 12 echo 'a' >> a
13 13 test $count -eq 0 && hg add
14 14 hg ci -m "msg $count" -d "$count 0"
15 15 echo % committed changeset $count
16 16 count=`expr $count + 1`
17 17 done
18 18
19 19 echo % log
20 20 hg log
21 21
22 22 echo % hg up -C
23 23 hg up -C
24 24
25 25 echo % bisect test
26 26 hg bisect -r
27 27 hg bisect -b
28 28 hg bisect -g 1
29 29 hg bisect -g
30 30 echo skip
31 31 hg bisect -s
32 32 hg bisect -g
33 33 hg bisect -g
34 34 hg bisect -b
35 35 hg bisect -g
36 36
37 37 echo % bisect reverse test
38 38 hg bisect -r
39 39 hg bisect -b null
40 40 hg bisect -g tip
41 41 hg bisect -g
42 42 echo skip
43 43 hg bisect -s
44 44 hg bisect -g
45 45 hg bisect -g
46 46 hg bisect -b
47 47 hg bisect -g
48 48
49 49 hg bisect -r
50 50 hg bisect -g tip
51 51 hg bisect -b tip || echo error
52 52
53 53 hg bisect -r
54 54 hg bisect -g null
55 55 hg bisect -bU tip
56 56 hg id
57 57
58 58 echo % reproduce AssertionError, issue1228 and issue1182
59 59 hg bisect -r
60 60 hg bisect -b 4
61 61 hg bisect -g 0
62 62 hg bisect -s
63 63 hg bisect -s
64 64 hg bisect -s
65 65
66 66 echo % reproduce non converging bisect, issue1182
67 67 hg bisect -r
68 68 hg bisect -g 0
69 69 hg bisect -b 2
70 70 hg bisect -s
71 71
72 72 echo % test no action
73 73 hg bisect -r
74 74 hg bisect || echo failure
75 75
76 76 echo % reproduce AssertionError, issue1445
77 77 hg bisect -r
78 78 hg bisect -b 6
79 79 hg bisect -g 0
80 80 hg bisect -s
81 81 hg bisect -s
82 82 hg bisect -s
83 83 hg bisect -s
84 84 hg bisect -g
85
86 set +e
87
88 echo % test invalid command
89 hg bisect -r
90 hg bisect --command 'foobar'
91
92 echo % test bisecting command
93 cat > script.py <<EOF
94 #!/usr/bin/env python
95 import sys
96 from mercurial import ui, hg
97 repo = hg.repository(ui.ui(), '.')
98 if repo['.'].rev() < 6:
99 sys.exit(1)
100 EOF
101 chmod +x script.py
102 hg bisect -r
103 hg bisect --good tip
104 hg bisect --bad 0
105 hg bisect --command "`pwd`/script.py"
106 true
@@ -1,305 +1,321
1 1 % init
2 2 % committing changes
3 3 adding a
4 4 % committed changeset 0
5 5 % committed changeset 1
6 6 % committed changeset 2
7 7 % committed changeset 3
8 8 % committed changeset 4
9 9 % committed changeset 5
10 10 % committed changeset 6
11 11 % committed changeset 7
12 12 % committed changeset 8
13 13 % committed changeset 9
14 14 % committed changeset 10
15 15 % committed changeset 11
16 16 % committed changeset 12
17 17 % committed changeset 13
18 18 % committed changeset 14
19 19 % committed changeset 15
20 20 % committed changeset 16
21 21 % committed changeset 17
22 22 % committed changeset 18
23 23 % committed changeset 19
24 24 % committed changeset 20
25 25 % committed changeset 21
26 26 % committed changeset 22
27 27 % committed changeset 23
28 28 % committed changeset 24
29 29 % committed changeset 25
30 30 % committed changeset 26
31 31 % committed changeset 27
32 32 % committed changeset 28
33 33 % committed changeset 29
34 34 % committed changeset 30
35 35 % committed changeset 31
36 36 % log
37 37 changeset: 31:58c80a7c8a40
38 38 tag: tip
39 39 user: test
40 40 date: Thu Jan 01 00:00:31 1970 +0000
41 41 summary: msg 31
42 42
43 43 changeset: 30:ed2d2f24b11c
44 44 user: test
45 45 date: Thu Jan 01 00:00:30 1970 +0000
46 46 summary: msg 30
47 47
48 48 changeset: 29:b5bd63375ab9
49 49 user: test
50 50 date: Thu Jan 01 00:00:29 1970 +0000
51 51 summary: msg 29
52 52
53 53 changeset: 28:8e0c2264c8af
54 54 user: test
55 55 date: Thu Jan 01 00:00:28 1970 +0000
56 56 summary: msg 28
57 57
58 58 changeset: 27:288867a866e9
59 59 user: test
60 60 date: Thu Jan 01 00:00:27 1970 +0000
61 61 summary: msg 27
62 62
63 63 changeset: 26:3efc6fd51aeb
64 64 user: test
65 65 date: Thu Jan 01 00:00:26 1970 +0000
66 66 summary: msg 26
67 67
68 68 changeset: 25:02a84173a97a
69 69 user: test
70 70 date: Thu Jan 01 00:00:25 1970 +0000
71 71 summary: msg 25
72 72
73 73 changeset: 24:10e0acd3809e
74 74 user: test
75 75 date: Thu Jan 01 00:00:24 1970 +0000
76 76 summary: msg 24
77 77
78 78 changeset: 23:5ec79163bff4
79 79 user: test
80 80 date: Thu Jan 01 00:00:23 1970 +0000
81 81 summary: msg 23
82 82
83 83 changeset: 22:06c7993750ce
84 84 user: test
85 85 date: Thu Jan 01 00:00:22 1970 +0000
86 86 summary: msg 22
87 87
88 88 changeset: 21:e5db6aa3fe2a
89 89 user: test
90 90 date: Thu Jan 01 00:00:21 1970 +0000
91 91 summary: msg 21
92 92
93 93 changeset: 20:7128fb4fdbc9
94 94 user: test
95 95 date: Thu Jan 01 00:00:20 1970 +0000
96 96 summary: msg 20
97 97
98 98 changeset: 19:52798545b482
99 99 user: test
100 100 date: Thu Jan 01 00:00:19 1970 +0000
101 101 summary: msg 19
102 102
103 103 changeset: 18:86977a90077e
104 104 user: test
105 105 date: Thu Jan 01 00:00:18 1970 +0000
106 106 summary: msg 18
107 107
108 108 changeset: 17:03515f4a9080
109 109 user: test
110 110 date: Thu Jan 01 00:00:17 1970 +0000
111 111 summary: msg 17
112 112
113 113 changeset: 16:a2e6ea4973e9
114 114 user: test
115 115 date: Thu Jan 01 00:00:16 1970 +0000
116 116 summary: msg 16
117 117
118 118 changeset: 15:e7fa0811edb0
119 119 user: test
120 120 date: Thu Jan 01 00:00:15 1970 +0000
121 121 summary: msg 15
122 122
123 123 changeset: 14:ce8f0998e922
124 124 user: test
125 125 date: Thu Jan 01 00:00:14 1970 +0000
126 126 summary: msg 14
127 127
128 128 changeset: 13:9d7d07bc967c
129 129 user: test
130 130 date: Thu Jan 01 00:00:13 1970 +0000
131 131 summary: msg 13
132 132
133 133 changeset: 12:1941b52820a5
134 134 user: test
135 135 date: Thu Jan 01 00:00:12 1970 +0000
136 136 summary: msg 12
137 137
138 138 changeset: 11:7b4cd9578619
139 139 user: test
140 140 date: Thu Jan 01 00:00:11 1970 +0000
141 141 summary: msg 11
142 142
143 143 changeset: 10:7c5eff49a6b6
144 144 user: test
145 145 date: Thu Jan 01 00:00:10 1970 +0000
146 146 summary: msg 10
147 147
148 148 changeset: 9:eb44510ef29a
149 149 user: test
150 150 date: Thu Jan 01 00:00:09 1970 +0000
151 151 summary: msg 9
152 152
153 153 changeset: 8:453eb4dba229
154 154 user: test
155 155 date: Thu Jan 01 00:00:08 1970 +0000
156 156 summary: msg 8
157 157
158 158 changeset: 7:03750880c6b5
159 159 user: test
160 160 date: Thu Jan 01 00:00:07 1970 +0000
161 161 summary: msg 7
162 162
163 163 changeset: 6:a3d5c6fdf0d3
164 164 user: test
165 165 date: Thu Jan 01 00:00:06 1970 +0000
166 166 summary: msg 6
167 167
168 168 changeset: 5:7874a09ea728
169 169 user: test
170 170 date: Thu Jan 01 00:00:05 1970 +0000
171 171 summary: msg 5
172 172
173 173 changeset: 4:9b2ba8336a65
174 174 user: test
175 175 date: Thu Jan 01 00:00:04 1970 +0000
176 176 summary: msg 4
177 177
178 178 changeset: 3:b53bea5e2fcb
179 179 user: test
180 180 date: Thu Jan 01 00:00:03 1970 +0000
181 181 summary: msg 3
182 182
183 183 changeset: 2:db07c04beaca
184 184 user: test
185 185 date: Thu Jan 01 00:00:02 1970 +0000
186 186 summary: msg 2
187 187
188 188 changeset: 1:5cd978ea5149
189 189 user: test
190 190 date: Thu Jan 01 00:00:01 1970 +0000
191 191 summary: msg 1
192 192
193 193 changeset: 0:b99c7b9c8e11
194 194 user: test
195 195 date: Thu Jan 01 00:00:00 1970 +0000
196 196 summary: msg 0
197 197
198 198 % hg up -C
199 199 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
200 200 % bisect test
201 201 Testing changeset 16:a2e6ea4973e9 (30 changesets remaining, ~4 tests)
202 202 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
203 203 Testing changeset 23:5ec79163bff4 (15 changesets remaining, ~3 tests)
204 204 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
205 205 skip
206 206 Testing changeset 24:10e0acd3809e (15 changesets remaining, ~3 tests)
207 207 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
208 208 Testing changeset 27:288867a866e9 (7 changesets remaining, ~2 tests)
209 209 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
210 210 Testing changeset 29:b5bd63375ab9 (4 changesets remaining, ~2 tests)
211 211 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
212 212 Testing changeset 28:8e0c2264c8af (2 changesets remaining, ~1 tests)
213 213 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
214 214 The first bad revision is:
215 215 changeset: 29:b5bd63375ab9
216 216 user: test
217 217 date: Thu Jan 01 00:00:29 1970 +0000
218 218 summary: msg 29
219 219
220 220 % bisect reverse test
221 221 Testing changeset 15:e7fa0811edb0 (32 changesets remaining, ~5 tests)
222 222 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
223 223 Testing changeset 7:03750880c6b5 (16 changesets remaining, ~4 tests)
224 224 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
225 225 skip
226 226 Testing changeset 6:a3d5c6fdf0d3 (16 changesets remaining, ~4 tests)
227 227 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
228 228 Testing changeset 2:db07c04beaca (7 changesets remaining, ~2 tests)
229 229 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
230 230 Testing changeset 0:b99c7b9c8e11 (3 changesets remaining, ~1 tests)
231 231 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
232 232 Testing changeset 1:5cd978ea5149 (2 changesets remaining, ~1 tests)
233 233 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
234 234 The first good revision is:
235 235 changeset: 1:5cd978ea5149
236 236 user: test
237 237 date: Thu Jan 01 00:00:01 1970 +0000
238 238 summary: msg 1
239 239
240 240 abort: Inconsistent state, 31:58c80a7c8a40 is good and bad
241 241 error
242 242 Testing changeset 15:e7fa0811edb0 (32 changesets remaining, ~5 tests)
243 243 5cd978ea5149
244 244 % reproduce AssertionError, issue1228 and issue1182
245 245 Testing changeset 2:db07c04beaca (4 changesets remaining, ~2 tests)
246 246 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
247 247 Testing changeset 1:5cd978ea5149 (4 changesets remaining, ~2 tests)
248 248 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
249 249 Testing changeset 3:b53bea5e2fcb (4 changesets remaining, ~2 tests)
250 250 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
251 251 Due to skipped revisions, the first bad revision could be any of:
252 252 changeset: 1:5cd978ea5149
253 253 user: test
254 254 date: Thu Jan 01 00:00:01 1970 +0000
255 255 summary: msg 1
256 256
257 257 changeset: 2:db07c04beaca
258 258 user: test
259 259 date: Thu Jan 01 00:00:02 1970 +0000
260 260 summary: msg 2
261 261
262 262 changeset: 3:b53bea5e2fcb
263 263 user: test
264 264 date: Thu Jan 01 00:00:03 1970 +0000
265 265 summary: msg 3
266 266
267 267 changeset: 4:9b2ba8336a65
268 268 user: test
269 269 date: Thu Jan 01 00:00:04 1970 +0000
270 270 summary: msg 4
271 271
272 272 % reproduce non converging bisect, issue1182
273 273 Testing changeset 1:5cd978ea5149 (2 changesets remaining, ~1 tests)
274 274 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
275 275 Due to skipped revisions, the first bad revision could be any of:
276 276 changeset: 1:5cd978ea5149
277 277 user: test
278 278 date: Thu Jan 01 00:00:01 1970 +0000
279 279 summary: msg 1
280 280
281 281 changeset: 2:db07c04beaca
282 282 user: test
283 283 date: Thu Jan 01 00:00:02 1970 +0000
284 284 summary: msg 2
285 285
286 286 % test no action
287 287 abort: cannot bisect (no known good revisions)
288 288 failure
289 289 % reproduce AssertionError, issue1445
290 290 Testing changeset 3:b53bea5e2fcb (6 changesets remaining, ~2 tests)
291 291 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
292 292 Testing changeset 2:db07c04beaca (6 changesets remaining, ~2 tests)
293 293 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
294 294 Testing changeset 4:9b2ba8336a65 (6 changesets remaining, ~2 tests)
295 295 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
296 296 Testing changeset 1:5cd978ea5149 (6 changesets remaining, ~2 tests)
297 297 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
298 298 Testing changeset 5:7874a09ea728 (6 changesets remaining, ~2 tests)
299 299 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
300 300 The first bad revision is:
301 301 changeset: 6:a3d5c6fdf0d3
302 302 user: test
303 303 date: Thu Jan 01 00:00:06 1970 +0000
304 304 summary: msg 6
305 305
306 % test invalid command
307 abort: cannot find executable: foobar
308 % test bisecting command
309 Testing changeset 15:e7fa0811edb0 (31 changesets remaining, ~4 tests)
310 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
311 Changeset 15:e7fa0811edb0: good
312 Changeset 7:03750880c6b5: good
313 Changeset 3:b53bea5e2fcb: bad
314 Changeset 5:7874a09ea728: bad
315 Changeset 6:a3d5c6fdf0d3: good
316 The first good revision is:
317 changeset: 6:a3d5c6fdf0d3
318 user: test
319 date: Thu Jan 01 00:00:06 1970 +0000
320 summary: msg 6
321
General Comments 0
You need to be logged in to leave comments. Login now