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