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