##// END OF EJS Templates
clfilter: enforce hidden changeset globally...
Pierre-Yves David -
r18267:5bb610f8 default
parent child Browse files
Show More

The requested changes are too big and content was truncated. Show full diff

@@ -1,56 +1,55 b''
1 1 # ASCII graph log extension for Mercurial
2 2 #
3 3 # Copyright 2007 Joel Rosdahl <joel@rosdahl.net>
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 view revision graphs from a shell
9 9
10 10 This extension adds a --graph option to the incoming, outgoing and log
11 11 commands. When this options is given, an ASCII representation of the
12 12 revision graph is also shown.
13 13 '''
14 14
15 15 from mercurial.i18n import _
16 16 from mercurial import cmdutil, commands
17 17
18 18 cmdtable = {}
19 19 command = cmdutil.command(cmdtable)
20 20 testedwith = 'internal'
21 21
22 22 @command('glog',
23 23 [('f', 'follow', None,
24 24 _('follow changeset history, or file history across copies and renames')),
25 25 ('', 'follow-first', None,
26 26 _('only follow the first parent of merge changesets (DEPRECATED)')),
27 27 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
28 28 ('C', 'copies', None, _('show copied files')),
29 29 ('k', 'keyword', [],
30 30 _('do case-insensitive search for a given text'), _('TEXT')),
31 31 ('r', 'rev', [], _('show the specified revision or range'), _('REV')),
32 32 ('', 'removed', None, _('include revisions where files were removed')),
33 33 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
34 34 ('u', 'user', [], _('revisions committed by user'), _('USER')),
35 35 ('', 'only-branch', [],
36 36 _('show only changesets within the given named branch (DEPRECATED)'),
37 37 _('BRANCH')),
38 38 ('b', 'branch', [],
39 39 _('show changesets within the given named branch'), _('BRANCH')),
40 40 ('P', 'prune', [],
41 41 _('do not display revision or any of its ancestors'), _('REV')),
42 ('', 'hidden', False, _('show hidden changesets (DEPRECATED)')),
43 42 ] + commands.logopts + commands.walkopts,
44 43 _('[OPTION]... [FILE]'))
45 44 def graphlog(ui, repo, *pats, **opts):
46 45 """show revision history alongside an ASCII revision graph
47 46
48 47 Print a revision history alongside a revision graph drawn with
49 48 ASCII characters.
50 49
51 50 Nodes printed as an @ character are parents of the working
52 51 directory.
53 52 """
54 53 return cmdutil.graphlog(ui, repo, *pats, **opts)
55 54
56 55 commands.inferrepo += " glog"
@@ -1,2011 +1,2009 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, revlog, copies
12 12 import match as matchmod
13 13 import subrepo, context, repair, graphmod, revset, phases, obsolete
14 14 import changelog
15 15 import lock as lockmod
16 16
17 17 def parsealiases(cmd):
18 18 return cmd.lstrip("^").split("|")
19 19
20 20 def findpossible(cmd, table, strict=False):
21 21 """
22 22 Return cmd -> (aliases, command table entry)
23 23 for each matching command.
24 24 Return debug commands (or their aliases) only if no normal command matches.
25 25 """
26 26 choice = {}
27 27 debugchoice = {}
28 28
29 29 if cmd in table:
30 30 # short-circuit exact matches, "log" alias beats "^log|history"
31 31 keys = [cmd]
32 32 else:
33 33 keys = table.keys()
34 34
35 35 for e in 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 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 ctx = repo[None]
88 88 for s in ctx.substate:
89 89 if ctx.sub(s).dirty():
90 90 raise util.Abort(_("uncommitted changes in subrepo %s") % s)
91 91
92 92 def logmessage(ui, opts):
93 93 """ get the log message according to -m and -l option """
94 94 message = opts.get('message')
95 95 logfile = opts.get('logfile')
96 96
97 97 if message and logfile:
98 98 raise util.Abort(_('options --message and --logfile are mutually '
99 99 'exclusive'))
100 100 if not message and logfile:
101 101 try:
102 102 if logfile == '-':
103 103 message = ui.fin.read()
104 104 else:
105 105 message = '\n'.join(util.readfile(logfile).splitlines())
106 106 except IOError, inst:
107 107 raise util.Abort(_("can't read commit message '%s': %s") %
108 108 (logfile, inst.strerror))
109 109 return message
110 110
111 111 def loglimit(opts):
112 112 """get the log limit according to option -l/--limit"""
113 113 limit = opts.get('limit')
114 114 if limit:
115 115 try:
116 116 limit = int(limit)
117 117 except ValueError:
118 118 raise util.Abort(_('limit must be a positive integer'))
119 119 if limit <= 0:
120 120 raise util.Abort(_('limit must be positive'))
121 121 else:
122 122 limit = None
123 123 return limit
124 124
125 125 def makefilename(repo, pat, node, desc=None,
126 126 total=None, seqno=None, revwidth=None, pathname=None):
127 127 node_expander = {
128 128 'H': lambda: hex(node),
129 129 'R': lambda: str(repo.changelog.rev(node)),
130 130 'h': lambda: short(node),
131 131 'm': lambda: re.sub('[^\w]', '_', str(desc))
132 132 }
133 133 expander = {
134 134 '%': lambda: '%',
135 135 'b': lambda: os.path.basename(repo.root),
136 136 }
137 137
138 138 try:
139 139 if node:
140 140 expander.update(node_expander)
141 141 if node:
142 142 expander['r'] = (lambda:
143 143 str(repo.changelog.rev(node)).zfill(revwidth or 0))
144 144 if total is not None:
145 145 expander['N'] = lambda: str(total)
146 146 if seqno is not None:
147 147 expander['n'] = lambda: str(seqno)
148 148 if total is not None and seqno is not None:
149 149 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
150 150 if pathname is not None:
151 151 expander['s'] = lambda: os.path.basename(pathname)
152 152 expander['d'] = lambda: os.path.dirname(pathname) or '.'
153 153 expander['p'] = lambda: pathname
154 154
155 155 newname = []
156 156 patlen = len(pat)
157 157 i = 0
158 158 while i < patlen:
159 159 c = pat[i]
160 160 if c == '%':
161 161 i += 1
162 162 c = pat[i]
163 163 c = expander[c]()
164 164 newname.append(c)
165 165 i += 1
166 166 return ''.join(newname)
167 167 except KeyError, inst:
168 168 raise util.Abort(_("invalid format spec '%%%s' in output filename") %
169 169 inst.args[0])
170 170
171 171 def makefileobj(repo, pat, node=None, desc=None, total=None,
172 172 seqno=None, revwidth=None, mode='wb', pathname=None):
173 173
174 174 writable = mode not in ('r', 'rb')
175 175
176 176 if not pat or pat == '-':
177 177 fp = writable and repo.ui.fout or repo.ui.fin
178 178 if util.safehasattr(fp, 'fileno'):
179 179 return os.fdopen(os.dup(fp.fileno()), mode)
180 180 else:
181 181 # if this fp can't be duped properly, return
182 182 # a dummy object that can be closed
183 183 class wrappedfileobj(object):
184 184 noop = lambda x: None
185 185 def __init__(self, f):
186 186 self.f = f
187 187 def __getattr__(self, attr):
188 188 if attr == 'close':
189 189 return self.noop
190 190 else:
191 191 return getattr(self.f, attr)
192 192
193 193 return wrappedfileobj(fp)
194 194 if util.safehasattr(pat, 'write') and writable:
195 195 return pat
196 196 if util.safehasattr(pat, 'read') and 'r' in mode:
197 197 return pat
198 198 return open(makefilename(repo, pat, node, desc, total, seqno, revwidth,
199 199 pathname),
200 200 mode)
201 201
202 202 def openrevlog(repo, cmd, file_, opts):
203 203 """opens the changelog, manifest, a filelog or a given revlog"""
204 204 cl = opts['changelog']
205 205 mf = opts['manifest']
206 206 msg = None
207 207 if cl and mf:
208 208 msg = _('cannot specify --changelog and --manifest at the same time')
209 209 elif cl or mf:
210 210 if file_:
211 211 msg = _('cannot specify filename with --changelog or --manifest')
212 212 elif not repo:
213 213 msg = _('cannot specify --changelog or --manifest '
214 214 'without a repository')
215 215 if msg:
216 216 raise util.Abort(msg)
217 217
218 218 r = None
219 219 if repo:
220 220 if cl:
221 221 r = repo.changelog
222 222 elif mf:
223 223 r = repo.manifest
224 224 elif file_:
225 225 filelog = repo.file(file_)
226 226 if len(filelog):
227 227 r = filelog
228 228 if not r:
229 229 if not file_:
230 230 raise error.CommandError(cmd, _('invalid arguments'))
231 231 if not os.path.isfile(file_):
232 232 raise util.Abort(_("revlog '%s' not found") % file_)
233 233 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False),
234 234 file_[:-2] + ".i")
235 235 return r
236 236
237 237 def copy(ui, repo, pats, opts, rename=False):
238 238 # called with the repo lock held
239 239 #
240 240 # hgsep => pathname that uses "/" to separate directories
241 241 # ossep => pathname that uses os.sep to separate directories
242 242 cwd = repo.getcwd()
243 243 targets = {}
244 244 after = opts.get("after")
245 245 dryrun = opts.get("dry_run")
246 246 wctx = repo[None]
247 247
248 248 def walkpat(pat):
249 249 srcs = []
250 250 badstates = after and '?' or '?r'
251 251 m = scmutil.match(repo[None], [pat], opts, globbed=True)
252 252 for abs in repo.walk(m):
253 253 state = repo.dirstate[abs]
254 254 rel = m.rel(abs)
255 255 exact = m.exact(abs)
256 256 if state in badstates:
257 257 if exact and state == '?':
258 258 ui.warn(_('%s: not copying - file is not managed\n') % rel)
259 259 if exact and state == 'r':
260 260 ui.warn(_('%s: not copying - file has been marked for'
261 261 ' remove\n') % rel)
262 262 continue
263 263 # abs: hgsep
264 264 # rel: ossep
265 265 srcs.append((abs, rel, exact))
266 266 return srcs
267 267
268 268 # abssrc: hgsep
269 269 # relsrc: ossep
270 270 # otarget: ossep
271 271 def copyfile(abssrc, relsrc, otarget, exact):
272 272 abstarget = scmutil.canonpath(repo.root, cwd, otarget)
273 273 if '/' in abstarget:
274 274 # We cannot normalize abstarget itself, this would prevent
275 275 # case only renames, like a => A.
276 276 abspath, absname = abstarget.rsplit('/', 1)
277 277 abstarget = repo.dirstate.normalize(abspath) + '/' + absname
278 278 reltarget = repo.pathto(abstarget, cwd)
279 279 target = repo.wjoin(abstarget)
280 280 src = repo.wjoin(abssrc)
281 281 state = repo.dirstate[abstarget]
282 282
283 283 scmutil.checkportable(ui, abstarget)
284 284
285 285 # check for collisions
286 286 prevsrc = targets.get(abstarget)
287 287 if prevsrc is not None:
288 288 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
289 289 (reltarget, repo.pathto(abssrc, cwd),
290 290 repo.pathto(prevsrc, cwd)))
291 291 return
292 292
293 293 # check for overwrites
294 294 exists = os.path.lexists(target)
295 295 samefile = False
296 296 if exists and abssrc != abstarget:
297 297 if (repo.dirstate.normalize(abssrc) ==
298 298 repo.dirstate.normalize(abstarget)):
299 299 if not rename:
300 300 ui.warn(_("%s: can't copy - same file\n") % reltarget)
301 301 return
302 302 exists = False
303 303 samefile = True
304 304
305 305 if not after and exists or after and state in 'mn':
306 306 if not opts['force']:
307 307 ui.warn(_('%s: not overwriting - file exists\n') %
308 308 reltarget)
309 309 return
310 310
311 311 if after:
312 312 if not exists:
313 313 if rename:
314 314 ui.warn(_('%s: not recording move - %s does not exist\n') %
315 315 (relsrc, reltarget))
316 316 else:
317 317 ui.warn(_('%s: not recording copy - %s does not exist\n') %
318 318 (relsrc, reltarget))
319 319 return
320 320 elif not dryrun:
321 321 try:
322 322 if exists:
323 323 os.unlink(target)
324 324 targetdir = os.path.dirname(target) or '.'
325 325 if not os.path.isdir(targetdir):
326 326 os.makedirs(targetdir)
327 327 if samefile:
328 328 tmp = target + "~hgrename"
329 329 os.rename(src, tmp)
330 330 os.rename(tmp, target)
331 331 else:
332 332 util.copyfile(src, target)
333 333 srcexists = True
334 334 except IOError, inst:
335 335 if inst.errno == errno.ENOENT:
336 336 ui.warn(_('%s: deleted in working copy\n') % relsrc)
337 337 srcexists = False
338 338 else:
339 339 ui.warn(_('%s: cannot copy - %s\n') %
340 340 (relsrc, inst.strerror))
341 341 return True # report a failure
342 342
343 343 if ui.verbose or not exact:
344 344 if rename:
345 345 ui.status(_('moving %s to %s\n') % (relsrc, reltarget))
346 346 else:
347 347 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
348 348
349 349 targets[abstarget] = abssrc
350 350
351 351 # fix up dirstate
352 352 scmutil.dirstatecopy(ui, repo, wctx, abssrc, abstarget,
353 353 dryrun=dryrun, cwd=cwd)
354 354 if rename and not dryrun:
355 355 if not after and srcexists and not samefile:
356 356 util.unlinkpath(repo.wjoin(abssrc))
357 357 wctx.forget([abssrc])
358 358
359 359 # pat: ossep
360 360 # dest ossep
361 361 # srcs: list of (hgsep, hgsep, ossep, bool)
362 362 # return: function that takes hgsep and returns ossep
363 363 def targetpathfn(pat, dest, srcs):
364 364 if os.path.isdir(pat):
365 365 abspfx = scmutil.canonpath(repo.root, cwd, pat)
366 366 abspfx = util.localpath(abspfx)
367 367 if destdirexists:
368 368 striplen = len(os.path.split(abspfx)[0])
369 369 else:
370 370 striplen = len(abspfx)
371 371 if striplen:
372 372 striplen += len(os.sep)
373 373 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
374 374 elif destdirexists:
375 375 res = lambda p: os.path.join(dest,
376 376 os.path.basename(util.localpath(p)))
377 377 else:
378 378 res = lambda p: dest
379 379 return res
380 380
381 381 # pat: ossep
382 382 # dest ossep
383 383 # srcs: list of (hgsep, hgsep, ossep, bool)
384 384 # return: function that takes hgsep and returns ossep
385 385 def targetpathafterfn(pat, dest, srcs):
386 386 if matchmod.patkind(pat):
387 387 # a mercurial pattern
388 388 res = lambda p: os.path.join(dest,
389 389 os.path.basename(util.localpath(p)))
390 390 else:
391 391 abspfx = scmutil.canonpath(repo.root, cwd, pat)
392 392 if len(abspfx) < len(srcs[0][0]):
393 393 # A directory. Either the target path contains the last
394 394 # component of the source path or it does not.
395 395 def evalpath(striplen):
396 396 score = 0
397 397 for s in srcs:
398 398 t = os.path.join(dest, util.localpath(s[0])[striplen:])
399 399 if os.path.lexists(t):
400 400 score += 1
401 401 return score
402 402
403 403 abspfx = util.localpath(abspfx)
404 404 striplen = len(abspfx)
405 405 if striplen:
406 406 striplen += len(os.sep)
407 407 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
408 408 score = evalpath(striplen)
409 409 striplen1 = len(os.path.split(abspfx)[0])
410 410 if striplen1:
411 411 striplen1 += len(os.sep)
412 412 if evalpath(striplen1) > score:
413 413 striplen = striplen1
414 414 res = lambda p: os.path.join(dest,
415 415 util.localpath(p)[striplen:])
416 416 else:
417 417 # a file
418 418 if destdirexists:
419 419 res = lambda p: os.path.join(dest,
420 420 os.path.basename(util.localpath(p)))
421 421 else:
422 422 res = lambda p: dest
423 423 return res
424 424
425 425
426 426 pats = scmutil.expandpats(pats)
427 427 if not pats:
428 428 raise util.Abort(_('no source or destination specified'))
429 429 if len(pats) == 1:
430 430 raise util.Abort(_('no destination specified'))
431 431 dest = pats.pop()
432 432 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
433 433 if not destdirexists:
434 434 if len(pats) > 1 or matchmod.patkind(pats[0]):
435 435 raise util.Abort(_('with multiple sources, destination must be an '
436 436 'existing directory'))
437 437 if util.endswithsep(dest):
438 438 raise util.Abort(_('destination %s is not a directory') % dest)
439 439
440 440 tfn = targetpathfn
441 441 if after:
442 442 tfn = targetpathafterfn
443 443 copylist = []
444 444 for pat in pats:
445 445 srcs = walkpat(pat)
446 446 if not srcs:
447 447 continue
448 448 copylist.append((tfn(pat, dest, srcs), srcs))
449 449 if not copylist:
450 450 raise util.Abort(_('no files to copy'))
451 451
452 452 errors = 0
453 453 for targetpath, srcs in copylist:
454 454 for abssrc, relsrc, exact in srcs:
455 455 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
456 456 errors += 1
457 457
458 458 if errors:
459 459 ui.warn(_('(consider using --after)\n'))
460 460
461 461 return errors != 0
462 462
463 463 def service(opts, parentfn=None, initfn=None, runfn=None, logfile=None,
464 464 runargs=None, appendpid=False):
465 465 '''Run a command as a service.'''
466 466
467 467 if opts['daemon'] and not opts['daemon_pipefds']:
468 468 # Signal child process startup with file removal
469 469 lockfd, lockpath = tempfile.mkstemp(prefix='hg-service-')
470 470 os.close(lockfd)
471 471 try:
472 472 if not runargs:
473 473 runargs = util.hgcmd() + sys.argv[1:]
474 474 runargs.append('--daemon-pipefds=%s' % lockpath)
475 475 # Don't pass --cwd to the child process, because we've already
476 476 # changed directory.
477 477 for i in xrange(1, len(runargs)):
478 478 if runargs[i].startswith('--cwd='):
479 479 del runargs[i]
480 480 break
481 481 elif runargs[i].startswith('--cwd'):
482 482 del runargs[i:i + 2]
483 483 break
484 484 def condfn():
485 485 return not os.path.exists(lockpath)
486 486 pid = util.rundetached(runargs, condfn)
487 487 if pid < 0:
488 488 raise util.Abort(_('child process failed to start'))
489 489 finally:
490 490 try:
491 491 os.unlink(lockpath)
492 492 except OSError, e:
493 493 if e.errno != errno.ENOENT:
494 494 raise
495 495 if parentfn:
496 496 return parentfn(pid)
497 497 else:
498 498 return
499 499
500 500 if initfn:
501 501 initfn()
502 502
503 503 if opts['pid_file']:
504 504 mode = appendpid and 'a' or 'w'
505 505 fp = open(opts['pid_file'], mode)
506 506 fp.write(str(os.getpid()) + '\n')
507 507 fp.close()
508 508
509 509 if opts['daemon_pipefds']:
510 510 lockpath = opts['daemon_pipefds']
511 511 try:
512 512 os.setsid()
513 513 except AttributeError:
514 514 pass
515 515 os.unlink(lockpath)
516 516 util.hidewindow()
517 517 sys.stdout.flush()
518 518 sys.stderr.flush()
519 519
520 520 nullfd = os.open(os.devnull, os.O_RDWR)
521 521 logfilefd = nullfd
522 522 if logfile:
523 523 logfilefd = os.open(logfile, os.O_RDWR | os.O_CREAT | os.O_APPEND)
524 524 os.dup2(nullfd, 0)
525 525 os.dup2(logfilefd, 1)
526 526 os.dup2(logfilefd, 2)
527 527 if nullfd not in (0, 1, 2):
528 528 os.close(nullfd)
529 529 if logfile and logfilefd not in (0, 1, 2):
530 530 os.close(logfilefd)
531 531
532 532 if runfn:
533 533 return runfn()
534 534
535 535 def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,
536 536 opts=None):
537 537 '''export changesets as hg patches.'''
538 538
539 539 total = len(revs)
540 540 revwidth = max([len(str(rev)) for rev in revs])
541 541
542 542 def single(rev, seqno, fp):
543 543 ctx = repo[rev]
544 544 node = ctx.node()
545 545 parents = [p.node() for p in ctx.parents() if p]
546 546 branch = ctx.branch()
547 547 if switch_parent:
548 548 parents.reverse()
549 549 prev = (parents and parents[0]) or nullid
550 550
551 551 shouldclose = False
552 552 if not fp and len(template) > 0:
553 553 desc_lines = ctx.description().rstrip().split('\n')
554 554 desc = desc_lines[0] #Commit always has a first line.
555 555 fp = makefileobj(repo, template, node, desc=desc, total=total,
556 556 seqno=seqno, revwidth=revwidth, mode='ab')
557 557 if fp != template:
558 558 shouldclose = True
559 559 if fp and fp != sys.stdout and util.safehasattr(fp, 'name'):
560 560 repo.ui.note("%s\n" % fp.name)
561 561
562 562 if not fp:
563 563 write = repo.ui.write
564 564 else:
565 565 def write(s, **kw):
566 566 fp.write(s)
567 567
568 568
569 569 write("# HG changeset patch\n")
570 570 write("# User %s\n" % ctx.user())
571 571 write("# Date %d %d\n" % ctx.date())
572 572 if branch and branch != 'default':
573 573 write("# Branch %s\n" % branch)
574 574 write("# Node ID %s\n" % hex(node))
575 575 write("# Parent %s\n" % hex(prev))
576 576 if len(parents) > 1:
577 577 write("# Parent %s\n" % hex(parents[1]))
578 578 write(ctx.description().rstrip())
579 579 write("\n\n")
580 580
581 581 for chunk, label in patch.diffui(repo, prev, node, opts=opts):
582 582 write(chunk, label=label)
583 583
584 584 if shouldclose:
585 585 fp.close()
586 586
587 587 for seqno, rev in enumerate(revs):
588 588 single(rev, seqno + 1, fp)
589 589
590 590 def diffordiffstat(ui, repo, diffopts, node1, node2, match,
591 591 changes=None, stat=False, fp=None, prefix='',
592 592 listsubrepos=False):
593 593 '''show diff or diffstat.'''
594 594 if fp is None:
595 595 write = ui.write
596 596 else:
597 597 def write(s, **kw):
598 598 fp.write(s)
599 599
600 600 if stat:
601 601 diffopts = diffopts.copy(context=0)
602 602 width = 80
603 603 if not ui.plain():
604 604 width = ui.termwidth()
605 605 chunks = patch.diff(repo, node1, node2, match, changes, diffopts,
606 606 prefix=prefix)
607 607 for chunk, label in patch.diffstatui(util.iterlines(chunks),
608 608 width=width,
609 609 git=diffopts.git):
610 610 write(chunk, label=label)
611 611 else:
612 612 for chunk, label in patch.diffui(repo, node1, node2, match,
613 613 changes, diffopts, prefix=prefix):
614 614 write(chunk, label=label)
615 615
616 616 if listsubrepos:
617 617 ctx1 = repo[node1]
618 618 ctx2 = repo[node2]
619 619 for subpath, sub in subrepo.itersubrepos(ctx1, ctx2):
620 620 tempnode2 = node2
621 621 try:
622 622 if node2 is not None:
623 623 tempnode2 = ctx2.substate[subpath][1]
624 624 except KeyError:
625 625 # A subrepo that existed in node1 was deleted between node1 and
626 626 # node2 (inclusive). Thus, ctx2's substate won't contain that
627 627 # subpath. The best we can do is to ignore it.
628 628 tempnode2 = None
629 629 submatch = matchmod.narrowmatcher(subpath, match)
630 630 sub.diff(ui, diffopts, tempnode2, submatch, changes=changes,
631 631 stat=stat, fp=fp, prefix=prefix)
632 632
633 633 class changeset_printer(object):
634 634 '''show changeset information when templating not requested.'''
635 635
636 636 def __init__(self, ui, repo, patch, diffopts, buffered):
637 637 self.ui = ui
638 638 self.repo = repo
639 639 self.buffered = buffered
640 640 self.patch = patch
641 641 self.diffopts = diffopts
642 642 self.header = {}
643 643 self.hunk = {}
644 644 self.lastheader = None
645 645 self.footer = None
646 646
647 647 def flush(self, rev):
648 648 if rev in self.header:
649 649 h = self.header[rev]
650 650 if h != self.lastheader:
651 651 self.lastheader = h
652 652 self.ui.write(h)
653 653 del self.header[rev]
654 654 if rev in self.hunk:
655 655 self.ui.write(self.hunk[rev])
656 656 del self.hunk[rev]
657 657 return 1
658 658 return 0
659 659
660 660 def close(self):
661 661 if self.footer:
662 662 self.ui.write(self.footer)
663 663
664 664 def show(self, ctx, copies=None, matchfn=None, **props):
665 665 if self.buffered:
666 666 self.ui.pushbuffer()
667 667 self._show(ctx, copies, matchfn, props)
668 668 self.hunk[ctx.rev()] = self.ui.popbuffer(labeled=True)
669 669 else:
670 670 self._show(ctx, copies, matchfn, props)
671 671
672 672 def _show(self, ctx, copies, matchfn, props):
673 673 '''show a single changeset or file revision'''
674 674 changenode = ctx.node()
675 675 rev = ctx.rev()
676 676
677 677 if self.ui.quiet:
678 678 self.ui.write("%d:%s\n" % (rev, short(changenode)),
679 679 label='log.node')
680 680 return
681 681
682 682 log = self.repo.changelog
683 683 date = util.datestr(ctx.date())
684 684
685 685 hexfunc = self.ui.debugflag and hex or short
686 686
687 687 parents = [(p, hexfunc(log.node(p)))
688 688 for p in self._meaningful_parentrevs(log, rev)]
689 689
690 690 # i18n: column positioning for "hg log"
691 691 self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)),
692 692 label='log.changeset changeset.%s' % ctx.phasestr())
693 693
694 694 branch = ctx.branch()
695 695 # don't show the default branch name
696 696 if branch != 'default':
697 697 # i18n: column positioning for "hg log"
698 698 self.ui.write(_("branch: %s\n") % branch,
699 699 label='log.branch')
700 700 for bookmark in self.repo.nodebookmarks(changenode):
701 701 # i18n: column positioning for "hg log"
702 702 self.ui.write(_("bookmark: %s\n") % bookmark,
703 703 label='log.bookmark')
704 704 for tag in self.repo.nodetags(changenode):
705 705 # i18n: column positioning for "hg log"
706 706 self.ui.write(_("tag: %s\n") % tag,
707 707 label='log.tag')
708 708 if self.ui.debugflag and ctx.phase():
709 709 # i18n: column positioning for "hg log"
710 710 self.ui.write(_("phase: %s\n") % _(ctx.phasestr()),
711 711 label='log.phase')
712 712 for parent in parents:
713 713 # i18n: column positioning for "hg log"
714 714 self.ui.write(_("parent: %d:%s\n") % parent,
715 715 label='log.parent changeset.%s' % ctx.phasestr())
716 716
717 717 if self.ui.debugflag:
718 718 mnode = ctx.manifestnode()
719 719 # i18n: column positioning for "hg log"
720 720 self.ui.write(_("manifest: %d:%s\n") %
721 721 (self.repo.manifest.rev(mnode), hex(mnode)),
722 722 label='ui.debug log.manifest')
723 723 # i18n: column positioning for "hg log"
724 724 self.ui.write(_("user: %s\n") % ctx.user(),
725 725 label='log.user')
726 726 # i18n: column positioning for "hg log"
727 727 self.ui.write(_("date: %s\n") % date,
728 728 label='log.date')
729 729
730 730 if self.ui.debugflag:
731 731 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
732 732 for key, value in zip([# i18n: column positioning for "hg log"
733 733 _("files:"),
734 734 # i18n: column positioning for "hg log"
735 735 _("files+:"),
736 736 # i18n: column positioning for "hg log"
737 737 _("files-:")], files):
738 738 if value:
739 739 self.ui.write("%-12s %s\n" % (key, " ".join(value)),
740 740 label='ui.debug log.files')
741 741 elif ctx.files() and self.ui.verbose:
742 742 # i18n: column positioning for "hg log"
743 743 self.ui.write(_("files: %s\n") % " ".join(ctx.files()),
744 744 label='ui.note log.files')
745 745 if copies and self.ui.verbose:
746 746 copies = ['%s (%s)' % c for c in copies]
747 747 # i18n: column positioning for "hg log"
748 748 self.ui.write(_("copies: %s\n") % ' '.join(copies),
749 749 label='ui.note log.copies')
750 750
751 751 extra = ctx.extra()
752 752 if extra and self.ui.debugflag:
753 753 for key, value in sorted(extra.items()):
754 754 # i18n: column positioning for "hg log"
755 755 self.ui.write(_("extra: %s=%s\n")
756 756 % (key, value.encode('string_escape')),
757 757 label='ui.debug log.extra')
758 758
759 759 description = ctx.description().strip()
760 760 if description:
761 761 if self.ui.verbose:
762 762 self.ui.write(_("description:\n"),
763 763 label='ui.note log.description')
764 764 self.ui.write(description,
765 765 label='ui.note log.description')
766 766 self.ui.write("\n\n")
767 767 else:
768 768 # i18n: column positioning for "hg log"
769 769 self.ui.write(_("summary: %s\n") %
770 770 description.splitlines()[0],
771 771 label='log.summary')
772 772 self.ui.write("\n")
773 773
774 774 self.showpatch(changenode, matchfn)
775 775
776 776 def showpatch(self, node, matchfn):
777 777 if not matchfn:
778 778 matchfn = self.patch
779 779 if matchfn:
780 780 stat = self.diffopts.get('stat')
781 781 diff = self.diffopts.get('patch')
782 782 diffopts = patch.diffopts(self.ui, self.diffopts)
783 783 prev = self.repo.changelog.parents(node)[0]
784 784 if stat:
785 785 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
786 786 match=matchfn, stat=True)
787 787 if diff:
788 788 if stat:
789 789 self.ui.write("\n")
790 790 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
791 791 match=matchfn, stat=False)
792 792 self.ui.write("\n")
793 793
794 794 def _meaningful_parentrevs(self, log, rev):
795 795 """Return list of meaningful (or all if debug) parentrevs for rev.
796 796
797 797 For merges (two non-nullrev revisions) both parents are meaningful.
798 798 Otherwise the first parent revision is considered meaningful if it
799 799 is not the preceding revision.
800 800 """
801 801 parents = log.parentrevs(rev)
802 802 if not self.ui.debugflag and parents[1] == nullrev:
803 803 if parents[0] >= rev - 1:
804 804 parents = []
805 805 else:
806 806 parents = [parents[0]]
807 807 return parents
808 808
809 809
810 810 class changeset_templater(changeset_printer):
811 811 '''format changeset information.'''
812 812
813 813 def __init__(self, ui, repo, patch, diffopts, mapfile, buffered):
814 814 changeset_printer.__init__(self, ui, repo, patch, diffopts, buffered)
815 815 formatnode = ui.debugflag and (lambda x: x) or (lambda x: x[:12])
816 816 defaulttempl = {
817 817 'parent': '{rev}:{node|formatnode} ',
818 818 'manifest': '{rev}:{node|formatnode}',
819 819 'file_copy': '{name} ({source})',
820 820 'extra': '{key}={value|stringescape}'
821 821 }
822 822 # filecopy is preserved for compatibility reasons
823 823 defaulttempl['filecopy'] = defaulttempl['file_copy']
824 824 self.t = templater.templater(mapfile, {'formatnode': formatnode},
825 825 cache=defaulttempl)
826 826 self.cache = {}
827 827
828 828 def use_template(self, t):
829 829 '''set template string to use'''
830 830 self.t.cache['changeset'] = t
831 831
832 832 def _meaningful_parentrevs(self, ctx):
833 833 """Return list of meaningful (or all if debug) parentrevs for rev.
834 834 """
835 835 parents = ctx.parents()
836 836 if len(parents) > 1:
837 837 return parents
838 838 if self.ui.debugflag:
839 839 return [parents[0], self.repo['null']]
840 840 if parents[0].rev() >= ctx.rev() - 1:
841 841 return []
842 842 return parents
843 843
844 844 def _show(self, ctx, copies, matchfn, props):
845 845 '''show a single changeset or file revision'''
846 846
847 847 showlist = templatekw.showlist
848 848
849 849 # showparents() behaviour depends on ui trace level which
850 850 # causes unexpected behaviours at templating level and makes
851 851 # it harder to extract it in a standalone function. Its
852 852 # behaviour cannot be changed so leave it here for now.
853 853 def showparents(**args):
854 854 ctx = args['ctx']
855 855 parents = [[('rev', p.rev()), ('node', p.hex())]
856 856 for p in self._meaningful_parentrevs(ctx)]
857 857 return showlist('parent', parents, **args)
858 858
859 859 props = props.copy()
860 860 props.update(templatekw.keywords)
861 861 props['parents'] = showparents
862 862 props['templ'] = self.t
863 863 props['ctx'] = ctx
864 864 props['repo'] = self.repo
865 865 props['revcache'] = {'copies': copies}
866 866 props['cache'] = self.cache
867 867
868 868 # find correct templates for current mode
869 869
870 870 tmplmodes = [
871 871 (True, None),
872 872 (self.ui.verbose, 'verbose'),
873 873 (self.ui.quiet, 'quiet'),
874 874 (self.ui.debugflag, 'debug'),
875 875 ]
876 876
877 877 types = {'header': '', 'footer':'', 'changeset': 'changeset'}
878 878 for mode, postfix in tmplmodes:
879 879 for type in types:
880 880 cur = postfix and ('%s_%s' % (type, postfix)) or type
881 881 if mode and cur in self.t:
882 882 types[type] = cur
883 883
884 884 try:
885 885
886 886 # write header
887 887 if types['header']:
888 888 h = templater.stringify(self.t(types['header'], **props))
889 889 if self.buffered:
890 890 self.header[ctx.rev()] = h
891 891 else:
892 892 if self.lastheader != h:
893 893 self.lastheader = h
894 894 self.ui.write(h)
895 895
896 896 # write changeset metadata, then patch if requested
897 897 key = types['changeset']
898 898 self.ui.write(templater.stringify(self.t(key, **props)))
899 899 self.showpatch(ctx.node(), matchfn)
900 900
901 901 if types['footer']:
902 902 if not self.footer:
903 903 self.footer = templater.stringify(self.t(types['footer'],
904 904 **props))
905 905
906 906 except KeyError, inst:
907 907 msg = _("%s: no key named '%s'")
908 908 raise util.Abort(msg % (self.t.mapfile, inst.args[0]))
909 909 except SyntaxError, inst:
910 910 raise util.Abort('%s: %s' % (self.t.mapfile, inst.args[0]))
911 911
912 912 def show_changeset(ui, repo, opts, buffered=False):
913 913 """show one changeset using template or regular display.
914 914
915 915 Display format will be the first non-empty hit of:
916 916 1. option 'template'
917 917 2. option 'style'
918 918 3. [ui] setting 'logtemplate'
919 919 4. [ui] setting 'style'
920 920 If all of these values are either the unset or the empty string,
921 921 regular display via changeset_printer() is done.
922 922 """
923 923 # options
924 924 patch = False
925 925 if opts.get('patch') or opts.get('stat'):
926 926 patch = scmutil.matchall(repo)
927 927
928 928 tmpl = opts.get('template')
929 929 style = None
930 930 if tmpl:
931 931 tmpl = templater.parsestring(tmpl, quoted=False)
932 932 else:
933 933 style = opts.get('style')
934 934
935 935 # ui settings
936 936 if not (tmpl or style):
937 937 tmpl = ui.config('ui', 'logtemplate')
938 938 if tmpl:
939 939 try:
940 940 tmpl = templater.parsestring(tmpl)
941 941 except SyntaxError:
942 942 tmpl = templater.parsestring(tmpl, quoted=False)
943 943 else:
944 944 style = util.expandpath(ui.config('ui', 'style', ''))
945 945
946 946 if not (tmpl or style):
947 947 return changeset_printer(ui, repo, patch, opts, buffered)
948 948
949 949 mapfile = None
950 950 if style and not tmpl:
951 951 mapfile = style
952 952 if not os.path.split(mapfile)[0]:
953 953 mapname = (templater.templatepath('map-cmdline.' + mapfile)
954 954 or templater.templatepath(mapfile))
955 955 if mapname:
956 956 mapfile = mapname
957 957
958 958 try:
959 959 t = changeset_templater(ui, repo, patch, opts, mapfile, buffered)
960 960 except SyntaxError, inst:
961 961 raise util.Abort(inst.args[0])
962 962 if tmpl:
963 963 t.use_template(tmpl)
964 964 return t
965 965
966 966 def finddate(ui, repo, date):
967 967 """Find the tipmost changeset that matches the given date spec"""
968 968
969 969 df = util.matchdate(date)
970 970 m = scmutil.matchall(repo)
971 971 results = {}
972 972
973 973 def prep(ctx, fns):
974 974 d = ctx.date()
975 975 if df(d[0]):
976 976 results[ctx.rev()] = d
977 977
978 978 for ctx in walkchangerevs(repo, m, {'rev': None}, prep):
979 979 rev = ctx.rev()
980 980 if rev in results:
981 981 ui.status(_("found revision %s from %s\n") %
982 982 (rev, util.datestr(results[rev])))
983 983 return str(rev)
984 984
985 985 raise util.Abort(_("revision matching date not found"))
986 986
987 987 def increasingwindows(start, end, windowsize=8, sizelimit=512):
988 988 if start < end:
989 989 while start < end:
990 990 yield start, min(windowsize, end - start)
991 991 start += windowsize
992 992 if windowsize < sizelimit:
993 993 windowsize *= 2
994 994 else:
995 995 while start > end:
996 996 yield start, min(windowsize, start - end - 1)
997 997 start -= windowsize
998 998 if windowsize < sizelimit:
999 999 windowsize *= 2
1000 1000
1001 1001 def walkchangerevs(repo, match, opts, prepare):
1002 1002 '''Iterate over files and the revs in which they changed.
1003 1003
1004 1004 Callers most commonly need to iterate backwards over the history
1005 1005 in which they are interested. Doing so has awful (quadratic-looking)
1006 1006 performance, so we use iterators in a "windowed" way.
1007 1007
1008 1008 We walk a window of revisions in the desired order. Within the
1009 1009 window, we first walk forwards to gather data, then in the desired
1010 1010 order (usually backwards) to display it.
1011 1011
1012 1012 This function returns an iterator yielding contexts. Before
1013 1013 yielding each context, the iterator will first call the prepare
1014 1014 function on each context in the window in forward order.'''
1015 1015
1016 1016 follow = opts.get('follow') or opts.get('follow_first')
1017 1017
1018 1018 if not len(repo):
1019 1019 return []
1020 1020 if opts.get('rev'):
1021 1021 revs = scmutil.revrange(repo, opts.get('rev'))
1022 1022 elif follow:
1023 1023 revs = repo.revs('reverse(:.)')
1024 1024 else:
1025 1025 revs = list(repo)
1026 1026 revs.reverse()
1027 1027 if not revs:
1028 1028 return []
1029 1029 wanted = set()
1030 1030 slowpath = match.anypats() or (match.files() and opts.get('removed'))
1031 1031 fncache = {}
1032 1032 change = repo.changectx
1033 1033
1034 1034 # First step is to fill wanted, the set of revisions that we want to yield.
1035 1035 # When it does not induce extra cost, we also fill fncache for revisions in
1036 1036 # wanted: a cache of filenames that were changed (ctx.files()) and that
1037 1037 # match the file filtering conditions.
1038 1038
1039 1039 if not slowpath and not match.files():
1040 1040 # No files, no patterns. Display all revs.
1041 1041 wanted = set(revs)
1042 1042 copies = []
1043 1043
1044 1044 if not slowpath and match.files():
1045 1045 # We only have to read through the filelog to find wanted revisions
1046 1046
1047 1047 minrev, maxrev = min(revs), max(revs)
1048 1048 def filerevgen(filelog, last):
1049 1049 """
1050 1050 Only files, no patterns. Check the history of each file.
1051 1051
1052 1052 Examines filelog entries within minrev, maxrev linkrev range
1053 1053 Returns an iterator yielding (linkrev, parentlinkrevs, copied)
1054 1054 tuples in backwards order
1055 1055 """
1056 1056 cl_count = len(repo)
1057 1057 revs = []
1058 1058 for j in xrange(0, last + 1):
1059 1059 linkrev = filelog.linkrev(j)
1060 1060 if linkrev < minrev:
1061 1061 continue
1062 1062 # only yield rev for which we have the changelog, it can
1063 1063 # happen while doing "hg log" during a pull or commit
1064 1064 if linkrev >= cl_count:
1065 1065 break
1066 1066
1067 1067 parentlinkrevs = []
1068 1068 for p in filelog.parentrevs(j):
1069 1069 if p != nullrev:
1070 1070 parentlinkrevs.append(filelog.linkrev(p))
1071 1071 n = filelog.node(j)
1072 1072 revs.append((linkrev, parentlinkrevs,
1073 1073 follow and filelog.renamed(n)))
1074 1074
1075 1075 return reversed(revs)
1076 1076 def iterfiles():
1077 1077 pctx = repo['.']
1078 1078 for filename in match.files():
1079 1079 if follow:
1080 1080 if filename not in pctx:
1081 1081 raise util.Abort(_('cannot follow file not in parent '
1082 1082 'revision: "%s"') % filename)
1083 1083 yield filename, pctx[filename].filenode()
1084 1084 else:
1085 1085 yield filename, None
1086 1086 for filename_node in copies:
1087 1087 yield filename_node
1088 1088 for file_, node in iterfiles():
1089 1089 filelog = repo.file(file_)
1090 1090 if not len(filelog):
1091 1091 if node is None:
1092 1092 # A zero count may be a directory or deleted file, so
1093 1093 # try to find matching entries on the slow path.
1094 1094 if follow:
1095 1095 raise util.Abort(
1096 1096 _('cannot follow nonexistent file: "%s"') % file_)
1097 1097 slowpath = True
1098 1098 break
1099 1099 else:
1100 1100 continue
1101 1101
1102 1102 if node is None:
1103 1103 last = len(filelog) - 1
1104 1104 else:
1105 1105 last = filelog.rev(node)
1106 1106
1107 1107
1108 1108 # keep track of all ancestors of the file
1109 1109 ancestors = set([filelog.linkrev(last)])
1110 1110
1111 1111 # iterate from latest to oldest revision
1112 1112 for rev, flparentlinkrevs, copied in filerevgen(filelog, last):
1113 1113 if not follow:
1114 1114 if rev > maxrev:
1115 1115 continue
1116 1116 else:
1117 1117 # Note that last might not be the first interesting
1118 1118 # rev to us:
1119 1119 # if the file has been changed after maxrev, we'll
1120 1120 # have linkrev(last) > maxrev, and we still need
1121 1121 # to explore the file graph
1122 1122 if rev not in ancestors:
1123 1123 continue
1124 1124 # XXX insert 1327 fix here
1125 1125 if flparentlinkrevs:
1126 1126 ancestors.update(flparentlinkrevs)
1127 1127
1128 1128 fncache.setdefault(rev, []).append(file_)
1129 1129 wanted.add(rev)
1130 1130 if copied:
1131 1131 copies.append(copied)
1132 1132
1133 1133 # We decided to fall back to the slowpath because at least one
1134 1134 # of the paths was not a file. Check to see if at least one of them
1135 1135 # existed in history, otherwise simply return
1136 1136 if slowpath:
1137 1137 for path in match.files():
1138 1138 if path == '.' or path in repo.store:
1139 1139 break
1140 1140 else:
1141 1141 return []
1142 1142
1143 1143 if slowpath:
1144 1144 # We have to read the changelog to match filenames against
1145 1145 # changed files
1146 1146
1147 1147 if follow:
1148 1148 raise util.Abort(_('can only follow copies/renames for explicit '
1149 1149 'filenames'))
1150 1150
1151 1151 # The slow path checks files modified in every changeset.
1152 1152 for i in sorted(revs):
1153 1153 ctx = change(i)
1154 1154 matches = filter(match, ctx.files())
1155 1155 if matches:
1156 1156 fncache[i] = matches
1157 1157 wanted.add(i)
1158 1158
1159 1159 class followfilter(object):
1160 1160 def __init__(self, onlyfirst=False):
1161 1161 self.startrev = nullrev
1162 1162 self.roots = set()
1163 1163 self.onlyfirst = onlyfirst
1164 1164
1165 1165 def match(self, rev):
1166 1166 def realparents(rev):
1167 1167 if self.onlyfirst:
1168 1168 return repo.changelog.parentrevs(rev)[0:1]
1169 1169 else:
1170 1170 return filter(lambda x: x != nullrev,
1171 1171 repo.changelog.parentrevs(rev))
1172 1172
1173 1173 if self.startrev == nullrev:
1174 1174 self.startrev = rev
1175 1175 return True
1176 1176
1177 1177 if rev > self.startrev:
1178 1178 # forward: all descendants
1179 1179 if not self.roots:
1180 1180 self.roots.add(self.startrev)
1181 1181 for parent in realparents(rev):
1182 1182 if parent in self.roots:
1183 1183 self.roots.add(rev)
1184 1184 return True
1185 1185 else:
1186 1186 # backwards: all parents
1187 1187 if not self.roots:
1188 1188 self.roots.update(realparents(self.startrev))
1189 1189 if rev in self.roots:
1190 1190 self.roots.remove(rev)
1191 1191 self.roots.update(realparents(rev))
1192 1192 return True
1193 1193
1194 1194 return False
1195 1195
1196 1196 # it might be worthwhile to do this in the iterator if the rev range
1197 1197 # is descending and the prune args are all within that range
1198 1198 for rev in opts.get('prune', ()):
1199 1199 rev = repo[rev].rev()
1200 1200 ff = followfilter()
1201 1201 stop = min(revs[0], revs[-1])
1202 1202 for x in xrange(rev, stop - 1, -1):
1203 1203 if ff.match(x):
1204 1204 wanted.discard(x)
1205 1205
1206 1206 # Now that wanted is correctly initialized, we can iterate over the
1207 1207 # revision range, yielding only revisions in wanted.
1208 1208 def iterate():
1209 1209 if follow and not match.files():
1210 1210 ff = followfilter(onlyfirst=opts.get('follow_first'))
1211 1211 def want(rev):
1212 1212 return ff.match(rev) and rev in wanted
1213 1213 else:
1214 1214 def want(rev):
1215 1215 return rev in wanted
1216 1216
1217 1217 for i, window in increasingwindows(0, len(revs)):
1218 1218 nrevs = [rev for rev in revs[i:i + window] if want(rev)]
1219 1219 for rev in sorted(nrevs):
1220 1220 fns = fncache.get(rev)
1221 1221 ctx = change(rev)
1222 1222 if not fns:
1223 1223 def fns_generator():
1224 1224 for f in ctx.files():
1225 1225 if match(f):
1226 1226 yield f
1227 1227 fns = fns_generator()
1228 1228 prepare(ctx, fns)
1229 1229 for rev in nrevs:
1230 1230 yield change(rev)
1231 1231 return iterate()
1232 1232
1233 1233 def _makegraphfilematcher(repo, pats, followfirst):
1234 1234 # When displaying a revision with --patch --follow FILE, we have
1235 1235 # to know which file of the revision must be diffed. With
1236 1236 # --follow, we want the names of the ancestors of FILE in the
1237 1237 # revision, stored in "fcache". "fcache" is populated by
1238 1238 # reproducing the graph traversal already done by --follow revset
1239 1239 # and relating linkrevs to file names (which is not "correct" but
1240 1240 # good enough).
1241 1241 fcache = {}
1242 1242 fcacheready = [False]
1243 1243 pctx = repo['.']
1244 1244 wctx = repo[None]
1245 1245
1246 1246 def populate():
1247 1247 for fn in pats:
1248 1248 for i in ((pctx[fn],), pctx[fn].ancestors(followfirst=followfirst)):
1249 1249 for c in i:
1250 1250 fcache.setdefault(c.linkrev(), set()).add(c.path())
1251 1251
1252 1252 def filematcher(rev):
1253 1253 if not fcacheready[0]:
1254 1254 # Lazy initialization
1255 1255 fcacheready[0] = True
1256 1256 populate()
1257 1257 return scmutil.match(wctx, fcache.get(rev, []), default='path')
1258 1258
1259 1259 return filematcher
1260 1260
1261 1261 def _makegraphlogrevset(repo, pats, opts, revs):
1262 1262 """Return (expr, filematcher) where expr is a revset string built
1263 1263 from log options and file patterns or None. If --stat or --patch
1264 1264 are not passed filematcher is None. Otherwise it is a callable
1265 1265 taking a revision number and returning a match objects filtering
1266 1266 the files to be detailed when displaying the revision.
1267 1267 """
1268 1268 opt2revset = {
1269 1269 'no_merges': ('not merge()', None),
1270 1270 'only_merges': ('merge()', None),
1271 1271 '_ancestors': ('ancestors(%(val)s)', None),
1272 1272 '_fancestors': ('_firstancestors(%(val)s)', None),
1273 1273 '_descendants': ('descendants(%(val)s)', None),
1274 1274 '_fdescendants': ('_firstdescendants(%(val)s)', None),
1275 1275 '_matchfiles': ('_matchfiles(%(val)s)', None),
1276 1276 'date': ('date(%(val)r)', None),
1277 1277 'branch': ('branch(%(val)r)', ' or '),
1278 1278 '_patslog': ('filelog(%(val)r)', ' or '),
1279 1279 '_patsfollow': ('follow(%(val)r)', ' or '),
1280 1280 '_patsfollowfirst': ('_followfirst(%(val)r)', ' or '),
1281 1281 'keyword': ('keyword(%(val)r)', ' or '),
1282 1282 'prune': ('not (%(val)r or ancestors(%(val)r))', ' and '),
1283 1283 'user': ('user(%(val)r)', ' or '),
1284 1284 }
1285 1285
1286 1286 opts = dict(opts)
1287 1287 # follow or not follow?
1288 1288 follow = opts.get('follow') or opts.get('follow_first')
1289 1289 followfirst = opts.get('follow_first') and 1 or 0
1290 1290 # --follow with FILE behaviour depends on revs...
1291 1291 startrev = revs[0]
1292 1292 followdescendants = (len(revs) > 1 and revs[0] < revs[1]) and 1 or 0
1293 1293
1294 1294 # branch and only_branch are really aliases and must be handled at
1295 1295 # the same time
1296 1296 opts['branch'] = opts.get('branch', []) + opts.get('only_branch', [])
1297 1297 opts['branch'] = [repo.lookupbranch(b) for b in opts['branch']]
1298 1298 # pats/include/exclude are passed to match.match() directly in
1299 1299 # _matchfiles() revset but walkchangerevs() builds its matcher with
1300 1300 # scmutil.match(). The difference is input pats are globbed on
1301 1301 # platforms without shell expansion (windows).
1302 1302 pctx = repo[None]
1303 1303 match, pats = scmutil.matchandpats(pctx, pats, opts)
1304 1304 slowpath = match.anypats() or (match.files() and opts.get('removed'))
1305 1305 if not slowpath:
1306 1306 for f in match.files():
1307 1307 if follow and f not in pctx:
1308 1308 raise util.Abort(_('cannot follow file not in parent '
1309 1309 'revision: "%s"') % f)
1310 1310 filelog = repo.file(f)
1311 1311 if not len(filelog):
1312 1312 # A zero count may be a directory or deleted file, so
1313 1313 # try to find matching entries on the slow path.
1314 1314 if follow:
1315 1315 raise util.Abort(
1316 1316 _('cannot follow nonexistent file: "%s"') % f)
1317 1317 slowpath = True
1318 1318
1319 1319 # We decided to fall back to the slowpath because at least one
1320 1320 # of the paths was not a file. Check to see if at least one of them
1321 1321 # existed in history - in that case, we'll continue down the
1322 1322 # slowpath; otherwise, we can turn off the slowpath
1323 1323 if slowpath:
1324 1324 for path in match.files():
1325 1325 if path == '.' or path in repo.store:
1326 1326 break
1327 1327 else:
1328 1328 slowpath = False
1329 1329
1330 1330 if slowpath:
1331 1331 # See walkchangerevs() slow path.
1332 1332 #
1333 1333 if follow:
1334 1334 raise util.Abort(_('can only follow copies/renames for explicit '
1335 1335 'filenames'))
1336 1336 # pats/include/exclude cannot be represented as separate
1337 1337 # revset expressions as their filtering logic applies at file
1338 1338 # level. For instance "-I a -X a" matches a revision touching
1339 1339 # "a" and "b" while "file(a) and not file(b)" does
1340 1340 # not. Besides, filesets are evaluated against the working
1341 1341 # directory.
1342 1342 matchargs = ['r:', 'd:relpath']
1343 1343 for p in pats:
1344 1344 matchargs.append('p:' + p)
1345 1345 for p in opts.get('include', []):
1346 1346 matchargs.append('i:' + p)
1347 1347 for p in opts.get('exclude', []):
1348 1348 matchargs.append('x:' + p)
1349 1349 matchargs = ','.join(('%r' % p) for p in matchargs)
1350 1350 opts['_matchfiles'] = matchargs
1351 1351 else:
1352 1352 if follow:
1353 1353 fpats = ('_patsfollow', '_patsfollowfirst')
1354 1354 fnopats = (('_ancestors', '_fancestors'),
1355 1355 ('_descendants', '_fdescendants'))
1356 1356 if pats:
1357 1357 # follow() revset interprets its file argument as a
1358 1358 # manifest entry, so use match.files(), not pats.
1359 1359 opts[fpats[followfirst]] = list(match.files())
1360 1360 else:
1361 1361 opts[fnopats[followdescendants][followfirst]] = str(startrev)
1362 1362 else:
1363 1363 opts['_patslog'] = list(pats)
1364 1364
1365 1365 filematcher = None
1366 1366 if opts.get('patch') or opts.get('stat'):
1367 1367 if follow:
1368 1368 filematcher = _makegraphfilematcher(repo, pats, followfirst)
1369 1369 else:
1370 1370 filematcher = lambda rev: match
1371 1371
1372 1372 expr = []
1373 1373 for op, val in opts.iteritems():
1374 1374 if not val:
1375 1375 continue
1376 1376 if op not in opt2revset:
1377 1377 continue
1378 1378 revop, andor = opt2revset[op]
1379 1379 if '%(val)' not in revop:
1380 1380 expr.append(revop)
1381 1381 else:
1382 1382 if not isinstance(val, list):
1383 1383 e = revop % {'val': val}
1384 1384 else:
1385 1385 e = '(' + andor.join((revop % {'val': v}) for v in val) + ')'
1386 1386 expr.append(e)
1387 1387
1388 1388 if expr:
1389 1389 expr = '(' + ' and '.join(expr) + ')'
1390 1390 else:
1391 1391 expr = None
1392 1392 return expr, filematcher
1393 1393
1394 1394 def getgraphlogrevs(repo, pats, opts):
1395 1395 """Return (revs, expr, filematcher) where revs is an iterable of
1396 1396 revision numbers, expr is a revset string built from log options
1397 1397 and file patterns or None, and used to filter 'revs'. If --stat or
1398 1398 --patch are not passed filematcher is None. Otherwise it is a
1399 1399 callable taking a revision number and returning a match objects
1400 1400 filtering the files to be detailed when displaying the revision.
1401 1401 """
1402 1402 if not len(repo):
1403 1403 return [], None, None
1404 1404 limit = loglimit(opts)
1405 1405 # Default --rev value depends on --follow but --follow behaviour
1406 1406 # depends on revisions resolved from --rev...
1407 1407 follow = opts.get('follow') or opts.get('follow_first')
1408 1408 possiblyunsorted = False # whether revs might need sorting
1409 if not opts.get('hidden'):
1410 repo = repo.filtered('hidden')
1411 1409 if opts.get('rev'):
1412 1410 revs = scmutil.revrange(repo, opts['rev'])
1413 1411 # Don't sort here because _makegraphlogrevset might depend on the
1414 1412 # order of revs
1415 1413 possiblyunsorted = True
1416 1414 else:
1417 1415 if follow and len(repo) > 0:
1418 1416 revs = repo.revs('reverse(:.)')
1419 1417 else:
1420 1418 revs = list(repo.changelog)
1421 1419 revs.reverse()
1422 1420 if not revs:
1423 1421 return [], None, None
1424 1422 expr, filematcher = _makegraphlogrevset(repo, pats, opts, revs)
1425 1423 if possiblyunsorted:
1426 1424 revs.sort(reverse=True)
1427 1425 if expr:
1428 1426 # Revset matchers often operate faster on revisions in changelog
1429 1427 # order, because most filters deal with the changelog.
1430 1428 revs.reverse()
1431 1429 matcher = revset.match(repo.ui, expr)
1432 1430 # Revset matches can reorder revisions. "A or B" typically returns
1433 1431 # returns the revision matching A then the revision matching B. Sort
1434 1432 # again to fix that.
1435 1433 revs = matcher(repo, revs)
1436 1434 revs.sort(reverse=True)
1437 1435 if limit is not None:
1438 1436 revs = revs[:limit]
1439 1437
1440 1438 return revs, expr, filematcher
1441 1439
1442 1440 def displaygraph(ui, dag, displayer, showparents, edgefn, getrenamed=None,
1443 1441 filematcher=None):
1444 1442 seen, state = [], graphmod.asciistate()
1445 1443 for rev, type, ctx, parents in dag:
1446 1444 char = 'o'
1447 1445 if ctx.node() in showparents:
1448 1446 char = '@'
1449 1447 elif ctx.obsolete():
1450 1448 char = 'x'
1451 1449 copies = None
1452 1450 if getrenamed and ctx.rev():
1453 1451 copies = []
1454 1452 for fn in ctx.files():
1455 1453 rename = getrenamed(fn, ctx.rev())
1456 1454 if rename:
1457 1455 copies.append((fn, rename[0]))
1458 1456 revmatchfn = None
1459 1457 if filematcher is not None:
1460 1458 revmatchfn = filematcher(ctx.rev())
1461 1459 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
1462 1460 lines = displayer.hunk.pop(rev).split('\n')
1463 1461 if not lines[-1]:
1464 1462 del lines[-1]
1465 1463 displayer.flush(rev)
1466 1464 edges = edgefn(type, char, lines, seen, rev, parents)
1467 1465 for type, char, lines, coldata in edges:
1468 1466 graphmod.ascii(ui, state, type, char, lines, coldata)
1469 1467 displayer.close()
1470 1468
1471 1469 def graphlog(ui, repo, *pats, **opts):
1472 1470 # Parameters are identical to log command ones
1473 1471 revs, expr, filematcher = getgraphlogrevs(repo, pats, opts)
1474 1472 revdag = graphmod.dagwalker(repo, revs)
1475 1473
1476 1474 getrenamed = None
1477 1475 if opts.get('copies'):
1478 1476 endrev = None
1479 1477 if opts.get('rev'):
1480 1478 endrev = max(scmutil.revrange(repo, opts.get('rev'))) + 1
1481 1479 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
1482 1480 displayer = show_changeset(ui, repo, opts, buffered=True)
1483 1481 showparents = [ctx.node() for ctx in repo[None].parents()]
1484 1482 displaygraph(ui, revdag, displayer, showparents,
1485 1483 graphmod.asciiedges, getrenamed, filematcher)
1486 1484
1487 1485 def checkunsupportedgraphflags(pats, opts):
1488 1486 for op in ["newest_first"]:
1489 1487 if op in opts and opts[op]:
1490 1488 raise util.Abort(_("-G/--graph option is incompatible with --%s")
1491 1489 % op.replace("_", "-"))
1492 1490
1493 1491 def graphrevs(repo, nodes, opts):
1494 1492 limit = loglimit(opts)
1495 1493 nodes.reverse()
1496 1494 if limit is not None:
1497 1495 nodes = nodes[:limit]
1498 1496 return graphmod.nodes(repo, nodes)
1499 1497
1500 1498 def add(ui, repo, match, dryrun, listsubrepos, prefix, explicitonly):
1501 1499 join = lambda f: os.path.join(prefix, f)
1502 1500 bad = []
1503 1501 oldbad = match.bad
1504 1502 match.bad = lambda x, y: bad.append(x) or oldbad(x, y)
1505 1503 names = []
1506 1504 wctx = repo[None]
1507 1505 cca = None
1508 1506 abort, warn = scmutil.checkportabilityalert(ui)
1509 1507 if abort or warn:
1510 1508 cca = scmutil.casecollisionauditor(ui, abort, repo.dirstate)
1511 1509 for f in repo.walk(match):
1512 1510 exact = match.exact(f)
1513 1511 if exact or not explicitonly and f not in repo.dirstate:
1514 1512 if cca:
1515 1513 cca(f)
1516 1514 names.append(f)
1517 1515 if ui.verbose or not exact:
1518 1516 ui.status(_('adding %s\n') % match.rel(join(f)))
1519 1517
1520 1518 for subpath in wctx.substate:
1521 1519 sub = wctx.sub(subpath)
1522 1520 try:
1523 1521 submatch = matchmod.narrowmatcher(subpath, match)
1524 1522 if listsubrepos:
1525 1523 bad.extend(sub.add(ui, submatch, dryrun, listsubrepos, prefix,
1526 1524 False))
1527 1525 else:
1528 1526 bad.extend(sub.add(ui, submatch, dryrun, listsubrepos, prefix,
1529 1527 True))
1530 1528 except error.LookupError:
1531 1529 ui.status(_("skipping missing subrepository: %s\n")
1532 1530 % join(subpath))
1533 1531
1534 1532 if not dryrun:
1535 1533 rejected = wctx.add(names, prefix)
1536 1534 bad.extend(f for f in rejected if f in match.files())
1537 1535 return bad
1538 1536
1539 1537 def forget(ui, repo, match, prefix, explicitonly):
1540 1538 join = lambda f: os.path.join(prefix, f)
1541 1539 bad = []
1542 1540 oldbad = match.bad
1543 1541 match.bad = lambda x, y: bad.append(x) or oldbad(x, y)
1544 1542 wctx = repo[None]
1545 1543 forgot = []
1546 1544 s = repo.status(match=match, clean=True)
1547 1545 forget = sorted(s[0] + s[1] + s[3] + s[6])
1548 1546 if explicitonly:
1549 1547 forget = [f for f in forget if match.exact(f)]
1550 1548
1551 1549 for subpath in wctx.substate:
1552 1550 sub = wctx.sub(subpath)
1553 1551 try:
1554 1552 submatch = matchmod.narrowmatcher(subpath, match)
1555 1553 subbad, subforgot = sub.forget(ui, submatch, prefix)
1556 1554 bad.extend([subpath + '/' + f for f in subbad])
1557 1555 forgot.extend([subpath + '/' + f for f in subforgot])
1558 1556 except error.LookupError:
1559 1557 ui.status(_("skipping missing subrepository: %s\n")
1560 1558 % join(subpath))
1561 1559
1562 1560 if not explicitonly:
1563 1561 for f in match.files():
1564 1562 if f not in repo.dirstate and not os.path.isdir(match.rel(join(f))):
1565 1563 if f not in forgot:
1566 1564 if os.path.exists(match.rel(join(f))):
1567 1565 ui.warn(_('not removing %s: '
1568 1566 'file is already untracked\n')
1569 1567 % match.rel(join(f)))
1570 1568 bad.append(f)
1571 1569
1572 1570 for f in forget:
1573 1571 if ui.verbose or not match.exact(f):
1574 1572 ui.status(_('removing %s\n') % match.rel(join(f)))
1575 1573
1576 1574 rejected = wctx.forget(forget, prefix)
1577 1575 bad.extend(f for f in rejected if f in match.files())
1578 1576 forgot.extend(forget)
1579 1577 return bad, forgot
1580 1578
1581 1579 def duplicatecopies(repo, rev, p1):
1582 1580 "Reproduce copies found in the source revision in the dirstate for grafts"
1583 1581 for dst, src in copies.pathcopies(repo[p1], repo[rev]).iteritems():
1584 1582 repo.dirstate.copy(src, dst)
1585 1583
1586 1584 def commit(ui, repo, commitfunc, pats, opts):
1587 1585 '''commit the specified files or all outstanding changes'''
1588 1586 date = opts.get('date')
1589 1587 if date:
1590 1588 opts['date'] = util.parsedate(date)
1591 1589 message = logmessage(ui, opts)
1592 1590
1593 1591 # extract addremove carefully -- this function can be called from a command
1594 1592 # that doesn't support addremove
1595 1593 if opts.get('addremove'):
1596 1594 scmutil.addremove(repo, pats, opts)
1597 1595
1598 1596 return commitfunc(ui, repo, message,
1599 1597 scmutil.match(repo[None], pats, opts), opts)
1600 1598
1601 1599 def amend(ui, repo, commitfunc, old, extra, pats, opts):
1602 1600 ui.note(_('amending changeset %s\n') % old)
1603 1601 base = old.p1()
1604 1602
1605 1603 wlock = lock = newid = None
1606 1604 try:
1607 1605 wlock = repo.wlock()
1608 1606 lock = repo.lock()
1609 1607 tr = repo.transaction('amend')
1610 1608 try:
1611 1609 # See if we got a message from -m or -l, if not, open the editor
1612 1610 # with the message of the changeset to amend
1613 1611 message = logmessage(ui, opts)
1614 1612 # ensure logfile does not conflict with later enforcement of the
1615 1613 # message. potential logfile content has been processed by
1616 1614 # `logmessage` anyway.
1617 1615 opts.pop('logfile')
1618 1616 # First, do a regular commit to record all changes in the working
1619 1617 # directory (if there are any)
1620 1618 ui.callhooks = False
1621 1619 currentbookmark = repo._bookmarkcurrent
1622 1620 try:
1623 1621 repo._bookmarkcurrent = None
1624 1622 opts['message'] = 'temporary amend commit for %s' % old
1625 1623 node = commit(ui, repo, commitfunc, pats, opts)
1626 1624 finally:
1627 1625 repo._bookmarkcurrent = currentbookmark
1628 1626 ui.callhooks = True
1629 1627 ctx = repo[node]
1630 1628
1631 1629 # Participating changesets:
1632 1630 #
1633 1631 # node/ctx o - new (intermediate) commit that contains changes
1634 1632 # | from working dir to go into amending commit
1635 1633 # | (or a workingctx if there were no changes)
1636 1634 # |
1637 1635 # old o - changeset to amend
1638 1636 # |
1639 1637 # base o - parent of amending changeset
1640 1638
1641 1639 # Update extra dict from amended commit (e.g. to preserve graft
1642 1640 # source)
1643 1641 extra.update(old.extra())
1644 1642
1645 1643 # Also update it from the intermediate commit or from the wctx
1646 1644 extra.update(ctx.extra())
1647 1645
1648 1646 files = set(old.files())
1649 1647
1650 1648 # Second, we use either the commit we just did, or if there were no
1651 1649 # changes the parent of the working directory as the version of the
1652 1650 # files in the final amend commit
1653 1651 if node:
1654 1652 ui.note(_('copying changeset %s to %s\n') % (ctx, base))
1655 1653
1656 1654 user = ctx.user()
1657 1655 date = ctx.date()
1658 1656 # Recompute copies (avoid recording a -> b -> a)
1659 1657 copied = copies.pathcopies(base, ctx)
1660 1658
1661 1659 # Prune files which were reverted by the updates: if old
1662 1660 # introduced file X and our intermediate commit, node,
1663 1661 # renamed that file, then those two files are the same and
1664 1662 # we can discard X from our list of files. Likewise if X
1665 1663 # was deleted, it's no longer relevant
1666 1664 files.update(ctx.files())
1667 1665
1668 1666 def samefile(f):
1669 1667 if f in ctx.manifest():
1670 1668 a = ctx.filectx(f)
1671 1669 if f in base.manifest():
1672 1670 b = base.filectx(f)
1673 1671 return (not a.cmp(b)
1674 1672 and a.flags() == b.flags())
1675 1673 else:
1676 1674 return False
1677 1675 else:
1678 1676 return f not in base.manifest()
1679 1677 files = [f for f in files if not samefile(f)]
1680 1678
1681 1679 def filectxfn(repo, ctx_, path):
1682 1680 try:
1683 1681 fctx = ctx[path]
1684 1682 flags = fctx.flags()
1685 1683 mctx = context.memfilectx(fctx.path(), fctx.data(),
1686 1684 islink='l' in flags,
1687 1685 isexec='x' in flags,
1688 1686 copied=copied.get(path))
1689 1687 return mctx
1690 1688 except KeyError:
1691 1689 raise IOError
1692 1690 else:
1693 1691 ui.note(_('copying changeset %s to %s\n') % (old, base))
1694 1692
1695 1693 # Use version of files as in the old cset
1696 1694 def filectxfn(repo, ctx_, path):
1697 1695 try:
1698 1696 return old.filectx(path)
1699 1697 except KeyError:
1700 1698 raise IOError
1701 1699
1702 1700 user = opts.get('user') or old.user()
1703 1701 date = opts.get('date') or old.date()
1704 1702 editmsg = False
1705 1703 if not message:
1706 1704 editmsg = True
1707 1705 message = old.description()
1708 1706
1709 1707 pureextra = extra.copy()
1710 1708 extra['amend_source'] = old.hex()
1711 1709
1712 1710 new = context.memctx(repo,
1713 1711 parents=[base.node(), nullid],
1714 1712 text=message,
1715 1713 files=files,
1716 1714 filectxfn=filectxfn,
1717 1715 user=user,
1718 1716 date=date,
1719 1717 extra=extra)
1720 1718 if editmsg:
1721 1719 new._text = commitforceeditor(repo, new, [])
1722 1720
1723 1721 newdesc = changelog.stripdesc(new.description())
1724 1722 if ((not node)
1725 1723 and newdesc == old.description()
1726 1724 and user == old.user()
1727 1725 and date == old.date()
1728 1726 and pureextra == old.extra()):
1729 1727 # nothing changed. continuing here would create a new node
1730 1728 # anyway because of the amend_source noise.
1731 1729 #
1732 1730 # This not what we expect from amend.
1733 1731 return old.node()
1734 1732
1735 1733 ph = repo.ui.config('phases', 'new-commit', phases.draft)
1736 1734 try:
1737 1735 repo.ui.setconfig('phases', 'new-commit', old.phase())
1738 1736 newid = repo.commitctx(new)
1739 1737 finally:
1740 1738 repo.ui.setconfig('phases', 'new-commit', ph)
1741 1739 if newid != old.node():
1742 1740 # Reroute the working copy parent to the new changeset
1743 1741 repo.setparents(newid, nullid)
1744 1742
1745 1743 # Move bookmarks from old parent to amend commit
1746 1744 bms = repo.nodebookmarks(old.node())
1747 1745 if bms:
1748 1746 marks = repo._bookmarks
1749 1747 for bm in bms:
1750 1748 marks[bm] = newid
1751 1749 marks.write()
1752 1750 #commit the whole amend process
1753 1751 if obsolete._enabled and newid != old.node():
1754 1752 # mark the new changeset as successor of the rewritten one
1755 1753 new = repo[newid]
1756 1754 obs = [(old, (new,))]
1757 1755 if node:
1758 1756 obs.append((ctx, ()))
1759 1757
1760 1758 obsolete.createmarkers(repo, obs)
1761 1759 tr.close()
1762 1760 finally:
1763 1761 tr.release()
1764 1762 if (not obsolete._enabled) and newid != old.node():
1765 1763 # Strip the intermediate commit (if there was one) and the amended
1766 1764 # commit
1767 1765 if node:
1768 1766 ui.note(_('stripping intermediate changeset %s\n') % ctx)
1769 1767 ui.note(_('stripping amended changeset %s\n') % old)
1770 1768 repair.strip(ui, repo, old.node(), topic='amend-backup')
1771 1769 finally:
1772 1770 if newid is None:
1773 1771 repo.dirstate.invalidate()
1774 1772 lockmod.release(wlock, lock)
1775 1773 return newid
1776 1774
1777 1775 def commiteditor(repo, ctx, subs):
1778 1776 if ctx.description():
1779 1777 return ctx.description()
1780 1778 return commitforceeditor(repo, ctx, subs)
1781 1779
1782 1780 def commitforceeditor(repo, ctx, subs):
1783 1781 edittext = []
1784 1782 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
1785 1783 if ctx.description():
1786 1784 edittext.append(ctx.description())
1787 1785 edittext.append("")
1788 1786 edittext.append("") # Empty line between message and comments.
1789 1787 edittext.append(_("HG: Enter commit message."
1790 1788 " Lines beginning with 'HG:' are removed."))
1791 1789 edittext.append(_("HG: Leave message empty to abort commit."))
1792 1790 edittext.append("HG: --")
1793 1791 edittext.append(_("HG: user: %s") % ctx.user())
1794 1792 if ctx.p2():
1795 1793 edittext.append(_("HG: branch merge"))
1796 1794 if ctx.branch():
1797 1795 edittext.append(_("HG: branch '%s'") % ctx.branch())
1798 1796 edittext.extend([_("HG: subrepo %s") % s for s in subs])
1799 1797 edittext.extend([_("HG: added %s") % f for f in added])
1800 1798 edittext.extend([_("HG: changed %s") % f for f in modified])
1801 1799 edittext.extend([_("HG: removed %s") % f for f in removed])
1802 1800 if not added and not modified and not removed:
1803 1801 edittext.append(_("HG: no files changed"))
1804 1802 edittext.append("")
1805 1803 # run editor in the repository root
1806 1804 olddir = os.getcwd()
1807 1805 os.chdir(repo.root)
1808 1806 text = repo.ui.edit("\n".join(edittext), ctx.user())
1809 1807 text = re.sub("(?m)^HG:.*(\n|$)", "", text)
1810 1808 os.chdir(olddir)
1811 1809
1812 1810 if not text.strip():
1813 1811 raise util.Abort(_("empty commit message"))
1814 1812
1815 1813 return text
1816 1814
1817 1815 def revert(ui, repo, ctx, parents, *pats, **opts):
1818 1816 parent, p2 = parents
1819 1817 node = ctx.node()
1820 1818
1821 1819 mf = ctx.manifest()
1822 1820 if node == parent:
1823 1821 pmf = mf
1824 1822 else:
1825 1823 pmf = None
1826 1824
1827 1825 # need all matching names in dirstate and manifest of target rev,
1828 1826 # so have to walk both. do not print errors if files exist in one
1829 1827 # but not other.
1830 1828
1831 1829 names = {}
1832 1830
1833 1831 wlock = repo.wlock()
1834 1832 try:
1835 1833 # walk dirstate.
1836 1834
1837 1835 m = scmutil.match(repo[None], pats, opts)
1838 1836 m.bad = lambda x, y: False
1839 1837 for abs in repo.walk(m):
1840 1838 names[abs] = m.rel(abs), m.exact(abs)
1841 1839
1842 1840 # walk target manifest.
1843 1841
1844 1842 def badfn(path, msg):
1845 1843 if path in names:
1846 1844 return
1847 1845 if path in ctx.substate:
1848 1846 return
1849 1847 path_ = path + '/'
1850 1848 for f in names:
1851 1849 if f.startswith(path_):
1852 1850 return
1853 1851 ui.warn("%s: %s\n" % (m.rel(path), msg))
1854 1852
1855 1853 m = scmutil.match(ctx, pats, opts)
1856 1854 m.bad = badfn
1857 1855 for abs in ctx.walk(m):
1858 1856 if abs not in names:
1859 1857 names[abs] = m.rel(abs), m.exact(abs)
1860 1858
1861 1859 # get the list of subrepos that must be reverted
1862 1860 targetsubs = [s for s in ctx.substate if m(s)]
1863 1861 m = scmutil.matchfiles(repo, names)
1864 1862 changes = repo.status(match=m)[:4]
1865 1863 modified, added, removed, deleted = map(set, changes)
1866 1864
1867 1865 # if f is a rename, also revert the source
1868 1866 cwd = repo.getcwd()
1869 1867 for f in added:
1870 1868 src = repo.dirstate.copied(f)
1871 1869 if src and src not in names and repo.dirstate[src] == 'r':
1872 1870 removed.add(src)
1873 1871 names[src] = (repo.pathto(src, cwd), True)
1874 1872
1875 1873 def removeforget(abs):
1876 1874 if repo.dirstate[abs] == 'a':
1877 1875 return _('forgetting %s\n')
1878 1876 return _('removing %s\n')
1879 1877
1880 1878 revert = ([], _('reverting %s\n'))
1881 1879 add = ([], _('adding %s\n'))
1882 1880 remove = ([], removeforget)
1883 1881 undelete = ([], _('undeleting %s\n'))
1884 1882
1885 1883 disptable = (
1886 1884 # dispatch table:
1887 1885 # file state
1888 1886 # action if in target manifest
1889 1887 # action if not in target manifest
1890 1888 # make backup if in target manifest
1891 1889 # make backup if not in target manifest
1892 1890 (modified, revert, remove, True, True),
1893 1891 (added, revert, remove, True, False),
1894 1892 (removed, undelete, None, False, False),
1895 1893 (deleted, revert, remove, False, False),
1896 1894 )
1897 1895
1898 1896 for abs, (rel, exact) in sorted(names.items()):
1899 1897 mfentry = mf.get(abs)
1900 1898 target = repo.wjoin(abs)
1901 1899 def handle(xlist, dobackup):
1902 1900 xlist[0].append(abs)
1903 1901 if (dobackup and not opts.get('no_backup') and
1904 1902 os.path.lexists(target)):
1905 1903 bakname = "%s.orig" % rel
1906 1904 ui.note(_('saving current version of %s as %s\n') %
1907 1905 (rel, bakname))
1908 1906 if not opts.get('dry_run'):
1909 1907 util.rename(target, bakname)
1910 1908 if ui.verbose or not exact:
1911 1909 msg = xlist[1]
1912 1910 if not isinstance(msg, basestring):
1913 1911 msg = msg(abs)
1914 1912 ui.status(msg % rel)
1915 1913 for table, hitlist, misslist, backuphit, backupmiss in disptable:
1916 1914 if abs not in table:
1917 1915 continue
1918 1916 # file has changed in dirstate
1919 1917 if mfentry:
1920 1918 handle(hitlist, backuphit)
1921 1919 elif misslist is not None:
1922 1920 handle(misslist, backupmiss)
1923 1921 break
1924 1922 else:
1925 1923 if abs not in repo.dirstate:
1926 1924 if mfentry:
1927 1925 handle(add, True)
1928 1926 elif exact:
1929 1927 ui.warn(_('file not managed: %s\n') % rel)
1930 1928 continue
1931 1929 # file has not changed in dirstate
1932 1930 if node == parent:
1933 1931 if exact:
1934 1932 ui.warn(_('no changes needed to %s\n') % rel)
1935 1933 continue
1936 1934 if pmf is None:
1937 1935 # only need parent manifest in this unlikely case,
1938 1936 # so do not read by default
1939 1937 pmf = repo[parent].manifest()
1940 1938 if abs in pmf and mfentry:
1941 1939 # if version of file is same in parent and target
1942 1940 # manifests, do nothing
1943 1941 if (pmf[abs] != mfentry or
1944 1942 pmf.flags(abs) != mf.flags(abs)):
1945 1943 handle(revert, False)
1946 1944 else:
1947 1945 handle(remove, False)
1948 1946
1949 1947 if not opts.get('dry_run'):
1950 1948 def checkout(f):
1951 1949 fc = ctx[f]
1952 1950 repo.wwrite(f, fc.data(), fc.flags())
1953 1951
1954 1952 audit_path = scmutil.pathauditor(repo.root)
1955 1953 for f in remove[0]:
1956 1954 if repo.dirstate[f] == 'a':
1957 1955 repo.dirstate.drop(f)
1958 1956 continue
1959 1957 audit_path(f)
1960 1958 try:
1961 1959 util.unlinkpath(repo.wjoin(f))
1962 1960 except OSError:
1963 1961 pass
1964 1962 repo.dirstate.remove(f)
1965 1963
1966 1964 normal = None
1967 1965 if node == parent:
1968 1966 # We're reverting to our parent. If possible, we'd like status
1969 1967 # to report the file as clean. We have to use normallookup for
1970 1968 # merges to avoid losing information about merged/dirty files.
1971 1969 if p2 != nullid:
1972 1970 normal = repo.dirstate.normallookup
1973 1971 else:
1974 1972 normal = repo.dirstate.normal
1975 1973 for f in revert[0]:
1976 1974 checkout(f)
1977 1975 if normal:
1978 1976 normal(f)
1979 1977
1980 1978 for f in add[0]:
1981 1979 checkout(f)
1982 1980 repo.dirstate.add(f)
1983 1981
1984 1982 normal = repo.dirstate.normallookup
1985 1983 if node == parent and p2 == nullid:
1986 1984 normal = repo.dirstate.normal
1987 1985 for f in undelete[0]:
1988 1986 checkout(f)
1989 1987 normal(f)
1990 1988
1991 1989 if targetsubs:
1992 1990 # Revert the subrepos on the revert list
1993 1991 for sub in targetsubs:
1994 1992 ctx.sub(sub).revert(ui, ctx.substate[sub], *pats, **opts)
1995 1993 finally:
1996 1994 wlock.release()
1997 1995
1998 1996 def command(table):
1999 1997 '''returns a function object bound to table which can be used as
2000 1998 a decorator for populating table as a command table'''
2001 1999
2002 2000 def cmd(name, options=(), synopsis=None):
2003 2001 def decorator(func):
2004 2002 if synopsis:
2005 2003 table[name] = func, list(options), synopsis
2006 2004 else:
2007 2005 table[name] = func, list(options)
2008 2006 return func
2009 2007 return decorator
2010 2008
2011 2009 return cmd
@@ -1,6041 +1,6039 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, difflib, time, tempfile, errno
12 12 import hg, scmutil, util, revlog, extensions, copies, error, bookmarks
13 13 import patch, help, encoding, templatekw, discovery
14 14 import archival, changegroup, cmdutil, hbisect
15 15 import sshserver, hgweb, hgweb.server, commandserver
16 16 import merge as mergemod
17 17 import minirst, revset, fileset
18 18 import dagparser, context, simplemerge, graphmod
19 19 import random, setdiscovery, treediscovery, dagutil, pvec, localrepo
20 20 import phases, obsolete
21 21
22 22 table = {}
23 23
24 24 command = cmdutil.command(table)
25 25
26 26 # common command options
27 27
28 28 globalopts = [
29 29 ('R', 'repository', '',
30 30 _('repository root directory or name of overlay bundle file'),
31 31 _('REPO')),
32 32 ('', 'cwd', '',
33 33 _('change working directory'), _('DIR')),
34 34 ('y', 'noninteractive', None,
35 35 _('do not prompt, automatically pick the first choice for all prompts')),
36 36 ('q', 'quiet', None, _('suppress output')),
37 37 ('v', 'verbose', None, _('enable additional output')),
38 38 ('', 'config', [],
39 39 _('set/override config option (use \'section.name=value\')'),
40 40 _('CONFIG')),
41 41 ('', 'debug', None, _('enable debugging output')),
42 42 ('', 'debugger', None, _('start debugger')),
43 43 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
44 44 _('ENCODE')),
45 45 ('', 'encodingmode', encoding.encodingmode,
46 46 _('set the charset encoding mode'), _('MODE')),
47 47 ('', 'traceback', None, _('always print a traceback on exception')),
48 48 ('', 'time', None, _('time how long the command takes')),
49 49 ('', 'profile', None, _('print command execution profile')),
50 50 ('', 'version', None, _('output version information and exit')),
51 51 ('h', 'help', None, _('display help and exit')),
52 ('', 'hidden', False, _('consider hidden changesets')),
52 53 ]
53 54
54 55 dryrunopts = [('n', 'dry-run', None,
55 56 _('do not perform actions, just print output'))]
56 57
57 58 remoteopts = [
58 59 ('e', 'ssh', '',
59 60 _('specify ssh command to use'), _('CMD')),
60 61 ('', 'remotecmd', '',
61 62 _('specify hg command to run on the remote side'), _('CMD')),
62 63 ('', 'insecure', None,
63 64 _('do not verify server certificate (ignoring web.cacerts config)')),
64 65 ]
65 66
66 67 walkopts = [
67 68 ('I', 'include', [],
68 69 _('include names matching the given patterns'), _('PATTERN')),
69 70 ('X', 'exclude', [],
70 71 _('exclude names matching the given patterns'), _('PATTERN')),
71 72 ]
72 73
73 74 commitopts = [
74 75 ('m', 'message', '',
75 76 _('use text as commit message'), _('TEXT')),
76 77 ('l', 'logfile', '',
77 78 _('read commit message from file'), _('FILE')),
78 79 ]
79 80
80 81 commitopts2 = [
81 82 ('d', 'date', '',
82 83 _('record the specified date as commit date'), _('DATE')),
83 84 ('u', 'user', '',
84 85 _('record the specified user as committer'), _('USER')),
85 86 ]
86 87
87 88 templateopts = [
88 89 ('', 'style', '',
89 90 _('display using template map file'), _('STYLE')),
90 91 ('', 'template', '',
91 92 _('display with template'), _('TEMPLATE')),
92 93 ]
93 94
94 95 logopts = [
95 96 ('p', 'patch', None, _('show patch')),
96 97 ('g', 'git', None, _('use git extended diff format')),
97 98 ('l', 'limit', '',
98 99 _('limit number of changes displayed'), _('NUM')),
99 100 ('M', 'no-merges', None, _('do not show merges')),
100 101 ('', 'stat', None, _('output diffstat-style summary of changes')),
101 102 ('G', 'graph', None, _("show the revision DAG")),
102 103 ] + templateopts
103 104
104 105 diffopts = [
105 106 ('a', 'text', None, _('treat all files as text')),
106 107 ('g', 'git', None, _('use git extended diff format')),
107 108 ('', 'nodates', None, _('omit dates from diff headers'))
108 109 ]
109 110
110 111 diffwsopts = [
111 112 ('w', 'ignore-all-space', None,
112 113 _('ignore white space when comparing lines')),
113 114 ('b', 'ignore-space-change', None,
114 115 _('ignore changes in the amount of white space')),
115 116 ('B', 'ignore-blank-lines', None,
116 117 _('ignore changes whose lines are all blank')),
117 118 ]
118 119
119 120 diffopts2 = [
120 121 ('p', 'show-function', None, _('show which function each change is in')),
121 122 ('', 'reverse', None, _('produce a diff that undoes the changes')),
122 123 ] + diffwsopts + [
123 124 ('U', 'unified', '',
124 125 _('number of lines of context to show'), _('NUM')),
125 126 ('', 'stat', None, _('output diffstat-style summary of changes')),
126 127 ]
127 128
128 129 mergetoolopts = [
129 130 ('t', 'tool', '', _('specify merge tool')),
130 131 ]
131 132
132 133 similarityopts = [
133 134 ('s', 'similarity', '',
134 135 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
135 136 ]
136 137
137 138 subrepoopts = [
138 139 ('S', 'subrepos', None,
139 140 _('recurse into subrepositories'))
140 141 ]
141 142
142 143 # Commands start here, listed alphabetically
143 144
144 145 @command('^add',
145 146 walkopts + subrepoopts + dryrunopts,
146 147 _('[OPTION]... [FILE]...'))
147 148 def add(ui, repo, *pats, **opts):
148 149 """add the specified files on the next commit
149 150
150 151 Schedule files to be version controlled and added to the
151 152 repository.
152 153
153 154 The files will be added to the repository at the next commit. To
154 155 undo an add before that, see :hg:`forget`.
155 156
156 157 If no names are given, add all files to the repository.
157 158
158 159 .. container:: verbose
159 160
160 161 An example showing how new (unknown) files are added
161 162 automatically by :hg:`add`::
162 163
163 164 $ ls
164 165 foo.c
165 166 $ hg status
166 167 ? foo.c
167 168 $ hg add
168 169 adding foo.c
169 170 $ hg status
170 171 A foo.c
171 172
172 173 Returns 0 if all files are successfully added.
173 174 """
174 175
175 176 m = scmutil.match(repo[None], pats, opts)
176 177 rejected = cmdutil.add(ui, repo, m, opts.get('dry_run'),
177 178 opts.get('subrepos'), prefix="", explicitonly=False)
178 179 return rejected and 1 or 0
179 180
180 181 @command('addremove',
181 182 similarityopts + walkopts + dryrunopts,
182 183 _('[OPTION]... [FILE]...'))
183 184 def addremove(ui, repo, *pats, **opts):
184 185 """add all new files, delete all missing files
185 186
186 187 Add all new files and remove all missing files from the
187 188 repository.
188 189
189 190 New files are ignored if they match any of the patterns in
190 191 ``.hgignore``. As with add, these changes take effect at the next
191 192 commit.
192 193
193 194 Use the -s/--similarity option to detect renamed files. This
194 195 option takes a percentage between 0 (disabled) and 100 (files must
195 196 be identical) as its parameter. With a parameter greater than 0,
196 197 this compares every removed file with every added file and records
197 198 those similar enough as renames. Detecting renamed files this way
198 199 can be expensive. After using this option, :hg:`status -C` can be
199 200 used to check which files were identified as moved or renamed. If
200 201 not specified, -s/--similarity defaults to 100 and only renames of
201 202 identical files are detected.
202 203
203 204 Returns 0 if all files are successfully added.
204 205 """
205 206 try:
206 207 sim = float(opts.get('similarity') or 100)
207 208 except ValueError:
208 209 raise util.Abort(_('similarity must be a number'))
209 210 if sim < 0 or sim > 100:
210 211 raise util.Abort(_('similarity must be between 0 and 100'))
211 212 return scmutil.addremove(repo, pats, opts, similarity=sim / 100.0)
212 213
213 214 @command('^annotate|blame',
214 215 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
215 216 ('', 'follow', None,
216 217 _('follow copies/renames and list the filename (DEPRECATED)')),
217 218 ('', 'no-follow', None, _("don't follow copies and renames")),
218 219 ('a', 'text', None, _('treat all files as text')),
219 220 ('u', 'user', None, _('list the author (long with -v)')),
220 221 ('f', 'file', None, _('list the filename')),
221 222 ('d', 'date', None, _('list the date (short with -q)')),
222 223 ('n', 'number', None, _('list the revision number (default)')),
223 224 ('c', 'changeset', None, _('list the changeset')),
224 225 ('l', 'line-number', None, _('show line number at the first appearance'))
225 226 ] + diffwsopts + walkopts,
226 227 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'))
227 228 def annotate(ui, repo, *pats, **opts):
228 229 """show changeset information by line for each file
229 230
230 231 List changes in files, showing the revision id responsible for
231 232 each line
232 233
233 234 This command is useful for discovering when a change was made and
234 235 by whom.
235 236
236 237 Without the -a/--text option, annotate will avoid processing files
237 238 it detects as binary. With -a, annotate will annotate the file
238 239 anyway, although the results will probably be neither useful
239 240 nor desirable.
240 241
241 242 Returns 0 on success.
242 243 """
243 244 if opts.get('follow'):
244 245 # --follow is deprecated and now just an alias for -f/--file
245 246 # to mimic the behavior of Mercurial before version 1.5
246 247 opts['file'] = True
247 248
248 249 datefunc = ui.quiet and util.shortdate or util.datestr
249 250 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
250 251
251 252 if not pats:
252 253 raise util.Abort(_('at least one filename or pattern is required'))
253 254
254 255 hexfn = ui.debugflag and hex or short
255 256
256 257 opmap = [('user', ' ', lambda x: ui.shortuser(x[0].user())),
257 258 ('number', ' ', lambda x: str(x[0].rev())),
258 259 ('changeset', ' ', lambda x: hexfn(x[0].node())),
259 260 ('date', ' ', getdate),
260 261 ('file', ' ', lambda x: x[0].path()),
261 262 ('line_number', ':', lambda x: str(x[1])),
262 263 ]
263 264
264 265 if (not opts.get('user') and not opts.get('changeset')
265 266 and not opts.get('date') and not opts.get('file')):
266 267 opts['number'] = True
267 268
268 269 linenumber = opts.get('line_number') is not None
269 270 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
270 271 raise util.Abort(_('at least one of -n/-c is required for -l'))
271 272
272 273 funcmap = [(func, sep) for op, sep, func in opmap if opts.get(op)]
273 274 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
274 275
275 276 def bad(x, y):
276 277 raise util.Abort("%s: %s" % (x, y))
277 278
278 279 ctx = scmutil.revsingle(repo, opts.get('rev'))
279 280 m = scmutil.match(ctx, pats, opts)
280 281 m.bad = bad
281 282 follow = not opts.get('no_follow')
282 283 diffopts = patch.diffopts(ui, opts, section='annotate')
283 284 for abs in ctx.walk(m):
284 285 fctx = ctx[abs]
285 286 if not opts.get('text') and util.binary(fctx.data()):
286 287 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
287 288 continue
288 289
289 290 lines = fctx.annotate(follow=follow, linenumber=linenumber,
290 291 diffopts=diffopts)
291 292 pieces = []
292 293
293 294 for f, sep in funcmap:
294 295 l = [f(n) for n, dummy in lines]
295 296 if l:
296 297 sized = [(x, encoding.colwidth(x)) for x in l]
297 298 ml = max([w for x, w in sized])
298 299 pieces.append(["%s%s%s" % (sep, ' ' * (ml - w), x)
299 300 for x, w in sized])
300 301
301 302 if pieces:
302 303 for p, l in zip(zip(*pieces), lines):
303 304 ui.write("%s: %s" % ("".join(p), l[1]))
304 305
305 306 if lines and not lines[-1][1].endswith('\n'):
306 307 ui.write('\n')
307 308
308 309 @command('archive',
309 310 [('', 'no-decode', None, _('do not pass files through decoders')),
310 311 ('p', 'prefix', '', _('directory prefix for files in archive'),
311 312 _('PREFIX')),
312 313 ('r', 'rev', '', _('revision to distribute'), _('REV')),
313 314 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
314 315 ] + subrepoopts + walkopts,
315 316 _('[OPTION]... DEST'))
316 317 def archive(ui, repo, dest, **opts):
317 318 '''create an unversioned archive of a repository revision
318 319
319 320 By default, the revision used is the parent of the working
320 321 directory; use -r/--rev to specify a different revision.
321 322
322 323 The archive type is automatically detected based on file
323 324 extension (or override using -t/--type).
324 325
325 326 .. container:: verbose
326 327
327 328 Examples:
328 329
329 330 - create a zip file containing the 1.0 release::
330 331
331 332 hg archive -r 1.0 project-1.0.zip
332 333
333 334 - create a tarball excluding .hg files::
334 335
335 336 hg archive project.tar.gz -X ".hg*"
336 337
337 338 Valid types are:
338 339
339 340 :``files``: a directory full of files (default)
340 341 :``tar``: tar archive, uncompressed
341 342 :``tbz2``: tar archive, compressed using bzip2
342 343 :``tgz``: tar archive, compressed using gzip
343 344 :``uzip``: zip archive, uncompressed
344 345 :``zip``: zip archive, compressed using deflate
345 346
346 347 The exact name of the destination archive or directory is given
347 348 using a format string; see :hg:`help export` for details.
348 349
349 350 Each member added to an archive file has a directory prefix
350 351 prepended. Use -p/--prefix to specify a format string for the
351 352 prefix. The default is the basename of the archive, with suffixes
352 353 removed.
353 354
354 355 Returns 0 on success.
355 356 '''
356 357
357 358 ctx = scmutil.revsingle(repo, opts.get('rev'))
358 359 if not ctx:
359 360 raise util.Abort(_('no working directory: please specify a revision'))
360 361 node = ctx.node()
361 362 dest = cmdutil.makefilename(repo, dest, node)
362 363 if os.path.realpath(dest) == repo.root:
363 364 raise util.Abort(_('repository root cannot be destination'))
364 365
365 366 kind = opts.get('type') or archival.guesskind(dest) or 'files'
366 367 prefix = opts.get('prefix')
367 368
368 369 if dest == '-':
369 370 if kind == 'files':
370 371 raise util.Abort(_('cannot archive plain files to stdout'))
371 372 dest = cmdutil.makefileobj(repo, dest)
372 373 if not prefix:
373 374 prefix = os.path.basename(repo.root) + '-%h'
374 375
375 376 prefix = cmdutil.makefilename(repo, prefix, node)
376 377 matchfn = scmutil.match(ctx, [], opts)
377 378 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
378 379 matchfn, prefix, subrepos=opts.get('subrepos'))
379 380
380 381 @command('backout',
381 382 [('', 'merge', None, _('merge with old dirstate parent after backout')),
382 383 ('', 'parent', '',
383 384 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
384 385 ('r', 'rev', '', _('revision to backout'), _('REV')),
385 386 ] + mergetoolopts + walkopts + commitopts + commitopts2,
386 387 _('[OPTION]... [-r] REV'))
387 388 def backout(ui, repo, node=None, rev=None, **opts):
388 389 '''reverse effect of earlier changeset
389 390
390 391 Prepare a new changeset with the effect of REV undone in the
391 392 current working directory.
392 393
393 394 If REV is the parent of the working directory, then this new changeset
394 395 is committed automatically. Otherwise, hg needs to merge the
395 396 changes and the merged result is left uncommitted.
396 397
397 398 .. note::
398 399 backout cannot be used to fix either an unwanted or
399 400 incorrect merge.
400 401
401 402 .. container:: verbose
402 403
403 404 By default, the pending changeset will have one parent,
404 405 maintaining a linear history. With --merge, the pending
405 406 changeset will instead have two parents: the old parent of the
406 407 working directory and a new child of REV that simply undoes REV.
407 408
408 409 Before version 1.7, the behavior without --merge was equivalent
409 410 to specifying --merge followed by :hg:`update --clean .` to
410 411 cancel the merge and leave the child of REV as a head to be
411 412 merged separately.
412 413
413 414 See :hg:`help dates` for a list of formats valid for -d/--date.
414 415
415 416 Returns 0 on success.
416 417 '''
417 418 if rev and node:
418 419 raise util.Abort(_("please specify just one revision"))
419 420
420 421 if not rev:
421 422 rev = node
422 423
423 424 if not rev:
424 425 raise util.Abort(_("please specify a revision to backout"))
425 426
426 427 date = opts.get('date')
427 428 if date:
428 429 opts['date'] = util.parsedate(date)
429 430
430 431 cmdutil.bailifchanged(repo)
431 432 node = scmutil.revsingle(repo, rev).node()
432 433
433 434 op1, op2 = repo.dirstate.parents()
434 435 a = repo.changelog.ancestor(op1, node)
435 436 if a != node:
436 437 raise util.Abort(_('cannot backout change on a different branch'))
437 438
438 439 p1, p2 = repo.changelog.parents(node)
439 440 if p1 == nullid:
440 441 raise util.Abort(_('cannot backout a change with no parents'))
441 442 if p2 != nullid:
442 443 if not opts.get('parent'):
443 444 raise util.Abort(_('cannot backout a merge changeset'))
444 445 p = repo.lookup(opts['parent'])
445 446 if p not in (p1, p2):
446 447 raise util.Abort(_('%s is not a parent of %s') %
447 448 (short(p), short(node)))
448 449 parent = p
449 450 else:
450 451 if opts.get('parent'):
451 452 raise util.Abort(_('cannot use --parent on non-merge changeset'))
452 453 parent = p1
453 454
454 455 # the backout should appear on the same branch
455 456 wlock = repo.wlock()
456 457 try:
457 458 branch = repo.dirstate.branch()
458 459 hg.clean(repo, node, show_stats=False)
459 460 repo.dirstate.setbranch(branch)
460 461 revert_opts = opts.copy()
461 462 revert_opts['date'] = None
462 463 revert_opts['all'] = True
463 464 revert_opts['rev'] = hex(parent)
464 465 revert_opts['no_backup'] = None
465 466 revert(ui, repo, **revert_opts)
466 467 if not opts.get('merge') and op1 != node:
467 468 try:
468 469 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
469 470 return hg.update(repo, op1)
470 471 finally:
471 472 ui.setconfig('ui', 'forcemerge', '')
472 473
473 474 commit_opts = opts.copy()
474 475 commit_opts['addremove'] = False
475 476 if not commit_opts['message'] and not commit_opts['logfile']:
476 477 # we don't translate commit messages
477 478 commit_opts['message'] = "Backed out changeset %s" % short(node)
478 479 commit_opts['force_editor'] = True
479 480 commit(ui, repo, **commit_opts)
480 481 def nice(node):
481 482 return '%d:%s' % (repo.changelog.rev(node), short(node))
482 483 ui.status(_('changeset %s backs out changeset %s\n') %
483 484 (nice(repo.changelog.tip()), nice(node)))
484 485 if opts.get('merge') and op1 != node:
485 486 hg.clean(repo, op1, show_stats=False)
486 487 ui.status(_('merging with changeset %s\n')
487 488 % nice(repo.changelog.tip()))
488 489 try:
489 490 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
490 491 return hg.merge(repo, hex(repo.changelog.tip()))
491 492 finally:
492 493 ui.setconfig('ui', 'forcemerge', '')
493 494 finally:
494 495 wlock.release()
495 496 return 0
496 497
497 498 @command('bisect',
498 499 [('r', 'reset', False, _('reset bisect state')),
499 500 ('g', 'good', False, _('mark changeset good')),
500 501 ('b', 'bad', False, _('mark changeset bad')),
501 502 ('s', 'skip', False, _('skip testing changeset')),
502 503 ('e', 'extend', False, _('extend the bisect range')),
503 504 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
504 505 ('U', 'noupdate', False, _('do not update to target'))],
505 506 _("[-gbsr] [-U] [-c CMD] [REV]"))
506 507 def bisect(ui, repo, rev=None, extra=None, command=None,
507 508 reset=None, good=None, bad=None, skip=None, extend=None,
508 509 noupdate=None):
509 510 """subdivision search of changesets
510 511
511 512 This command helps to find changesets which introduce problems. To
512 513 use, mark the earliest changeset you know exhibits the problem as
513 514 bad, then mark the latest changeset which is free from the problem
514 515 as good. Bisect will update your working directory to a revision
515 516 for testing (unless the -U/--noupdate option is specified). Once
516 517 you have performed tests, mark the working directory as good or
517 518 bad, and bisect will either update to another candidate changeset
518 519 or announce that it has found the bad revision.
519 520
520 521 As a shortcut, you can also use the revision argument to mark a
521 522 revision as good or bad without checking it out first.
522 523
523 524 If you supply a command, it will be used for automatic bisection.
524 525 The environment variable HG_NODE will contain the ID of the
525 526 changeset being tested. The exit status of the command will be
526 527 used to mark revisions as good or bad: status 0 means good, 125
527 528 means to skip the revision, 127 (command not found) will abort the
528 529 bisection, and any other non-zero exit status means the revision
529 530 is bad.
530 531
531 532 .. container:: verbose
532 533
533 534 Some examples:
534 535
535 536 - start a bisection with known bad revision 12, and good revision 34::
536 537
537 538 hg bisect --bad 34
538 539 hg bisect --good 12
539 540
540 541 - advance the current bisection by marking current revision as good or
541 542 bad::
542 543
543 544 hg bisect --good
544 545 hg bisect --bad
545 546
546 547 - mark the current revision, or a known revision, to be skipped (e.g. if
547 548 that revision is not usable because of another issue)::
548 549
549 550 hg bisect --skip
550 551 hg bisect --skip 23
551 552
552 553 - skip all revisions that do not touch directories ``foo`` or ``bar``
553 554
554 555 hg bisect --skip '!( file("path:foo") & file("path:bar") )'
555 556
556 557 - forget the current bisection::
557 558
558 559 hg bisect --reset
559 560
560 561 - use 'make && make tests' to automatically find the first broken
561 562 revision::
562 563
563 564 hg bisect --reset
564 565 hg bisect --bad 34
565 566 hg bisect --good 12
566 567 hg bisect --command 'make && make tests'
567 568
568 569 - see all changesets whose states are already known in the current
569 570 bisection::
570 571
571 572 hg log -r "bisect(pruned)"
572 573
573 574 - see the changeset currently being bisected (especially useful
574 575 if running with -U/--noupdate)::
575 576
576 577 hg log -r "bisect(current)"
577 578
578 579 - see all changesets that took part in the current bisection::
579 580
580 581 hg log -r "bisect(range)"
581 582
582 583 - with the graphlog extension, you can even get a nice graph::
583 584
584 585 hg log --graph -r "bisect(range)"
585 586
586 587 See :hg:`help revsets` for more about the `bisect()` keyword.
587 588
588 589 Returns 0 on success.
589 590 """
590 591 def extendbisectrange(nodes, good):
591 592 # bisect is incomplete when it ends on a merge node and
592 593 # one of the parent was not checked.
593 594 parents = repo[nodes[0]].parents()
594 595 if len(parents) > 1:
595 596 side = good and state['bad'] or state['good']
596 597 num = len(set(i.node() for i in parents) & set(side))
597 598 if num == 1:
598 599 return parents[0].ancestor(parents[1])
599 600 return None
600 601
601 602 def print_result(nodes, good):
602 603 displayer = cmdutil.show_changeset(ui, repo, {})
603 604 if len(nodes) == 1:
604 605 # narrowed it down to a single revision
605 606 if good:
606 607 ui.write(_("The first good revision is:\n"))
607 608 else:
608 609 ui.write(_("The first bad revision is:\n"))
609 610 displayer.show(repo[nodes[0]])
610 611 extendnode = extendbisectrange(nodes, good)
611 612 if extendnode is not None:
612 613 ui.write(_('Not all ancestors of this changeset have been'
613 614 ' checked.\nUse bisect --extend to continue the '
614 615 'bisection from\nthe common ancestor, %s.\n')
615 616 % extendnode)
616 617 else:
617 618 # multiple possible revisions
618 619 if good:
619 620 ui.write(_("Due to skipped revisions, the first "
620 621 "good revision could be any of:\n"))
621 622 else:
622 623 ui.write(_("Due to skipped revisions, the first "
623 624 "bad revision could be any of:\n"))
624 625 for n in nodes:
625 626 displayer.show(repo[n])
626 627 displayer.close()
627 628
628 629 def check_state(state, interactive=True):
629 630 if not state['good'] or not state['bad']:
630 631 if (good or bad or skip or reset) and interactive:
631 632 return
632 633 if not state['good']:
633 634 raise util.Abort(_('cannot bisect (no known good revisions)'))
634 635 else:
635 636 raise util.Abort(_('cannot bisect (no known bad revisions)'))
636 637 return True
637 638
638 639 # backward compatibility
639 640 if rev in "good bad reset init".split():
640 641 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
641 642 cmd, rev, extra = rev, extra, None
642 643 if cmd == "good":
643 644 good = True
644 645 elif cmd == "bad":
645 646 bad = True
646 647 else:
647 648 reset = True
648 649 elif extra or good + bad + skip + reset + extend + bool(command) > 1:
649 650 raise util.Abort(_('incompatible arguments'))
650 651
651 652 if reset:
652 653 p = repo.join("bisect.state")
653 654 if os.path.exists(p):
654 655 os.unlink(p)
655 656 return
656 657
657 658 state = hbisect.load_state(repo)
658 659
659 660 if command:
660 661 changesets = 1
661 662 try:
662 663 node = state['current'][0]
663 664 except LookupError:
664 665 if noupdate:
665 666 raise util.Abort(_('current bisect revision is unknown - '
666 667 'start a new bisect to fix'))
667 668 node, p2 = repo.dirstate.parents()
668 669 if p2 != nullid:
669 670 raise util.Abort(_('current bisect revision is a merge'))
670 671 try:
671 672 while changesets:
672 673 # update state
673 674 state['current'] = [node]
674 675 hbisect.save_state(repo, state)
675 676 status = util.system(command,
676 677 environ={'HG_NODE': hex(node)},
677 678 out=ui.fout)
678 679 if status == 125:
679 680 transition = "skip"
680 681 elif status == 0:
681 682 transition = "good"
682 683 # status < 0 means process was killed
683 684 elif status == 127:
684 685 raise util.Abort(_("failed to execute %s") % command)
685 686 elif status < 0:
686 687 raise util.Abort(_("%s killed") % command)
687 688 else:
688 689 transition = "bad"
689 690 ctx = scmutil.revsingle(repo, rev, node)
690 691 rev = None # clear for future iterations
691 692 state[transition].append(ctx.node())
692 693 ui.status(_('changeset %d:%s: %s\n') % (ctx, ctx, transition))
693 694 check_state(state, interactive=False)
694 695 # bisect
695 696 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
696 697 # update to next check
697 698 node = nodes[0]
698 699 if not noupdate:
699 700 cmdutil.bailifchanged(repo)
700 701 hg.clean(repo, node, show_stats=False)
701 702 finally:
702 703 state['current'] = [node]
703 704 hbisect.save_state(repo, state)
704 705 print_result(nodes, good)
705 706 return
706 707
707 708 # update state
708 709
709 710 if rev:
710 711 nodes = [repo.lookup(i) for i in scmutil.revrange(repo, [rev])]
711 712 else:
712 713 nodes = [repo.lookup('.')]
713 714
714 715 if good or bad or skip:
715 716 if good:
716 717 state['good'] += nodes
717 718 elif bad:
718 719 state['bad'] += nodes
719 720 elif skip:
720 721 state['skip'] += nodes
721 722 hbisect.save_state(repo, state)
722 723
723 724 if not check_state(state):
724 725 return
725 726
726 727 # actually bisect
727 728 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
728 729 if extend:
729 730 if not changesets:
730 731 extendnode = extendbisectrange(nodes, good)
731 732 if extendnode is not None:
732 733 ui.write(_("Extending search to changeset %d:%s\n"
733 734 % (extendnode.rev(), extendnode)))
734 735 state['current'] = [extendnode.node()]
735 736 hbisect.save_state(repo, state)
736 737 if noupdate:
737 738 return
738 739 cmdutil.bailifchanged(repo)
739 740 return hg.clean(repo, extendnode.node())
740 741 raise util.Abort(_("nothing to extend"))
741 742
742 743 if changesets == 0:
743 744 print_result(nodes, good)
744 745 else:
745 746 assert len(nodes) == 1 # only a single node can be tested next
746 747 node = nodes[0]
747 748 # compute the approximate number of remaining tests
748 749 tests, size = 0, 2
749 750 while size <= changesets:
750 751 tests, size = tests + 1, size * 2
751 752 rev = repo.changelog.rev(node)
752 753 ui.write(_("Testing changeset %d:%s "
753 754 "(%d changesets remaining, ~%d tests)\n")
754 755 % (rev, short(node), changesets, tests))
755 756 state['current'] = [node]
756 757 hbisect.save_state(repo, state)
757 758 if not noupdate:
758 759 cmdutil.bailifchanged(repo)
759 760 return hg.clean(repo, node)
760 761
761 762 @command('bookmarks|bookmark',
762 763 [('f', 'force', False, _('force')),
763 764 ('r', 'rev', '', _('revision'), _('REV')),
764 765 ('d', 'delete', False, _('delete a given bookmark')),
765 766 ('m', 'rename', '', _('rename a given bookmark'), _('NAME')),
766 767 ('i', 'inactive', False, _('mark a bookmark inactive'))],
767 768 _('hg bookmarks [-f] [-d] [-i] [-m NAME] [-r REV] [NAME]'))
768 769 def bookmark(ui, repo, mark=None, rev=None, force=False, delete=False,
769 770 rename=None, inactive=False):
770 771 '''track a line of development with movable markers
771 772
772 773 Bookmarks are pointers to certain commits that move when committing.
773 774 Bookmarks are local. They can be renamed, copied and deleted. It is
774 775 possible to use :hg:`merge NAME` to merge from a given bookmark, and
775 776 :hg:`update NAME` to update to a given bookmark.
776 777
777 778 You can use :hg:`bookmark NAME` to set a bookmark on the working
778 779 directory's parent revision with the given name. If you specify
779 780 a revision using -r REV (where REV may be an existing bookmark),
780 781 the bookmark is assigned to that revision.
781 782
782 783 Bookmarks can be pushed and pulled between repositories (see :hg:`help
783 784 push` and :hg:`help pull`). This requires both the local and remote
784 785 repositories to support bookmarks. For versions prior to 1.8, this means
785 786 the bookmarks extension must be enabled.
786 787
787 788 With -i/--inactive, the new bookmark will not be made the active
788 789 bookmark. If -r/--rev is given, the new bookmark will not be made
789 790 active even if -i/--inactive is not given. If no NAME is given, the
790 791 current active bookmark will be marked inactive.
791 792 '''
792 793 hexfn = ui.debugflag and hex or short
793 794 marks = repo._bookmarks
794 795 cur = repo.changectx('.').node()
795 796
796 797 def checkformat(mark):
797 798 mark = mark.strip()
798 799 if not mark:
799 800 raise util.Abort(_("bookmark names cannot consist entirely of "
800 801 "whitespace"))
801 802 scmutil.checknewlabel(repo, mark, 'bookmark')
802 803 return mark
803 804
804 805 def checkconflict(repo, mark, force=False):
805 806 if mark in marks and not force:
806 807 raise util.Abort(_("bookmark '%s' already exists "
807 808 "(use -f to force)") % mark)
808 809 if ((mark in repo.branchmap() or mark == repo.dirstate.branch())
809 810 and not force):
810 811 raise util.Abort(
811 812 _("a bookmark cannot have the name of an existing branch"))
812 813
813 814 if delete and rename:
814 815 raise util.Abort(_("--delete and --rename are incompatible"))
815 816 if delete and rev:
816 817 raise util.Abort(_("--rev is incompatible with --delete"))
817 818 if rename and rev:
818 819 raise util.Abort(_("--rev is incompatible with --rename"))
819 820 if mark is None and (delete or rev):
820 821 raise util.Abort(_("bookmark name required"))
821 822
822 823 if delete:
823 824 if mark not in marks:
824 825 raise util.Abort(_("bookmark '%s' does not exist") % mark)
825 826 if mark == repo._bookmarkcurrent:
826 827 bookmarks.setcurrent(repo, None)
827 828 del marks[mark]
828 829 marks.write()
829 830
830 831 elif rename:
831 832 if mark is None:
832 833 raise util.Abort(_("new bookmark name required"))
833 834 mark = checkformat(mark)
834 835 if rename not in marks:
835 836 raise util.Abort(_("bookmark '%s' does not exist") % rename)
836 837 checkconflict(repo, mark, force)
837 838 marks[mark] = marks[rename]
838 839 if repo._bookmarkcurrent == rename and not inactive:
839 840 bookmarks.setcurrent(repo, mark)
840 841 del marks[rename]
841 842 marks.write()
842 843
843 844 elif mark is not None:
844 845 mark = checkformat(mark)
845 846 if inactive and mark == repo._bookmarkcurrent:
846 847 bookmarks.setcurrent(repo, None)
847 848 return
848 849 checkconflict(repo, mark, force)
849 850 if rev:
850 851 marks[mark] = scmutil.revsingle(repo, rev).node()
851 852 else:
852 853 marks[mark] = cur
853 854 if not inactive and cur == marks[mark]:
854 855 bookmarks.setcurrent(repo, mark)
855 856 marks.write()
856 857
857 858 # Same message whether trying to deactivate the current bookmark (-i
858 859 # with no NAME) or listing bookmarks
859 860 elif len(marks) == 0:
860 861 ui.status(_("no bookmarks set\n"))
861 862
862 863 elif inactive:
863 864 if not repo._bookmarkcurrent:
864 865 ui.status(_("no active bookmark\n"))
865 866 else:
866 867 bookmarks.setcurrent(repo, None)
867 868
868 869 else: # show bookmarks
869 870 for bmark, n in sorted(marks.iteritems()):
870 871 current = repo._bookmarkcurrent
871 872 if bmark == current and n == cur:
872 873 prefix, label = '*', 'bookmarks.current'
873 874 else:
874 875 prefix, label = ' ', ''
875 876
876 877 if ui.quiet:
877 878 ui.write("%s\n" % bmark, label=label)
878 879 else:
879 880 ui.write(" %s %-25s %d:%s\n" % (
880 881 prefix, bmark, repo.changelog.rev(n), hexfn(n)),
881 882 label=label)
882 883
883 884 @command('branch',
884 885 [('f', 'force', None,
885 886 _('set branch name even if it shadows an existing branch')),
886 887 ('C', 'clean', None, _('reset branch name to parent branch name'))],
887 888 _('[-fC] [NAME]'))
888 889 def branch(ui, repo, label=None, **opts):
889 890 """set or show the current branch name
890 891
891 892 .. note::
892 893 Branch names are permanent and global. Use :hg:`bookmark` to create a
893 894 light-weight bookmark instead. See :hg:`help glossary` for more
894 895 information about named branches and bookmarks.
895 896
896 897 With no argument, show the current branch name. With one argument,
897 898 set the working directory branch name (the branch will not exist
898 899 in the repository until the next commit). Standard practice
899 900 recommends that primary development take place on the 'default'
900 901 branch.
901 902
902 903 Unless -f/--force is specified, branch will not let you set a
903 904 branch name that already exists, even if it's inactive.
904 905
905 906 Use -C/--clean to reset the working directory branch to that of
906 907 the parent of the working directory, negating a previous branch
907 908 change.
908 909
909 910 Use the command :hg:`update` to switch to an existing branch. Use
910 911 :hg:`commit --close-branch` to mark this branch as closed.
911 912
912 913 Returns 0 on success.
913 914 """
914 915 if not opts.get('clean') and not label:
915 916 ui.write("%s\n" % repo.dirstate.branch())
916 917 return
917 918
918 919 wlock = repo.wlock()
919 920 try:
920 921 if opts.get('clean'):
921 922 label = repo[None].p1().branch()
922 923 repo.dirstate.setbranch(label)
923 924 ui.status(_('reset working directory to branch %s\n') % label)
924 925 elif label:
925 926 if not opts.get('force') and label in repo.branchmap():
926 927 if label not in [p.branch() for p in repo.parents()]:
927 928 raise util.Abort(_('a branch of the same name already'
928 929 ' exists'),
929 930 # i18n: "it" refers to an existing branch
930 931 hint=_("use 'hg update' to switch to it"))
931 932 scmutil.checknewlabel(repo, label, 'branch')
932 933 repo.dirstate.setbranch(label)
933 934 ui.status(_('marked working directory as branch %s\n') % label)
934 935 ui.status(_('(branches are permanent and global, '
935 936 'did you want a bookmark?)\n'))
936 937 finally:
937 938 wlock.release()
938 939
939 940 @command('branches',
940 941 [('a', 'active', False, _('show only branches that have unmerged heads')),
941 942 ('c', 'closed', False, _('show normal and closed branches'))],
942 943 _('[-ac]'))
943 944 def branches(ui, repo, active=False, closed=False):
944 945 """list repository named branches
945 946
946 947 List the repository's named branches, indicating which ones are
947 948 inactive. If -c/--closed is specified, also list branches which have
948 949 been marked closed (see :hg:`commit --close-branch`).
949 950
950 951 If -a/--active is specified, only show active branches. A branch
951 952 is considered active if it contains repository heads.
952 953
953 954 Use the command :hg:`update` to switch to an existing branch.
954 955
955 956 Returns 0.
956 957 """
957 958
958 959 hexfunc = ui.debugflag and hex or short
959 960
960 961 activebranches = set([repo[n].branch() for n in repo.heads()])
961 962 branches = []
962 963 for tag, heads in repo.branchmap().iteritems():
963 964 for h in reversed(heads):
964 965 ctx = repo[h]
965 966 isopen = not ctx.closesbranch()
966 967 if isopen:
967 968 tip = ctx
968 969 break
969 970 else:
970 971 tip = repo[heads[-1]]
971 972 isactive = tag in activebranches and isopen
972 973 branches.append((tip, isactive, isopen))
973 974 branches.sort(key=lambda i: (i[1], i[0].rev(), i[0].branch(), i[2]),
974 975 reverse=True)
975 976
976 977 for ctx, isactive, isopen in branches:
977 978 if (not active) or isactive:
978 979 if isactive:
979 980 label = 'branches.active'
980 981 notice = ''
981 982 elif not isopen:
982 983 if not closed:
983 984 continue
984 985 label = 'branches.closed'
985 986 notice = _(' (closed)')
986 987 else:
987 988 label = 'branches.inactive'
988 989 notice = _(' (inactive)')
989 990 if ctx.branch() == repo.dirstate.branch():
990 991 label = 'branches.current'
991 992 rev = str(ctx.rev()).rjust(31 - encoding.colwidth(ctx.branch()))
992 993 rev = ui.label('%s:%s' % (rev, hexfunc(ctx.node())),
993 994 'log.changeset changeset.%s' % ctx.phasestr())
994 995 tag = ui.label(ctx.branch(), label)
995 996 if ui.quiet:
996 997 ui.write("%s\n" % tag)
997 998 else:
998 999 ui.write("%s %s%s\n" % (tag, rev, notice))
999 1000
1000 1001 @command('bundle',
1001 1002 [('f', 'force', None, _('run even when the destination is unrelated')),
1002 1003 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
1003 1004 _('REV')),
1004 1005 ('b', 'branch', [], _('a specific branch you would like to bundle'),
1005 1006 _('BRANCH')),
1006 1007 ('', 'base', [],
1007 1008 _('a base changeset assumed to be available at the destination'),
1008 1009 _('REV')),
1009 1010 ('a', 'all', None, _('bundle all changesets in the repository')),
1010 1011 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
1011 1012 ] + remoteopts,
1012 1013 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]'))
1013 1014 def bundle(ui, repo, fname, dest=None, **opts):
1014 1015 """create a changegroup file
1015 1016
1016 1017 Generate a compressed changegroup file collecting changesets not
1017 1018 known to be in another repository.
1018 1019
1019 1020 If you omit the destination repository, then hg assumes the
1020 1021 destination will have all the nodes you specify with --base
1021 1022 parameters. To create a bundle containing all changesets, use
1022 1023 -a/--all (or --base null).
1023 1024
1024 1025 You can change compression method with the -t/--type option.
1025 1026 The available compression methods are: none, bzip2, and
1026 1027 gzip (by default, bundles are compressed using bzip2).
1027 1028
1028 1029 The bundle file can then be transferred using conventional means
1029 1030 and applied to another repository with the unbundle or pull
1030 1031 command. This is useful when direct push and pull are not
1031 1032 available or when exporting an entire repository is undesirable.
1032 1033
1033 1034 Applying bundles preserves all changeset contents including
1034 1035 permissions, copy/rename information, and revision history.
1035 1036
1036 1037 Returns 0 on success, 1 if no changes found.
1037 1038 """
1038 1039 revs = None
1039 1040 if 'rev' in opts:
1040 1041 revs = scmutil.revrange(repo, opts['rev'])
1041 1042
1042 1043 bundletype = opts.get('type', 'bzip2').lower()
1043 1044 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
1044 1045 bundletype = btypes.get(bundletype)
1045 1046 if bundletype not in changegroup.bundletypes:
1046 1047 raise util.Abort(_('unknown bundle type specified with --type'))
1047 1048
1048 1049 if opts.get('all'):
1049 1050 base = ['null']
1050 1051 else:
1051 1052 base = scmutil.revrange(repo, opts.get('base'))
1052 1053 if base:
1053 1054 if dest:
1054 1055 raise util.Abort(_("--base is incompatible with specifying "
1055 1056 "a destination"))
1056 1057 common = [repo.lookup(rev) for rev in base]
1057 1058 heads = revs and map(repo.lookup, revs) or revs
1058 1059 cg = repo.getbundle('bundle', heads=heads, common=common)
1059 1060 outgoing = None
1060 1061 else:
1061 1062 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1062 1063 dest, branches = hg.parseurl(dest, opts.get('branch'))
1063 1064 other = hg.peer(repo, opts, dest)
1064 1065 revs, checkout = hg.addbranchrevs(repo, other, branches, revs)
1065 1066 heads = revs and map(repo.lookup, revs) or revs
1066 1067 outgoing = discovery.findcommonoutgoing(repo, other,
1067 1068 onlyheads=heads,
1068 1069 force=opts.get('force'),
1069 1070 portable=True)
1070 1071 cg = repo.getlocalbundle('bundle', outgoing)
1071 1072 if not cg:
1072 1073 scmutil.nochangesfound(ui, repo, outgoing and outgoing.excluded)
1073 1074 return 1
1074 1075
1075 1076 changegroup.writebundle(cg, fname, bundletype)
1076 1077
1077 1078 @command('cat',
1078 1079 [('o', 'output', '',
1079 1080 _('print output to file with formatted name'), _('FORMAT')),
1080 1081 ('r', 'rev', '', _('print the given revision'), _('REV')),
1081 1082 ('', 'decode', None, _('apply any matching decode filter')),
1082 1083 ] + walkopts,
1083 1084 _('[OPTION]... FILE...'))
1084 1085 def cat(ui, repo, file1, *pats, **opts):
1085 1086 """output the current or given revision of files
1086 1087
1087 1088 Print the specified files as they were at the given revision. If
1088 1089 no revision is given, the parent of the working directory is used,
1089 1090 or tip if no revision is checked out.
1090 1091
1091 1092 Output may be to a file, in which case the name of the file is
1092 1093 given using a format string. The formatting rules are the same as
1093 1094 for the export command, with the following additions:
1094 1095
1095 1096 :``%s``: basename of file being printed
1096 1097 :``%d``: dirname of file being printed, or '.' if in repository root
1097 1098 :``%p``: root-relative path name of file being printed
1098 1099
1099 1100 Returns 0 on success.
1100 1101 """
1101 1102 ctx = scmutil.revsingle(repo, opts.get('rev'))
1102 1103 err = 1
1103 1104 m = scmutil.match(ctx, (file1,) + pats, opts)
1104 1105 for abs in ctx.walk(m):
1105 1106 fp = cmdutil.makefileobj(repo, opts.get('output'), ctx.node(),
1106 1107 pathname=abs)
1107 1108 data = ctx[abs].data()
1108 1109 if opts.get('decode'):
1109 1110 data = repo.wwritedata(abs, data)
1110 1111 fp.write(data)
1111 1112 fp.close()
1112 1113 err = 0
1113 1114 return err
1114 1115
1115 1116 @command('^clone',
1116 1117 [('U', 'noupdate', None,
1117 1118 _('the clone will include an empty working copy (only a repository)')),
1118 1119 ('u', 'updaterev', '', _('revision, tag or branch to check out'), _('REV')),
1119 1120 ('r', 'rev', [], _('include the specified changeset'), _('REV')),
1120 1121 ('b', 'branch', [], _('clone only the specified branch'), _('BRANCH')),
1121 1122 ('', 'pull', None, _('use pull protocol to copy metadata')),
1122 1123 ('', 'uncompressed', None, _('use uncompressed transfer (fast over LAN)')),
1123 1124 ] + remoteopts,
1124 1125 _('[OPTION]... SOURCE [DEST]'))
1125 1126 def clone(ui, source, dest=None, **opts):
1126 1127 """make a copy of an existing repository
1127 1128
1128 1129 Create a copy of an existing repository in a new directory.
1129 1130
1130 1131 If no destination directory name is specified, it defaults to the
1131 1132 basename of the source.
1132 1133
1133 1134 The location of the source is added to the new repository's
1134 1135 ``.hg/hgrc`` file, as the default to be used for future pulls.
1135 1136
1136 1137 Only local paths and ``ssh://`` URLs are supported as
1137 1138 destinations. For ``ssh://`` destinations, no working directory or
1138 1139 ``.hg/hgrc`` will be created on the remote side.
1139 1140
1140 1141 To pull only a subset of changesets, specify one or more revisions
1141 1142 identifiers with -r/--rev or branches with -b/--branch. The
1142 1143 resulting clone will contain only the specified changesets and
1143 1144 their ancestors. These options (or 'clone src#rev dest') imply
1144 1145 --pull, even for local source repositories. Note that specifying a
1145 1146 tag will include the tagged changeset but not the changeset
1146 1147 containing the tag.
1147 1148
1148 1149 To check out a particular version, use -u/--update, or
1149 1150 -U/--noupdate to create a clone with no working directory.
1150 1151
1151 1152 .. container:: verbose
1152 1153
1153 1154 For efficiency, hardlinks are used for cloning whenever the
1154 1155 source and destination are on the same filesystem (note this
1155 1156 applies only to the repository data, not to the working
1156 1157 directory). Some filesystems, such as AFS, implement hardlinking
1157 1158 incorrectly, but do not report errors. In these cases, use the
1158 1159 --pull option to avoid hardlinking.
1159 1160
1160 1161 In some cases, you can clone repositories and the working
1161 1162 directory using full hardlinks with ::
1162 1163
1163 1164 $ cp -al REPO REPOCLONE
1164 1165
1165 1166 This is the fastest way to clone, but it is not always safe. The
1166 1167 operation is not atomic (making sure REPO is not modified during
1167 1168 the operation is up to you) and you have to make sure your
1168 1169 editor breaks hardlinks (Emacs and most Linux Kernel tools do
1169 1170 so). Also, this is not compatible with certain extensions that
1170 1171 place their metadata under the .hg directory, such as mq.
1171 1172
1172 1173 Mercurial will update the working directory to the first applicable
1173 1174 revision from this list:
1174 1175
1175 1176 a) null if -U or the source repository has no changesets
1176 1177 b) if -u . and the source repository is local, the first parent of
1177 1178 the source repository's working directory
1178 1179 c) the changeset specified with -u (if a branch name, this means the
1179 1180 latest head of that branch)
1180 1181 d) the changeset specified with -r
1181 1182 e) the tipmost head specified with -b
1182 1183 f) the tipmost head specified with the url#branch source syntax
1183 1184 g) the tipmost head of the default branch
1184 1185 h) tip
1185 1186
1186 1187 Examples:
1187 1188
1188 1189 - clone a remote repository to a new directory named hg/::
1189 1190
1190 1191 hg clone http://selenic.com/hg
1191 1192
1192 1193 - create a lightweight local clone::
1193 1194
1194 1195 hg clone project/ project-feature/
1195 1196
1196 1197 - clone from an absolute path on an ssh server (note double-slash)::
1197 1198
1198 1199 hg clone ssh://user@server//home/projects/alpha/
1199 1200
1200 1201 - do a high-speed clone over a LAN while checking out a
1201 1202 specified version::
1202 1203
1203 1204 hg clone --uncompressed http://server/repo -u 1.5
1204 1205
1205 1206 - create a repository without changesets after a particular revision::
1206 1207
1207 1208 hg clone -r 04e544 experimental/ good/
1208 1209
1209 1210 - clone (and track) a particular named branch::
1210 1211
1211 1212 hg clone http://selenic.com/hg#stable
1212 1213
1213 1214 See :hg:`help urls` for details on specifying URLs.
1214 1215
1215 1216 Returns 0 on success.
1216 1217 """
1217 1218 if opts.get('noupdate') and opts.get('updaterev'):
1218 1219 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
1219 1220
1220 1221 r = hg.clone(ui, opts, source, dest,
1221 1222 pull=opts.get('pull'),
1222 1223 stream=opts.get('uncompressed'),
1223 1224 rev=opts.get('rev'),
1224 1225 update=opts.get('updaterev') or not opts.get('noupdate'),
1225 1226 branch=opts.get('branch'))
1226 1227
1227 1228 return r is None
1228 1229
1229 1230 @command('^commit|ci',
1230 1231 [('A', 'addremove', None,
1231 1232 _('mark new/missing files as added/removed before committing')),
1232 1233 ('', 'close-branch', None,
1233 1234 _('mark a branch as closed, hiding it from the branch list')),
1234 1235 ('', 'amend', None, _('amend the parent of the working dir')),
1235 1236 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1236 1237 _('[OPTION]... [FILE]...'))
1237 1238 def commit(ui, repo, *pats, **opts):
1238 1239 """commit the specified files or all outstanding changes
1239 1240
1240 1241 Commit changes to the given files into the repository. Unlike a
1241 1242 centralized SCM, this operation is a local operation. See
1242 1243 :hg:`push` for a way to actively distribute your changes.
1243 1244
1244 1245 If a list of files is omitted, all changes reported by :hg:`status`
1245 1246 will be committed.
1246 1247
1247 1248 If you are committing the result of a merge, do not provide any
1248 1249 filenames or -I/-X filters.
1249 1250
1250 1251 If no commit message is specified, Mercurial starts your
1251 1252 configured editor where you can enter a message. In case your
1252 1253 commit fails, you will find a backup of your message in
1253 1254 ``.hg/last-message.txt``.
1254 1255
1255 1256 The --amend flag can be used to amend the parent of the
1256 1257 working directory with a new commit that contains the changes
1257 1258 in the parent in addition to those currently reported by :hg:`status`,
1258 1259 if there are any. The old commit is stored in a backup bundle in
1259 1260 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1260 1261 on how to restore it).
1261 1262
1262 1263 Message, user and date are taken from the amended commit unless
1263 1264 specified. When a message isn't specified on the command line,
1264 1265 the editor will open with the message of the amended commit.
1265 1266
1266 1267 It is not possible to amend public changesets (see :hg:`help phases`)
1267 1268 or changesets that have children.
1268 1269
1269 1270 See :hg:`help dates` for a list of formats valid for -d/--date.
1270 1271
1271 1272 Returns 0 on success, 1 if nothing changed.
1272 1273 """
1273 1274 if opts.get('subrepos'):
1274 1275 # Let --subrepos on the command line override config setting.
1275 1276 ui.setconfig('ui', 'commitsubrepos', True)
1276 1277
1277 1278 extra = {}
1278 1279 if opts.get('close_branch'):
1279 1280 if repo['.'].node() not in repo.branchheads():
1280 1281 # The topo heads set is included in the branch heads set of the
1281 1282 # current branch, so it's sufficient to test branchheads
1282 1283 raise util.Abort(_('can only close branch heads'))
1283 1284 extra['close'] = 1
1284 1285
1285 1286 branch = repo[None].branch()
1286 1287 bheads = repo.branchheads(branch)
1287 1288
1288 1289 if opts.get('amend'):
1289 1290 if ui.configbool('ui', 'commitsubrepos'):
1290 1291 raise util.Abort(_('cannot amend recursively'))
1291 1292
1292 1293 old = repo['.']
1293 1294 if old.phase() == phases.public:
1294 1295 raise util.Abort(_('cannot amend public changesets'))
1295 1296 if len(old.parents()) > 1:
1296 1297 raise util.Abort(_('cannot amend merge changesets'))
1297 1298 if len(repo[None].parents()) > 1:
1298 1299 raise util.Abort(_('cannot amend while merging'))
1299 1300 if (not obsolete._enabled) and old.children():
1300 1301 raise util.Abort(_('cannot amend changeset with children'))
1301 1302
1302 1303 e = cmdutil.commiteditor
1303 1304 if opts.get('force_editor'):
1304 1305 e = cmdutil.commitforceeditor
1305 1306
1306 1307 def commitfunc(ui, repo, message, match, opts):
1307 1308 editor = e
1308 1309 # message contains text from -m or -l, if it's empty,
1309 1310 # open the editor with the old message
1310 1311 if not message:
1311 1312 message = old.description()
1312 1313 editor = cmdutil.commitforceeditor
1313 1314 return repo.commit(message,
1314 1315 opts.get('user') or old.user(),
1315 1316 opts.get('date') or old.date(),
1316 1317 match,
1317 1318 editor=editor,
1318 1319 extra=extra)
1319 1320
1320 1321 current = repo._bookmarkcurrent
1321 1322 marks = old.bookmarks()
1322 1323 node = cmdutil.amend(ui, repo, commitfunc, old, extra, pats, opts)
1323 1324 if node == old.node():
1324 1325 ui.status(_("nothing changed\n"))
1325 1326 return 1
1326 1327 elif marks:
1327 1328 ui.debug('moving bookmarks %r from %s to %s\n' %
1328 1329 (marks, old.hex(), hex(node)))
1329 1330 newmarks = repo._bookmarks
1330 1331 for bm in marks:
1331 1332 newmarks[bm] = node
1332 1333 if bm == current:
1333 1334 bookmarks.setcurrent(repo, bm)
1334 1335 newmarks.write()
1335 1336 else:
1336 1337 e = cmdutil.commiteditor
1337 1338 if opts.get('force_editor'):
1338 1339 e = cmdutil.commitforceeditor
1339 1340
1340 1341 def commitfunc(ui, repo, message, match, opts):
1341 1342 return repo.commit(message, opts.get('user'), opts.get('date'),
1342 1343 match, editor=e, extra=extra)
1343 1344
1344 1345 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1345 1346
1346 1347 if not node:
1347 1348 stat = repo.status(match=scmutil.match(repo[None], pats, opts))
1348 1349 if stat[3]:
1349 1350 ui.status(_("nothing changed (%d missing files, see "
1350 1351 "'hg status')\n") % len(stat[3]))
1351 1352 else:
1352 1353 ui.status(_("nothing changed\n"))
1353 1354 return 1
1354 1355
1355 1356 ctx = repo[node]
1356 1357 parents = ctx.parents()
1357 1358
1358 1359 if (not opts.get('amend') and bheads and node not in bheads and not
1359 1360 [x for x in parents if x.node() in bheads and x.branch() == branch]):
1360 1361 ui.status(_('created new head\n'))
1361 1362 # The message is not printed for initial roots. For the other
1362 1363 # changesets, it is printed in the following situations:
1363 1364 #
1364 1365 # Par column: for the 2 parents with ...
1365 1366 # N: null or no parent
1366 1367 # B: parent is on another named branch
1367 1368 # C: parent is a regular non head changeset
1368 1369 # H: parent was a branch head of the current branch
1369 1370 # Msg column: whether we print "created new head" message
1370 1371 # In the following, it is assumed that there already exists some
1371 1372 # initial branch heads of the current branch, otherwise nothing is
1372 1373 # printed anyway.
1373 1374 #
1374 1375 # Par Msg Comment
1375 1376 # N N y additional topo root
1376 1377 #
1377 1378 # B N y additional branch root
1378 1379 # C N y additional topo head
1379 1380 # H N n usual case
1380 1381 #
1381 1382 # B B y weird additional branch root
1382 1383 # C B y branch merge
1383 1384 # H B n merge with named branch
1384 1385 #
1385 1386 # C C y additional head from merge
1386 1387 # C H n merge with a head
1387 1388 #
1388 1389 # H H n head merge: head count decreases
1389 1390
1390 1391 if not opts.get('close_branch'):
1391 1392 for r in parents:
1392 1393 if r.closesbranch() and r.branch() == branch:
1393 1394 ui.status(_('reopening closed branch head %d\n') % r)
1394 1395
1395 1396 if ui.debugflag:
1396 1397 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
1397 1398 elif ui.verbose:
1398 1399 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
1399 1400
1400 1401 @command('copy|cp',
1401 1402 [('A', 'after', None, _('record a copy that has already occurred')),
1402 1403 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1403 1404 ] + walkopts + dryrunopts,
1404 1405 _('[OPTION]... [SOURCE]... DEST'))
1405 1406 def copy(ui, repo, *pats, **opts):
1406 1407 """mark files as copied for the next commit
1407 1408
1408 1409 Mark dest as having copies of source files. If dest is a
1409 1410 directory, copies are put in that directory. If dest is a file,
1410 1411 the source must be a single file.
1411 1412
1412 1413 By default, this command copies the contents of files as they
1413 1414 exist in the working directory. If invoked with -A/--after, the
1414 1415 operation is recorded, but no copying is performed.
1415 1416
1416 1417 This command takes effect with the next commit. To undo a copy
1417 1418 before that, see :hg:`revert`.
1418 1419
1419 1420 Returns 0 on success, 1 if errors are encountered.
1420 1421 """
1421 1422 wlock = repo.wlock(False)
1422 1423 try:
1423 1424 return cmdutil.copy(ui, repo, pats, opts)
1424 1425 finally:
1425 1426 wlock.release()
1426 1427
1427 1428 @command('debugancestor', [], _('[INDEX] REV1 REV2'))
1428 1429 def debugancestor(ui, repo, *args):
1429 1430 """find the ancestor revision of two revisions in a given index"""
1430 1431 if len(args) == 3:
1431 1432 index, rev1, rev2 = args
1432 1433 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), index)
1433 1434 lookup = r.lookup
1434 1435 elif len(args) == 2:
1435 1436 if not repo:
1436 1437 raise util.Abort(_("there is no Mercurial repository here "
1437 1438 "(.hg not found)"))
1438 1439 rev1, rev2 = args
1439 1440 r = repo.changelog
1440 1441 lookup = repo.lookup
1441 1442 else:
1442 1443 raise util.Abort(_('either two or three arguments required'))
1443 1444 a = r.ancestor(lookup(rev1), lookup(rev2))
1444 1445 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1445 1446
1446 1447 @command('debugbuilddag',
1447 1448 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
1448 1449 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
1449 1450 ('n', 'new-file', None, _('add new file at each rev'))],
1450 1451 _('[OPTION]... [TEXT]'))
1451 1452 def debugbuilddag(ui, repo, text=None,
1452 1453 mergeable_file=False,
1453 1454 overwritten_file=False,
1454 1455 new_file=False):
1455 1456 """builds a repo with a given DAG from scratch in the current empty repo
1456 1457
1457 1458 The description of the DAG is read from stdin if not given on the
1458 1459 command line.
1459 1460
1460 1461 Elements:
1461 1462
1462 1463 - "+n" is a linear run of n nodes based on the current default parent
1463 1464 - "." is a single node based on the current default parent
1464 1465 - "$" resets the default parent to null (implied at the start);
1465 1466 otherwise the default parent is always the last node created
1466 1467 - "<p" sets the default parent to the backref p
1467 1468 - "*p" is a fork at parent p, which is a backref
1468 1469 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
1469 1470 - "/p2" is a merge of the preceding node and p2
1470 1471 - ":tag" defines a local tag for the preceding node
1471 1472 - "@branch" sets the named branch for subsequent nodes
1472 1473 - "#...\\n" is a comment up to the end of the line
1473 1474
1474 1475 Whitespace between the above elements is ignored.
1475 1476
1476 1477 A backref is either
1477 1478
1478 1479 - a number n, which references the node curr-n, where curr is the current
1479 1480 node, or
1480 1481 - the name of a local tag you placed earlier using ":tag", or
1481 1482 - empty to denote the default parent.
1482 1483
1483 1484 All string valued-elements are either strictly alphanumeric, or must
1484 1485 be enclosed in double quotes ("..."), with "\\" as escape character.
1485 1486 """
1486 1487
1487 1488 if text is None:
1488 1489 ui.status(_("reading DAG from stdin\n"))
1489 1490 text = ui.fin.read()
1490 1491
1491 1492 cl = repo.changelog
1492 1493 if len(cl) > 0:
1493 1494 raise util.Abort(_('repository is not empty'))
1494 1495
1495 1496 # determine number of revs in DAG
1496 1497 total = 0
1497 1498 for type, data in dagparser.parsedag(text):
1498 1499 if type == 'n':
1499 1500 total += 1
1500 1501
1501 1502 if mergeable_file:
1502 1503 linesperrev = 2
1503 1504 # make a file with k lines per rev
1504 1505 initialmergedlines = [str(i) for i in xrange(0, total * linesperrev)]
1505 1506 initialmergedlines.append("")
1506 1507
1507 1508 tags = []
1508 1509
1509 1510 lock = tr = None
1510 1511 try:
1511 1512 lock = repo.lock()
1512 1513 tr = repo.transaction("builddag")
1513 1514
1514 1515 at = -1
1515 1516 atbranch = 'default'
1516 1517 nodeids = []
1517 1518 id = 0
1518 1519 ui.progress(_('building'), id, unit=_('revisions'), total=total)
1519 1520 for type, data in dagparser.parsedag(text):
1520 1521 if type == 'n':
1521 1522 ui.note(('node %s\n' % str(data)))
1522 1523 id, ps = data
1523 1524
1524 1525 files = []
1525 1526 fctxs = {}
1526 1527
1527 1528 p2 = None
1528 1529 if mergeable_file:
1529 1530 fn = "mf"
1530 1531 p1 = repo[ps[0]]
1531 1532 if len(ps) > 1:
1532 1533 p2 = repo[ps[1]]
1533 1534 pa = p1.ancestor(p2)
1534 1535 base, local, other = [x[fn].data() for x in (pa, p1,
1535 1536 p2)]
1536 1537 m3 = simplemerge.Merge3Text(base, local, other)
1537 1538 ml = [l.strip() for l in m3.merge_lines()]
1538 1539 ml.append("")
1539 1540 elif at > 0:
1540 1541 ml = p1[fn].data().split("\n")
1541 1542 else:
1542 1543 ml = initialmergedlines
1543 1544 ml[id * linesperrev] += " r%i" % id
1544 1545 mergedtext = "\n".join(ml)
1545 1546 files.append(fn)
1546 1547 fctxs[fn] = context.memfilectx(fn, mergedtext)
1547 1548
1548 1549 if overwritten_file:
1549 1550 fn = "of"
1550 1551 files.append(fn)
1551 1552 fctxs[fn] = context.memfilectx(fn, "r%i\n" % id)
1552 1553
1553 1554 if new_file:
1554 1555 fn = "nf%i" % id
1555 1556 files.append(fn)
1556 1557 fctxs[fn] = context.memfilectx(fn, "r%i\n" % id)
1557 1558 if len(ps) > 1:
1558 1559 if not p2:
1559 1560 p2 = repo[ps[1]]
1560 1561 for fn in p2:
1561 1562 if fn.startswith("nf"):
1562 1563 files.append(fn)
1563 1564 fctxs[fn] = p2[fn]
1564 1565
1565 1566 def fctxfn(repo, cx, path):
1566 1567 return fctxs.get(path)
1567 1568
1568 1569 if len(ps) == 0 or ps[0] < 0:
1569 1570 pars = [None, None]
1570 1571 elif len(ps) == 1:
1571 1572 pars = [nodeids[ps[0]], None]
1572 1573 else:
1573 1574 pars = [nodeids[p] for p in ps]
1574 1575 cx = context.memctx(repo, pars, "r%i" % id, files, fctxfn,
1575 1576 date=(id, 0),
1576 1577 user="debugbuilddag",
1577 1578 extra={'branch': atbranch})
1578 1579 nodeid = repo.commitctx(cx)
1579 1580 nodeids.append(nodeid)
1580 1581 at = id
1581 1582 elif type == 'l':
1582 1583 id, name = data
1583 1584 ui.note(('tag %s\n' % name))
1584 1585 tags.append("%s %s\n" % (hex(repo.changelog.node(id)), name))
1585 1586 elif type == 'a':
1586 1587 ui.note(('branch %s\n' % data))
1587 1588 atbranch = data
1588 1589 ui.progress(_('building'), id, unit=_('revisions'), total=total)
1589 1590 tr.close()
1590 1591
1591 1592 if tags:
1592 1593 repo.opener.write("localtags", "".join(tags))
1593 1594 finally:
1594 1595 ui.progress(_('building'), None)
1595 1596 release(tr, lock)
1596 1597
1597 1598 @command('debugbundle', [('a', 'all', None, _('show all details'))], _('FILE'))
1598 1599 def debugbundle(ui, bundlepath, all=None, **opts):
1599 1600 """lists the contents of a bundle"""
1600 1601 f = hg.openpath(ui, bundlepath)
1601 1602 try:
1602 1603 gen = changegroup.readbundle(f, bundlepath)
1603 1604 if all:
1604 1605 ui.write(("format: id, p1, p2, cset, delta base, len(delta)\n"))
1605 1606
1606 1607 def showchunks(named):
1607 1608 ui.write("\n%s\n" % named)
1608 1609 chain = None
1609 1610 while True:
1610 1611 chunkdata = gen.deltachunk(chain)
1611 1612 if not chunkdata:
1612 1613 break
1613 1614 node = chunkdata['node']
1614 1615 p1 = chunkdata['p1']
1615 1616 p2 = chunkdata['p2']
1616 1617 cs = chunkdata['cs']
1617 1618 deltabase = chunkdata['deltabase']
1618 1619 delta = chunkdata['delta']
1619 1620 ui.write("%s %s %s %s %s %s\n" %
1620 1621 (hex(node), hex(p1), hex(p2),
1621 1622 hex(cs), hex(deltabase), len(delta)))
1622 1623 chain = node
1623 1624
1624 1625 chunkdata = gen.changelogheader()
1625 1626 showchunks("changelog")
1626 1627 chunkdata = gen.manifestheader()
1627 1628 showchunks("manifest")
1628 1629 while True:
1629 1630 chunkdata = gen.filelogheader()
1630 1631 if not chunkdata:
1631 1632 break
1632 1633 fname = chunkdata['filename']
1633 1634 showchunks(fname)
1634 1635 else:
1635 1636 chunkdata = gen.changelogheader()
1636 1637 chain = None
1637 1638 while True:
1638 1639 chunkdata = gen.deltachunk(chain)
1639 1640 if not chunkdata:
1640 1641 break
1641 1642 node = chunkdata['node']
1642 1643 ui.write("%s\n" % hex(node))
1643 1644 chain = node
1644 1645 finally:
1645 1646 f.close()
1646 1647
1647 1648 @command('debugcheckstate', [], '')
1648 1649 def debugcheckstate(ui, repo):
1649 1650 """validate the correctness of the current dirstate"""
1650 1651 parent1, parent2 = repo.dirstate.parents()
1651 1652 m1 = repo[parent1].manifest()
1652 1653 m2 = repo[parent2].manifest()
1653 1654 errors = 0
1654 1655 for f in repo.dirstate:
1655 1656 state = repo.dirstate[f]
1656 1657 if state in "nr" and f not in m1:
1657 1658 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1658 1659 errors += 1
1659 1660 if state in "a" and f in m1:
1660 1661 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1661 1662 errors += 1
1662 1663 if state in "m" and f not in m1 and f not in m2:
1663 1664 ui.warn(_("%s in state %s, but not in either manifest\n") %
1664 1665 (f, state))
1665 1666 errors += 1
1666 1667 for f in m1:
1667 1668 state = repo.dirstate[f]
1668 1669 if state not in "nrm":
1669 1670 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1670 1671 errors += 1
1671 1672 if errors:
1672 1673 error = _(".hg/dirstate inconsistent with current parent's manifest")
1673 1674 raise util.Abort(error)
1674 1675
1675 1676 @command('debugcommands', [], _('[COMMAND]'))
1676 1677 def debugcommands(ui, cmd='', *args):
1677 1678 """list all available commands and options"""
1678 1679 for cmd, vals in sorted(table.iteritems()):
1679 1680 cmd = cmd.split('|')[0].strip('^')
1680 1681 opts = ', '.join([i[1] for i in vals[1]])
1681 1682 ui.write('%s: %s\n' % (cmd, opts))
1682 1683
1683 1684 @command('debugcomplete',
1684 1685 [('o', 'options', None, _('show the command options'))],
1685 1686 _('[-o] CMD'))
1686 1687 def debugcomplete(ui, cmd='', **opts):
1687 1688 """returns the completion list associated with the given command"""
1688 1689
1689 1690 if opts.get('options'):
1690 1691 options = []
1691 1692 otables = [globalopts]
1692 1693 if cmd:
1693 1694 aliases, entry = cmdutil.findcmd(cmd, table, False)
1694 1695 otables.append(entry[1])
1695 1696 for t in otables:
1696 1697 for o in t:
1697 1698 if "(DEPRECATED)" in o[3]:
1698 1699 continue
1699 1700 if o[0]:
1700 1701 options.append('-%s' % o[0])
1701 1702 options.append('--%s' % o[1])
1702 1703 ui.write("%s\n" % "\n".join(options))
1703 1704 return
1704 1705
1705 1706 cmdlist = cmdutil.findpossible(cmd, table)
1706 1707 if ui.verbose:
1707 1708 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1708 1709 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1709 1710
1710 1711 @command('debugdag',
1711 1712 [('t', 'tags', None, _('use tags as labels')),
1712 1713 ('b', 'branches', None, _('annotate with branch names')),
1713 1714 ('', 'dots', None, _('use dots for runs')),
1714 1715 ('s', 'spaces', None, _('separate elements by spaces'))],
1715 1716 _('[OPTION]... [FILE [REV]...]'))
1716 1717 def debugdag(ui, repo, file_=None, *revs, **opts):
1717 1718 """format the changelog or an index DAG as a concise textual description
1718 1719
1719 1720 If you pass a revlog index, the revlog's DAG is emitted. If you list
1720 1721 revision numbers, they get labeled in the output as rN.
1721 1722
1722 1723 Otherwise, the changelog DAG of the current repo is emitted.
1723 1724 """
1724 1725 spaces = opts.get('spaces')
1725 1726 dots = opts.get('dots')
1726 1727 if file_:
1727 1728 rlog = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1728 1729 revs = set((int(r) for r in revs))
1729 1730 def events():
1730 1731 for r in rlog:
1731 1732 yield 'n', (r, list(set(p for p in rlog.parentrevs(r)
1732 1733 if p != -1)))
1733 1734 if r in revs:
1734 1735 yield 'l', (r, "r%i" % r)
1735 1736 elif repo:
1736 1737 cl = repo.changelog
1737 1738 tags = opts.get('tags')
1738 1739 branches = opts.get('branches')
1739 1740 if tags:
1740 1741 labels = {}
1741 1742 for l, n in repo.tags().items():
1742 1743 labels.setdefault(cl.rev(n), []).append(l)
1743 1744 def events():
1744 1745 b = "default"
1745 1746 for r in cl:
1746 1747 if branches:
1747 1748 newb = cl.read(cl.node(r))[5]['branch']
1748 1749 if newb != b:
1749 1750 yield 'a', newb
1750 1751 b = newb
1751 1752 yield 'n', (r, list(set(p for p in cl.parentrevs(r)
1752 1753 if p != -1)))
1753 1754 if tags:
1754 1755 ls = labels.get(r)
1755 1756 if ls:
1756 1757 for l in ls:
1757 1758 yield 'l', (r, l)
1758 1759 else:
1759 1760 raise util.Abort(_('need repo for changelog dag'))
1760 1761
1761 1762 for line in dagparser.dagtextlines(events(),
1762 1763 addspaces=spaces,
1763 1764 wraplabels=True,
1764 1765 wrapannotations=True,
1765 1766 wrapnonlinear=dots,
1766 1767 usedots=dots,
1767 1768 maxlinewidth=70):
1768 1769 ui.write(line)
1769 1770 ui.write("\n")
1770 1771
1771 1772 @command('debugdata',
1772 1773 [('c', 'changelog', False, _('open changelog')),
1773 1774 ('m', 'manifest', False, _('open manifest'))],
1774 1775 _('-c|-m|FILE REV'))
1775 1776 def debugdata(ui, repo, file_, rev = None, **opts):
1776 1777 """dump the contents of a data file revision"""
1777 1778 if opts.get('changelog') or opts.get('manifest'):
1778 1779 file_, rev = None, file_
1779 1780 elif rev is None:
1780 1781 raise error.CommandError('debugdata', _('invalid arguments'))
1781 1782 r = cmdutil.openrevlog(repo, 'debugdata', file_, opts)
1782 1783 try:
1783 1784 ui.write(r.revision(r.lookup(rev)))
1784 1785 except KeyError:
1785 1786 raise util.Abort(_('invalid revision identifier %s') % rev)
1786 1787
1787 1788 @command('debugdate',
1788 1789 [('e', 'extended', None, _('try extended date formats'))],
1789 1790 _('[-e] DATE [RANGE]'))
1790 1791 def debugdate(ui, date, range=None, **opts):
1791 1792 """parse and display a date"""
1792 1793 if opts["extended"]:
1793 1794 d = util.parsedate(date, util.extendeddateformats)
1794 1795 else:
1795 1796 d = util.parsedate(date)
1796 1797 ui.write(("internal: %s %s\n") % d)
1797 1798 ui.write(("standard: %s\n") % util.datestr(d))
1798 1799 if range:
1799 1800 m = util.matchdate(range)
1800 1801 ui.write(("match: %s\n") % m(d[0]))
1801 1802
1802 1803 @command('debugdiscovery',
1803 1804 [('', 'old', None, _('use old-style discovery')),
1804 1805 ('', 'nonheads', None,
1805 1806 _('use old-style discovery with non-heads included')),
1806 1807 ] + remoteopts,
1807 1808 _('[-l REV] [-r REV] [-b BRANCH]... [OTHER]'))
1808 1809 def debugdiscovery(ui, repo, remoteurl="default", **opts):
1809 1810 """runs the changeset discovery protocol in isolation"""
1810 1811 remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl),
1811 1812 opts.get('branch'))
1812 1813 remote = hg.peer(repo, opts, remoteurl)
1813 1814 ui.status(_('comparing with %s\n') % util.hidepassword(remoteurl))
1814 1815
1815 1816 # make sure tests are repeatable
1816 1817 random.seed(12323)
1817 1818
1818 1819 def doit(localheads, remoteheads, remote=remote):
1819 1820 if opts.get('old'):
1820 1821 if localheads:
1821 1822 raise util.Abort('cannot use localheads with old style '
1822 1823 'discovery')
1823 1824 if not util.safehasattr(remote, 'branches'):
1824 1825 # enable in-client legacy support
1825 1826 remote = localrepo.locallegacypeer(remote.local())
1826 1827 common, _in, hds = treediscovery.findcommonincoming(repo, remote,
1827 1828 force=True)
1828 1829 common = set(common)
1829 1830 if not opts.get('nonheads'):
1830 1831 ui.write(("unpruned common: %s\n") %
1831 1832 " ".join(sorted(short(n) for n in common)))
1832 1833 dag = dagutil.revlogdag(repo.changelog)
1833 1834 all = dag.ancestorset(dag.internalizeall(common))
1834 1835 common = dag.externalizeall(dag.headsetofconnecteds(all))
1835 1836 else:
1836 1837 common, any, hds = setdiscovery.findcommonheads(ui, repo, remote)
1837 1838 common = set(common)
1838 1839 rheads = set(hds)
1839 1840 lheads = set(repo.heads())
1840 1841 ui.write(("common heads: %s\n") %
1841 1842 " ".join(sorted(short(n) for n in common)))
1842 1843 if lheads <= common:
1843 1844 ui.write(("local is subset\n"))
1844 1845 elif rheads <= common:
1845 1846 ui.write(("remote is subset\n"))
1846 1847
1847 1848 serverlogs = opts.get('serverlog')
1848 1849 if serverlogs:
1849 1850 for filename in serverlogs:
1850 1851 logfile = open(filename, 'r')
1851 1852 try:
1852 1853 line = logfile.readline()
1853 1854 while line:
1854 1855 parts = line.strip().split(';')
1855 1856 op = parts[1]
1856 1857 if op == 'cg':
1857 1858 pass
1858 1859 elif op == 'cgss':
1859 1860 doit(parts[2].split(' '), parts[3].split(' '))
1860 1861 elif op == 'unb':
1861 1862 doit(parts[3].split(' '), parts[2].split(' '))
1862 1863 line = logfile.readline()
1863 1864 finally:
1864 1865 logfile.close()
1865 1866
1866 1867 else:
1867 1868 remoterevs, _checkout = hg.addbranchrevs(repo, remote, branches,
1868 1869 opts.get('remote_head'))
1869 1870 localrevs = opts.get('local_head')
1870 1871 doit(localrevs, remoterevs)
1871 1872
1872 1873 @command('debugfileset',
1873 1874 [('r', 'rev', '', _('apply the filespec on this revision'), _('REV'))],
1874 1875 _('[-r REV] FILESPEC'))
1875 1876 def debugfileset(ui, repo, expr, **opts):
1876 1877 '''parse and apply a fileset specification'''
1877 1878 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
1878 1879 if ui.verbose:
1879 1880 tree = fileset.parse(expr)[0]
1880 1881 ui.note(tree, "\n")
1881 1882
1882 1883 for f in fileset.getfileset(ctx, expr):
1883 1884 ui.write("%s\n" % f)
1884 1885
1885 1886 @command('debugfsinfo', [], _('[PATH]'))
1886 1887 def debugfsinfo(ui, path = "."):
1887 1888 """show information detected about current filesystem"""
1888 1889 util.writefile('.debugfsinfo', '')
1889 1890 ui.write(('exec: %s\n') % (util.checkexec(path) and 'yes' or 'no'))
1890 1891 ui.write(('symlink: %s\n') % (util.checklink(path) and 'yes' or 'no'))
1891 1892 ui.write(('case-sensitive: %s\n') % (util.checkcase('.debugfsinfo')
1892 1893 and 'yes' or 'no'))
1893 1894 os.unlink('.debugfsinfo')
1894 1895
1895 1896 @command('debuggetbundle',
1896 1897 [('H', 'head', [], _('id of head node'), _('ID')),
1897 1898 ('C', 'common', [], _('id of common node'), _('ID')),
1898 1899 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE'))],
1899 1900 _('REPO FILE [-H|-C ID]...'))
1900 1901 def debuggetbundle(ui, repopath, bundlepath, head=None, common=None, **opts):
1901 1902 """retrieves a bundle from a repo
1902 1903
1903 1904 Every ID must be a full-length hex node id string. Saves the bundle to the
1904 1905 given file.
1905 1906 """
1906 1907 repo = hg.peer(ui, opts, repopath)
1907 1908 if not repo.capable('getbundle'):
1908 1909 raise util.Abort("getbundle() not supported by target repository")
1909 1910 args = {}
1910 1911 if common:
1911 1912 args['common'] = [bin(s) for s in common]
1912 1913 if head:
1913 1914 args['heads'] = [bin(s) for s in head]
1914 1915 bundle = repo.getbundle('debug', **args)
1915 1916
1916 1917 bundletype = opts.get('type', 'bzip2').lower()
1917 1918 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
1918 1919 bundletype = btypes.get(bundletype)
1919 1920 if bundletype not in changegroup.bundletypes:
1920 1921 raise util.Abort(_('unknown bundle type specified with --type'))
1921 1922 changegroup.writebundle(bundle, bundlepath, bundletype)
1922 1923
1923 1924 @command('debugignore', [], '')
1924 1925 def debugignore(ui, repo, *values, **opts):
1925 1926 """display the combined ignore pattern"""
1926 1927 ignore = repo.dirstate._ignore
1927 1928 includepat = getattr(ignore, 'includepat', None)
1928 1929 if includepat is not None:
1929 1930 ui.write("%s\n" % includepat)
1930 1931 else:
1931 1932 raise util.Abort(_("no ignore patterns found"))
1932 1933
1933 1934 @command('debugindex',
1934 1935 [('c', 'changelog', False, _('open changelog')),
1935 1936 ('m', 'manifest', False, _('open manifest')),
1936 1937 ('f', 'format', 0, _('revlog format'), _('FORMAT'))],
1937 1938 _('[-f FORMAT] -c|-m|FILE'))
1938 1939 def debugindex(ui, repo, file_ = None, **opts):
1939 1940 """dump the contents of an index file"""
1940 1941 r = cmdutil.openrevlog(repo, 'debugindex', file_, opts)
1941 1942 format = opts.get('format', 0)
1942 1943 if format not in (0, 1):
1943 1944 raise util.Abort(_("unknown format %d") % format)
1944 1945
1945 1946 generaldelta = r.version & revlog.REVLOGGENERALDELTA
1946 1947 if generaldelta:
1947 1948 basehdr = ' delta'
1948 1949 else:
1949 1950 basehdr = ' base'
1950 1951
1951 1952 if format == 0:
1952 1953 ui.write(" rev offset length " + basehdr + " linkrev"
1953 1954 " nodeid p1 p2\n")
1954 1955 elif format == 1:
1955 1956 ui.write(" rev flag offset length"
1956 1957 " size " + basehdr + " link p1 p2"
1957 1958 " nodeid\n")
1958 1959
1959 1960 for i in r:
1960 1961 node = r.node(i)
1961 1962 if generaldelta:
1962 1963 base = r.deltaparent(i)
1963 1964 else:
1964 1965 base = r.chainbase(i)
1965 1966 if format == 0:
1966 1967 try:
1967 1968 pp = r.parents(node)
1968 1969 except Exception:
1969 1970 pp = [nullid, nullid]
1970 1971 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1971 1972 i, r.start(i), r.length(i), base, r.linkrev(i),
1972 1973 short(node), short(pp[0]), short(pp[1])))
1973 1974 elif format == 1:
1974 1975 pr = r.parentrevs(i)
1975 1976 ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d % 6d %s\n" % (
1976 1977 i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
1977 1978 base, r.linkrev(i), pr[0], pr[1], short(node)))
1978 1979
1979 1980 @command('debugindexdot', [], _('FILE'))
1980 1981 def debugindexdot(ui, repo, file_):
1981 1982 """dump an index DAG as a graphviz dot file"""
1982 1983 r = None
1983 1984 if repo:
1984 1985 filelog = repo.file(file_)
1985 1986 if len(filelog):
1986 1987 r = filelog
1987 1988 if not r:
1988 1989 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1989 1990 ui.write(("digraph G {\n"))
1990 1991 for i in r:
1991 1992 node = r.node(i)
1992 1993 pp = r.parents(node)
1993 1994 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1994 1995 if pp[1] != nullid:
1995 1996 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1996 1997 ui.write("}\n")
1997 1998
1998 1999 @command('debuginstall', [], '')
1999 2000 def debuginstall(ui):
2000 2001 '''test Mercurial installation
2001 2002
2002 2003 Returns 0 on success.
2003 2004 '''
2004 2005
2005 2006 def writetemp(contents):
2006 2007 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
2007 2008 f = os.fdopen(fd, "wb")
2008 2009 f.write(contents)
2009 2010 f.close()
2010 2011 return name
2011 2012
2012 2013 problems = 0
2013 2014
2014 2015 # encoding
2015 2016 ui.status(_("checking encoding (%s)...\n") % encoding.encoding)
2016 2017 try:
2017 2018 encoding.fromlocal("test")
2018 2019 except util.Abort, inst:
2019 2020 ui.write(" %s\n" % inst)
2020 2021 ui.write(_(" (check that your locale is properly set)\n"))
2021 2022 problems += 1
2022 2023
2023 2024 # Python lib
2024 2025 ui.status(_("checking Python lib (%s)...\n")
2025 2026 % os.path.dirname(os.__file__))
2026 2027
2027 2028 # compiled modules
2028 2029 ui.status(_("checking installed modules (%s)...\n")
2029 2030 % os.path.dirname(__file__))
2030 2031 try:
2031 2032 import bdiff, mpatch, base85, osutil
2032 2033 dir(bdiff), dir(mpatch), dir(base85), dir(osutil) # quiet pyflakes
2033 2034 except Exception, inst:
2034 2035 ui.write(" %s\n" % inst)
2035 2036 ui.write(_(" One or more extensions could not be found"))
2036 2037 ui.write(_(" (check that you compiled the extensions)\n"))
2037 2038 problems += 1
2038 2039
2039 2040 # templates
2040 2041 import templater
2041 2042 p = templater.templatepath()
2042 2043 ui.status(_("checking templates (%s)...\n") % ' '.join(p))
2043 2044 try:
2044 2045 templater.templater(templater.templatepath("map-cmdline.default"))
2045 2046 except Exception, inst:
2046 2047 ui.write(" %s\n" % inst)
2047 2048 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
2048 2049 problems += 1
2049 2050
2050 2051 # editor
2051 2052 ui.status(_("checking commit editor...\n"))
2052 2053 editor = ui.geteditor()
2053 2054 cmdpath = util.findexe(editor) or util.findexe(editor.split()[0])
2054 2055 if not cmdpath:
2055 2056 if editor == 'vi':
2056 2057 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
2057 2058 ui.write(_(" (specify a commit editor in your configuration"
2058 2059 " file)\n"))
2059 2060 else:
2060 2061 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
2061 2062 ui.write(_(" (specify a commit editor in your configuration"
2062 2063 " file)\n"))
2063 2064 problems += 1
2064 2065
2065 2066 # check username
2066 2067 ui.status(_("checking username...\n"))
2067 2068 try:
2068 2069 ui.username()
2069 2070 except util.Abort, e:
2070 2071 ui.write(" %s\n" % e)
2071 2072 ui.write(_(" (specify a username in your configuration file)\n"))
2072 2073 problems += 1
2073 2074
2074 2075 if not problems:
2075 2076 ui.status(_("no problems detected\n"))
2076 2077 else:
2077 2078 ui.write(_("%s problems detected,"
2078 2079 " please check your install!\n") % problems)
2079 2080
2080 2081 return problems
2081 2082
2082 2083 @command('debugknown', [], _('REPO ID...'))
2083 2084 def debugknown(ui, repopath, *ids, **opts):
2084 2085 """test whether node ids are known to a repo
2085 2086
2086 2087 Every ID must be a full-length hex node id string. Returns a list of 0s
2087 2088 and 1s indicating unknown/known.
2088 2089 """
2089 2090 repo = hg.peer(ui, opts, repopath)
2090 2091 if not repo.capable('known'):
2091 2092 raise util.Abort("known() not supported by target repository")
2092 2093 flags = repo.known([bin(s) for s in ids])
2093 2094 ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
2094 2095
2095 2096 @command('debugobsolete',
2096 2097 [('', 'flags', 0, _('markers flag')),
2097 2098 ] + commitopts2,
2098 2099 _('[OBSOLETED [REPLACEMENT] [REPL... ]'))
2099 2100 def debugobsolete(ui, repo, precursor=None, *successors, **opts):
2100 2101 """create arbitrary obsolete marker"""
2101 2102 def parsenodeid(s):
2102 2103 try:
2103 2104 # We do not use revsingle/revrange functions here to accept
2104 2105 # arbitrary node identifiers, possibly not present in the
2105 2106 # local repository.
2106 2107 n = bin(s)
2107 2108 if len(n) != len(nullid):
2108 2109 raise TypeError()
2109 2110 return n
2110 2111 except TypeError:
2111 2112 raise util.Abort('changeset references must be full hexadecimal '
2112 2113 'node identifiers')
2113 2114
2114 2115 if precursor is not None:
2115 2116 metadata = {}
2116 2117 if 'date' in opts:
2117 2118 metadata['date'] = opts['date']
2118 2119 metadata['user'] = opts['user'] or ui.username()
2119 2120 succs = tuple(parsenodeid(succ) for succ in successors)
2120 2121 l = repo.lock()
2121 2122 try:
2122 2123 tr = repo.transaction('debugobsolete')
2123 2124 try:
2124 2125 repo.obsstore.create(tr, parsenodeid(precursor), succs,
2125 2126 opts['flags'], metadata)
2126 2127 tr.close()
2127 2128 finally:
2128 2129 tr.release()
2129 2130 finally:
2130 2131 l.release()
2131 2132 else:
2132 2133 for m in obsolete.allmarkers(repo):
2133 2134 ui.write(hex(m.precnode()))
2134 2135 for repl in m.succnodes():
2135 2136 ui.write(' ')
2136 2137 ui.write(hex(repl))
2137 2138 ui.write(' %X ' % m._data[2])
2138 2139 ui.write('{%s}' % (', '.join('%r: %r' % t for t in
2139 2140 sorted(m.metadata().items()))))
2140 2141 ui.write('\n')
2141 2142
2142 2143 @command('debugpushkey', [], _('REPO NAMESPACE [KEY OLD NEW]'))
2143 2144 def debugpushkey(ui, repopath, namespace, *keyinfo, **opts):
2144 2145 '''access the pushkey key/value protocol
2145 2146
2146 2147 With two args, list the keys in the given namespace.
2147 2148
2148 2149 With five args, set a key to new if it currently is set to old.
2149 2150 Reports success or failure.
2150 2151 '''
2151 2152
2152 2153 target = hg.peer(ui, {}, repopath)
2153 2154 if keyinfo:
2154 2155 key, old, new = keyinfo
2155 2156 r = target.pushkey(namespace, key, old, new)
2156 2157 ui.status(str(r) + '\n')
2157 2158 return not r
2158 2159 else:
2159 2160 for k, v in sorted(target.listkeys(namespace).iteritems()):
2160 2161 ui.write("%s\t%s\n" % (k.encode('string-escape'),
2161 2162 v.encode('string-escape')))
2162 2163
2163 2164 @command('debugpvec', [], _('A B'))
2164 2165 def debugpvec(ui, repo, a, b=None):
2165 2166 ca = scmutil.revsingle(repo, a)
2166 2167 cb = scmutil.revsingle(repo, b)
2167 2168 pa = pvec.ctxpvec(ca)
2168 2169 pb = pvec.ctxpvec(cb)
2169 2170 if pa == pb:
2170 2171 rel = "="
2171 2172 elif pa > pb:
2172 2173 rel = ">"
2173 2174 elif pa < pb:
2174 2175 rel = "<"
2175 2176 elif pa | pb:
2176 2177 rel = "|"
2177 2178 ui.write(_("a: %s\n") % pa)
2178 2179 ui.write(_("b: %s\n") % pb)
2179 2180 ui.write(_("depth(a): %d depth(b): %d\n") % (pa._depth, pb._depth))
2180 2181 ui.write(_("delta: %d hdist: %d distance: %d relation: %s\n") %
2181 2182 (abs(pa._depth - pb._depth), pvec._hamming(pa._vec, pb._vec),
2182 2183 pa.distance(pb), rel))
2183 2184
2184 2185 @command('debugrebuildstate',
2185 2186 [('r', 'rev', '', _('revision to rebuild to'), _('REV'))],
2186 2187 _('[-r REV] [REV]'))
2187 2188 def debugrebuildstate(ui, repo, rev="tip"):
2188 2189 """rebuild the dirstate as it would look like for the given revision"""
2189 2190 ctx = scmutil.revsingle(repo, rev)
2190 2191 wlock = repo.wlock()
2191 2192 try:
2192 2193 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
2193 2194 finally:
2194 2195 wlock.release()
2195 2196
2196 2197 @command('debugrename',
2197 2198 [('r', 'rev', '', _('revision to debug'), _('REV'))],
2198 2199 _('[-r REV] FILE'))
2199 2200 def debugrename(ui, repo, file1, *pats, **opts):
2200 2201 """dump rename information"""
2201 2202
2202 2203 ctx = scmutil.revsingle(repo, opts.get('rev'))
2203 2204 m = scmutil.match(ctx, (file1,) + pats, opts)
2204 2205 for abs in ctx.walk(m):
2205 2206 fctx = ctx[abs]
2206 2207 o = fctx.filelog().renamed(fctx.filenode())
2207 2208 rel = m.rel(abs)
2208 2209 if o:
2209 2210 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
2210 2211 else:
2211 2212 ui.write(_("%s not renamed\n") % rel)
2212 2213
2213 2214 @command('debugrevlog',
2214 2215 [('c', 'changelog', False, _('open changelog')),
2215 2216 ('m', 'manifest', False, _('open manifest')),
2216 2217 ('d', 'dump', False, _('dump index data'))],
2217 2218 _('-c|-m|FILE'))
2218 2219 def debugrevlog(ui, repo, file_ = None, **opts):
2219 2220 """show data and statistics about a revlog"""
2220 2221 r = cmdutil.openrevlog(repo, 'debugrevlog', file_, opts)
2221 2222
2222 2223 if opts.get("dump"):
2223 2224 numrevs = len(r)
2224 2225 ui.write("# rev p1rev p2rev start end deltastart base p1 p2"
2225 2226 " rawsize totalsize compression heads\n")
2226 2227 ts = 0
2227 2228 heads = set()
2228 2229 for rev in xrange(numrevs):
2229 2230 dbase = r.deltaparent(rev)
2230 2231 if dbase == -1:
2231 2232 dbase = rev
2232 2233 cbase = r.chainbase(rev)
2233 2234 p1, p2 = r.parentrevs(rev)
2234 2235 rs = r.rawsize(rev)
2235 2236 ts = ts + rs
2236 2237 heads -= set(r.parentrevs(rev))
2237 2238 heads.add(rev)
2238 2239 ui.write("%d %d %d %d %d %d %d %d %d %d %d %d %d\n" %
2239 2240 (rev, p1, p2, r.start(rev), r.end(rev),
2240 2241 r.start(dbase), r.start(cbase),
2241 2242 r.start(p1), r.start(p2),
2242 2243 rs, ts, ts / r.end(rev), len(heads)))
2243 2244 return 0
2244 2245
2245 2246 v = r.version
2246 2247 format = v & 0xFFFF
2247 2248 flags = []
2248 2249 gdelta = False
2249 2250 if v & revlog.REVLOGNGINLINEDATA:
2250 2251 flags.append('inline')
2251 2252 if v & revlog.REVLOGGENERALDELTA:
2252 2253 gdelta = True
2253 2254 flags.append('generaldelta')
2254 2255 if not flags:
2255 2256 flags = ['(none)']
2256 2257
2257 2258 nummerges = 0
2258 2259 numfull = 0
2259 2260 numprev = 0
2260 2261 nump1 = 0
2261 2262 nump2 = 0
2262 2263 numother = 0
2263 2264 nump1prev = 0
2264 2265 nump2prev = 0
2265 2266 chainlengths = []
2266 2267
2267 2268 datasize = [None, 0, 0L]
2268 2269 fullsize = [None, 0, 0L]
2269 2270 deltasize = [None, 0, 0L]
2270 2271
2271 2272 def addsize(size, l):
2272 2273 if l[0] is None or size < l[0]:
2273 2274 l[0] = size
2274 2275 if size > l[1]:
2275 2276 l[1] = size
2276 2277 l[2] += size
2277 2278
2278 2279 numrevs = len(r)
2279 2280 for rev in xrange(numrevs):
2280 2281 p1, p2 = r.parentrevs(rev)
2281 2282 delta = r.deltaparent(rev)
2282 2283 if format > 0:
2283 2284 addsize(r.rawsize(rev), datasize)
2284 2285 if p2 != nullrev:
2285 2286 nummerges += 1
2286 2287 size = r.length(rev)
2287 2288 if delta == nullrev:
2288 2289 chainlengths.append(0)
2289 2290 numfull += 1
2290 2291 addsize(size, fullsize)
2291 2292 else:
2292 2293 chainlengths.append(chainlengths[delta] + 1)
2293 2294 addsize(size, deltasize)
2294 2295 if delta == rev - 1:
2295 2296 numprev += 1
2296 2297 if delta == p1:
2297 2298 nump1prev += 1
2298 2299 elif delta == p2:
2299 2300 nump2prev += 1
2300 2301 elif delta == p1:
2301 2302 nump1 += 1
2302 2303 elif delta == p2:
2303 2304 nump2 += 1
2304 2305 elif delta != nullrev:
2305 2306 numother += 1
2306 2307
2307 2308 # Adjust size min value for empty cases
2308 2309 for size in (datasize, fullsize, deltasize):
2309 2310 if size[0] is None:
2310 2311 size[0] = 0
2311 2312
2312 2313 numdeltas = numrevs - numfull
2313 2314 numoprev = numprev - nump1prev - nump2prev
2314 2315 totalrawsize = datasize[2]
2315 2316 datasize[2] /= numrevs
2316 2317 fulltotal = fullsize[2]
2317 2318 fullsize[2] /= numfull
2318 2319 deltatotal = deltasize[2]
2319 2320 if numrevs - numfull > 0:
2320 2321 deltasize[2] /= numrevs - numfull
2321 2322 totalsize = fulltotal + deltatotal
2322 2323 avgchainlen = sum(chainlengths) / numrevs
2323 2324 compratio = totalrawsize / totalsize
2324 2325
2325 2326 basedfmtstr = '%%%dd\n'
2326 2327 basepcfmtstr = '%%%dd %s(%%5.2f%%%%)\n'
2327 2328
2328 2329 def dfmtstr(max):
2329 2330 return basedfmtstr % len(str(max))
2330 2331 def pcfmtstr(max, padding=0):
2331 2332 return basepcfmtstr % (len(str(max)), ' ' * padding)
2332 2333
2333 2334 def pcfmt(value, total):
2334 2335 return (value, 100 * float(value) / total)
2335 2336
2336 2337 ui.write(('format : %d\n') % format)
2337 2338 ui.write(('flags : %s\n') % ', '.join(flags))
2338 2339
2339 2340 ui.write('\n')
2340 2341 fmt = pcfmtstr(totalsize)
2341 2342 fmt2 = dfmtstr(totalsize)
2342 2343 ui.write(('revisions : ') + fmt2 % numrevs)
2343 2344 ui.write((' merges : ') + fmt % pcfmt(nummerges, numrevs))
2344 2345 ui.write((' normal : ') + fmt % pcfmt(numrevs - nummerges, numrevs))
2345 2346 ui.write(('revisions : ') + fmt2 % numrevs)
2346 2347 ui.write((' full : ') + fmt % pcfmt(numfull, numrevs))
2347 2348 ui.write((' deltas : ') + fmt % pcfmt(numdeltas, numrevs))
2348 2349 ui.write(('revision size : ') + fmt2 % totalsize)
2349 2350 ui.write((' full : ') + fmt % pcfmt(fulltotal, totalsize))
2350 2351 ui.write((' deltas : ') + fmt % pcfmt(deltatotal, totalsize))
2351 2352
2352 2353 ui.write('\n')
2353 2354 fmt = dfmtstr(max(avgchainlen, compratio))
2354 2355 ui.write(('avg chain length : ') + fmt % avgchainlen)
2355 2356 ui.write(('compression ratio : ') + fmt % compratio)
2356 2357
2357 2358 if format > 0:
2358 2359 ui.write('\n')
2359 2360 ui.write(('uncompressed data size (min/max/avg) : %d / %d / %d\n')
2360 2361 % tuple(datasize))
2361 2362 ui.write(('full revision size (min/max/avg) : %d / %d / %d\n')
2362 2363 % tuple(fullsize))
2363 2364 ui.write(('delta size (min/max/avg) : %d / %d / %d\n')
2364 2365 % tuple(deltasize))
2365 2366
2366 2367 if numdeltas > 0:
2367 2368 ui.write('\n')
2368 2369 fmt = pcfmtstr(numdeltas)
2369 2370 fmt2 = pcfmtstr(numdeltas, 4)
2370 2371 ui.write(('deltas against prev : ') + fmt % pcfmt(numprev, numdeltas))
2371 2372 if numprev > 0:
2372 2373 ui.write((' where prev = p1 : ') + fmt2 % pcfmt(nump1prev,
2373 2374 numprev))
2374 2375 ui.write((' where prev = p2 : ') + fmt2 % pcfmt(nump2prev,
2375 2376 numprev))
2376 2377 ui.write((' other : ') + fmt2 % pcfmt(numoprev,
2377 2378 numprev))
2378 2379 if gdelta:
2379 2380 ui.write(('deltas against p1 : ')
2380 2381 + fmt % pcfmt(nump1, numdeltas))
2381 2382 ui.write(('deltas against p2 : ')
2382 2383 + fmt % pcfmt(nump2, numdeltas))
2383 2384 ui.write(('deltas against other : ') + fmt % pcfmt(numother,
2384 2385 numdeltas))
2385 2386
2386 2387 @command('debugrevspec', [], ('REVSPEC'))
2387 2388 def debugrevspec(ui, repo, expr):
2388 2389 """parse and apply a revision specification
2389 2390
2390 2391 Use --verbose to print the parsed tree before and after aliases
2391 2392 expansion.
2392 2393 """
2393 2394 if ui.verbose:
2394 2395 tree = revset.parse(expr)[0]
2395 2396 ui.note(revset.prettyformat(tree), "\n")
2396 2397 newtree = revset.findaliases(ui, tree)
2397 2398 if newtree != tree:
2398 2399 ui.note(revset.prettyformat(newtree), "\n")
2399 2400 func = revset.match(ui, expr)
2400 2401 for c in func(repo, range(len(repo))):
2401 2402 ui.write("%s\n" % c)
2402 2403
2403 2404 @command('debugsetparents', [], _('REV1 [REV2]'))
2404 2405 def debugsetparents(ui, repo, rev1, rev2=None):
2405 2406 """manually set the parents of the current working directory
2406 2407
2407 2408 This is useful for writing repository conversion tools, but should
2408 2409 be used with care.
2409 2410
2410 2411 Returns 0 on success.
2411 2412 """
2412 2413
2413 2414 r1 = scmutil.revsingle(repo, rev1).node()
2414 2415 r2 = scmutil.revsingle(repo, rev2, 'null').node()
2415 2416
2416 2417 wlock = repo.wlock()
2417 2418 try:
2418 2419 repo.setparents(r1, r2)
2419 2420 finally:
2420 2421 wlock.release()
2421 2422
2422 2423 @command('debugstate',
2423 2424 [('', 'nodates', None, _('do not display the saved mtime')),
2424 2425 ('', 'datesort', None, _('sort by saved mtime'))],
2425 2426 _('[OPTION]...'))
2426 2427 def debugstate(ui, repo, nodates=None, datesort=None):
2427 2428 """show the contents of the current dirstate"""
2428 2429 timestr = ""
2429 2430 showdate = not nodates
2430 2431 if datesort:
2431 2432 keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
2432 2433 else:
2433 2434 keyfunc = None # sort by filename
2434 2435 for file_, ent in sorted(repo.dirstate._map.iteritems(), key=keyfunc):
2435 2436 if showdate:
2436 2437 if ent[3] == -1:
2437 2438 # Pad or slice to locale representation
2438 2439 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ",
2439 2440 time.localtime(0)))
2440 2441 timestr = 'unset'
2441 2442 timestr = (timestr[:locale_len] +
2442 2443 ' ' * (locale_len - len(timestr)))
2443 2444 else:
2444 2445 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
2445 2446 time.localtime(ent[3]))
2446 2447 if ent[1] & 020000:
2447 2448 mode = 'lnk'
2448 2449 else:
2449 2450 mode = '%3o' % (ent[1] & 0777 & ~util.umask)
2450 2451 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
2451 2452 for f in repo.dirstate.copies():
2452 2453 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
2453 2454
2454 2455 @command('debugsub',
2455 2456 [('r', 'rev', '',
2456 2457 _('revision to check'), _('REV'))],
2457 2458 _('[-r REV] [REV]'))
2458 2459 def debugsub(ui, repo, rev=None):
2459 2460 ctx = scmutil.revsingle(repo, rev, None)
2460 2461 for k, v in sorted(ctx.substate.items()):
2461 2462 ui.write(('path %s\n') % k)
2462 2463 ui.write((' source %s\n') % v[0])
2463 2464 ui.write((' revision %s\n') % v[1])
2464 2465
2465 2466 @command('debugsuccessorssets',
2466 2467 [],
2467 2468 _('[REV]'))
2468 2469 def debugsuccessorssets(ui, repo, *revs):
2469 2470 """show set of successors for revision
2470 2471
2471 2472 A successors set of changeset A is a consistent group of revisions that
2472 2473 succeed A. It contains non-obsolete changesets only.
2473 2474
2474 2475 In most cases a changeset A has a single successors set containing a single
2475 2476 successors (changeset A replaced by A').
2476 2477
2477 2478 A changeset that is made obsolete with no successors are called "pruned".
2478 2479 Such changesets have no successors sets at all.
2479 2480
2480 2481 A changeset that has been "split" will have a successors set containing
2481 2482 more than one successors.
2482 2483
2483 2484 A changeset that has been rewritten in multiple different ways is called
2484 2485 "divergent". Such changesets have multiple successor sets (each of which
2485 2486 may also be split, i.e. have multiple successors).
2486 2487
2487 2488 Results are displayed as follows::
2488 2489
2489 2490 <rev1>
2490 2491 <successors-1A>
2491 2492 <rev2>
2492 2493 <successors-2A>
2493 2494 <successors-2B1> <successors-2B2> <successors-2B3>
2494 2495
2495 2496 Here rev2 has two possible (i.e. divergent) successors sets. The first
2496 2497 holds one element, whereas the second holds three (i.e. the changeset has
2497 2498 been split).
2498 2499 """
2499 2500 # passed to successorssets caching computation from one call to another
2500 2501 cache = {}
2501 2502 ctx2str = str
2502 2503 node2str = short
2503 2504 if ui.debug():
2504 2505 def ctx2str(ctx):
2505 2506 return ctx.hex()
2506 2507 node2str = hex
2507 2508 for rev in scmutil.revrange(repo, revs):
2508 2509 ctx = repo[rev]
2509 2510 ui.write('%s\n'% ctx2str(ctx))
2510 2511 for succsset in obsolete.successorssets(repo, ctx.node(), cache):
2511 2512 if succsset:
2512 2513 ui.write(' ')
2513 2514 ui.write(node2str(succsset[0]))
2514 2515 for node in succsset[1:]:
2515 2516 ui.write(' ')
2516 2517 ui.write(node2str(node))
2517 2518 ui.write('\n')
2518 2519
2519 2520 @command('debugwalk', walkopts, _('[OPTION]... [FILE]...'))
2520 2521 def debugwalk(ui, repo, *pats, **opts):
2521 2522 """show how files match on given patterns"""
2522 2523 m = scmutil.match(repo[None], pats, opts)
2523 2524 items = list(repo.walk(m))
2524 2525 if not items:
2525 2526 return
2526 2527 f = lambda fn: fn
2527 2528 if ui.configbool('ui', 'slash') and os.sep != '/':
2528 2529 f = lambda fn: util.normpath(fn)
2529 2530 fmt = 'f %%-%ds %%-%ds %%s' % (
2530 2531 max([len(abs) for abs in items]),
2531 2532 max([len(m.rel(abs)) for abs in items]))
2532 2533 for abs in items:
2533 2534 line = fmt % (abs, f(m.rel(abs)), m.exact(abs) and 'exact' or '')
2534 2535 ui.write("%s\n" % line.rstrip())
2535 2536
2536 2537 @command('debugwireargs',
2537 2538 [('', 'three', '', 'three'),
2538 2539 ('', 'four', '', 'four'),
2539 2540 ('', 'five', '', 'five'),
2540 2541 ] + remoteopts,
2541 2542 _('REPO [OPTIONS]... [ONE [TWO]]'))
2542 2543 def debugwireargs(ui, repopath, *vals, **opts):
2543 2544 repo = hg.peer(ui, opts, repopath)
2544 2545 for opt in remoteopts:
2545 2546 del opts[opt[1]]
2546 2547 args = {}
2547 2548 for k, v in opts.iteritems():
2548 2549 if v:
2549 2550 args[k] = v
2550 2551 # run twice to check that we don't mess up the stream for the next command
2551 2552 res1 = repo.debugwireargs(*vals, **args)
2552 2553 res2 = repo.debugwireargs(*vals, **args)
2553 2554 ui.write("%s\n" % res1)
2554 2555 if res1 != res2:
2555 2556 ui.warn("%s\n" % res2)
2556 2557
2557 2558 @command('^diff',
2558 2559 [('r', 'rev', [], _('revision'), _('REV')),
2559 2560 ('c', 'change', '', _('change made by revision'), _('REV'))
2560 2561 ] + diffopts + diffopts2 + walkopts + subrepoopts,
2561 2562 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'))
2562 2563 def diff(ui, repo, *pats, **opts):
2563 2564 """diff repository (or selected files)
2564 2565
2565 2566 Show differences between revisions for the specified files.
2566 2567
2567 2568 Differences between files are shown using the unified diff format.
2568 2569
2569 2570 .. note::
2570 2571 diff may generate unexpected results for merges, as it will
2571 2572 default to comparing against the working directory's first
2572 2573 parent changeset if no revisions are specified.
2573 2574
2574 2575 When two revision arguments are given, then changes are shown
2575 2576 between those revisions. If only one revision is specified then
2576 2577 that revision is compared to the working directory, and, when no
2577 2578 revisions are specified, the working directory files are compared
2578 2579 to its parent.
2579 2580
2580 2581 Alternatively you can specify -c/--change with a revision to see
2581 2582 the changes in that changeset relative to its first parent.
2582 2583
2583 2584 Without the -a/--text option, diff will avoid generating diffs of
2584 2585 files it detects as binary. With -a, diff will generate a diff
2585 2586 anyway, probably with undesirable results.
2586 2587
2587 2588 Use the -g/--git option to generate diffs in the git extended diff
2588 2589 format. For more information, read :hg:`help diffs`.
2589 2590
2590 2591 .. container:: verbose
2591 2592
2592 2593 Examples:
2593 2594
2594 2595 - compare a file in the current working directory to its parent::
2595 2596
2596 2597 hg diff foo.c
2597 2598
2598 2599 - compare two historical versions of a directory, with rename info::
2599 2600
2600 2601 hg diff --git -r 1.0:1.2 lib/
2601 2602
2602 2603 - get change stats relative to the last change on some date::
2603 2604
2604 2605 hg diff --stat -r "date('may 2')"
2605 2606
2606 2607 - diff all newly-added files that contain a keyword::
2607 2608
2608 2609 hg diff "set:added() and grep(GNU)"
2609 2610
2610 2611 - compare a revision and its parents::
2611 2612
2612 2613 hg diff -c 9353 # compare against first parent
2613 2614 hg diff -r 9353^:9353 # same using revset syntax
2614 2615 hg diff -r 9353^2:9353 # compare against the second parent
2615 2616
2616 2617 Returns 0 on success.
2617 2618 """
2618 2619
2619 2620 revs = opts.get('rev')
2620 2621 change = opts.get('change')
2621 2622 stat = opts.get('stat')
2622 2623 reverse = opts.get('reverse')
2623 2624
2624 2625 if revs and change:
2625 2626 msg = _('cannot specify --rev and --change at the same time')
2626 2627 raise util.Abort(msg)
2627 2628 elif change:
2628 2629 node2 = scmutil.revsingle(repo, change, None).node()
2629 2630 node1 = repo[node2].p1().node()
2630 2631 else:
2631 2632 node1, node2 = scmutil.revpair(repo, revs)
2632 2633
2633 2634 if reverse:
2634 2635 node1, node2 = node2, node1
2635 2636
2636 2637 diffopts = patch.diffopts(ui, opts)
2637 2638 m = scmutil.match(repo[node2], pats, opts)
2638 2639 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
2639 2640 listsubrepos=opts.get('subrepos'))
2640 2641
2641 2642 @command('^export',
2642 2643 [('o', 'output', '',
2643 2644 _('print output to file with formatted name'), _('FORMAT')),
2644 2645 ('', 'switch-parent', None, _('diff against the second parent')),
2645 2646 ('r', 'rev', [], _('revisions to export'), _('REV')),
2646 2647 ] + diffopts,
2647 2648 _('[OPTION]... [-o OUTFILESPEC] [-r] REV...'))
2648 2649 def export(ui, repo, *changesets, **opts):
2649 2650 """dump the header and diffs for one or more changesets
2650 2651
2651 2652 Print the changeset header and diffs for one or more revisions.
2652 2653
2653 2654 The information shown in the changeset header is: author, date,
2654 2655 branch name (if non-default), changeset hash, parent(s) and commit
2655 2656 comment.
2656 2657
2657 2658 .. note::
2658 2659 export may generate unexpected diff output for merge
2659 2660 changesets, as it will compare the merge changeset against its
2660 2661 first parent only.
2661 2662
2662 2663 Output may be to a file, in which case the name of the file is
2663 2664 given using a format string. The formatting rules are as follows:
2664 2665
2665 2666 :``%%``: literal "%" character
2666 2667 :``%H``: changeset hash (40 hexadecimal digits)
2667 2668 :``%N``: number of patches being generated
2668 2669 :``%R``: changeset revision number
2669 2670 :``%b``: basename of the exporting repository
2670 2671 :``%h``: short-form changeset hash (12 hexadecimal digits)
2671 2672 :``%m``: first line of the commit message (only alphanumeric characters)
2672 2673 :``%n``: zero-padded sequence number, starting at 1
2673 2674 :``%r``: zero-padded changeset revision number
2674 2675
2675 2676 Without the -a/--text option, export will avoid generating diffs
2676 2677 of files it detects as binary. With -a, export will generate a
2677 2678 diff anyway, probably with undesirable results.
2678 2679
2679 2680 Use the -g/--git option to generate diffs in the git extended diff
2680 2681 format. See :hg:`help diffs` for more information.
2681 2682
2682 2683 With the --switch-parent option, the diff will be against the
2683 2684 second parent. It can be useful to review a merge.
2684 2685
2685 2686 .. container:: verbose
2686 2687
2687 2688 Examples:
2688 2689
2689 2690 - use export and import to transplant a bugfix to the current
2690 2691 branch::
2691 2692
2692 2693 hg export -r 9353 | hg import -
2693 2694
2694 2695 - export all the changesets between two revisions to a file with
2695 2696 rename information::
2696 2697
2697 2698 hg export --git -r 123:150 > changes.txt
2698 2699
2699 2700 - split outgoing changes into a series of patches with
2700 2701 descriptive names::
2701 2702
2702 2703 hg export -r "outgoing()" -o "%n-%m.patch"
2703 2704
2704 2705 Returns 0 on success.
2705 2706 """
2706 2707 changesets += tuple(opts.get('rev', []))
2707 2708 revs = scmutil.revrange(repo, changesets)
2708 2709 if not revs:
2709 2710 raise util.Abort(_("export requires at least one changeset"))
2710 2711 if len(revs) > 1:
2711 2712 ui.note(_('exporting patches:\n'))
2712 2713 else:
2713 2714 ui.note(_('exporting patch:\n'))
2714 2715 cmdutil.export(repo, revs, template=opts.get('output'),
2715 2716 switch_parent=opts.get('switch_parent'),
2716 2717 opts=patch.diffopts(ui, opts))
2717 2718
2718 2719 @command('^forget', walkopts, _('[OPTION]... FILE...'))
2719 2720 def forget(ui, repo, *pats, **opts):
2720 2721 """forget the specified files on the next commit
2721 2722
2722 2723 Mark the specified files so they will no longer be tracked
2723 2724 after the next commit.
2724 2725
2725 2726 This only removes files from the current branch, not from the
2726 2727 entire project history, and it does not delete them from the
2727 2728 working directory.
2728 2729
2729 2730 To undo a forget before the next commit, see :hg:`add`.
2730 2731
2731 2732 .. container:: verbose
2732 2733
2733 2734 Examples:
2734 2735
2735 2736 - forget newly-added binary files::
2736 2737
2737 2738 hg forget "set:added() and binary()"
2738 2739
2739 2740 - forget files that would be excluded by .hgignore::
2740 2741
2741 2742 hg forget "set:hgignore()"
2742 2743
2743 2744 Returns 0 on success.
2744 2745 """
2745 2746
2746 2747 if not pats:
2747 2748 raise util.Abort(_('no files specified'))
2748 2749
2749 2750 m = scmutil.match(repo[None], pats, opts)
2750 2751 rejected = cmdutil.forget(ui, repo, m, prefix="", explicitonly=False)[0]
2751 2752 return rejected and 1 or 0
2752 2753
2753 2754 @command(
2754 2755 'graft',
2755 2756 [('r', 'rev', [], _('revisions to graft'), _('REV')),
2756 2757 ('c', 'continue', False, _('resume interrupted graft')),
2757 2758 ('e', 'edit', False, _('invoke editor on commit messages')),
2758 2759 ('', 'log', None, _('append graft info to log message')),
2759 2760 ('D', 'currentdate', False,
2760 2761 _('record the current date as commit date')),
2761 2762 ('U', 'currentuser', False,
2762 2763 _('record the current user as committer'), _('DATE'))]
2763 2764 + commitopts2 + mergetoolopts + dryrunopts,
2764 2765 _('[OPTION]... [-r] REV...'))
2765 2766 def graft(ui, repo, *revs, **opts):
2766 2767 '''copy changes from other branches onto the current branch
2767 2768
2768 2769 This command uses Mercurial's merge logic to copy individual
2769 2770 changes from other branches without merging branches in the
2770 2771 history graph. This is sometimes known as 'backporting' or
2771 2772 'cherry-picking'. By default, graft will copy user, date, and
2772 2773 description from the source changesets.
2773 2774
2774 2775 Changesets that are ancestors of the current revision, that have
2775 2776 already been grafted, or that are merges will be skipped.
2776 2777
2777 2778 If --log is specified, log messages will have a comment appended
2778 2779 of the form::
2779 2780
2780 2781 (grafted from CHANGESETHASH)
2781 2782
2782 2783 If a graft merge results in conflicts, the graft process is
2783 2784 interrupted so that the current merge can be manually resolved.
2784 2785 Once all conflicts are addressed, the graft process can be
2785 2786 continued with the -c/--continue option.
2786 2787
2787 2788 .. note::
2788 2789 The -c/--continue option does not reapply earlier options.
2789 2790
2790 2791 .. container:: verbose
2791 2792
2792 2793 Examples:
2793 2794
2794 2795 - copy a single change to the stable branch and edit its description::
2795 2796
2796 2797 hg update stable
2797 2798 hg graft --edit 9393
2798 2799
2799 2800 - graft a range of changesets with one exception, updating dates::
2800 2801
2801 2802 hg graft -D "2085::2093 and not 2091"
2802 2803
2803 2804 - continue a graft after resolving conflicts::
2804 2805
2805 2806 hg graft -c
2806 2807
2807 2808 - show the source of a grafted changeset::
2808 2809
2809 2810 hg log --debug -r tip
2810 2811
2811 2812 Returns 0 on successful completion.
2812 2813 '''
2813 2814
2814 2815 revs = list(revs)
2815 2816 revs.extend(opts['rev'])
2816 2817
2817 2818 if not opts.get('user') and opts.get('currentuser'):
2818 2819 opts['user'] = ui.username()
2819 2820 if not opts.get('date') and opts.get('currentdate'):
2820 2821 opts['date'] = "%d %d" % util.makedate()
2821 2822
2822 2823 editor = None
2823 2824 if opts.get('edit'):
2824 2825 editor = cmdutil.commitforceeditor
2825 2826
2826 2827 cont = False
2827 2828 if opts['continue']:
2828 2829 cont = True
2829 2830 if revs:
2830 2831 raise util.Abort(_("can't specify --continue and revisions"))
2831 2832 # read in unfinished revisions
2832 2833 try:
2833 2834 nodes = repo.opener.read('graftstate').splitlines()
2834 2835 revs = [repo[node].rev() for node in nodes]
2835 2836 except IOError, inst:
2836 2837 if inst.errno != errno.ENOENT:
2837 2838 raise
2838 2839 raise util.Abort(_("no graft state found, can't continue"))
2839 2840 else:
2840 2841 cmdutil.bailifchanged(repo)
2841 2842 if not revs:
2842 2843 raise util.Abort(_('no revisions specified'))
2843 2844 revs = scmutil.revrange(repo, revs)
2844 2845
2845 2846 # check for merges
2846 2847 for rev in repo.revs('%ld and merge()', revs):
2847 2848 ui.warn(_('skipping ungraftable merge revision %s\n') % rev)
2848 2849 revs.remove(rev)
2849 2850 if not revs:
2850 2851 return -1
2851 2852
2852 2853 # check for ancestors of dest branch
2853 2854 for rev in repo.revs('::. and %ld', revs):
2854 2855 ui.warn(_('skipping ancestor revision %s\n') % rev)
2855 2856 revs.remove(rev)
2856 2857 if not revs:
2857 2858 return -1
2858 2859
2859 2860 # analyze revs for earlier grafts
2860 2861 ids = {}
2861 2862 for ctx in repo.set("%ld", revs):
2862 2863 ids[ctx.hex()] = ctx.rev()
2863 2864 n = ctx.extra().get('source')
2864 2865 if n:
2865 2866 ids[n] = ctx.rev()
2866 2867
2867 2868 # check ancestors for earlier grafts
2868 2869 ui.debug('scanning for duplicate grafts\n')
2869 2870 for ctx in repo.set("::. - ::%ld", revs):
2870 2871 n = ctx.extra().get('source')
2871 2872 if n in ids:
2872 2873 r = repo[n].rev()
2873 2874 if r in revs:
2874 2875 ui.warn(_('skipping already grafted revision %s\n') % r)
2875 2876 revs.remove(r)
2876 2877 elif ids[n] in revs:
2877 2878 ui.warn(_('skipping already grafted revision %s '
2878 2879 '(same origin %d)\n') % (ids[n], r))
2879 2880 revs.remove(ids[n])
2880 2881 elif ctx.hex() in ids:
2881 2882 r = ids[ctx.hex()]
2882 2883 ui.warn(_('skipping already grafted revision %s '
2883 2884 '(was grafted from %d)\n') % (r, ctx.rev()))
2884 2885 revs.remove(r)
2885 2886 if not revs:
2886 2887 return -1
2887 2888
2888 2889 wlock = repo.wlock()
2889 2890 try:
2890 2891 current = repo['.']
2891 2892 for pos, ctx in enumerate(repo.set("%ld", revs)):
2892 2893
2893 2894 ui.status(_('grafting revision %s\n') % ctx.rev())
2894 2895 if opts.get('dry_run'):
2895 2896 continue
2896 2897
2897 2898 source = ctx.extra().get('source')
2898 2899 if not source:
2899 2900 source = ctx.hex()
2900 2901 extra = {'source': source}
2901 2902 user = ctx.user()
2902 2903 if opts.get('user'):
2903 2904 user = opts['user']
2904 2905 date = ctx.date()
2905 2906 if opts.get('date'):
2906 2907 date = opts['date']
2907 2908 message = ctx.description()
2908 2909 if opts.get('log'):
2909 2910 message += '\n(grafted from %s)' % ctx.hex()
2910 2911
2911 2912 # we don't merge the first commit when continuing
2912 2913 if not cont:
2913 2914 # perform the graft merge with p1(rev) as 'ancestor'
2914 2915 try:
2915 2916 # ui.forcemerge is an internal variable, do not document
2916 2917 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
2917 2918 stats = mergemod.update(repo, ctx.node(), True, True, False,
2918 2919 ctx.p1().node())
2919 2920 finally:
2920 2921 repo.ui.setconfig('ui', 'forcemerge', '')
2921 2922 # report any conflicts
2922 2923 if stats and stats[3] > 0:
2923 2924 # write out state for --continue
2924 2925 nodelines = [repo[rev].hex() + "\n" for rev in revs[pos:]]
2925 2926 repo.opener.write('graftstate', ''.join(nodelines))
2926 2927 raise util.Abort(
2927 2928 _("unresolved conflicts, can't continue"),
2928 2929 hint=_('use hg resolve and hg graft --continue'))
2929 2930 else:
2930 2931 cont = False
2931 2932
2932 2933 # drop the second merge parent
2933 2934 repo.setparents(current.node(), nullid)
2934 2935 repo.dirstate.write()
2935 2936 # fix up dirstate for copies and renames
2936 2937 cmdutil.duplicatecopies(repo, ctx.rev(), ctx.p1().rev())
2937 2938
2938 2939 # commit
2939 2940 node = repo.commit(text=message, user=user,
2940 2941 date=date, extra=extra, editor=editor)
2941 2942 if node is None:
2942 2943 ui.status(_('graft for revision %s is empty\n') % ctx.rev())
2943 2944 else:
2944 2945 current = repo[node]
2945 2946 finally:
2946 2947 wlock.release()
2947 2948
2948 2949 # remove state when we complete successfully
2949 2950 if not opts.get('dry_run') and os.path.exists(repo.join('graftstate')):
2950 2951 util.unlinkpath(repo.join('graftstate'))
2951 2952
2952 2953 return 0
2953 2954
2954 2955 @command('grep',
2955 2956 [('0', 'print0', None, _('end fields with NUL')),
2956 2957 ('', 'all', None, _('print all revisions that match')),
2957 2958 ('a', 'text', None, _('treat all files as text')),
2958 2959 ('f', 'follow', None,
2959 2960 _('follow changeset history,'
2960 2961 ' or file history across copies and renames')),
2961 2962 ('i', 'ignore-case', None, _('ignore case when matching')),
2962 2963 ('l', 'files-with-matches', None,
2963 2964 _('print only filenames and revisions that match')),
2964 2965 ('n', 'line-number', None, _('print matching line numbers')),
2965 2966 ('r', 'rev', [],
2966 2967 _('only search files changed within revision range'), _('REV')),
2967 2968 ('u', 'user', None, _('list the author (long with -v)')),
2968 2969 ('d', 'date', None, _('list the date (short with -q)')),
2969 2970 ] + walkopts,
2970 2971 _('[OPTION]... PATTERN [FILE]...'))
2971 2972 def grep(ui, repo, pattern, *pats, **opts):
2972 2973 """search for a pattern in specified files and revisions
2973 2974
2974 2975 Search revisions of files for a regular expression.
2975 2976
2976 2977 This command behaves differently than Unix grep. It only accepts
2977 2978 Python/Perl regexps. It searches repository history, not the
2978 2979 working directory. It always prints the revision number in which a
2979 2980 match appears.
2980 2981
2981 2982 By default, grep only prints output for the first revision of a
2982 2983 file in which it finds a match. To get it to print every revision
2983 2984 that contains a change in match status ("-" for a match that
2984 2985 becomes a non-match, or "+" for a non-match that becomes a match),
2985 2986 use the --all flag.
2986 2987
2987 2988 Returns 0 if a match is found, 1 otherwise.
2988 2989 """
2989 2990 reflags = re.M
2990 2991 if opts.get('ignore_case'):
2991 2992 reflags |= re.I
2992 2993 try:
2993 2994 regexp = re.compile(pattern, reflags)
2994 2995 except re.error, inst:
2995 2996 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
2996 2997 return 1
2997 2998 sep, eol = ':', '\n'
2998 2999 if opts.get('print0'):
2999 3000 sep = eol = '\0'
3000 3001
3001 3002 getfile = util.lrucachefunc(repo.file)
3002 3003
3003 3004 def matchlines(body):
3004 3005 begin = 0
3005 3006 linenum = 0
3006 3007 while begin < len(body):
3007 3008 match = regexp.search(body, begin)
3008 3009 if not match:
3009 3010 break
3010 3011 mstart, mend = match.span()
3011 3012 linenum += body.count('\n', begin, mstart) + 1
3012 3013 lstart = body.rfind('\n', begin, mstart) + 1 or begin
3013 3014 begin = body.find('\n', mend) + 1 or len(body) + 1
3014 3015 lend = begin - 1
3015 3016 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
3016 3017
3017 3018 class linestate(object):
3018 3019 def __init__(self, line, linenum, colstart, colend):
3019 3020 self.line = line
3020 3021 self.linenum = linenum
3021 3022 self.colstart = colstart
3022 3023 self.colend = colend
3023 3024
3024 3025 def __hash__(self):
3025 3026 return hash((self.linenum, self.line))
3026 3027
3027 3028 def __eq__(self, other):
3028 3029 return self.line == other.line
3029 3030
3030 3031 matches = {}
3031 3032 copies = {}
3032 3033 def grepbody(fn, rev, body):
3033 3034 matches[rev].setdefault(fn, [])
3034 3035 m = matches[rev][fn]
3035 3036 for lnum, cstart, cend, line in matchlines(body):
3036 3037 s = linestate(line, lnum, cstart, cend)
3037 3038 m.append(s)
3038 3039
3039 3040 def difflinestates(a, b):
3040 3041 sm = difflib.SequenceMatcher(None, a, b)
3041 3042 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
3042 3043 if tag == 'insert':
3043 3044 for i in xrange(blo, bhi):
3044 3045 yield ('+', b[i])
3045 3046 elif tag == 'delete':
3046 3047 for i in xrange(alo, ahi):
3047 3048 yield ('-', a[i])
3048 3049 elif tag == 'replace':
3049 3050 for i in xrange(alo, ahi):
3050 3051 yield ('-', a[i])
3051 3052 for i in xrange(blo, bhi):
3052 3053 yield ('+', b[i])
3053 3054
3054 3055 def display(fn, ctx, pstates, states):
3055 3056 rev = ctx.rev()
3056 3057 datefunc = ui.quiet and util.shortdate or util.datestr
3057 3058 found = False
3058 3059 filerevmatches = {}
3059 3060 def binary():
3060 3061 flog = getfile(fn)
3061 3062 return util.binary(flog.read(ctx.filenode(fn)))
3062 3063
3063 3064 if opts.get('all'):
3064 3065 iter = difflinestates(pstates, states)
3065 3066 else:
3066 3067 iter = [('', l) for l in states]
3067 3068 for change, l in iter:
3068 3069 cols = [(fn, 'grep.filename'), (str(rev), 'grep.rev')]
3069 3070 before, match, after = None, None, None
3070 3071
3071 3072 if opts.get('line_number'):
3072 3073 cols.append((str(l.linenum), 'grep.linenumber'))
3073 3074 if opts.get('all'):
3074 3075 cols.append((change, 'grep.change'))
3075 3076 if opts.get('user'):
3076 3077 cols.append((ui.shortuser(ctx.user()), 'grep.user'))
3077 3078 if opts.get('date'):
3078 3079 cols.append((datefunc(ctx.date()), 'grep.date'))
3079 3080 if opts.get('files_with_matches'):
3080 3081 c = (fn, rev)
3081 3082 if c in filerevmatches:
3082 3083 continue
3083 3084 filerevmatches[c] = 1
3084 3085 else:
3085 3086 before = l.line[:l.colstart]
3086 3087 match = l.line[l.colstart:l.colend]
3087 3088 after = l.line[l.colend:]
3088 3089 for col, label in cols[:-1]:
3089 3090 ui.write(col, label=label)
3090 3091 ui.write(sep, label='grep.sep')
3091 3092 ui.write(cols[-1][0], label=cols[-1][1])
3092 3093 if before is not None:
3093 3094 ui.write(sep, label='grep.sep')
3094 3095 if not opts.get('text') and binary():
3095 3096 ui.write(" Binary file matches")
3096 3097 else:
3097 3098 ui.write(before)
3098 3099 ui.write(match, label='grep.match')
3099 3100 ui.write(after)
3100 3101 ui.write(eol)
3101 3102 found = True
3102 3103 return found
3103 3104
3104 3105 skip = {}
3105 3106 revfiles = {}
3106 3107 matchfn = scmutil.match(repo[None], pats, opts)
3107 3108 found = False
3108 3109 follow = opts.get('follow')
3109 3110
3110 3111 def prep(ctx, fns):
3111 3112 rev = ctx.rev()
3112 3113 pctx = ctx.p1()
3113 3114 parent = pctx.rev()
3114 3115 matches.setdefault(rev, {})
3115 3116 matches.setdefault(parent, {})
3116 3117 files = revfiles.setdefault(rev, [])
3117 3118 for fn in fns:
3118 3119 flog = getfile(fn)
3119 3120 try:
3120 3121 fnode = ctx.filenode(fn)
3121 3122 except error.LookupError:
3122 3123 continue
3123 3124
3124 3125 copied = flog.renamed(fnode)
3125 3126 copy = follow and copied and copied[0]
3126 3127 if copy:
3127 3128 copies.setdefault(rev, {})[fn] = copy
3128 3129 if fn in skip:
3129 3130 if copy:
3130 3131 skip[copy] = True
3131 3132 continue
3132 3133 files.append(fn)
3133 3134
3134 3135 if fn not in matches[rev]:
3135 3136 grepbody(fn, rev, flog.read(fnode))
3136 3137
3137 3138 pfn = copy or fn
3138 3139 if pfn not in matches[parent]:
3139 3140 try:
3140 3141 fnode = pctx.filenode(pfn)
3141 3142 grepbody(pfn, parent, flog.read(fnode))
3142 3143 except error.LookupError:
3143 3144 pass
3144 3145
3145 3146 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
3146 3147 rev = ctx.rev()
3147 3148 parent = ctx.p1().rev()
3148 3149 for fn in sorted(revfiles.get(rev, [])):
3149 3150 states = matches[rev][fn]
3150 3151 copy = copies.get(rev, {}).get(fn)
3151 3152 if fn in skip:
3152 3153 if copy:
3153 3154 skip[copy] = True
3154 3155 continue
3155 3156 pstates = matches.get(parent, {}).get(copy or fn, [])
3156 3157 if pstates or states:
3157 3158 r = display(fn, ctx, pstates, states)
3158 3159 found = found or r
3159 3160 if r and not opts.get('all'):
3160 3161 skip[fn] = True
3161 3162 if copy:
3162 3163 skip[copy] = True
3163 3164 del matches[rev]
3164 3165 del revfiles[rev]
3165 3166
3166 3167 return not found
3167 3168
3168 3169 @command('heads',
3169 3170 [('r', 'rev', '',
3170 3171 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
3171 3172 ('t', 'topo', False, _('show topological heads only')),
3172 3173 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
3173 3174 ('c', 'closed', False, _('show normal and closed branch heads')),
3174 3175 ] + templateopts,
3175 3176 _('[-ct] [-r STARTREV] [REV]...'))
3176 3177 def heads(ui, repo, *branchrevs, **opts):
3177 3178 """show current repository heads or show branch heads
3178 3179
3179 3180 With no arguments, show all repository branch heads.
3180 3181
3181 3182 Repository "heads" are changesets with no child changesets. They are
3182 3183 where development generally takes place and are the usual targets
3183 3184 for update and merge operations. Branch heads are changesets that have
3184 3185 no child changeset on the same branch.
3185 3186
3186 3187 If one or more REVs are given, only branch heads on the branches
3187 3188 associated with the specified changesets are shown. This means
3188 3189 that you can use :hg:`heads foo` to see the heads on a branch
3189 3190 named ``foo``.
3190 3191
3191 3192 If -c/--closed is specified, also show branch heads marked closed
3192 3193 (see :hg:`commit --close-branch`).
3193 3194
3194 3195 If STARTREV is specified, only those heads that are descendants of
3195 3196 STARTREV will be displayed.
3196 3197
3197 3198 If -t/--topo is specified, named branch mechanics will be ignored and only
3198 3199 changesets without children will be shown.
3199 3200
3200 3201 Returns 0 if matching heads are found, 1 if not.
3201 3202 """
3202 3203
3203 3204 start = None
3204 3205 if 'rev' in opts:
3205 3206 start = scmutil.revsingle(repo, opts['rev'], None).node()
3206 3207
3207 3208 if opts.get('topo'):
3208 3209 heads = [repo[h] for h in repo.heads(start)]
3209 3210 else:
3210 3211 heads = []
3211 3212 for branch in repo.branchmap():
3212 3213 heads += repo.branchheads(branch, start, opts.get('closed'))
3213 3214 heads = [repo[h] for h in heads]
3214 3215
3215 3216 if branchrevs:
3216 3217 branches = set(repo[br].branch() for br in branchrevs)
3217 3218 heads = [h for h in heads if h.branch() in branches]
3218 3219
3219 3220 if opts.get('active') and branchrevs:
3220 3221 dagheads = repo.heads(start)
3221 3222 heads = [h for h in heads if h.node() in dagheads]
3222 3223
3223 3224 if branchrevs:
3224 3225 haveheads = set(h.branch() for h in heads)
3225 3226 if branches - haveheads:
3226 3227 headless = ', '.join(b for b in branches - haveheads)
3227 3228 msg = _('no open branch heads found on branches %s')
3228 3229 if opts.get('rev'):
3229 3230 msg += _(' (started at %s)') % opts['rev']
3230 3231 ui.warn((msg + '\n') % headless)
3231 3232
3232 3233 if not heads:
3233 3234 return 1
3234 3235
3235 3236 heads = sorted(heads, key=lambda x: -x.rev())
3236 3237 displayer = cmdutil.show_changeset(ui, repo, opts)
3237 3238 for ctx in heads:
3238 3239 displayer.show(ctx)
3239 3240 displayer.close()
3240 3241
3241 3242 @command('help',
3242 3243 [('e', 'extension', None, _('show only help for extensions')),
3243 3244 ('c', 'command', None, _('show only help for commands')),
3244 3245 ('k', 'keyword', '', _('show topics matching keyword')),
3245 3246 ],
3246 3247 _('[-ec] [TOPIC]'))
3247 3248 def help_(ui, name=None, unknowncmd=False, full=True, **opts):
3248 3249 """show help for a given topic or a help overview
3249 3250
3250 3251 With no arguments, print a list of commands with short help messages.
3251 3252
3252 3253 Given a topic, extension, or command name, print help for that
3253 3254 topic.
3254 3255
3255 3256 Returns 0 if successful.
3256 3257 """
3257 3258
3258 3259 textwidth = min(ui.termwidth(), 80) - 2
3259 3260
3260 3261 def helpcmd(name):
3261 3262 try:
3262 3263 aliases, entry = cmdutil.findcmd(name, table, strict=unknowncmd)
3263 3264 except error.AmbiguousCommand, inst:
3264 3265 # py3k fix: except vars can't be used outside the scope of the
3265 3266 # except block, nor can be used inside a lambda. python issue4617
3266 3267 prefix = inst.args[0]
3267 3268 select = lambda c: c.lstrip('^').startswith(prefix)
3268 3269 rst = helplist(select)
3269 3270 return rst
3270 3271
3271 3272 rst = []
3272 3273
3273 3274 # check if it's an invalid alias and display its error if it is
3274 3275 if getattr(entry[0], 'badalias', False):
3275 3276 if not unknowncmd:
3276 3277 ui.pushbuffer()
3277 3278 entry[0](ui)
3278 3279 rst.append(ui.popbuffer())
3279 3280 return rst
3280 3281
3281 3282 # synopsis
3282 3283 if len(entry) > 2:
3283 3284 if entry[2].startswith('hg'):
3284 3285 rst.append("%s\n" % entry[2])
3285 3286 else:
3286 3287 rst.append('hg %s %s\n' % (aliases[0], entry[2]))
3287 3288 else:
3288 3289 rst.append('hg %s\n' % aliases[0])
3289 3290 # aliases
3290 3291 if full and not ui.quiet and len(aliases) > 1:
3291 3292 rst.append(_("\naliases: %s\n") % ', '.join(aliases[1:]))
3292 3293 rst.append('\n')
3293 3294
3294 3295 # description
3295 3296 doc = gettext(entry[0].__doc__)
3296 3297 if not doc:
3297 3298 doc = _("(no help text available)")
3298 3299 if util.safehasattr(entry[0], 'definition'): # aliased command
3299 3300 if entry[0].definition.startswith('!'): # shell alias
3300 3301 doc = _('shell alias for::\n\n %s') % entry[0].definition[1:]
3301 3302 else:
3302 3303 doc = _('alias for: hg %s\n\n%s') % (entry[0].definition, doc)
3303 3304 doc = doc.splitlines(True)
3304 3305 if ui.quiet or not full:
3305 3306 rst.append(doc[0])
3306 3307 else:
3307 3308 rst.extend(doc)
3308 3309 rst.append('\n')
3309 3310
3310 3311 # check if this command shadows a non-trivial (multi-line)
3311 3312 # extension help text
3312 3313 try:
3313 3314 mod = extensions.find(name)
3314 3315 doc = gettext(mod.__doc__) or ''
3315 3316 if '\n' in doc.strip():
3316 3317 msg = _('use "hg help -e %s" to show help for '
3317 3318 'the %s extension') % (name, name)
3318 3319 rst.append('\n%s\n' % msg)
3319 3320 except KeyError:
3320 3321 pass
3321 3322
3322 3323 # options
3323 3324 if not ui.quiet and entry[1]:
3324 3325 rst.append('\n%s\n\n' % _("options:"))
3325 3326 rst.append(help.optrst(entry[1], ui.verbose))
3326 3327
3327 3328 if ui.verbose:
3328 3329 rst.append('\n%s\n\n' % _("global options:"))
3329 3330 rst.append(help.optrst(globalopts, ui.verbose))
3330 3331
3331 3332 if not ui.verbose:
3332 3333 if not full:
3333 3334 rst.append(_('\nuse "hg help %s" to show the full help text\n')
3334 3335 % name)
3335 3336 elif not ui.quiet:
3336 3337 omitted = _('use "hg -v help %s" to show more complete'
3337 3338 ' help and the global options') % name
3338 3339 notomitted = _('use "hg -v help %s" to show'
3339 3340 ' the global options') % name
3340 3341 help.indicateomitted(rst, omitted, notomitted)
3341 3342
3342 3343 return rst
3343 3344
3344 3345
3345 3346 def helplist(select=None):
3346 3347 # list of commands
3347 3348 if name == "shortlist":
3348 3349 header = _('basic commands:\n\n')
3349 3350 else:
3350 3351 header = _('list of commands:\n\n')
3351 3352
3352 3353 h = {}
3353 3354 cmds = {}
3354 3355 for c, e in table.iteritems():
3355 3356 f = c.split("|", 1)[0]
3356 3357 if select and not select(f):
3357 3358 continue
3358 3359 if (not select and name != 'shortlist' and
3359 3360 e[0].__module__ != __name__):
3360 3361 continue
3361 3362 if name == "shortlist" and not f.startswith("^"):
3362 3363 continue
3363 3364 f = f.lstrip("^")
3364 3365 if not ui.debugflag and f.startswith("debug"):
3365 3366 continue
3366 3367 doc = e[0].__doc__
3367 3368 if doc and 'DEPRECATED' in doc and not ui.verbose:
3368 3369 continue
3369 3370 doc = gettext(doc)
3370 3371 if not doc:
3371 3372 doc = _("(no help text available)")
3372 3373 h[f] = doc.splitlines()[0].rstrip()
3373 3374 cmds[f] = c.lstrip("^")
3374 3375
3375 3376 rst = []
3376 3377 if not h:
3377 3378 if not ui.quiet:
3378 3379 rst.append(_('no commands defined\n'))
3379 3380 return rst
3380 3381
3381 3382 if not ui.quiet:
3382 3383 rst.append(header)
3383 3384 fns = sorted(h)
3384 3385 for f in fns:
3385 3386 if ui.verbose:
3386 3387 commands = cmds[f].replace("|",", ")
3387 3388 rst.append(" :%s: %s\n" % (commands, h[f]))
3388 3389 else:
3389 3390 rst.append(' :%s: %s\n' % (f, h[f]))
3390 3391
3391 3392 if not name:
3392 3393 exts = help.listexts(_('enabled extensions:'), extensions.enabled())
3393 3394 if exts:
3394 3395 rst.append('\n')
3395 3396 rst.extend(exts)
3396 3397
3397 3398 rst.append(_("\nadditional help topics:\n\n"))
3398 3399 topics = []
3399 3400 for names, header, doc in help.helptable:
3400 3401 topics.append((names[0], header))
3401 3402 for t, desc in topics:
3402 3403 rst.append(" :%s: %s\n" % (t, desc))
3403 3404
3404 3405 optlist = []
3405 3406 if not ui.quiet:
3406 3407 if ui.verbose:
3407 3408 optlist.append((_("global options:"), globalopts))
3408 3409 if name == 'shortlist':
3409 3410 optlist.append((_('use "hg help" for the full list '
3410 3411 'of commands'), ()))
3411 3412 else:
3412 3413 if name == 'shortlist':
3413 3414 msg = _('use "hg help" for the full list of commands '
3414 3415 'or "hg -v" for details')
3415 3416 elif name and not full:
3416 3417 msg = _('use "hg help %s" to show the full help '
3417 3418 'text') % name
3418 3419 else:
3419 3420 msg = _('use "hg -v help%s" to show builtin aliases and '
3420 3421 'global options') % (name and " " + name or "")
3421 3422 optlist.append((msg, ()))
3422 3423
3423 3424 if optlist:
3424 3425 for title, options in optlist:
3425 3426 rst.append('\n%s\n' % title)
3426 3427 if options:
3427 3428 rst.append('\n%s\n' % help.optrst(options, ui.verbose))
3428 3429 return rst
3429 3430
3430 3431 def helptopic(name):
3431 3432 for names, header, doc in help.helptable:
3432 3433 if name in names:
3433 3434 break
3434 3435 else:
3435 3436 raise error.UnknownCommand(name)
3436 3437
3437 3438 rst = ["%s\n\n" % header]
3438 3439 # description
3439 3440 if not doc:
3440 3441 rst.append(" %s\n" % _("(no help text available)"))
3441 3442 if util.safehasattr(doc, '__call__'):
3442 3443 rst += [" %s\n" % l for l in doc().splitlines()]
3443 3444
3444 3445 if not ui.verbose:
3445 3446 omitted = (_('use "hg help -v %s" to show more complete help') %
3446 3447 name)
3447 3448 help.indicateomitted(rst, omitted)
3448 3449
3449 3450 try:
3450 3451 cmdutil.findcmd(name, table)
3451 3452 rst.append(_('\nuse "hg help -c %s" to see help for '
3452 3453 'the %s command\n') % (name, name))
3453 3454 except error.UnknownCommand:
3454 3455 pass
3455 3456 return rst
3456 3457
3457 3458 def helpext(name):
3458 3459 try:
3459 3460 mod = extensions.find(name)
3460 3461 doc = gettext(mod.__doc__) or _('no help text available')
3461 3462 except KeyError:
3462 3463 mod = None
3463 3464 doc = extensions.disabledext(name)
3464 3465 if not doc:
3465 3466 raise error.UnknownCommand(name)
3466 3467
3467 3468 if '\n' not in doc:
3468 3469 head, tail = doc, ""
3469 3470 else:
3470 3471 head, tail = doc.split('\n', 1)
3471 3472 rst = [_('%s extension - %s\n\n') % (name.split('.')[-1], head)]
3472 3473 if tail:
3473 3474 rst.extend(tail.splitlines(True))
3474 3475 rst.append('\n')
3475 3476
3476 3477 if not ui.verbose:
3477 3478 omitted = (_('use "hg help -v %s" to show more complete help') %
3478 3479 name)
3479 3480 help.indicateomitted(rst, omitted)
3480 3481
3481 3482 if mod:
3482 3483 try:
3483 3484 ct = mod.cmdtable
3484 3485 except AttributeError:
3485 3486 ct = {}
3486 3487 modcmds = set([c.split('|', 1)[0] for c in ct])
3487 3488 rst.extend(helplist(modcmds.__contains__))
3488 3489 else:
3489 3490 rst.append(_('use "hg help extensions" for information on enabling '
3490 3491 'extensions\n'))
3491 3492 return rst
3492 3493
3493 3494 def helpextcmd(name):
3494 3495 cmd, ext, mod = extensions.disabledcmd(ui, name,
3495 3496 ui.configbool('ui', 'strict'))
3496 3497 doc = gettext(mod.__doc__).splitlines()[0]
3497 3498
3498 3499 rst = help.listexts(_("'%s' is provided by the following "
3499 3500 "extension:") % cmd, {ext: doc}, indent=4)
3500 3501 rst.append('\n')
3501 3502 rst.append(_('use "hg help extensions" for information on enabling '
3502 3503 'extensions\n'))
3503 3504 return rst
3504 3505
3505 3506
3506 3507 rst = []
3507 3508 kw = opts.get('keyword')
3508 3509 if kw:
3509 3510 matches = help.topicmatch(kw)
3510 3511 for t, title in (('topics', _('Topics')),
3511 3512 ('commands', _('Commands')),
3512 3513 ('extensions', _('Extensions')),
3513 3514 ('extensioncommands', _('Extension Commands'))):
3514 3515 if matches[t]:
3515 3516 rst.append('%s:\n\n' % title)
3516 3517 rst.extend(minirst.maketable(sorted(matches[t]), 1))
3517 3518 rst.append('\n')
3518 3519 elif name and name != 'shortlist':
3519 3520 i = None
3520 3521 if unknowncmd:
3521 3522 queries = (helpextcmd,)
3522 3523 elif opts.get('extension'):
3523 3524 queries = (helpext,)
3524 3525 elif opts.get('command'):
3525 3526 queries = (helpcmd,)
3526 3527 else:
3527 3528 queries = (helptopic, helpcmd, helpext, helpextcmd)
3528 3529 for f in queries:
3529 3530 try:
3530 3531 rst = f(name)
3531 3532 i = None
3532 3533 break
3533 3534 except error.UnknownCommand, inst:
3534 3535 i = inst
3535 3536 if i:
3536 3537 raise i
3537 3538 else:
3538 3539 # program name
3539 3540 if not ui.quiet:
3540 3541 rst = [_("Mercurial Distributed SCM\n"), '\n']
3541 3542 rst.extend(helplist())
3542 3543
3543 3544 keep = ui.verbose and ['verbose'] or []
3544 3545 text = ''.join(rst)
3545 3546 formatted, pruned = minirst.format(text, textwidth, keep=keep)
3546 3547 if 'verbose' in pruned:
3547 3548 keep.append('omitted')
3548 3549 else:
3549 3550 keep.append('notomitted')
3550 3551 formatted, pruned = minirst.format(text, textwidth, keep=keep)
3551 3552 ui.write(formatted)
3552 3553
3553 3554
3554 3555 @command('identify|id',
3555 3556 [('r', 'rev', '',
3556 3557 _('identify the specified revision'), _('REV')),
3557 3558 ('n', 'num', None, _('show local revision number')),
3558 3559 ('i', 'id', None, _('show global revision id')),
3559 3560 ('b', 'branch', None, _('show branch')),
3560 3561 ('t', 'tags', None, _('show tags')),
3561 3562 ('B', 'bookmarks', None, _('show bookmarks')),
3562 3563 ] + remoteopts,
3563 3564 _('[-nibtB] [-r REV] [SOURCE]'))
3564 3565 def identify(ui, repo, source=None, rev=None,
3565 3566 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
3566 3567 """identify the working copy or specified revision
3567 3568
3568 3569 Print a summary identifying the repository state at REV using one or
3569 3570 two parent hash identifiers, followed by a "+" if the working
3570 3571 directory has uncommitted changes, the branch name (if not default),
3571 3572 a list of tags, and a list of bookmarks.
3572 3573
3573 3574 When REV is not given, print a summary of the current state of the
3574 3575 repository.
3575 3576
3576 3577 Specifying a path to a repository root or Mercurial bundle will
3577 3578 cause lookup to operate on that repository/bundle.
3578 3579
3579 3580 .. container:: verbose
3580 3581
3581 3582 Examples:
3582 3583
3583 3584 - generate a build identifier for the working directory::
3584 3585
3585 3586 hg id --id > build-id.dat
3586 3587
3587 3588 - find the revision corresponding to a tag::
3588 3589
3589 3590 hg id -n -r 1.3
3590 3591
3591 3592 - check the most recent revision of a remote repository::
3592 3593
3593 3594 hg id -r tip http://selenic.com/hg/
3594 3595
3595 3596 Returns 0 if successful.
3596 3597 """
3597 3598
3598 3599 if not repo and not source:
3599 3600 raise util.Abort(_("there is no Mercurial repository here "
3600 3601 "(.hg not found)"))
3601 3602
3602 3603 hexfunc = ui.debugflag and hex or short
3603 3604 default = not (num or id or branch or tags or bookmarks)
3604 3605 output = []
3605 3606 revs = []
3606 3607
3607 3608 if source:
3608 3609 source, branches = hg.parseurl(ui.expandpath(source))
3609 3610 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
3610 3611 repo = peer.local()
3611 3612 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
3612 3613
3613 3614 if not repo:
3614 3615 if num or branch or tags:
3615 3616 raise util.Abort(
3616 3617 _("can't query remote revision number, branch, or tags"))
3617 3618 if not rev and revs:
3618 3619 rev = revs[0]
3619 3620 if not rev:
3620 3621 rev = "tip"
3621 3622
3622 3623 remoterev = peer.lookup(rev)
3623 3624 if default or id:
3624 3625 output = [hexfunc(remoterev)]
3625 3626
3626 3627 def getbms():
3627 3628 bms = []
3628 3629
3629 3630 if 'bookmarks' in peer.listkeys('namespaces'):
3630 3631 hexremoterev = hex(remoterev)
3631 3632 bms = [bm for bm, bmr in peer.listkeys('bookmarks').iteritems()
3632 3633 if bmr == hexremoterev]
3633 3634
3634 3635 return bms
3635 3636
3636 3637 if bookmarks:
3637 3638 output.extend(getbms())
3638 3639 elif default and not ui.quiet:
3639 3640 # multiple bookmarks for a single parent separated by '/'
3640 3641 bm = '/'.join(getbms())
3641 3642 if bm:
3642 3643 output.append(bm)
3643 3644 else:
3644 3645 if not rev:
3645 3646 ctx = repo[None]
3646 3647 parents = ctx.parents()
3647 3648 changed = ""
3648 3649 if default or id or num:
3649 3650 if (util.any(repo.status())
3650 3651 or util.any(ctx.sub(s).dirty() for s in ctx.substate)):
3651 3652 changed = '+'
3652 3653 if default or id:
3653 3654 output = ["%s%s" %
3654 3655 ('+'.join([hexfunc(p.node()) for p in parents]), changed)]
3655 3656 if num:
3656 3657 output.append("%s%s" %
3657 3658 ('+'.join([str(p.rev()) for p in parents]), changed))
3658 3659 else:
3659 3660 ctx = scmutil.revsingle(repo, rev)
3660 3661 if default or id:
3661 3662 output = [hexfunc(ctx.node())]
3662 3663 if num:
3663 3664 output.append(str(ctx.rev()))
3664 3665
3665 3666 if default and not ui.quiet:
3666 3667 b = ctx.branch()
3667 3668 if b != 'default':
3668 3669 output.append("(%s)" % b)
3669 3670
3670 3671 # multiple tags for a single parent separated by '/'
3671 3672 t = '/'.join(ctx.tags())
3672 3673 if t:
3673 3674 output.append(t)
3674 3675
3675 3676 # multiple bookmarks for a single parent separated by '/'
3676 3677 bm = '/'.join(ctx.bookmarks())
3677 3678 if bm:
3678 3679 output.append(bm)
3679 3680 else:
3680 3681 if branch:
3681 3682 output.append(ctx.branch())
3682 3683
3683 3684 if tags:
3684 3685 output.extend(ctx.tags())
3685 3686
3686 3687 if bookmarks:
3687 3688 output.extend(ctx.bookmarks())
3688 3689
3689 3690 ui.write("%s\n" % ' '.join(output))
3690 3691
3691 3692 @command('import|patch',
3692 3693 [('p', 'strip', 1,
3693 3694 _('directory strip option for patch. This has the same '
3694 3695 'meaning as the corresponding patch option'), _('NUM')),
3695 3696 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
3696 3697 ('e', 'edit', False, _('invoke editor on commit messages')),
3697 3698 ('f', 'force', None, _('skip check for outstanding uncommitted changes')),
3698 3699 ('', 'no-commit', None,
3699 3700 _("don't commit, just update the working directory")),
3700 3701 ('', 'bypass', None,
3701 3702 _("apply patch without touching the working directory")),
3702 3703 ('', 'exact', None,
3703 3704 _('apply patch to the nodes from which it was generated')),
3704 3705 ('', 'import-branch', None,
3705 3706 _('use any branch information in patch (implied by --exact)'))] +
3706 3707 commitopts + commitopts2 + similarityopts,
3707 3708 _('[OPTION]... PATCH...'))
3708 3709 def import_(ui, repo, patch1=None, *patches, **opts):
3709 3710 """import an ordered set of patches
3710 3711
3711 3712 Import a list of patches and commit them individually (unless
3712 3713 --no-commit is specified).
3713 3714
3714 3715 If there are outstanding changes in the working directory, import
3715 3716 will abort unless given the -f/--force flag.
3716 3717
3717 3718 You can import a patch straight from a mail message. Even patches
3718 3719 as attachments work (to use the body part, it must have type
3719 3720 text/plain or text/x-patch). From and Subject headers of email
3720 3721 message are used as default committer and commit message. All
3721 3722 text/plain body parts before first diff are added to commit
3722 3723 message.
3723 3724
3724 3725 If the imported patch was generated by :hg:`export`, user and
3725 3726 description from patch override values from message headers and
3726 3727 body. Values given on command line with -m/--message and -u/--user
3727 3728 override these.
3728 3729
3729 3730 If --exact is specified, import will set the working directory to
3730 3731 the parent of each patch before applying it, and will abort if the
3731 3732 resulting changeset has a different ID than the one recorded in
3732 3733 the patch. This may happen due to character set problems or other
3733 3734 deficiencies in the text patch format.
3734 3735
3735 3736 Use --bypass to apply and commit patches directly to the
3736 3737 repository, not touching the working directory. Without --exact,
3737 3738 patches will be applied on top of the working directory parent
3738 3739 revision.
3739 3740
3740 3741 With -s/--similarity, hg will attempt to discover renames and
3741 3742 copies in the patch in the same way as :hg:`addremove`.
3742 3743
3743 3744 To read a patch from standard input, use "-" as the patch name. If
3744 3745 a URL is specified, the patch will be downloaded from it.
3745 3746 See :hg:`help dates` for a list of formats valid for -d/--date.
3746 3747
3747 3748 .. container:: verbose
3748 3749
3749 3750 Examples:
3750 3751
3751 3752 - import a traditional patch from a website and detect renames::
3752 3753
3753 3754 hg import -s 80 http://example.com/bugfix.patch
3754 3755
3755 3756 - import a changeset from an hgweb server::
3756 3757
3757 3758 hg import http://www.selenic.com/hg/rev/5ca8c111e9aa
3758 3759
3759 3760 - import all the patches in an Unix-style mbox::
3760 3761
3761 3762 hg import incoming-patches.mbox
3762 3763
3763 3764 - attempt to exactly restore an exported changeset (not always
3764 3765 possible)::
3765 3766
3766 3767 hg import --exact proposed-fix.patch
3767 3768
3768 3769 Returns 0 on success.
3769 3770 """
3770 3771
3771 3772 if not patch1:
3772 3773 raise util.Abort(_('need at least one patch to import'))
3773 3774
3774 3775 patches = (patch1,) + patches
3775 3776
3776 3777 date = opts.get('date')
3777 3778 if date:
3778 3779 opts['date'] = util.parsedate(date)
3779 3780
3780 3781 editor = cmdutil.commiteditor
3781 3782 if opts.get('edit'):
3782 3783 editor = cmdutil.commitforceeditor
3783 3784
3784 3785 update = not opts.get('bypass')
3785 3786 if not update and opts.get('no_commit'):
3786 3787 raise util.Abort(_('cannot use --no-commit with --bypass'))
3787 3788 try:
3788 3789 sim = float(opts.get('similarity') or 0)
3789 3790 except ValueError:
3790 3791 raise util.Abort(_('similarity must be a number'))
3791 3792 if sim < 0 or sim > 100:
3792 3793 raise util.Abort(_('similarity must be between 0 and 100'))
3793 3794 if sim and not update:
3794 3795 raise util.Abort(_('cannot use --similarity with --bypass'))
3795 3796
3796 3797 if (opts.get('exact') or not opts.get('force')) and update:
3797 3798 cmdutil.bailifchanged(repo)
3798 3799
3799 3800 base = opts["base"]
3800 3801 strip = opts["strip"]
3801 3802 wlock = lock = tr = None
3802 3803 msgs = []
3803 3804
3804 3805 def checkexact(repo, n, nodeid):
3805 3806 if opts.get('exact') and hex(n) != nodeid:
3806 3807 repo.rollback()
3807 3808 raise util.Abort(_('patch is damaged or loses information'))
3808 3809
3809 3810 def tryone(ui, hunk, parents):
3810 3811 tmpname, message, user, date, branch, nodeid, p1, p2 = \
3811 3812 patch.extract(ui, hunk)
3812 3813
3813 3814 if not tmpname:
3814 3815 return (None, None)
3815 3816 msg = _('applied to working directory')
3816 3817
3817 3818 try:
3818 3819 cmdline_message = cmdutil.logmessage(ui, opts)
3819 3820 if cmdline_message:
3820 3821 # pickup the cmdline msg
3821 3822 message = cmdline_message
3822 3823 elif message:
3823 3824 # pickup the patch msg
3824 3825 message = message.strip()
3825 3826 else:
3826 3827 # launch the editor
3827 3828 message = None
3828 3829 ui.debug('message:\n%s\n' % message)
3829 3830
3830 3831 if len(parents) == 1:
3831 3832 parents.append(repo[nullid])
3832 3833 if opts.get('exact'):
3833 3834 if not nodeid or not p1:
3834 3835 raise util.Abort(_('not a Mercurial patch'))
3835 3836 p1 = repo[p1]
3836 3837 p2 = repo[p2 or nullid]
3837 3838 elif p2:
3838 3839 try:
3839 3840 p1 = repo[p1]
3840 3841 p2 = repo[p2]
3841 3842 # Without any options, consider p2 only if the
3842 3843 # patch is being applied on top of the recorded
3843 3844 # first parent.
3844 3845 if p1 != parents[0]:
3845 3846 p1 = parents[0]
3846 3847 p2 = repo[nullid]
3847 3848 except error.RepoError:
3848 3849 p1, p2 = parents
3849 3850 else:
3850 3851 p1, p2 = parents
3851 3852
3852 3853 n = None
3853 3854 if update:
3854 3855 if p1 != parents[0]:
3855 3856 hg.clean(repo, p1.node())
3856 3857 if p2 != parents[1]:
3857 3858 repo.setparents(p1.node(), p2.node())
3858 3859
3859 3860 if opts.get('exact') or opts.get('import_branch'):
3860 3861 repo.dirstate.setbranch(branch or 'default')
3861 3862
3862 3863 files = set()
3863 3864 patch.patch(ui, repo, tmpname, strip=strip, files=files,
3864 3865 eolmode=None, similarity=sim / 100.0)
3865 3866 files = list(files)
3866 3867 if opts.get('no_commit'):
3867 3868 if message:
3868 3869 msgs.append(message)
3869 3870 else:
3870 3871 if opts.get('exact') or p2:
3871 3872 # If you got here, you either use --force and know what
3872 3873 # you are doing or used --exact or a merge patch while
3873 3874 # being updated to its first parent.
3874 3875 m = None
3875 3876 else:
3876 3877 m = scmutil.matchfiles(repo, files or [])
3877 3878 n = repo.commit(message, opts.get('user') or user,
3878 3879 opts.get('date') or date, match=m,
3879 3880 editor=editor)
3880 3881 checkexact(repo, n, nodeid)
3881 3882 else:
3882 3883 if opts.get('exact') or opts.get('import_branch'):
3883 3884 branch = branch or 'default'
3884 3885 else:
3885 3886 branch = p1.branch()
3886 3887 store = patch.filestore()
3887 3888 try:
3888 3889 files = set()
3889 3890 try:
3890 3891 patch.patchrepo(ui, repo, p1, store, tmpname, strip,
3891 3892 files, eolmode=None)
3892 3893 except patch.PatchError, e:
3893 3894 raise util.Abort(str(e))
3894 3895 memctx = patch.makememctx(repo, (p1.node(), p2.node()),
3895 3896 message,
3896 3897 opts.get('user') or user,
3897 3898 opts.get('date') or date,
3898 3899 branch, files, store,
3899 3900 editor=cmdutil.commiteditor)
3900 3901 repo.savecommitmessage(memctx.description())
3901 3902 n = memctx.commit()
3902 3903 checkexact(repo, n, nodeid)
3903 3904 finally:
3904 3905 store.close()
3905 3906 if n:
3906 3907 # i18n: refers to a short changeset id
3907 3908 msg = _('created %s') % short(n)
3908 3909 return (msg, n)
3909 3910 finally:
3910 3911 os.unlink(tmpname)
3911 3912
3912 3913 try:
3913 3914 try:
3914 3915 wlock = repo.wlock()
3915 3916 if not opts.get('no_commit'):
3916 3917 lock = repo.lock()
3917 3918 tr = repo.transaction('import')
3918 3919 parents = repo.parents()
3919 3920 for patchurl in patches:
3920 3921 if patchurl == '-':
3921 3922 ui.status(_('applying patch from stdin\n'))
3922 3923 patchfile = ui.fin
3923 3924 patchurl = 'stdin' # for error message
3924 3925 else:
3925 3926 patchurl = os.path.join(base, patchurl)
3926 3927 ui.status(_('applying %s\n') % patchurl)
3927 3928 patchfile = hg.openpath(ui, patchurl)
3928 3929
3929 3930 haspatch = False
3930 3931 for hunk in patch.split(patchfile):
3931 3932 (msg, node) = tryone(ui, hunk, parents)
3932 3933 if msg:
3933 3934 haspatch = True
3934 3935 ui.note(msg + '\n')
3935 3936 if update or opts.get('exact'):
3936 3937 parents = repo.parents()
3937 3938 else:
3938 3939 parents = [repo[node]]
3939 3940
3940 3941 if not haspatch:
3941 3942 raise util.Abort(_('%s: no diffs found') % patchurl)
3942 3943
3943 3944 if tr:
3944 3945 tr.close()
3945 3946 if msgs:
3946 3947 repo.savecommitmessage('\n* * *\n'.join(msgs))
3947 3948 except: # re-raises
3948 3949 # wlock.release() indirectly calls dirstate.write(): since
3949 3950 # we're crashing, we do not want to change the working dir
3950 3951 # parent after all, so make sure it writes nothing
3951 3952 repo.dirstate.invalidate()
3952 3953 raise
3953 3954 finally:
3954 3955 if tr:
3955 3956 tr.release()
3956 3957 release(lock, wlock)
3957 3958
3958 3959 @command('incoming|in',
3959 3960 [('f', 'force', None,
3960 3961 _('run even if remote repository is unrelated')),
3961 3962 ('n', 'newest-first', None, _('show newest record first')),
3962 3963 ('', 'bundle', '',
3963 3964 _('file to store the bundles into'), _('FILE')),
3964 3965 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3965 3966 ('B', 'bookmarks', False, _("compare bookmarks")),
3966 3967 ('b', 'branch', [],
3967 3968 _('a specific branch you would like to pull'), _('BRANCH')),
3968 3969 ] + logopts + remoteopts + subrepoopts,
3969 3970 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
3970 3971 def incoming(ui, repo, source="default", **opts):
3971 3972 """show new changesets found in source
3972 3973
3973 3974 Show new changesets found in the specified path/URL or the default
3974 3975 pull location. These are the changesets that would have been pulled
3975 3976 if a pull at the time you issued this command.
3976 3977
3977 3978 For remote repository, using --bundle avoids downloading the
3978 3979 changesets twice if the incoming is followed by a pull.
3979 3980
3980 3981 See pull for valid source format details.
3981 3982
3982 3983 Returns 0 if there are incoming changes, 1 otherwise.
3983 3984 """
3984 3985 if opts.get('graph'):
3985 3986 cmdutil.checkunsupportedgraphflags([], opts)
3986 3987 def display(other, chlist, displayer):
3987 3988 revdag = cmdutil.graphrevs(other, chlist, opts)
3988 3989 showparents = [ctx.node() for ctx in repo[None].parents()]
3989 3990 cmdutil.displaygraph(ui, revdag, displayer, showparents,
3990 3991 graphmod.asciiedges)
3991 3992
3992 3993 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
3993 3994 return 0
3994 3995
3995 3996 if opts.get('bundle') and opts.get('subrepos'):
3996 3997 raise util.Abort(_('cannot combine --bundle and --subrepos'))
3997 3998
3998 3999 if opts.get('bookmarks'):
3999 4000 source, branches = hg.parseurl(ui.expandpath(source),
4000 4001 opts.get('branch'))
4001 4002 other = hg.peer(repo, opts, source)
4002 4003 if 'bookmarks' not in other.listkeys('namespaces'):
4003 4004 ui.warn(_("remote doesn't support bookmarks\n"))
4004 4005 return 0
4005 4006 ui.status(_('comparing with %s\n') % util.hidepassword(source))
4006 4007 return bookmarks.diff(ui, repo, other)
4007 4008
4008 4009 repo._subtoppath = ui.expandpath(source)
4009 4010 try:
4010 4011 return hg.incoming(ui, repo, source, opts)
4011 4012 finally:
4012 4013 del repo._subtoppath
4013 4014
4014 4015
4015 4016 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'))
4016 4017 def init(ui, dest=".", **opts):
4017 4018 """create a new repository in the given directory
4018 4019
4019 4020 Initialize a new repository in the given directory. If the given
4020 4021 directory does not exist, it will be created.
4021 4022
4022 4023 If no directory is given, the current directory is used.
4023 4024
4024 4025 It is possible to specify an ``ssh://`` URL as the destination.
4025 4026 See :hg:`help urls` for more information.
4026 4027
4027 4028 Returns 0 on success.
4028 4029 """
4029 4030 hg.peer(ui, opts, ui.expandpath(dest), create=True)
4030 4031
4031 4032 @command('locate',
4032 4033 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
4033 4034 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
4034 4035 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
4035 4036 ] + walkopts,
4036 4037 _('[OPTION]... [PATTERN]...'))
4037 4038 def locate(ui, repo, *pats, **opts):
4038 4039 """locate files matching specific patterns
4039 4040
4040 4041 Print files under Mercurial control in the working directory whose
4041 4042 names match the given patterns.
4042 4043
4043 4044 By default, this command searches all directories in the working
4044 4045 directory. To search just the current directory and its
4045 4046 subdirectories, use "--include .".
4046 4047
4047 4048 If no patterns are given to match, this command prints the names
4048 4049 of all files under Mercurial control in the working directory.
4049 4050
4050 4051 If you want to feed the output of this command into the "xargs"
4051 4052 command, use the -0 option to both this command and "xargs". This
4052 4053 will avoid the problem of "xargs" treating single filenames that
4053 4054 contain whitespace as multiple filenames.
4054 4055
4055 4056 Returns 0 if a match is found, 1 otherwise.
4056 4057 """
4057 4058 end = opts.get('print0') and '\0' or '\n'
4058 4059 rev = scmutil.revsingle(repo, opts.get('rev'), None).node()
4059 4060
4060 4061 ret = 1
4061 4062 m = scmutil.match(repo[rev], pats, opts, default='relglob')
4062 4063 m.bad = lambda x, y: False
4063 4064 for abs in repo[rev].walk(m):
4064 4065 if not rev and abs not in repo.dirstate:
4065 4066 continue
4066 4067 if opts.get('fullpath'):
4067 4068 ui.write(repo.wjoin(abs), end)
4068 4069 else:
4069 4070 ui.write(((pats and m.rel(abs)) or abs), end)
4070 4071 ret = 0
4071 4072
4072 4073 return ret
4073 4074
4074 4075 @command('^log|history',
4075 4076 [('f', 'follow', None,
4076 4077 _('follow changeset history, or file history across copies and renames')),
4077 4078 ('', 'follow-first', None,
4078 4079 _('only follow the first parent of merge changesets (DEPRECATED)')),
4079 4080 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
4080 4081 ('C', 'copies', None, _('show copied files')),
4081 4082 ('k', 'keyword', [],
4082 4083 _('do case-insensitive search for a given text'), _('TEXT')),
4083 4084 ('r', 'rev', [], _('show the specified revision or range'), _('REV')),
4084 4085 ('', 'removed', None, _('include revisions where files were removed')),
4085 4086 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
4086 4087 ('u', 'user', [], _('revisions committed by user'), _('USER')),
4087 4088 ('', 'only-branch', [],
4088 4089 _('show only changesets within the given named branch (DEPRECATED)'),
4089 4090 _('BRANCH')),
4090 4091 ('b', 'branch', [],
4091 4092 _('show changesets within the given named branch'), _('BRANCH')),
4092 4093 ('P', 'prune', [],
4093 4094 _('do not display revision or any of its ancestors'), _('REV')),
4094 ('', 'hidden', False, _('show hidden changesets (DEPRECATED)')),
4095 4095 ] + logopts + walkopts,
4096 4096 _('[OPTION]... [FILE]'))
4097 4097 def log(ui, repo, *pats, **opts):
4098 4098 """show revision history of entire repository or files
4099 4099
4100 4100 Print the revision history of the specified files or the entire
4101 4101 project.
4102 4102
4103 4103 If no revision range is specified, the default is ``tip:0`` unless
4104 4104 --follow is set, in which case the working directory parent is
4105 4105 used as the starting revision.
4106 4106
4107 4107 File history is shown without following rename or copy history of
4108 4108 files. Use -f/--follow with a filename to follow history across
4109 4109 renames and copies. --follow without a filename will only show
4110 4110 ancestors or descendants of the starting revision.
4111 4111
4112 4112 By default this command prints revision number and changeset id,
4113 4113 tags, non-trivial parents, user, date and time, and a summary for
4114 4114 each commit. When the -v/--verbose switch is used, the list of
4115 4115 changed files and full commit message are shown.
4116 4116
4117 4117 .. note::
4118 4118 log -p/--patch may generate unexpected diff output for merge
4119 4119 changesets, as it will only compare the merge changeset against
4120 4120 its first parent. Also, only files different from BOTH parents
4121 4121 will appear in files:.
4122 4122
4123 4123 .. note::
4124 4124 for performance reasons, log FILE may omit duplicate changes
4125 4125 made on branches and will not show deletions. To see all
4126 4126 changes including duplicates and deletions, use the --removed
4127 4127 switch.
4128 4128
4129 4129 .. container:: verbose
4130 4130
4131 4131 Some examples:
4132 4132
4133 4133 - changesets with full descriptions and file lists::
4134 4134
4135 4135 hg log -v
4136 4136
4137 4137 - changesets ancestral to the working directory::
4138 4138
4139 4139 hg log -f
4140 4140
4141 4141 - last 10 commits on the current branch::
4142 4142
4143 4143 hg log -l 10 -b .
4144 4144
4145 4145 - changesets showing all modifications of a file, including removals::
4146 4146
4147 4147 hg log --removed file.c
4148 4148
4149 4149 - all changesets that touch a directory, with diffs, excluding merges::
4150 4150
4151 4151 hg log -Mp lib/
4152 4152
4153 4153 - all revision numbers that match a keyword::
4154 4154
4155 4155 hg log -k bug --template "{rev}\\n"
4156 4156
4157 4157 - check if a given changeset is included is a tagged release::
4158 4158
4159 4159 hg log -r "a21ccf and ancestor(1.9)"
4160 4160
4161 4161 - find all changesets by some user in a date range::
4162 4162
4163 4163 hg log -k alice -d "may 2008 to jul 2008"
4164 4164
4165 4165 - summary of all changesets after the last tag::
4166 4166
4167 4167 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
4168 4168
4169 4169 See :hg:`help dates` for a list of formats valid for -d/--date.
4170 4170
4171 4171 See :hg:`help revisions` and :hg:`help revsets` for more about
4172 4172 specifying revisions.
4173 4173
4174 4174 See :hg:`help templates` for more about pre-packaged styles and
4175 4175 specifying custom templates.
4176 4176
4177 4177 Returns 0 on success.
4178 4178 """
4179 4179 if opts.get('graph'):
4180 4180 return cmdutil.graphlog(ui, repo, *pats, **opts)
4181 4181
4182 4182 matchfn = scmutil.match(repo[None], pats, opts)
4183 4183 limit = cmdutil.loglimit(opts)
4184 4184 count = 0
4185 4185
4186 4186 getrenamed, endrev = None, None
4187 4187 if opts.get('copies'):
4188 4188 if opts.get('rev'):
4189 4189 endrev = max(scmutil.revrange(repo, opts.get('rev'))) + 1
4190 4190 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
4191 4191
4192 4192 df = False
4193 4193 if opts.get("date"):
4194 4194 df = util.matchdate(opts["date"])
4195 4195
4196 4196 branches = opts.get('branch', []) + opts.get('only_branch', [])
4197 4197 opts['branch'] = [repo.lookupbranch(b) for b in branches]
4198 4198
4199 4199 displayer = cmdutil.show_changeset(ui, repo, opts, True)
4200 4200 def prep(ctx, fns):
4201 4201 rev = ctx.rev()
4202 4202 parents = [p for p in repo.changelog.parentrevs(rev)
4203 4203 if p != nullrev]
4204 4204 if opts.get('no_merges') and len(parents) == 2:
4205 4205 return
4206 4206 if opts.get('only_merges') and len(parents) != 2:
4207 4207 return
4208 4208 if opts.get('branch') and ctx.branch() not in opts['branch']:
4209 4209 return
4210 if not opts.get('hidden') and ctx.hidden():
4211 return
4212 4210 if df and not df(ctx.date()[0]):
4213 4211 return
4214 4212
4215 4213 lower = encoding.lower
4216 4214 if opts.get('user'):
4217 4215 luser = lower(ctx.user())
4218 4216 for k in [lower(x) for x in opts['user']]:
4219 4217 if (k in luser):
4220 4218 break
4221 4219 else:
4222 4220 return
4223 4221 if opts.get('keyword'):
4224 4222 luser = lower(ctx.user())
4225 4223 ldesc = lower(ctx.description())
4226 4224 lfiles = lower(" ".join(ctx.files()))
4227 4225 for k in [lower(x) for x in opts['keyword']]:
4228 4226 if (k in luser or k in ldesc or k in lfiles):
4229 4227 break
4230 4228 else:
4231 4229 return
4232 4230
4233 4231 copies = None
4234 4232 if getrenamed is not None and rev:
4235 4233 copies = []
4236 4234 for fn in ctx.files():
4237 4235 rename = getrenamed(fn, rev)
4238 4236 if rename:
4239 4237 copies.append((fn, rename[0]))
4240 4238
4241 4239 revmatchfn = None
4242 4240 if opts.get('patch') or opts.get('stat'):
4243 4241 if opts.get('follow') or opts.get('follow_first'):
4244 4242 # note: this might be wrong when following through merges
4245 4243 revmatchfn = scmutil.match(repo[None], fns, default='path')
4246 4244 else:
4247 4245 revmatchfn = matchfn
4248 4246
4249 4247 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
4250 4248
4251 4249 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
4252 4250 if count == limit:
4253 4251 break
4254 4252 if displayer.flush(ctx.rev()):
4255 4253 count += 1
4256 4254 displayer.close()
4257 4255
4258 4256 @command('manifest',
4259 4257 [('r', 'rev', '', _('revision to display'), _('REV')),
4260 4258 ('', 'all', False, _("list files from all revisions"))],
4261 4259 _('[-r REV]'))
4262 4260 def manifest(ui, repo, node=None, rev=None, **opts):
4263 4261 """output the current or given revision of the project manifest
4264 4262
4265 4263 Print a list of version controlled files for the given revision.
4266 4264 If no revision is given, the first parent of the working directory
4267 4265 is used, or the null revision if no revision is checked out.
4268 4266
4269 4267 With -v, print file permissions, symlink and executable bits.
4270 4268 With --debug, print file revision hashes.
4271 4269
4272 4270 If option --all is specified, the list of all files from all revisions
4273 4271 is printed. This includes deleted and renamed files.
4274 4272
4275 4273 Returns 0 on success.
4276 4274 """
4277 4275
4278 4276 fm = ui.formatter('manifest', opts)
4279 4277
4280 4278 if opts.get('all'):
4281 4279 if rev or node:
4282 4280 raise util.Abort(_("can't specify a revision with --all"))
4283 4281
4284 4282 res = []
4285 4283 prefix = "data/"
4286 4284 suffix = ".i"
4287 4285 plen = len(prefix)
4288 4286 slen = len(suffix)
4289 4287 lock = repo.lock()
4290 4288 try:
4291 4289 for fn, b, size in repo.store.datafiles():
4292 4290 if size != 0 and fn[-slen:] == suffix and fn[:plen] == prefix:
4293 4291 res.append(fn[plen:-slen])
4294 4292 finally:
4295 4293 lock.release()
4296 4294 for f in res:
4297 4295 fm.startitem()
4298 4296 fm.write("path", '%s\n', f)
4299 4297 fm.end()
4300 4298 return
4301 4299
4302 4300 if rev and node:
4303 4301 raise util.Abort(_("please specify just one revision"))
4304 4302
4305 4303 if not node:
4306 4304 node = rev
4307 4305
4308 4306 char = {'l': '@', 'x': '*', '': ''}
4309 4307 mode = {'l': '644', 'x': '755', '': '644'}
4310 4308 ctx = scmutil.revsingle(repo, node)
4311 4309 mf = ctx.manifest()
4312 4310 for f in ctx:
4313 4311 fm.startitem()
4314 4312 fl = ctx[f].flags()
4315 4313 fm.condwrite(ui.debugflag, 'hash', '%s ', hex(mf[f]))
4316 4314 fm.condwrite(ui.verbose, 'mode type', '%s %1s ', mode[fl], char[fl])
4317 4315 fm.write('path', '%s\n', f)
4318 4316 fm.end()
4319 4317
4320 4318 @command('^merge',
4321 4319 [('f', 'force', None, _('force a merge with outstanding changes')),
4322 4320 ('r', 'rev', '', _('revision to merge'), _('REV')),
4323 4321 ('P', 'preview', None,
4324 4322 _('review revisions to merge (no merge is performed)'))
4325 4323 ] + mergetoolopts,
4326 4324 _('[-P] [-f] [[-r] REV]'))
4327 4325 def merge(ui, repo, node=None, **opts):
4328 4326 """merge working directory with another revision
4329 4327
4330 4328 The current working directory is updated with all changes made in
4331 4329 the requested revision since the last common predecessor revision.
4332 4330
4333 4331 Files that changed between either parent are marked as changed for
4334 4332 the next commit and a commit must be performed before any further
4335 4333 updates to the repository are allowed. The next commit will have
4336 4334 two parents.
4337 4335
4338 4336 ``--tool`` can be used to specify the merge tool used for file
4339 4337 merges. It overrides the HGMERGE environment variable and your
4340 4338 configuration files. See :hg:`help merge-tools` for options.
4341 4339
4342 4340 If no revision is specified, the working directory's parent is a
4343 4341 head revision, and the current branch contains exactly one other
4344 4342 head, the other head is merged with by default. Otherwise, an
4345 4343 explicit revision with which to merge with must be provided.
4346 4344
4347 4345 :hg:`resolve` must be used to resolve unresolved files.
4348 4346
4349 4347 To undo an uncommitted merge, use :hg:`update --clean .` which
4350 4348 will check out a clean copy of the original merge parent, losing
4351 4349 all changes.
4352 4350
4353 4351 Returns 0 on success, 1 if there are unresolved files.
4354 4352 """
4355 4353
4356 4354 if opts.get('rev') and node:
4357 4355 raise util.Abort(_("please specify just one revision"))
4358 4356 if not node:
4359 4357 node = opts.get('rev')
4360 4358
4361 4359 if node:
4362 4360 node = scmutil.revsingle(repo, node).node()
4363 4361
4364 4362 if not node and repo._bookmarkcurrent:
4365 4363 bmheads = repo.bookmarkheads(repo._bookmarkcurrent)
4366 4364 curhead = repo[repo._bookmarkcurrent].node()
4367 4365 if len(bmheads) == 2:
4368 4366 if curhead == bmheads[0]:
4369 4367 node = bmheads[1]
4370 4368 else:
4371 4369 node = bmheads[0]
4372 4370 elif len(bmheads) > 2:
4373 4371 raise util.Abort(_("multiple matching bookmarks to merge - "
4374 4372 "please merge with an explicit rev or bookmark"),
4375 4373 hint=_("run 'hg heads' to see all heads"))
4376 4374 elif len(bmheads) <= 1:
4377 4375 raise util.Abort(_("no matching bookmark to merge - "
4378 4376 "please merge with an explicit rev or bookmark"),
4379 4377 hint=_("run 'hg heads' to see all heads"))
4380 4378
4381 4379 if not node and not repo._bookmarkcurrent:
4382 4380 branch = repo[None].branch()
4383 4381 bheads = repo.branchheads(branch)
4384 4382 nbhs = [bh for bh in bheads if not repo[bh].bookmarks()]
4385 4383
4386 4384 if len(nbhs) > 2:
4387 4385 raise util.Abort(_("branch '%s' has %d heads - "
4388 4386 "please merge with an explicit rev")
4389 4387 % (branch, len(bheads)),
4390 4388 hint=_("run 'hg heads .' to see heads"))
4391 4389
4392 4390 parent = repo.dirstate.p1()
4393 4391 if len(nbhs) <= 1:
4394 4392 if len(bheads) > 1:
4395 4393 raise util.Abort(_("heads are bookmarked - "
4396 4394 "please merge with an explicit rev"),
4397 4395 hint=_("run 'hg heads' to see all heads"))
4398 4396 if len(repo.heads()) > 1:
4399 4397 raise util.Abort(_("branch '%s' has one head - "
4400 4398 "please merge with an explicit rev")
4401 4399 % branch,
4402 4400 hint=_("run 'hg heads' to see all heads"))
4403 4401 msg, hint = _('nothing to merge'), None
4404 4402 if parent != repo.lookup(branch):
4405 4403 hint = _("use 'hg update' instead")
4406 4404 raise util.Abort(msg, hint=hint)
4407 4405
4408 4406 if parent not in bheads:
4409 4407 raise util.Abort(_('working directory not at a head revision'),
4410 4408 hint=_("use 'hg update' or merge with an "
4411 4409 "explicit revision"))
4412 4410 if parent == nbhs[0]:
4413 4411 node = nbhs[-1]
4414 4412 else:
4415 4413 node = nbhs[0]
4416 4414
4417 4415 if opts.get('preview'):
4418 4416 # find nodes that are ancestors of p2 but not of p1
4419 4417 p1 = repo.lookup('.')
4420 4418 p2 = repo.lookup(node)
4421 4419 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4422 4420
4423 4421 displayer = cmdutil.show_changeset(ui, repo, opts)
4424 4422 for node in nodes:
4425 4423 displayer.show(repo[node])
4426 4424 displayer.close()
4427 4425 return 0
4428 4426
4429 4427 try:
4430 4428 # ui.forcemerge is an internal variable, do not document
4431 4429 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
4432 4430 return hg.merge(repo, node, force=opts.get('force'))
4433 4431 finally:
4434 4432 ui.setconfig('ui', 'forcemerge', '')
4435 4433
4436 4434 @command('outgoing|out',
4437 4435 [('f', 'force', None, _('run even when the destination is unrelated')),
4438 4436 ('r', 'rev', [],
4439 4437 _('a changeset intended to be included in the destination'), _('REV')),
4440 4438 ('n', 'newest-first', None, _('show newest record first')),
4441 4439 ('B', 'bookmarks', False, _('compare bookmarks')),
4442 4440 ('b', 'branch', [], _('a specific branch you would like to push'),
4443 4441 _('BRANCH')),
4444 4442 ] + logopts + remoteopts + subrepoopts,
4445 4443 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
4446 4444 def outgoing(ui, repo, dest=None, **opts):
4447 4445 """show changesets not found in the destination
4448 4446
4449 4447 Show changesets not found in the specified destination repository
4450 4448 or the default push location. These are the changesets that would
4451 4449 be pushed if a push was requested.
4452 4450
4453 4451 See pull for details of valid destination formats.
4454 4452
4455 4453 Returns 0 if there are outgoing changes, 1 otherwise.
4456 4454 """
4457 4455 if opts.get('graph'):
4458 4456 cmdutil.checkunsupportedgraphflags([], opts)
4459 4457 o = hg._outgoing(ui, repo, dest, opts)
4460 4458 if o is None:
4461 4459 return
4462 4460
4463 4461 revdag = cmdutil.graphrevs(repo, o, opts)
4464 4462 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
4465 4463 showparents = [ctx.node() for ctx in repo[None].parents()]
4466 4464 cmdutil.displaygraph(ui, revdag, displayer, showparents,
4467 4465 graphmod.asciiedges)
4468 4466 return 0
4469 4467
4470 4468 if opts.get('bookmarks'):
4471 4469 dest = ui.expandpath(dest or 'default-push', dest or 'default')
4472 4470 dest, branches = hg.parseurl(dest, opts.get('branch'))
4473 4471 other = hg.peer(repo, opts, dest)
4474 4472 if 'bookmarks' not in other.listkeys('namespaces'):
4475 4473 ui.warn(_("remote doesn't support bookmarks\n"))
4476 4474 return 0
4477 4475 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
4478 4476 return bookmarks.diff(ui, other, repo)
4479 4477
4480 4478 repo._subtoppath = ui.expandpath(dest or 'default-push', dest or 'default')
4481 4479 try:
4482 4480 return hg.outgoing(ui, repo, dest, opts)
4483 4481 finally:
4484 4482 del repo._subtoppath
4485 4483
4486 4484 @command('parents',
4487 4485 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
4488 4486 ] + templateopts,
4489 4487 _('[-r REV] [FILE]'))
4490 4488 def parents(ui, repo, file_=None, **opts):
4491 4489 """show the parents of the working directory or revision
4492 4490
4493 4491 Print the working directory's parent revisions. If a revision is
4494 4492 given via -r/--rev, the parent of that revision will be printed.
4495 4493 If a file argument is given, the revision in which the file was
4496 4494 last changed (before the working directory revision or the
4497 4495 argument to --rev if given) is printed.
4498 4496
4499 4497 Returns 0 on success.
4500 4498 """
4501 4499
4502 4500 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
4503 4501
4504 4502 if file_:
4505 4503 m = scmutil.match(ctx, (file_,), opts)
4506 4504 if m.anypats() or len(m.files()) != 1:
4507 4505 raise util.Abort(_('can only specify an explicit filename'))
4508 4506 file_ = m.files()[0]
4509 4507 filenodes = []
4510 4508 for cp in ctx.parents():
4511 4509 if not cp:
4512 4510 continue
4513 4511 try:
4514 4512 filenodes.append(cp.filenode(file_))
4515 4513 except error.LookupError:
4516 4514 pass
4517 4515 if not filenodes:
4518 4516 raise util.Abort(_("'%s' not found in manifest!") % file_)
4519 4517 fl = repo.file(file_)
4520 4518 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
4521 4519 else:
4522 4520 p = [cp.node() for cp in ctx.parents()]
4523 4521
4524 4522 displayer = cmdutil.show_changeset(ui, repo, opts)
4525 4523 for n in p:
4526 4524 if n != nullid:
4527 4525 displayer.show(repo[n])
4528 4526 displayer.close()
4529 4527
4530 4528 @command('paths', [], _('[NAME]'))
4531 4529 def paths(ui, repo, search=None):
4532 4530 """show aliases for remote repositories
4533 4531
4534 4532 Show definition of symbolic path name NAME. If no name is given,
4535 4533 show definition of all available names.
4536 4534
4537 4535 Option -q/--quiet suppresses all output when searching for NAME
4538 4536 and shows only the path names when listing all definitions.
4539 4537
4540 4538 Path names are defined in the [paths] section of your
4541 4539 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
4542 4540 repository, ``.hg/hgrc`` is used, too.
4543 4541
4544 4542 The path names ``default`` and ``default-push`` have a special
4545 4543 meaning. When performing a push or pull operation, they are used
4546 4544 as fallbacks if no location is specified on the command-line.
4547 4545 When ``default-push`` is set, it will be used for push and
4548 4546 ``default`` will be used for pull; otherwise ``default`` is used
4549 4547 as the fallback for both. When cloning a repository, the clone
4550 4548 source is written as ``default`` in ``.hg/hgrc``. Note that
4551 4549 ``default`` and ``default-push`` apply to all inbound (e.g.
4552 4550 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email` and
4553 4551 :hg:`bundle`) operations.
4554 4552
4555 4553 See :hg:`help urls` for more information.
4556 4554
4557 4555 Returns 0 on success.
4558 4556 """
4559 4557 if search:
4560 4558 for name, path in ui.configitems("paths"):
4561 4559 if name == search:
4562 4560 ui.status("%s\n" % util.hidepassword(path))
4563 4561 return
4564 4562 if not ui.quiet:
4565 4563 ui.warn(_("not found!\n"))
4566 4564 return 1
4567 4565 else:
4568 4566 for name, path in ui.configitems("paths"):
4569 4567 if ui.quiet:
4570 4568 ui.write("%s\n" % name)
4571 4569 else:
4572 4570 ui.write("%s = %s\n" % (name, util.hidepassword(path)))
4573 4571
4574 4572 @command('phase',
4575 4573 [('p', 'public', False, _('set changeset phase to public')),
4576 4574 ('d', 'draft', False, _('set changeset phase to draft')),
4577 4575 ('s', 'secret', False, _('set changeset phase to secret')),
4578 4576 ('f', 'force', False, _('allow to move boundary backward')),
4579 4577 ('r', 'rev', [], _('target revision'), _('REV')),
4580 4578 ],
4581 4579 _('[-p|-d|-s] [-f] [-r] REV...'))
4582 4580 def phase(ui, repo, *revs, **opts):
4583 4581 """set or show the current phase name
4584 4582
4585 4583 With no argument, show the phase name of specified revisions.
4586 4584
4587 4585 With one of -p/--public, -d/--draft or -s/--secret, change the
4588 4586 phase value of the specified revisions.
4589 4587
4590 4588 Unless -f/--force is specified, :hg:`phase` won't move changeset from a
4591 4589 lower phase to an higher phase. Phases are ordered as follows::
4592 4590
4593 4591 public < draft < secret
4594 4592
4595 4593 Return 0 on success, 1 if no phases were changed or some could not
4596 4594 be changed.
4597 4595 """
4598 4596 # search for a unique phase argument
4599 4597 targetphase = None
4600 4598 for idx, name in enumerate(phases.phasenames):
4601 4599 if opts[name]:
4602 4600 if targetphase is not None:
4603 4601 raise util.Abort(_('only one phase can be specified'))
4604 4602 targetphase = idx
4605 4603
4606 4604 # look for specified revision
4607 4605 revs = list(revs)
4608 4606 revs.extend(opts['rev'])
4609 4607 if not revs:
4610 4608 raise util.Abort(_('no revisions specified'))
4611 4609
4612 4610 revs = scmutil.revrange(repo, revs)
4613 4611
4614 4612 lock = None
4615 4613 ret = 0
4616 4614 if targetphase is None:
4617 4615 # display
4618 4616 for r in revs:
4619 4617 ctx = repo[r]
4620 4618 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
4621 4619 else:
4622 4620 lock = repo.lock()
4623 4621 try:
4624 4622 # set phase
4625 4623 if not revs:
4626 4624 raise util.Abort(_('empty revision set'))
4627 4625 nodes = [repo[r].node() for r in revs]
4628 4626 olddata = repo._phasecache.getphaserevs(repo)[:]
4629 4627 phases.advanceboundary(repo, targetphase, nodes)
4630 4628 if opts['force']:
4631 4629 phases.retractboundary(repo, targetphase, nodes)
4632 4630 finally:
4633 4631 lock.release()
4634 4632 # moving revision from public to draft may hide them
4635 4633 # We have to check result on an unfiltered repository
4636 4634 unfi = repo.unfiltered()
4637 4635 newdata = repo._phasecache.getphaserevs(unfi)
4638 4636 changes = sum(o != newdata[i] for i, o in enumerate(olddata))
4639 4637 cl = unfi.changelog
4640 4638 rejected = [n for n in nodes
4641 4639 if newdata[cl.rev(n)] < targetphase]
4642 4640 if rejected:
4643 4641 ui.warn(_('cannot move %i changesets to a more permissive '
4644 4642 'phase, use --force\n') % len(rejected))
4645 4643 ret = 1
4646 4644 if changes:
4647 4645 msg = _('phase changed for %i changesets\n') % changes
4648 4646 if ret:
4649 4647 ui.status(msg)
4650 4648 else:
4651 4649 ui.note(msg)
4652 4650 else:
4653 4651 ui.warn(_('no phases changed\n'))
4654 4652 ret = 1
4655 4653 return ret
4656 4654
4657 4655 def postincoming(ui, repo, modheads, optupdate, checkout):
4658 4656 if modheads == 0:
4659 4657 return
4660 4658 if optupdate:
4661 4659 movemarkfrom = repo['.'].node()
4662 4660 try:
4663 4661 ret = hg.update(repo, checkout)
4664 4662 except util.Abort, inst:
4665 4663 ui.warn(_("not updating: %s\n") % str(inst))
4666 4664 return 0
4667 4665 if not ret and not checkout:
4668 4666 if bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
4669 4667 ui.status(_("updating bookmark %s\n") % repo._bookmarkcurrent)
4670 4668 return ret
4671 4669 if modheads > 1:
4672 4670 currentbranchheads = len(repo.branchheads())
4673 4671 if currentbranchheads == modheads:
4674 4672 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
4675 4673 elif currentbranchheads > 1:
4676 4674 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to "
4677 4675 "merge)\n"))
4678 4676 else:
4679 4677 ui.status(_("(run 'hg heads' to see heads)\n"))
4680 4678 else:
4681 4679 ui.status(_("(run 'hg update' to get a working copy)\n"))
4682 4680
4683 4681 @command('^pull',
4684 4682 [('u', 'update', None,
4685 4683 _('update to new branch head if changesets were pulled')),
4686 4684 ('f', 'force', None, _('run even when remote repository is unrelated')),
4687 4685 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
4688 4686 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
4689 4687 ('b', 'branch', [], _('a specific branch you would like to pull'),
4690 4688 _('BRANCH')),
4691 4689 ] + remoteopts,
4692 4690 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
4693 4691 def pull(ui, repo, source="default", **opts):
4694 4692 """pull changes from the specified source
4695 4693
4696 4694 Pull changes from a remote repository to a local one.
4697 4695
4698 4696 This finds all changes from the repository at the specified path
4699 4697 or URL and adds them to a local repository (the current one unless
4700 4698 -R is specified). By default, this does not update the copy of the
4701 4699 project in the working directory.
4702 4700
4703 4701 Use :hg:`incoming` if you want to see what would have been added
4704 4702 by a pull at the time you issued this command. If you then decide
4705 4703 to add those changes to the repository, you should use :hg:`pull
4706 4704 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
4707 4705
4708 4706 If SOURCE is omitted, the 'default' path will be used.
4709 4707 See :hg:`help urls` for more information.
4710 4708
4711 4709 Returns 0 on success, 1 if an update had unresolved files.
4712 4710 """
4713 4711 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
4714 4712 other = hg.peer(repo, opts, source)
4715 4713 ui.status(_('pulling from %s\n') % util.hidepassword(source))
4716 4714 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
4717 4715
4718 4716 if opts.get('bookmark'):
4719 4717 if not revs:
4720 4718 revs = []
4721 4719 rb = other.listkeys('bookmarks')
4722 4720 for b in opts['bookmark']:
4723 4721 if b not in rb:
4724 4722 raise util.Abort(_('remote bookmark %s not found!') % b)
4725 4723 revs.append(rb[b])
4726 4724
4727 4725 if revs:
4728 4726 try:
4729 4727 revs = [other.lookup(rev) for rev in revs]
4730 4728 except error.CapabilityError:
4731 4729 err = _("other repository doesn't support revision lookup, "
4732 4730 "so a rev cannot be specified.")
4733 4731 raise util.Abort(err)
4734 4732
4735 4733 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
4736 4734 bookmarks.updatefromremote(ui, repo, other, source)
4737 4735 if checkout:
4738 4736 checkout = str(repo.changelog.rev(other.lookup(checkout)))
4739 4737 repo._subtoppath = source
4740 4738 try:
4741 4739 ret = postincoming(ui, repo, modheads, opts.get('update'), checkout)
4742 4740
4743 4741 finally:
4744 4742 del repo._subtoppath
4745 4743
4746 4744 # update specified bookmarks
4747 4745 if opts.get('bookmark'):
4748 4746 marks = repo._bookmarks
4749 4747 for b in opts['bookmark']:
4750 4748 # explicit pull overrides local bookmark if any
4751 4749 ui.status(_("importing bookmark %s\n") % b)
4752 4750 marks[b] = repo[rb[b]].node()
4753 4751 marks.write()
4754 4752
4755 4753 return ret
4756 4754
4757 4755 @command('^push',
4758 4756 [('f', 'force', None, _('force push')),
4759 4757 ('r', 'rev', [],
4760 4758 _('a changeset intended to be included in the destination'),
4761 4759 _('REV')),
4762 4760 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
4763 4761 ('b', 'branch', [],
4764 4762 _('a specific branch you would like to push'), _('BRANCH')),
4765 4763 ('', 'new-branch', False, _('allow pushing a new branch')),
4766 4764 ] + remoteopts,
4767 4765 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
4768 4766 def push(ui, repo, dest=None, **opts):
4769 4767 """push changes to the specified destination
4770 4768
4771 4769 Push changesets from the local repository to the specified
4772 4770 destination.
4773 4771
4774 4772 This operation is symmetrical to pull: it is identical to a pull
4775 4773 in the destination repository from the current one.
4776 4774
4777 4775 By default, push will not allow creation of new heads at the
4778 4776 destination, since multiple heads would make it unclear which head
4779 4777 to use. In this situation, it is recommended to pull and merge
4780 4778 before pushing.
4781 4779
4782 4780 Use --new-branch if you want to allow push to create a new named
4783 4781 branch that is not present at the destination. This allows you to
4784 4782 only create a new branch without forcing other changes.
4785 4783
4786 4784 Use -f/--force to override the default behavior and push all
4787 4785 changesets on all branches.
4788 4786
4789 4787 If -r/--rev is used, the specified revision and all its ancestors
4790 4788 will be pushed to the remote repository.
4791 4789
4792 4790 If -B/--bookmark is used, the specified bookmarked revision, its
4793 4791 ancestors, and the bookmark will be pushed to the remote
4794 4792 repository.
4795 4793
4796 4794 Please see :hg:`help urls` for important details about ``ssh://``
4797 4795 URLs. If DESTINATION is omitted, a default path will be used.
4798 4796
4799 4797 Returns 0 if push was successful, 1 if nothing to push.
4800 4798 """
4801 4799
4802 4800 if opts.get('bookmark'):
4803 4801 for b in opts['bookmark']:
4804 4802 # translate -B options to -r so changesets get pushed
4805 4803 if b in repo._bookmarks:
4806 4804 opts.setdefault('rev', []).append(b)
4807 4805 else:
4808 4806 # if we try to push a deleted bookmark, translate it to null
4809 4807 # this lets simultaneous -r, -b options continue working
4810 4808 opts.setdefault('rev', []).append("null")
4811 4809
4812 4810 dest = ui.expandpath(dest or 'default-push', dest or 'default')
4813 4811 dest, branches = hg.parseurl(dest, opts.get('branch'))
4814 4812 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
4815 4813 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
4816 4814 other = hg.peer(repo, opts, dest)
4817 4815 if revs:
4818 4816 revs = [repo.lookup(r) for r in scmutil.revrange(repo, revs)]
4819 4817
4820 4818 repo._subtoppath = dest
4821 4819 try:
4822 4820 # push subrepos depth-first for coherent ordering
4823 4821 c = repo['']
4824 4822 subs = c.substate # only repos that are committed
4825 4823 for s in sorted(subs):
4826 4824 if c.sub(s).push(opts) == 0:
4827 4825 return False
4828 4826 finally:
4829 4827 del repo._subtoppath
4830 4828 result = repo.push(other, opts.get('force'), revs=revs,
4831 4829 newbranch=opts.get('new_branch'))
4832 4830
4833 4831 result = not result
4834 4832
4835 4833 if opts.get('bookmark'):
4836 4834 rb = other.listkeys('bookmarks')
4837 4835 for b in opts['bookmark']:
4838 4836 # explicit push overrides remote bookmark if any
4839 4837 if b in repo._bookmarks:
4840 4838 ui.status(_("exporting bookmark %s\n") % b)
4841 4839 new = repo[b].hex()
4842 4840 elif b in rb:
4843 4841 ui.status(_("deleting remote bookmark %s\n") % b)
4844 4842 new = '' # delete
4845 4843 else:
4846 4844 ui.warn(_('bookmark %s does not exist on the local '
4847 4845 'or remote repository!\n') % b)
4848 4846 return 2
4849 4847 old = rb.get(b, '')
4850 4848 r = other.pushkey('bookmarks', b, old, new)
4851 4849 if not r:
4852 4850 ui.warn(_('updating bookmark %s failed!\n') % b)
4853 4851 if not result:
4854 4852 result = 2
4855 4853
4856 4854 return result
4857 4855
4858 4856 @command('recover', [])
4859 4857 def recover(ui, repo):
4860 4858 """roll back an interrupted transaction
4861 4859
4862 4860 Recover from an interrupted commit or pull.
4863 4861
4864 4862 This command tries to fix the repository status after an
4865 4863 interrupted operation. It should only be necessary when Mercurial
4866 4864 suggests it.
4867 4865
4868 4866 Returns 0 if successful, 1 if nothing to recover or verify fails.
4869 4867 """
4870 4868 if repo.recover():
4871 4869 return hg.verify(repo)
4872 4870 return 1
4873 4871
4874 4872 @command('^remove|rm',
4875 4873 [('A', 'after', None, _('record delete for missing files')),
4876 4874 ('f', 'force', None,
4877 4875 _('remove (and delete) file even if added or modified')),
4878 4876 ] + walkopts,
4879 4877 _('[OPTION]... FILE...'))
4880 4878 def remove(ui, repo, *pats, **opts):
4881 4879 """remove the specified files on the next commit
4882 4880
4883 4881 Schedule the indicated files for removal from the current branch.
4884 4882
4885 4883 This command schedules the files to be removed at the next commit.
4886 4884 To undo a remove before that, see :hg:`revert`. To undo added
4887 4885 files, see :hg:`forget`.
4888 4886
4889 4887 .. container:: verbose
4890 4888
4891 4889 -A/--after can be used to remove only files that have already
4892 4890 been deleted, -f/--force can be used to force deletion, and -Af
4893 4891 can be used to remove files from the next revision without
4894 4892 deleting them from the working directory.
4895 4893
4896 4894 The following table details the behavior of remove for different
4897 4895 file states (columns) and option combinations (rows). The file
4898 4896 states are Added [A], Clean [C], Modified [M] and Missing [!]
4899 4897 (as reported by :hg:`status`). The actions are Warn, Remove
4900 4898 (from branch) and Delete (from disk):
4901 4899
4902 4900 ======= == == == ==
4903 4901 A C M !
4904 4902 ======= == == == ==
4905 4903 none W RD W R
4906 4904 -f R RD RD R
4907 4905 -A W W W R
4908 4906 -Af R R R R
4909 4907 ======= == == == ==
4910 4908
4911 4909 Note that remove never deletes files in Added [A] state from the
4912 4910 working directory, not even if option --force is specified.
4913 4911
4914 4912 Returns 0 on success, 1 if any warnings encountered.
4915 4913 """
4916 4914
4917 4915 ret = 0
4918 4916 after, force = opts.get('after'), opts.get('force')
4919 4917 if not pats and not after:
4920 4918 raise util.Abort(_('no files specified'))
4921 4919
4922 4920 m = scmutil.match(repo[None], pats, opts)
4923 4921 s = repo.status(match=m, clean=True)
4924 4922 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
4925 4923
4926 4924 # warn about failure to delete explicit files/dirs
4927 4925 wctx = repo[None]
4928 4926 for f in m.files():
4929 4927 if f in repo.dirstate or f in wctx.dirs():
4930 4928 continue
4931 4929 if os.path.exists(m.rel(f)):
4932 4930 if os.path.isdir(m.rel(f)):
4933 4931 ui.warn(_('not removing %s: no tracked files\n') % m.rel(f))
4934 4932 else:
4935 4933 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
4936 4934 # missing files will generate a warning elsewhere
4937 4935 ret = 1
4938 4936
4939 4937 if force:
4940 4938 list = modified + deleted + clean + added
4941 4939 elif after:
4942 4940 list = deleted
4943 4941 for f in modified + added + clean:
4944 4942 ui.warn(_('not removing %s: file still exists\n') % m.rel(f))
4945 4943 ret = 1
4946 4944 else:
4947 4945 list = deleted + clean
4948 4946 for f in modified:
4949 4947 ui.warn(_('not removing %s: file is modified (use -f'
4950 4948 ' to force removal)\n') % m.rel(f))
4951 4949 ret = 1
4952 4950 for f in added:
4953 4951 ui.warn(_('not removing %s: file has been marked for add'
4954 4952 ' (use forget to undo)\n') % m.rel(f))
4955 4953 ret = 1
4956 4954
4957 4955 for f in sorted(list):
4958 4956 if ui.verbose or not m.exact(f):
4959 4957 ui.status(_('removing %s\n') % m.rel(f))
4960 4958
4961 4959 wlock = repo.wlock()
4962 4960 try:
4963 4961 if not after:
4964 4962 for f in list:
4965 4963 if f in added:
4966 4964 continue # we never unlink added files on remove
4967 4965 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
4968 4966 repo[None].forget(list)
4969 4967 finally:
4970 4968 wlock.release()
4971 4969
4972 4970 return ret
4973 4971
4974 4972 @command('rename|move|mv',
4975 4973 [('A', 'after', None, _('record a rename that has already occurred')),
4976 4974 ('f', 'force', None, _('forcibly copy over an existing managed file')),
4977 4975 ] + walkopts + dryrunopts,
4978 4976 _('[OPTION]... SOURCE... DEST'))
4979 4977 def rename(ui, repo, *pats, **opts):
4980 4978 """rename files; equivalent of copy + remove
4981 4979
4982 4980 Mark dest as copies of sources; mark sources for deletion. If dest
4983 4981 is a directory, copies are put in that directory. If dest is a
4984 4982 file, there can only be one source.
4985 4983
4986 4984 By default, this command copies the contents of files as they
4987 4985 exist in the working directory. If invoked with -A/--after, the
4988 4986 operation is recorded, but no copying is performed.
4989 4987
4990 4988 This command takes effect at the next commit. To undo a rename
4991 4989 before that, see :hg:`revert`.
4992 4990
4993 4991 Returns 0 on success, 1 if errors are encountered.
4994 4992 """
4995 4993 wlock = repo.wlock(False)
4996 4994 try:
4997 4995 return cmdutil.copy(ui, repo, pats, opts, rename=True)
4998 4996 finally:
4999 4997 wlock.release()
5000 4998
5001 4999 @command('resolve',
5002 5000 [('a', 'all', None, _('select all unresolved files')),
5003 5001 ('l', 'list', None, _('list state of files needing merge')),
5004 5002 ('m', 'mark', None, _('mark files as resolved')),
5005 5003 ('u', 'unmark', None, _('mark files as unresolved')),
5006 5004 ('n', 'no-status', None, _('hide status prefix'))]
5007 5005 + mergetoolopts + walkopts,
5008 5006 _('[OPTION]... [FILE]...'))
5009 5007 def resolve(ui, repo, *pats, **opts):
5010 5008 """redo merges or set/view the merge status of files
5011 5009
5012 5010 Merges with unresolved conflicts are often the result of
5013 5011 non-interactive merging using the ``internal:merge`` configuration
5014 5012 setting, or a command-line merge tool like ``diff3``. The resolve
5015 5013 command is used to manage the files involved in a merge, after
5016 5014 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
5017 5015 working directory must have two parents). See :hg:`help
5018 5016 merge-tools` for information on configuring merge tools.
5019 5017
5020 5018 The resolve command can be used in the following ways:
5021 5019
5022 5020 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
5023 5021 files, discarding any previous merge attempts. Re-merging is not
5024 5022 performed for files already marked as resolved. Use ``--all/-a``
5025 5023 to select all unresolved files. ``--tool`` can be used to specify
5026 5024 the merge tool used for the given files. It overrides the HGMERGE
5027 5025 environment variable and your configuration files. Previous file
5028 5026 contents are saved with a ``.orig`` suffix.
5029 5027
5030 5028 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
5031 5029 (e.g. after having manually fixed-up the files). The default is
5032 5030 to mark all unresolved files.
5033 5031
5034 5032 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
5035 5033 default is to mark all resolved files.
5036 5034
5037 5035 - :hg:`resolve -l`: list files which had or still have conflicts.
5038 5036 In the printed list, ``U`` = unresolved and ``R`` = resolved.
5039 5037
5040 5038 Note that Mercurial will not let you commit files with unresolved
5041 5039 merge conflicts. You must use :hg:`resolve -m ...` before you can
5042 5040 commit after a conflicting merge.
5043 5041
5044 5042 Returns 0 on success, 1 if any files fail a resolve attempt.
5045 5043 """
5046 5044
5047 5045 all, mark, unmark, show, nostatus = \
5048 5046 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
5049 5047
5050 5048 if (show and (mark or unmark)) or (mark and unmark):
5051 5049 raise util.Abort(_("too many options specified"))
5052 5050 if pats and all:
5053 5051 raise util.Abort(_("can't specify --all and patterns"))
5054 5052 if not (all or pats or show or mark or unmark):
5055 5053 raise util.Abort(_('no files or directories specified; '
5056 5054 'use --all to remerge all files'))
5057 5055
5058 5056 ms = mergemod.mergestate(repo)
5059 5057 m = scmutil.match(repo[None], pats, opts)
5060 5058 ret = 0
5061 5059
5062 5060 for f in ms:
5063 5061 if m(f):
5064 5062 if show:
5065 5063 if nostatus:
5066 5064 ui.write("%s\n" % f)
5067 5065 else:
5068 5066 ui.write("%s %s\n" % (ms[f].upper(), f),
5069 5067 label='resolve.' +
5070 5068 {'u': 'unresolved', 'r': 'resolved'}[ms[f]])
5071 5069 elif mark:
5072 5070 ms.mark(f, "r")
5073 5071 elif unmark:
5074 5072 ms.mark(f, "u")
5075 5073 else:
5076 5074 wctx = repo[None]
5077 5075 mctx = wctx.parents()[-1]
5078 5076
5079 5077 # backup pre-resolve (merge uses .orig for its own purposes)
5080 5078 a = repo.wjoin(f)
5081 5079 util.copyfile(a, a + ".resolve")
5082 5080
5083 5081 try:
5084 5082 # resolve file
5085 5083 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
5086 5084 if ms.resolve(f, wctx, mctx):
5087 5085 ret = 1
5088 5086 finally:
5089 5087 ui.setconfig('ui', 'forcemerge', '')
5090 5088 ms.commit()
5091 5089
5092 5090 # replace filemerge's .orig file with our resolve file
5093 5091 util.rename(a + ".resolve", a + ".orig")
5094 5092
5095 5093 ms.commit()
5096 5094 return ret
5097 5095
5098 5096 @command('revert',
5099 5097 [('a', 'all', None, _('revert all changes when no arguments given')),
5100 5098 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5101 5099 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
5102 5100 ('C', 'no-backup', None, _('do not save backup copies of files')),
5103 5101 ] + walkopts + dryrunopts,
5104 5102 _('[OPTION]... [-r REV] [NAME]...'))
5105 5103 def revert(ui, repo, *pats, **opts):
5106 5104 """restore files to their checkout state
5107 5105
5108 5106 .. note::
5109 5107
5110 5108 To check out earlier revisions, you should use :hg:`update REV`.
5111 5109 To cancel an uncommitted merge (and lose your changes), use
5112 5110 :hg:`update --clean .`.
5113 5111
5114 5112 With no revision specified, revert the specified files or directories
5115 5113 to the contents they had in the parent of the working directory.
5116 5114 This restores the contents of files to an unmodified
5117 5115 state and unschedules adds, removes, copies, and renames. If the
5118 5116 working directory has two parents, you must explicitly specify a
5119 5117 revision.
5120 5118
5121 5119 Using the -r/--rev or -d/--date options, revert the given files or
5122 5120 directories to their states as of a specific revision. Because
5123 5121 revert does not change the working directory parents, this will
5124 5122 cause these files to appear modified. This can be helpful to "back
5125 5123 out" some or all of an earlier change. See :hg:`backout` for a
5126 5124 related method.
5127 5125
5128 5126 Modified files are saved with a .orig suffix before reverting.
5129 5127 To disable these backups, use --no-backup.
5130 5128
5131 5129 See :hg:`help dates` for a list of formats valid for -d/--date.
5132 5130
5133 5131 Returns 0 on success.
5134 5132 """
5135 5133
5136 5134 if opts.get("date"):
5137 5135 if opts.get("rev"):
5138 5136 raise util.Abort(_("you can't specify a revision and a date"))
5139 5137 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
5140 5138
5141 5139 parent, p2 = repo.dirstate.parents()
5142 5140 if not opts.get('rev') and p2 != nullid:
5143 5141 # revert after merge is a trap for new users (issue2915)
5144 5142 raise util.Abort(_('uncommitted merge with no revision specified'),
5145 5143 hint=_('use "hg update" or see "hg help revert"'))
5146 5144
5147 5145 ctx = scmutil.revsingle(repo, opts.get('rev'))
5148 5146
5149 5147 if not pats and not opts.get('all'):
5150 5148 msg = _("no files or directories specified")
5151 5149 if p2 != nullid:
5152 5150 hint = _("uncommitted merge, use --all to discard all changes,"
5153 5151 " or 'hg update -C .' to abort the merge")
5154 5152 raise util.Abort(msg, hint=hint)
5155 5153 dirty = util.any(repo.status())
5156 5154 node = ctx.node()
5157 5155 if node != parent:
5158 5156 if dirty:
5159 5157 hint = _("uncommitted changes, use --all to discard all"
5160 5158 " changes, or 'hg update %s' to update") % ctx.rev()
5161 5159 else:
5162 5160 hint = _("use --all to revert all files,"
5163 5161 " or 'hg update %s' to update") % ctx.rev()
5164 5162 elif dirty:
5165 5163 hint = _("uncommitted changes, use --all to discard all changes")
5166 5164 else:
5167 5165 hint = _("use --all to revert all files")
5168 5166 raise util.Abort(msg, hint=hint)
5169 5167
5170 5168 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats, **opts)
5171 5169
5172 5170 @command('rollback', dryrunopts +
5173 5171 [('f', 'force', False, _('ignore safety measures'))])
5174 5172 def rollback(ui, repo, **opts):
5175 5173 """roll back the last transaction (dangerous)
5176 5174
5177 5175 This command should be used with care. There is only one level of
5178 5176 rollback, and there is no way to undo a rollback. It will also
5179 5177 restore the dirstate at the time of the last transaction, losing
5180 5178 any dirstate changes since that time. This command does not alter
5181 5179 the working directory.
5182 5180
5183 5181 Transactions are used to encapsulate the effects of all commands
5184 5182 that create new changesets or propagate existing changesets into a
5185 5183 repository.
5186 5184
5187 5185 .. container:: verbose
5188 5186
5189 5187 For example, the following commands are transactional, and their
5190 5188 effects can be rolled back:
5191 5189
5192 5190 - commit
5193 5191 - import
5194 5192 - pull
5195 5193 - push (with this repository as the destination)
5196 5194 - unbundle
5197 5195
5198 5196 To avoid permanent data loss, rollback will refuse to rollback a
5199 5197 commit transaction if it isn't checked out. Use --force to
5200 5198 override this protection.
5201 5199
5202 5200 This command is not intended for use on public repositories. Once
5203 5201 changes are visible for pull by other users, rolling a transaction
5204 5202 back locally is ineffective (someone else may already have pulled
5205 5203 the changes). Furthermore, a race is possible with readers of the
5206 5204 repository; for example an in-progress pull from the repository
5207 5205 may fail if a rollback is performed.
5208 5206
5209 5207 Returns 0 on success, 1 if no rollback data is available.
5210 5208 """
5211 5209 return repo.rollback(dryrun=opts.get('dry_run'),
5212 5210 force=opts.get('force'))
5213 5211
5214 5212 @command('root', [])
5215 5213 def root(ui, repo):
5216 5214 """print the root (top) of the current working directory
5217 5215
5218 5216 Print the root directory of the current repository.
5219 5217
5220 5218 Returns 0 on success.
5221 5219 """
5222 5220 ui.write(repo.root + "\n")
5223 5221
5224 5222 @command('^serve',
5225 5223 [('A', 'accesslog', '', _('name of access log file to write to'),
5226 5224 _('FILE')),
5227 5225 ('d', 'daemon', None, _('run server in background')),
5228 5226 ('', 'daemon-pipefds', '', _('used internally by daemon mode'), _('NUM')),
5229 5227 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
5230 5228 # use string type, then we can check if something was passed
5231 5229 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
5232 5230 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
5233 5231 _('ADDR')),
5234 5232 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
5235 5233 _('PREFIX')),
5236 5234 ('n', 'name', '',
5237 5235 _('name to show in web pages (default: working directory)'), _('NAME')),
5238 5236 ('', 'web-conf', '',
5239 5237 _('name of the hgweb config file (see "hg help hgweb")'), _('FILE')),
5240 5238 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
5241 5239 _('FILE')),
5242 5240 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
5243 5241 ('', 'stdio', None, _('for remote clients')),
5244 5242 ('', 'cmdserver', '', _('for remote clients'), _('MODE')),
5245 5243 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
5246 5244 ('', 'style', '', _('template style to use'), _('STYLE')),
5247 5245 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
5248 5246 ('', 'certificate', '', _('SSL certificate file'), _('FILE'))],
5249 5247 _('[OPTION]...'))
5250 5248 def serve(ui, repo, **opts):
5251 5249 """start stand-alone webserver
5252 5250
5253 5251 Start a local HTTP repository browser and pull server. You can use
5254 5252 this for ad-hoc sharing and browsing of repositories. It is
5255 5253 recommended to use a real web server to serve a repository for
5256 5254 longer periods of time.
5257 5255
5258 5256 Please note that the server does not implement access control.
5259 5257 This means that, by default, anybody can read from the server and
5260 5258 nobody can write to it by default. Set the ``web.allow_push``
5261 5259 option to ``*`` to allow everybody to push to the server. You
5262 5260 should use a real web server if you need to authenticate users.
5263 5261
5264 5262 By default, the server logs accesses to stdout and errors to
5265 5263 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
5266 5264 files.
5267 5265
5268 5266 To have the server choose a free port number to listen on, specify
5269 5267 a port number of 0; in this case, the server will print the port
5270 5268 number it uses.
5271 5269
5272 5270 Returns 0 on success.
5273 5271 """
5274 5272
5275 5273 if opts["stdio"] and opts["cmdserver"]:
5276 5274 raise util.Abort(_("cannot use --stdio with --cmdserver"))
5277 5275
5278 5276 def checkrepo():
5279 5277 if repo is None:
5280 5278 raise error.RepoError(_("there is no Mercurial repository here"
5281 5279 " (.hg not found)"))
5282 5280
5283 5281 if opts["stdio"]:
5284 5282 checkrepo()
5285 5283 s = sshserver.sshserver(ui, repo)
5286 5284 s.serve_forever()
5287 5285
5288 5286 if opts["cmdserver"]:
5289 5287 checkrepo()
5290 5288 s = commandserver.server(ui, repo, opts["cmdserver"])
5291 5289 return s.serve()
5292 5290
5293 5291 # this way we can check if something was given in the command-line
5294 5292 if opts.get('port'):
5295 5293 opts['port'] = util.getport(opts.get('port'))
5296 5294
5297 5295 baseui = repo and repo.baseui or ui
5298 5296 optlist = ("name templates style address port prefix ipv6"
5299 5297 " accesslog errorlog certificate encoding")
5300 5298 for o in optlist.split():
5301 5299 val = opts.get(o, '')
5302 5300 if val in (None, ''): # should check against default options instead
5303 5301 continue
5304 5302 baseui.setconfig("web", o, val)
5305 5303 if repo and repo.ui != baseui:
5306 5304 repo.ui.setconfig("web", o, val)
5307 5305
5308 5306 o = opts.get('web_conf') or opts.get('webdir_conf')
5309 5307 if not o:
5310 5308 if not repo:
5311 5309 raise error.RepoError(_("there is no Mercurial repository"
5312 5310 " here (.hg not found)"))
5313 5311 o = repo.root
5314 5312
5315 5313 app = hgweb.hgweb(o, baseui=ui)
5316 5314
5317 5315 class service(object):
5318 5316 def init(self):
5319 5317 util.setsignalhandler()
5320 5318 self.httpd = hgweb.server.create_server(ui, app)
5321 5319
5322 5320 if opts['port'] and not ui.verbose:
5323 5321 return
5324 5322
5325 5323 if self.httpd.prefix:
5326 5324 prefix = self.httpd.prefix.strip('/') + '/'
5327 5325 else:
5328 5326 prefix = ''
5329 5327
5330 5328 port = ':%d' % self.httpd.port
5331 5329 if port == ':80':
5332 5330 port = ''
5333 5331
5334 5332 bindaddr = self.httpd.addr
5335 5333 if bindaddr == '0.0.0.0':
5336 5334 bindaddr = '*'
5337 5335 elif ':' in bindaddr: # IPv6
5338 5336 bindaddr = '[%s]' % bindaddr
5339 5337
5340 5338 fqaddr = self.httpd.fqaddr
5341 5339 if ':' in fqaddr:
5342 5340 fqaddr = '[%s]' % fqaddr
5343 5341 if opts['port']:
5344 5342 write = ui.status
5345 5343 else:
5346 5344 write = ui.write
5347 5345 write(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
5348 5346 (fqaddr, port, prefix, bindaddr, self.httpd.port))
5349 5347
5350 5348 def run(self):
5351 5349 self.httpd.serve_forever()
5352 5350
5353 5351 service = service()
5354 5352
5355 5353 cmdutil.service(opts, initfn=service.init, runfn=service.run)
5356 5354
5357 5355 @command('showconfig|debugconfig',
5358 5356 [('u', 'untrusted', None, _('show untrusted configuration options'))],
5359 5357 _('[-u] [NAME]...'))
5360 5358 def showconfig(ui, repo, *values, **opts):
5361 5359 """show combined config settings from all hgrc files
5362 5360
5363 5361 With no arguments, print names and values of all config items.
5364 5362
5365 5363 With one argument of the form section.name, print just the value
5366 5364 of that config item.
5367 5365
5368 5366 With multiple arguments, print names and values of all config
5369 5367 items with matching section names.
5370 5368
5371 5369 With --debug, the source (filename and line number) is printed
5372 5370 for each config item.
5373 5371
5374 5372 Returns 0 on success.
5375 5373 """
5376 5374
5377 5375 for f in scmutil.rcpath():
5378 5376 ui.debug('read config from: %s\n' % f)
5379 5377 untrusted = bool(opts.get('untrusted'))
5380 5378 if values:
5381 5379 sections = [v for v in values if '.' not in v]
5382 5380 items = [v for v in values if '.' in v]
5383 5381 if len(items) > 1 or items and sections:
5384 5382 raise util.Abort(_('only one config item permitted'))
5385 5383 for section, name, value in ui.walkconfig(untrusted=untrusted):
5386 5384 value = str(value).replace('\n', '\\n')
5387 5385 sectname = section + '.' + name
5388 5386 if values:
5389 5387 for v in values:
5390 5388 if v == section:
5391 5389 ui.debug('%s: ' %
5392 5390 ui.configsource(section, name, untrusted))
5393 5391 ui.write('%s=%s\n' % (sectname, value))
5394 5392 elif v == sectname:
5395 5393 ui.debug('%s: ' %
5396 5394 ui.configsource(section, name, untrusted))
5397 5395 ui.write(value, '\n')
5398 5396 else:
5399 5397 ui.debug('%s: ' %
5400 5398 ui.configsource(section, name, untrusted))
5401 5399 ui.write('%s=%s\n' % (sectname, value))
5402 5400
5403 5401 @command('^status|st',
5404 5402 [('A', 'all', None, _('show status of all files')),
5405 5403 ('m', 'modified', None, _('show only modified files')),
5406 5404 ('a', 'added', None, _('show only added files')),
5407 5405 ('r', 'removed', None, _('show only removed files')),
5408 5406 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
5409 5407 ('c', 'clean', None, _('show only files without changes')),
5410 5408 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
5411 5409 ('i', 'ignored', None, _('show only ignored files')),
5412 5410 ('n', 'no-status', None, _('hide status prefix')),
5413 5411 ('C', 'copies', None, _('show source of copied files')),
5414 5412 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
5415 5413 ('', 'rev', [], _('show difference from revision'), _('REV')),
5416 5414 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
5417 5415 ] + walkopts + subrepoopts,
5418 5416 _('[OPTION]... [FILE]...'))
5419 5417 def status(ui, repo, *pats, **opts):
5420 5418 """show changed files in the working directory
5421 5419
5422 5420 Show status of files in the repository. If names are given, only
5423 5421 files that match are shown. Files that are clean or ignored or
5424 5422 the source of a copy/move operation, are not listed unless
5425 5423 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
5426 5424 Unless options described with "show only ..." are given, the
5427 5425 options -mardu are used.
5428 5426
5429 5427 Option -q/--quiet hides untracked (unknown and ignored) files
5430 5428 unless explicitly requested with -u/--unknown or -i/--ignored.
5431 5429
5432 5430 .. note::
5433 5431 status may appear to disagree with diff if permissions have
5434 5432 changed or a merge has occurred. The standard diff format does
5435 5433 not report permission changes and diff only reports changes
5436 5434 relative to one merge parent.
5437 5435
5438 5436 If one revision is given, it is used as the base revision.
5439 5437 If two revisions are given, the differences between them are
5440 5438 shown. The --change option can also be used as a shortcut to list
5441 5439 the changed files of a revision from its first parent.
5442 5440
5443 5441 The codes used to show the status of files are::
5444 5442
5445 5443 M = modified
5446 5444 A = added
5447 5445 R = removed
5448 5446 C = clean
5449 5447 ! = missing (deleted by non-hg command, but still tracked)
5450 5448 ? = not tracked
5451 5449 I = ignored
5452 5450 = origin of the previous file listed as A (added)
5453 5451
5454 5452 .. container:: verbose
5455 5453
5456 5454 Examples:
5457 5455
5458 5456 - show changes in the working directory relative to a
5459 5457 changeset::
5460 5458
5461 5459 hg status --rev 9353
5462 5460
5463 5461 - show all changes including copies in an existing changeset::
5464 5462
5465 5463 hg status --copies --change 9353
5466 5464
5467 5465 - get a NUL separated list of added files, suitable for xargs::
5468 5466
5469 5467 hg status -an0
5470 5468
5471 5469 Returns 0 on success.
5472 5470 """
5473 5471
5474 5472 revs = opts.get('rev')
5475 5473 change = opts.get('change')
5476 5474
5477 5475 if revs and change:
5478 5476 msg = _('cannot specify --rev and --change at the same time')
5479 5477 raise util.Abort(msg)
5480 5478 elif change:
5481 5479 node2 = scmutil.revsingle(repo, change, None).node()
5482 5480 node1 = repo[node2].p1().node()
5483 5481 else:
5484 5482 node1, node2 = scmutil.revpair(repo, revs)
5485 5483
5486 5484 cwd = (pats and repo.getcwd()) or ''
5487 5485 end = opts.get('print0') and '\0' or '\n'
5488 5486 copy = {}
5489 5487 states = 'modified added removed deleted unknown ignored clean'.split()
5490 5488 show = [k for k in states if opts.get(k)]
5491 5489 if opts.get('all'):
5492 5490 show += ui.quiet and (states[:4] + ['clean']) or states
5493 5491 if not show:
5494 5492 show = ui.quiet and states[:4] or states[:5]
5495 5493
5496 5494 stat = repo.status(node1, node2, scmutil.match(repo[node2], pats, opts),
5497 5495 'ignored' in show, 'clean' in show, 'unknown' in show,
5498 5496 opts.get('subrepos'))
5499 5497 changestates = zip(states, 'MAR!?IC', stat)
5500 5498
5501 5499 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
5502 5500 copy = copies.pathcopies(repo[node1], repo[node2])
5503 5501
5504 5502 fm = ui.formatter('status', opts)
5505 5503 fmt = '%s' + end
5506 5504 showchar = not opts.get('no_status')
5507 5505
5508 5506 for state, char, files in changestates:
5509 5507 if state in show:
5510 5508 label = 'status.' + state
5511 5509 for f in files:
5512 5510 fm.startitem()
5513 5511 fm.condwrite(showchar, 'status', '%s ', char, label=label)
5514 5512 fm.write('path', fmt, repo.pathto(f, cwd), label=label)
5515 5513 if f in copy:
5516 5514 fm.write("copy", ' %s' + end, repo.pathto(copy[f], cwd),
5517 5515 label='status.copied')
5518 5516 fm.end()
5519 5517
5520 5518 @command('^summary|sum',
5521 5519 [('', 'remote', None, _('check for push and pull'))], '[--remote]')
5522 5520 def summary(ui, repo, **opts):
5523 5521 """summarize working directory state
5524 5522
5525 5523 This generates a brief summary of the working directory state,
5526 5524 including parents, branch, commit status, and available updates.
5527 5525
5528 5526 With the --remote option, this will check the default paths for
5529 5527 incoming and outgoing changes. This can be time-consuming.
5530 5528
5531 5529 Returns 0 on success.
5532 5530 """
5533 5531
5534 5532 ctx = repo[None]
5535 5533 parents = ctx.parents()
5536 5534 pnode = parents[0].node()
5537 5535 marks = []
5538 5536
5539 5537 for p in parents:
5540 5538 # label with log.changeset (instead of log.parent) since this
5541 5539 # shows a working directory parent *changeset*:
5542 5540 # i18n: column positioning for "hg summary"
5543 5541 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
5544 5542 label='log.changeset changeset.%s' % p.phasestr())
5545 5543 ui.write(' '.join(p.tags()), label='log.tag')
5546 5544 if p.bookmarks():
5547 5545 marks.extend(p.bookmarks())
5548 5546 if p.rev() == -1:
5549 5547 if not len(repo):
5550 5548 ui.write(_(' (empty repository)'))
5551 5549 else:
5552 5550 ui.write(_(' (no revision checked out)'))
5553 5551 ui.write('\n')
5554 5552 if p.description():
5555 5553 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
5556 5554 label='log.summary')
5557 5555
5558 5556 branch = ctx.branch()
5559 5557 bheads = repo.branchheads(branch)
5560 5558 # i18n: column positioning for "hg summary"
5561 5559 m = _('branch: %s\n') % branch
5562 5560 if branch != 'default':
5563 5561 ui.write(m, label='log.branch')
5564 5562 else:
5565 5563 ui.status(m, label='log.branch')
5566 5564
5567 5565 if marks:
5568 5566 current = repo._bookmarkcurrent
5569 5567 # i18n: column positioning for "hg summary"
5570 5568 ui.write(_('bookmarks:'), label='log.bookmark')
5571 5569 if current is not None:
5572 5570 try:
5573 5571 marks.remove(current)
5574 5572 ui.write(' *' + current, label='bookmarks.current')
5575 5573 except ValueError:
5576 5574 # current bookmark not in parent ctx marks
5577 5575 pass
5578 5576 for m in marks:
5579 5577 ui.write(' ' + m, label='log.bookmark')
5580 5578 ui.write('\n', label='log.bookmark')
5581 5579
5582 5580 st = list(repo.status(unknown=True))[:6]
5583 5581
5584 5582 c = repo.dirstate.copies()
5585 5583 copied, renamed = [], []
5586 5584 for d, s in c.iteritems():
5587 5585 if s in st[2]:
5588 5586 st[2].remove(s)
5589 5587 renamed.append(d)
5590 5588 else:
5591 5589 copied.append(d)
5592 5590 if d in st[1]:
5593 5591 st[1].remove(d)
5594 5592 st.insert(3, renamed)
5595 5593 st.insert(4, copied)
5596 5594
5597 5595 ms = mergemod.mergestate(repo)
5598 5596 st.append([f for f in ms if ms[f] == 'u'])
5599 5597
5600 5598 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
5601 5599 st.append(subs)
5602 5600
5603 5601 labels = [ui.label(_('%d modified'), 'status.modified'),
5604 5602 ui.label(_('%d added'), 'status.added'),
5605 5603 ui.label(_('%d removed'), 'status.removed'),
5606 5604 ui.label(_('%d renamed'), 'status.copied'),
5607 5605 ui.label(_('%d copied'), 'status.copied'),
5608 5606 ui.label(_('%d deleted'), 'status.deleted'),
5609 5607 ui.label(_('%d unknown'), 'status.unknown'),
5610 5608 ui.label(_('%d ignored'), 'status.ignored'),
5611 5609 ui.label(_('%d unresolved'), 'resolve.unresolved'),
5612 5610 ui.label(_('%d subrepos'), 'status.modified')]
5613 5611 t = []
5614 5612 for s, l in zip(st, labels):
5615 5613 if s:
5616 5614 t.append(l % len(s))
5617 5615
5618 5616 t = ', '.join(t)
5619 5617 cleanworkdir = False
5620 5618
5621 5619 if len(parents) > 1:
5622 5620 t += _(' (merge)')
5623 5621 elif branch != parents[0].branch():
5624 5622 t += _(' (new branch)')
5625 5623 elif (parents[0].closesbranch() and
5626 5624 pnode in repo.branchheads(branch, closed=True)):
5627 5625 t += _(' (head closed)')
5628 5626 elif not (st[0] or st[1] or st[2] or st[3] or st[4] or st[9]):
5629 5627 t += _(' (clean)')
5630 5628 cleanworkdir = True
5631 5629 elif pnode not in bheads:
5632 5630 t += _(' (new branch head)')
5633 5631
5634 5632 if cleanworkdir:
5635 5633 # i18n: column positioning for "hg summary"
5636 5634 ui.status(_('commit: %s\n') % t.strip())
5637 5635 else:
5638 5636 # i18n: column positioning for "hg summary"
5639 5637 ui.write(_('commit: %s\n') % t.strip())
5640 5638
5641 5639 # all ancestors of branch heads - all ancestors of parent = new csets
5642 5640 new = [0] * len(repo)
5643 5641 cl = repo.changelog
5644 5642 for a in [cl.rev(n) for n in bheads]:
5645 5643 new[a] = 1
5646 5644 for a in cl.ancestors([cl.rev(n) for n in bheads]):
5647 5645 new[a] = 1
5648 5646 for a in [p.rev() for p in parents]:
5649 5647 if a >= 0:
5650 5648 new[a] = 0
5651 5649 for a in cl.ancestors([p.rev() for p in parents]):
5652 5650 new[a] = 0
5653 5651 new = sum(new)
5654 5652
5655 5653 if new == 0:
5656 5654 # i18n: column positioning for "hg summary"
5657 5655 ui.status(_('update: (current)\n'))
5658 5656 elif pnode not in bheads:
5659 5657 # i18n: column positioning for "hg summary"
5660 5658 ui.write(_('update: %d new changesets (update)\n') % new)
5661 5659 else:
5662 5660 # i18n: column positioning for "hg summary"
5663 5661 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
5664 5662 (new, len(bheads)))
5665 5663
5666 5664 if opts.get('remote'):
5667 5665 t = []
5668 5666 source, branches = hg.parseurl(ui.expandpath('default'))
5669 5667 other = hg.peer(repo, {}, source)
5670 5668 revs, checkout = hg.addbranchrevs(repo, other, branches,
5671 5669 opts.get('rev'))
5672 5670 ui.debug('comparing with %s\n' % util.hidepassword(source))
5673 5671 repo.ui.pushbuffer()
5674 5672 commoninc = discovery.findcommonincoming(repo, other)
5675 5673 _common, incoming, _rheads = commoninc
5676 5674 repo.ui.popbuffer()
5677 5675 if incoming:
5678 5676 t.append(_('1 or more incoming'))
5679 5677
5680 5678 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
5681 5679 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
5682 5680 if source != dest:
5683 5681 other = hg.peer(repo, {}, dest)
5684 5682 commoninc = None
5685 5683 ui.debug('comparing with %s\n' % util.hidepassword(dest))
5686 5684 repo.ui.pushbuffer()
5687 5685 outgoing = discovery.findcommonoutgoing(repo, other,
5688 5686 commoninc=commoninc)
5689 5687 repo.ui.popbuffer()
5690 5688 o = outgoing.missing
5691 5689 if o:
5692 5690 t.append(_('%d outgoing') % len(o))
5693 5691 if 'bookmarks' in other.listkeys('namespaces'):
5694 5692 lmarks = repo.listkeys('bookmarks')
5695 5693 rmarks = other.listkeys('bookmarks')
5696 5694 diff = set(rmarks) - set(lmarks)
5697 5695 if len(diff) > 0:
5698 5696 t.append(_('%d incoming bookmarks') % len(diff))
5699 5697 diff = set(lmarks) - set(rmarks)
5700 5698 if len(diff) > 0:
5701 5699 t.append(_('%d outgoing bookmarks') % len(diff))
5702 5700
5703 5701 if t:
5704 5702 # i18n: column positioning for "hg summary"
5705 5703 ui.write(_('remote: %s\n') % (', '.join(t)))
5706 5704 else:
5707 5705 # i18n: column positioning for "hg summary"
5708 5706 ui.status(_('remote: (synced)\n'))
5709 5707
5710 5708 @command('tag',
5711 5709 [('f', 'force', None, _('force tag')),
5712 5710 ('l', 'local', None, _('make the tag local')),
5713 5711 ('r', 'rev', '', _('revision to tag'), _('REV')),
5714 5712 ('', 'remove', None, _('remove a tag')),
5715 5713 # -l/--local is already there, commitopts cannot be used
5716 5714 ('e', 'edit', None, _('edit commit message')),
5717 5715 ('m', 'message', '', _('use <text> as commit message'), _('TEXT')),
5718 5716 ] + commitopts2,
5719 5717 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
5720 5718 def tag(ui, repo, name1, *names, **opts):
5721 5719 """add one or more tags for the current or given revision
5722 5720
5723 5721 Name a particular revision using <name>.
5724 5722
5725 5723 Tags are used to name particular revisions of the repository and are
5726 5724 very useful to compare different revisions, to go back to significant
5727 5725 earlier versions or to mark branch points as releases, etc. Changing
5728 5726 an existing tag is normally disallowed; use -f/--force to override.
5729 5727
5730 5728 If no revision is given, the parent of the working directory is
5731 5729 used, or tip if no revision is checked out.
5732 5730
5733 5731 To facilitate version control, distribution, and merging of tags,
5734 5732 they are stored as a file named ".hgtags" which is managed similarly
5735 5733 to other project files and can be hand-edited if necessary. This
5736 5734 also means that tagging creates a new commit. The file
5737 5735 ".hg/localtags" is used for local tags (not shared among
5738 5736 repositories).
5739 5737
5740 5738 Tag commits are usually made at the head of a branch. If the parent
5741 5739 of the working directory is not a branch head, :hg:`tag` aborts; use
5742 5740 -f/--force to force the tag commit to be based on a non-head
5743 5741 changeset.
5744 5742
5745 5743 See :hg:`help dates` for a list of formats valid for -d/--date.
5746 5744
5747 5745 Since tag names have priority over branch names during revision
5748 5746 lookup, using an existing branch name as a tag name is discouraged.
5749 5747
5750 5748 Returns 0 on success.
5751 5749 """
5752 5750 wlock = lock = None
5753 5751 try:
5754 5752 wlock = repo.wlock()
5755 5753 lock = repo.lock()
5756 5754 rev_ = "."
5757 5755 names = [t.strip() for t in (name1,) + names]
5758 5756 if len(names) != len(set(names)):
5759 5757 raise util.Abort(_('tag names must be unique'))
5760 5758 for n in names:
5761 5759 scmutil.checknewlabel(repo, n, 'tag')
5762 5760 if not n:
5763 5761 raise util.Abort(_('tag names cannot consist entirely of '
5764 5762 'whitespace'))
5765 5763 if opts.get('rev') and opts.get('remove'):
5766 5764 raise util.Abort(_("--rev and --remove are incompatible"))
5767 5765 if opts.get('rev'):
5768 5766 rev_ = opts['rev']
5769 5767 message = opts.get('message')
5770 5768 if opts.get('remove'):
5771 5769 expectedtype = opts.get('local') and 'local' or 'global'
5772 5770 for n in names:
5773 5771 if not repo.tagtype(n):
5774 5772 raise util.Abort(_("tag '%s' does not exist") % n)
5775 5773 if repo.tagtype(n) != expectedtype:
5776 5774 if expectedtype == 'global':
5777 5775 raise util.Abort(_("tag '%s' is not a global tag") % n)
5778 5776 else:
5779 5777 raise util.Abort(_("tag '%s' is not a local tag") % n)
5780 5778 rev_ = nullid
5781 5779 if not message:
5782 5780 # we don't translate commit messages
5783 5781 message = 'Removed tag %s' % ', '.join(names)
5784 5782 elif not opts.get('force'):
5785 5783 for n in names:
5786 5784 if n in repo.tags():
5787 5785 raise util.Abort(_("tag '%s' already exists "
5788 5786 "(use -f to force)") % n)
5789 5787 if not opts.get('local'):
5790 5788 p1, p2 = repo.dirstate.parents()
5791 5789 if p2 != nullid:
5792 5790 raise util.Abort(_('uncommitted merge'))
5793 5791 bheads = repo.branchheads()
5794 5792 if not opts.get('force') and bheads and p1 not in bheads:
5795 5793 raise util.Abort(_('not at a branch head (use -f to force)'))
5796 5794 r = scmutil.revsingle(repo, rev_).node()
5797 5795
5798 5796 if not message:
5799 5797 # we don't translate commit messages
5800 5798 message = ('Added tag %s for changeset %s' %
5801 5799 (', '.join(names), short(r)))
5802 5800
5803 5801 date = opts.get('date')
5804 5802 if date:
5805 5803 date = util.parsedate(date)
5806 5804
5807 5805 if opts.get('edit'):
5808 5806 message = ui.edit(message, ui.username())
5809 5807
5810 5808 # don't allow tagging the null rev
5811 5809 if (not opts.get('remove') and
5812 5810 scmutil.revsingle(repo, rev_).rev() == nullrev):
5813 5811 raise util.Abort(_("null revision specified"))
5814 5812
5815 5813 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
5816 5814 finally:
5817 5815 release(lock, wlock)
5818 5816
5819 5817 @command('tags', [], '')
5820 5818 def tags(ui, repo, **opts):
5821 5819 """list repository tags
5822 5820
5823 5821 This lists both regular and local tags. When the -v/--verbose
5824 5822 switch is used, a third column "local" is printed for local tags.
5825 5823
5826 5824 Returns 0 on success.
5827 5825 """
5828 5826
5829 5827 fm = ui.formatter('tags', opts)
5830 5828 hexfunc = ui.debugflag and hex or short
5831 5829 tagtype = ""
5832 5830
5833 5831 for t, n in reversed(repo.tagslist()):
5834 5832 hn = hexfunc(n)
5835 5833 label = 'tags.normal'
5836 5834 tagtype = ''
5837 5835 if repo.tagtype(t) == 'local':
5838 5836 label = 'tags.local'
5839 5837 tagtype = 'local'
5840 5838
5841 5839 fm.startitem()
5842 5840 fm.write('tag', '%s', t, label=label)
5843 5841 fmt = " " * (30 - encoding.colwidth(t)) + ' %5d:%s'
5844 5842 fm.condwrite(not ui.quiet, 'rev id', fmt,
5845 5843 repo.changelog.rev(n), hn, label=label)
5846 5844 fm.condwrite(ui.verbose and tagtype, 'type', ' %s',
5847 5845 tagtype, label=label)
5848 5846 fm.plain('\n')
5849 5847 fm.end()
5850 5848
5851 5849 @command('tip',
5852 5850 [('p', 'patch', None, _('show patch')),
5853 5851 ('g', 'git', None, _('use git extended diff format')),
5854 5852 ] + templateopts,
5855 5853 _('[-p] [-g]'))
5856 5854 def tip(ui, repo, **opts):
5857 5855 """show the tip revision
5858 5856
5859 5857 The tip revision (usually just called the tip) is the changeset
5860 5858 most recently added to the repository (and therefore the most
5861 5859 recently changed head).
5862 5860
5863 5861 If you have just made a commit, that commit will be the tip. If
5864 5862 you have just pulled changes from another repository, the tip of
5865 5863 that repository becomes the current tip. The "tip" tag is special
5866 5864 and cannot be renamed or assigned to a different changeset.
5867 5865
5868 5866 Returns 0 on success.
5869 5867 """
5870 5868 displayer = cmdutil.show_changeset(ui, repo, opts)
5871 5869 displayer.show(repo[len(repo) - 1])
5872 5870 displayer.close()
5873 5871
5874 5872 @command('unbundle',
5875 5873 [('u', 'update', None,
5876 5874 _('update to new branch head if changesets were unbundled'))],
5877 5875 _('[-u] FILE...'))
5878 5876 def unbundle(ui, repo, fname1, *fnames, **opts):
5879 5877 """apply one or more changegroup files
5880 5878
5881 5879 Apply one or more compressed changegroup files generated by the
5882 5880 bundle command.
5883 5881
5884 5882 Returns 0 on success, 1 if an update has unresolved files.
5885 5883 """
5886 5884 fnames = (fname1,) + fnames
5887 5885
5888 5886 lock = repo.lock()
5889 5887 wc = repo['.']
5890 5888 try:
5891 5889 for fname in fnames:
5892 5890 f = hg.openpath(ui, fname)
5893 5891 gen = changegroup.readbundle(f, fname)
5894 5892 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
5895 5893 finally:
5896 5894 lock.release()
5897 5895 bookmarks.updatecurrentbookmark(repo, wc.node(), wc.branch())
5898 5896 return postincoming(ui, repo, modheads, opts.get('update'), None)
5899 5897
5900 5898 @command('^update|up|checkout|co',
5901 5899 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
5902 5900 ('c', 'check', None,
5903 5901 _('update across branches if no uncommitted changes')),
5904 5902 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5905 5903 ('r', 'rev', '', _('revision'), _('REV'))],
5906 5904 _('[-c] [-C] [-d DATE] [[-r] REV]'))
5907 5905 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
5908 5906 """update working directory (or switch revisions)
5909 5907
5910 5908 Update the repository's working directory to the specified
5911 5909 changeset. If no changeset is specified, update to the tip of the
5912 5910 current named branch and move the current bookmark (see :hg:`help
5913 5911 bookmarks`).
5914 5912
5915 5913 Update sets the working directory's parent revision to the specified
5916 5914 changeset (see :hg:`help parents`).
5917 5915
5918 5916 If the changeset is not a descendant or ancestor of the working
5919 5917 directory's parent, the update is aborted. With the -c/--check
5920 5918 option, the working directory is checked for uncommitted changes; if
5921 5919 none are found, the working directory is updated to the specified
5922 5920 changeset.
5923 5921
5924 5922 .. container:: verbose
5925 5923
5926 5924 The following rules apply when the working directory contains
5927 5925 uncommitted changes:
5928 5926
5929 5927 1. If neither -c/--check nor -C/--clean is specified, and if
5930 5928 the requested changeset is an ancestor or descendant of
5931 5929 the working directory's parent, the uncommitted changes
5932 5930 are merged into the requested changeset and the merged
5933 5931 result is left uncommitted. If the requested changeset is
5934 5932 not an ancestor or descendant (that is, it is on another
5935 5933 branch), the update is aborted and the uncommitted changes
5936 5934 are preserved.
5937 5935
5938 5936 2. With the -c/--check option, the update is aborted and the
5939 5937 uncommitted changes are preserved.
5940 5938
5941 5939 3. With the -C/--clean option, uncommitted changes are discarded and
5942 5940 the working directory is updated to the requested changeset.
5943 5941
5944 5942 To cancel an uncommitted merge (and lose your changes), use
5945 5943 :hg:`update --clean .`.
5946 5944
5947 5945 Use null as the changeset to remove the working directory (like
5948 5946 :hg:`clone -U`).
5949 5947
5950 5948 If you want to revert just one file to an older revision, use
5951 5949 :hg:`revert [-r REV] NAME`.
5952 5950
5953 5951 See :hg:`help dates` for a list of formats valid for -d/--date.
5954 5952
5955 5953 Returns 0 on success, 1 if there are unresolved files.
5956 5954 """
5957 5955 if rev and node:
5958 5956 raise util.Abort(_("please specify just one revision"))
5959 5957
5960 5958 if rev is None or rev == '':
5961 5959 rev = node
5962 5960
5963 5961 # with no argument, we also move the current bookmark, if any
5964 5962 movemarkfrom = None
5965 5963 if rev is None:
5966 5964 movemarkfrom = repo['.'].node()
5967 5965
5968 5966 # if we defined a bookmark, we have to remember the original bookmark name
5969 5967 brev = rev
5970 5968 rev = scmutil.revsingle(repo, rev, rev).rev()
5971 5969
5972 5970 if check and clean:
5973 5971 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
5974 5972
5975 5973 if date:
5976 5974 if rev is not None:
5977 5975 raise util.Abort(_("you can't specify a revision and a date"))
5978 5976 rev = cmdutil.finddate(ui, repo, date)
5979 5977
5980 5978 if check:
5981 5979 c = repo[None]
5982 5980 if c.dirty(merge=False, branch=False, missing=True):
5983 5981 raise util.Abort(_("uncommitted local changes"))
5984 5982 if rev is None:
5985 5983 rev = repo[repo[None].branch()].rev()
5986 5984 mergemod._checkunknown(repo, repo[None], repo[rev])
5987 5985
5988 5986 if clean:
5989 5987 ret = hg.clean(repo, rev)
5990 5988 else:
5991 5989 ret = hg.update(repo, rev)
5992 5990
5993 5991 if not ret and movemarkfrom:
5994 5992 if bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
5995 5993 ui.status(_("updating bookmark %s\n") % repo._bookmarkcurrent)
5996 5994 elif brev in repo._bookmarks:
5997 5995 bookmarks.setcurrent(repo, brev)
5998 5996 elif brev:
5999 5997 bookmarks.unsetcurrent(repo)
6000 5998
6001 5999 return ret
6002 6000
6003 6001 @command('verify', [])
6004 6002 def verify(ui, repo):
6005 6003 """verify the integrity of the repository
6006 6004
6007 6005 Verify the integrity of the current repository.
6008 6006
6009 6007 This will perform an extensive check of the repository's
6010 6008 integrity, validating the hashes and checksums of each entry in
6011 6009 the changelog, manifest, and tracked files, as well as the
6012 6010 integrity of their crosslinks and indices.
6013 6011
6014 6012 Please see http://mercurial.selenic.com/wiki/RepositoryCorruption
6015 6013 for more information about recovery from corruption of the
6016 6014 repository.
6017 6015
6018 6016 Returns 0 on success, 1 if errors are encountered.
6019 6017 """
6020 6018 return hg.verify(repo)
6021 6019
6022 6020 @command('version', [])
6023 6021 def version_(ui):
6024 6022 """output version and copyright information"""
6025 6023 ui.write(_("Mercurial Distributed SCM (version %s)\n")
6026 6024 % util.version())
6027 6025 ui.status(_(
6028 6026 "(see http://mercurial.selenic.com for more information)\n"
6029 6027 "\nCopyright (C) 2005-2012 Matt Mackall and others\n"
6030 6028 "This is free software; see the source for copying conditions. "
6031 6029 "There is NO\nwarranty; "
6032 6030 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
6033 6031 ))
6034 6032
6035 6033 norepo = ("clone init version help debugcommands debugcomplete"
6036 6034 " debugdate debuginstall debugfsinfo debugpushkey debugwireargs"
6037 6035 " debugknown debuggetbundle debugbundle")
6038 6036 optionalrepo = ("identify paths serve showconfig debugancestor debugdag"
6039 6037 " debugdata debugindex debugindexdot debugrevlog")
6040 6038 inferrepo = ("add addremove annotate cat commit diff grep forget log parents"
6041 6039 " remove resolve status debugwalk")
@@ -1,831 +1,835 b''
1 1 # dispatch.py - command dispatching 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 i18n import _
9 9 import os, sys, atexit, signal, pdb, socket, errno, shlex, time, traceback, re
10 10 import util, commands, hg, fancyopts, extensions, hook, error
11 11 import cmdutil, encoding
12 12 import ui as uimod
13 13
14 14 class request(object):
15 15 def __init__(self, args, ui=None, repo=None, fin=None, fout=None,
16 16 ferr=None):
17 17 self.args = args
18 18 self.ui = ui
19 19 self.repo = repo
20 20
21 21 # input/output/error streams
22 22 self.fin = fin
23 23 self.fout = fout
24 24 self.ferr = ferr
25 25
26 26 def run():
27 27 "run the command in sys.argv"
28 28 sys.exit((dispatch(request(sys.argv[1:])) or 0) & 255)
29 29
30 30 def dispatch(req):
31 31 "run the command specified in req.args"
32 32 if req.ferr:
33 33 ferr = req.ferr
34 34 elif req.ui:
35 35 ferr = req.ui.ferr
36 36 else:
37 37 ferr = sys.stderr
38 38
39 39 try:
40 40 if not req.ui:
41 41 req.ui = uimod.ui()
42 42 if '--traceback' in req.args:
43 43 req.ui.setconfig('ui', 'traceback', 'on')
44 44
45 45 # set ui streams from the request
46 46 if req.fin:
47 47 req.ui.fin = req.fin
48 48 if req.fout:
49 49 req.ui.fout = req.fout
50 50 if req.ferr:
51 51 req.ui.ferr = req.ferr
52 52 except util.Abort, inst:
53 53 ferr.write(_("abort: %s\n") % inst)
54 54 if inst.hint:
55 55 ferr.write(_("(%s)\n") % inst.hint)
56 56 return -1
57 57 except error.ParseError, inst:
58 58 if len(inst.args) > 1:
59 59 ferr.write(_("hg: parse error at %s: %s\n") %
60 60 (inst.args[1], inst.args[0]))
61 61 else:
62 62 ferr.write(_("hg: parse error: %s\n") % inst.args[0])
63 63 return -1
64 64
65 65 return _runcatch(req)
66 66
67 67 def _runcatch(req):
68 68 def catchterm(*args):
69 69 raise error.SignalInterrupt
70 70
71 71 ui = req.ui
72 72 try:
73 73 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
74 74 num = getattr(signal, name, None)
75 75 if num:
76 76 signal.signal(num, catchterm)
77 77 except ValueError:
78 78 pass # happens if called in a thread
79 79
80 80 try:
81 81 try:
82 82 # enter the debugger before command execution
83 83 if '--debugger' in req.args:
84 84 ui.warn(_("entering debugger - "
85 85 "type c to continue starting hg or h for help\n"))
86 86 pdb.set_trace()
87 87 try:
88 88 return _dispatch(req)
89 89 finally:
90 90 ui.flush()
91 91 except: # re-raises
92 92 # enter the debugger when we hit an exception
93 93 if '--debugger' in req.args:
94 94 traceback.print_exc()
95 95 pdb.post_mortem(sys.exc_info()[2])
96 96 ui.traceback()
97 97 raise
98 98
99 99 # Global exception handling, alphabetically
100 100 # Mercurial-specific first, followed by built-in and library exceptions
101 101 except error.AmbiguousCommand, inst:
102 102 ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
103 103 (inst.args[0], " ".join(inst.args[1])))
104 104 except error.ParseError, inst:
105 105 if len(inst.args) > 1:
106 106 ui.warn(_("hg: parse error at %s: %s\n") %
107 107 (inst.args[1], inst.args[0]))
108 108 else:
109 109 ui.warn(_("hg: parse error: %s\n") % inst.args[0])
110 110 return -1
111 111 except error.LockHeld, inst:
112 112 if inst.errno == errno.ETIMEDOUT:
113 113 reason = _('timed out waiting for lock held by %s') % inst.locker
114 114 else:
115 115 reason = _('lock held by %s') % inst.locker
116 116 ui.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
117 117 except error.LockUnavailable, inst:
118 118 ui.warn(_("abort: could not lock %s: %s\n") %
119 119 (inst.desc or inst.filename, inst.strerror))
120 120 except error.CommandError, inst:
121 121 if inst.args[0]:
122 122 ui.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
123 123 commands.help_(ui, inst.args[0], full=False, command=True)
124 124 else:
125 125 ui.warn(_("hg: %s\n") % inst.args[1])
126 126 commands.help_(ui, 'shortlist')
127 127 except error.OutOfBandError, inst:
128 128 ui.warn(_("abort: remote error:\n"))
129 129 ui.warn(''.join(inst.args))
130 130 except error.RepoError, inst:
131 131 ui.warn(_("abort: %s!\n") % inst)
132 132 if inst.hint:
133 133 ui.warn(_("(%s)\n") % inst.hint)
134 134 except error.ResponseError, inst:
135 135 ui.warn(_("abort: %s") % inst.args[0])
136 136 if not isinstance(inst.args[1], basestring):
137 137 ui.warn(" %r\n" % (inst.args[1],))
138 138 elif not inst.args[1]:
139 139 ui.warn(_(" empty string\n"))
140 140 else:
141 141 ui.warn("\n%r\n" % util.ellipsis(inst.args[1]))
142 142 except error.RevlogError, inst:
143 143 ui.warn(_("abort: %s!\n") % inst)
144 144 except error.SignalInterrupt:
145 145 ui.warn(_("killed!\n"))
146 146 except error.UnknownCommand, inst:
147 147 ui.warn(_("hg: unknown command '%s'\n") % inst.args[0])
148 148 try:
149 149 # check if the command is in a disabled extension
150 150 # (but don't check for extensions themselves)
151 151 commands.help_(ui, inst.args[0], unknowncmd=True)
152 152 except error.UnknownCommand:
153 153 commands.help_(ui, 'shortlist')
154 154 except util.Abort, inst:
155 155 ui.warn(_("abort: %s\n") % inst)
156 156 if inst.hint:
157 157 ui.warn(_("(%s)\n") % inst.hint)
158 158 except ImportError, inst:
159 159 ui.warn(_("abort: %s!\n") % inst)
160 160 m = str(inst).split()[-1]
161 161 if m in "mpatch bdiff".split():
162 162 ui.warn(_("(did you forget to compile extensions?)\n"))
163 163 elif m in "zlib".split():
164 164 ui.warn(_("(is your Python install correct?)\n"))
165 165 except IOError, inst:
166 166 if util.safehasattr(inst, "code"):
167 167 ui.warn(_("abort: %s\n") % inst)
168 168 elif util.safehasattr(inst, "reason"):
169 169 try: # usually it is in the form (errno, strerror)
170 170 reason = inst.reason.args[1]
171 171 except (AttributeError, IndexError):
172 172 # it might be anything, for example a string
173 173 reason = inst.reason
174 174 ui.warn(_("abort: error: %s\n") % reason)
175 175 elif util.safehasattr(inst, "args") and inst.args[0] == errno.EPIPE:
176 176 if ui.debugflag:
177 177 ui.warn(_("broken pipe\n"))
178 178 elif getattr(inst, "strerror", None):
179 179 if getattr(inst, "filename", None):
180 180 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
181 181 else:
182 182 ui.warn(_("abort: %s\n") % inst.strerror)
183 183 else:
184 184 raise
185 185 except OSError, inst:
186 186 if getattr(inst, "filename", None) is not None:
187 187 ui.warn(_("abort: %s: '%s'\n") % (inst.strerror, inst.filename))
188 188 else:
189 189 ui.warn(_("abort: %s\n") % inst.strerror)
190 190 except KeyboardInterrupt:
191 191 try:
192 192 ui.warn(_("interrupted!\n"))
193 193 except IOError, inst:
194 194 if inst.errno == errno.EPIPE:
195 195 if ui.debugflag:
196 196 ui.warn(_("\nbroken pipe\n"))
197 197 else:
198 198 raise
199 199 except MemoryError:
200 200 ui.warn(_("abort: out of memory\n"))
201 201 except SystemExit, inst:
202 202 # Commands shouldn't sys.exit directly, but give a return code.
203 203 # Just in case catch this and and pass exit code to caller.
204 204 return inst.code
205 205 except socket.error, inst:
206 206 ui.warn(_("abort: %s\n") % inst.args[-1])
207 207 except: # re-raises
208 208 myver = util.version()
209 209 # For compatibility checking, we discard the portion of the hg
210 210 # version after the + on the assumption that if a "normal
211 211 # user" is running a build with a + in it the packager
212 212 # probably built from fairly close to a tag and anyone with a
213 213 # 'make local' copy of hg (where the version number can be out
214 214 # of date) will be clueful enough to notice the implausible
215 215 # version number and try updating.
216 216 compare = myver.split('+')[0]
217 217 ct = tuplever(compare)
218 218 worst = None, ct, ''
219 219 for name, mod in extensions.extensions():
220 220 testedwith = getattr(mod, 'testedwith', '')
221 221 report = getattr(mod, 'buglink', _('the extension author.'))
222 222 if not testedwith.strip():
223 223 # We found an untested extension. It's likely the culprit.
224 224 worst = name, 'unknown', report
225 225 break
226 226 if compare not in testedwith.split() and testedwith != 'internal':
227 227 tested = [tuplever(v) for v in testedwith.split()]
228 228 lower = [t for t in tested if t < ct]
229 229 nearest = max(lower or tested)
230 230 if worst[0] is None or nearest < worst[1]:
231 231 worst = name, nearest, report
232 232 if worst[0] is not None:
233 233 name, testedwith, report = worst
234 234 if not isinstance(testedwith, str):
235 235 testedwith = '.'.join([str(c) for c in testedwith])
236 236 warning = (_('** Unknown exception encountered with '
237 237 'possibly-broken third-party extension %s\n'
238 238 '** which supports versions %s of Mercurial.\n'
239 239 '** Please disable %s and try your action again.\n'
240 240 '** If that fixes the bug please report it to %s\n')
241 241 % (name, testedwith, name, report))
242 242 else:
243 243 warning = (_("** unknown exception encountered, "
244 244 "please report by visiting\n") +
245 245 _("** http://mercurial.selenic.com/wiki/BugTracker\n"))
246 246 warning += ((_("** Python %s\n") % sys.version.replace('\n', '')) +
247 247 (_("** Mercurial Distributed SCM (version %s)\n") % myver) +
248 248 (_("** Extensions loaded: %s\n") %
249 249 ", ".join([x[0] for x in extensions.extensions()])))
250 250 ui.warn(warning)
251 251 raise
252 252
253 253 return -1
254 254
255 255 def tuplever(v):
256 256 try:
257 257 return tuple([int(i) for i in v.split('.')])
258 258 except ValueError:
259 259 return tuple()
260 260
261 261 def aliasargs(fn, givenargs):
262 262 args = getattr(fn, 'args', [])
263 263 if args:
264 264 cmd = ' '.join(map(util.shellquote, args))
265 265
266 266 nums = []
267 267 def replacer(m):
268 268 num = int(m.group(1)) - 1
269 269 nums.append(num)
270 270 if num < len(givenargs):
271 271 return givenargs[num]
272 272 raise util.Abort(_('too few arguments for command alias'))
273 273 cmd = re.sub(r'\$(\d+|\$)', replacer, cmd)
274 274 givenargs = [x for i, x in enumerate(givenargs)
275 275 if i not in nums]
276 276 args = shlex.split(cmd)
277 277 return args + givenargs
278 278
279 279 class cmdalias(object):
280 280 def __init__(self, name, definition, cmdtable):
281 281 self.name = self.cmd = name
282 282 self.cmdname = ''
283 283 self.definition = definition
284 284 self.args = []
285 285 self.opts = []
286 286 self.help = ''
287 287 self.norepo = True
288 288 self.optionalrepo = False
289 289 self.badalias = False
290 290
291 291 try:
292 292 aliases, entry = cmdutil.findcmd(self.name, cmdtable)
293 293 for alias, e in cmdtable.iteritems():
294 294 if e is entry:
295 295 self.cmd = alias
296 296 break
297 297 self.shadows = True
298 298 except error.UnknownCommand:
299 299 self.shadows = False
300 300
301 301 if not self.definition:
302 302 def fn(ui, *args):
303 303 ui.warn(_("no definition for alias '%s'\n") % self.name)
304 304 return 1
305 305 self.fn = fn
306 306 self.badalias = True
307 307 return
308 308
309 309 if self.definition.startswith('!'):
310 310 self.shell = True
311 311 def fn(ui, *args):
312 312 env = {'HG_ARGS': ' '.join((self.name,) + args)}
313 313 def _checkvar(m):
314 314 if m.groups()[0] == '$':
315 315 return m.group()
316 316 elif int(m.groups()[0]) <= len(args):
317 317 return m.group()
318 318 else:
319 319 ui.debug("No argument found for substitution "
320 320 "of %i variable in alias '%s' definition."
321 321 % (int(m.groups()[0]), self.name))
322 322 return ''
323 323 cmd = re.sub(r'\$(\d+|\$)', _checkvar, self.definition[1:])
324 324 replace = dict((str(i + 1), arg) for i, arg in enumerate(args))
325 325 replace['0'] = self.name
326 326 replace['@'] = ' '.join(args)
327 327 cmd = util.interpolate(r'\$', replace, cmd, escape_prefix=True)
328 328 return util.system(cmd, environ=env, out=ui.fout)
329 329 self.fn = fn
330 330 return
331 331
332 332 args = shlex.split(self.definition)
333 333 self.cmdname = cmd = args.pop(0)
334 334 args = map(util.expandpath, args)
335 335
336 336 for invalidarg in ("--cwd", "-R", "--repository", "--repo"):
337 337 if _earlygetopt([invalidarg], args):
338 338 def fn(ui, *args):
339 339 ui.warn(_("error in definition for alias '%s': %s may only "
340 340 "be given on the command line\n")
341 341 % (self.name, invalidarg))
342 342 return 1
343 343
344 344 self.fn = fn
345 345 self.badalias = True
346 346 return
347 347
348 348 try:
349 349 tableentry = cmdutil.findcmd(cmd, cmdtable, False)[1]
350 350 if len(tableentry) > 2:
351 351 self.fn, self.opts, self.help = tableentry
352 352 else:
353 353 self.fn, self.opts = tableentry
354 354
355 355 self.args = aliasargs(self.fn, args)
356 356 if cmd not in commands.norepo.split(' '):
357 357 self.norepo = False
358 358 if cmd in commands.optionalrepo.split(' '):
359 359 self.optionalrepo = True
360 360 if self.help.startswith("hg " + cmd):
361 361 # drop prefix in old-style help lines so hg shows the alias
362 362 self.help = self.help[4 + len(cmd):]
363 363 self.__doc__ = self.fn.__doc__
364 364
365 365 except error.UnknownCommand:
366 366 def fn(ui, *args):
367 367 ui.warn(_("alias '%s' resolves to unknown command '%s'\n") \
368 368 % (self.name, cmd))
369 369 try:
370 370 # check if the command is in a disabled extension
371 371 commands.help_(ui, cmd, unknowncmd=True)
372 372 except error.UnknownCommand:
373 373 pass
374 374 return 1
375 375 self.fn = fn
376 376 self.badalias = True
377 377 except error.AmbiguousCommand:
378 378 def fn(ui, *args):
379 379 ui.warn(_("alias '%s' resolves to ambiguous command '%s'\n") \
380 380 % (self.name, cmd))
381 381 return 1
382 382 self.fn = fn
383 383 self.badalias = True
384 384
385 385 def __call__(self, ui, *args, **opts):
386 386 if self.shadows:
387 387 ui.debug("alias '%s' shadows command '%s'\n" %
388 388 (self.name, self.cmdname))
389 389
390 390 if util.safehasattr(self, 'shell'):
391 391 return self.fn(ui, *args, **opts)
392 392 else:
393 393 try:
394 394 util.checksignature(self.fn)(ui, *args, **opts)
395 395 except error.SignatureError:
396 396 args = ' '.join([self.cmdname] + self.args)
397 397 ui.debug("alias '%s' expands to '%s'\n" % (self.name, args))
398 398 raise
399 399
400 400 def addaliases(ui, cmdtable):
401 401 # aliases are processed after extensions have been loaded, so they
402 402 # may use extension commands. Aliases can also use other alias definitions,
403 403 # but only if they have been defined prior to the current definition.
404 404 for alias, definition in ui.configitems('alias'):
405 405 aliasdef = cmdalias(alias, definition, cmdtable)
406 406
407 407 try:
408 408 olddef = cmdtable[aliasdef.cmd][0]
409 409 if olddef.definition == aliasdef.definition:
410 410 continue
411 411 except (KeyError, AttributeError):
412 412 # definition might not exist or it might not be a cmdalias
413 413 pass
414 414
415 415 cmdtable[aliasdef.name] = (aliasdef, aliasdef.opts, aliasdef.help)
416 416 if aliasdef.norepo:
417 417 commands.norepo += ' %s' % alias
418 418 if aliasdef.optionalrepo:
419 419 commands.optionalrepo += ' %s' % alias
420 420
421 421 def _parse(ui, args):
422 422 options = {}
423 423 cmdoptions = {}
424 424
425 425 try:
426 426 args = fancyopts.fancyopts(args, commands.globalopts, options)
427 427 except fancyopts.getopt.GetoptError, inst:
428 428 raise error.CommandError(None, inst)
429 429
430 430 if args:
431 431 cmd, args = args[0], args[1:]
432 432 aliases, entry = cmdutil.findcmd(cmd, commands.table,
433 433 ui.configbool("ui", "strict"))
434 434 cmd = aliases[0]
435 435 args = aliasargs(entry[0], args)
436 436 defaults = ui.config("defaults", cmd)
437 437 if defaults:
438 438 args = map(util.expandpath, shlex.split(defaults)) + args
439 439 c = list(entry[1])
440 440 else:
441 441 cmd = None
442 442 c = []
443 443
444 444 # combine global options into local
445 445 for o in commands.globalopts:
446 446 c.append((o[0], o[1], options[o[1]], o[3]))
447 447
448 448 try:
449 449 args = fancyopts.fancyopts(args, c, cmdoptions, True)
450 450 except fancyopts.getopt.GetoptError, inst:
451 451 raise error.CommandError(cmd, inst)
452 452
453 453 # separate global options back out
454 454 for o in commands.globalopts:
455 455 n = o[1]
456 456 options[n] = cmdoptions[n]
457 457 del cmdoptions[n]
458 458
459 459 return (cmd, cmd and entry[0] or None, args, options, cmdoptions)
460 460
461 461 def _parseconfig(ui, config):
462 462 """parse the --config options from the command line"""
463 463 configs = []
464 464
465 465 for cfg in config:
466 466 try:
467 467 name, value = cfg.split('=', 1)
468 468 section, name = name.split('.', 1)
469 469 if not section or not name:
470 470 raise IndexError
471 471 ui.setconfig(section, name, value)
472 472 configs.append((section, name, value))
473 473 except (IndexError, ValueError):
474 474 raise util.Abort(_('malformed --config option: %r '
475 475 '(use --config section.name=value)') % cfg)
476 476
477 477 return configs
478 478
479 479 def _earlygetopt(aliases, args):
480 480 """Return list of values for an option (or aliases).
481 481
482 482 The values are listed in the order they appear in args.
483 483 The options and values are removed from args.
484 484 """
485 485 try:
486 486 argcount = args.index("--")
487 487 except ValueError:
488 488 argcount = len(args)
489 489 shortopts = [opt for opt in aliases if len(opt) == 2]
490 490 values = []
491 491 pos = 0
492 492 while pos < argcount:
493 493 if args[pos] in aliases:
494 494 if pos + 1 >= argcount:
495 495 # ignore and let getopt report an error if there is no value
496 496 break
497 497 del args[pos]
498 498 values.append(args.pop(pos))
499 499 argcount -= 2
500 500 elif args[pos][:2] in shortopts:
501 501 # short option can have no following space, e.g. hg log -Rfoo
502 502 values.append(args.pop(pos)[2:])
503 503 argcount -= 1
504 504 else:
505 505 pos += 1
506 506 return values
507 507
508 508 def runcommand(lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions):
509 509 # run pre-hook, and abort if it fails
510 510 ret = hook.hook(lui, repo, "pre-%s" % cmd, False, args=" ".join(fullargs),
511 511 pats=cmdpats, opts=cmdoptions)
512 512 if ret:
513 513 return ret
514 514 ret = _runcommand(ui, options, cmd, d)
515 515 # run post-hook, passing command result
516 516 hook.hook(lui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
517 517 result=ret, pats=cmdpats, opts=cmdoptions)
518 518 return ret
519 519
520 520 def _getlocal(ui, rpath):
521 521 """Return (path, local ui object) for the given target path.
522 522
523 523 Takes paths in [cwd]/.hg/hgrc into account."
524 524 """
525 525 try:
526 526 wd = os.getcwd()
527 527 except OSError, e:
528 528 raise util.Abort(_("error getting current working directory: %s") %
529 529 e.strerror)
530 530 path = cmdutil.findrepo(wd) or ""
531 531 if not path:
532 532 lui = ui
533 533 else:
534 534 lui = ui.copy()
535 535 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
536 536
537 537 if rpath and rpath[-1]:
538 538 path = lui.expandpath(rpath[-1])
539 539 lui = ui.copy()
540 540 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
541 541
542 542 return path, lui
543 543
544 544 def _checkshellalias(lui, ui, args):
545 545 options = {}
546 546
547 547 try:
548 548 args = fancyopts.fancyopts(args, commands.globalopts, options)
549 549 except fancyopts.getopt.GetoptError:
550 550 return
551 551
552 552 if not args:
553 553 return
554 554
555 555 norepo = commands.norepo
556 556 optionalrepo = commands.optionalrepo
557 557 def restorecommands():
558 558 commands.norepo = norepo
559 559 commands.optionalrepo = optionalrepo
560 560
561 561 cmdtable = commands.table.copy()
562 562 addaliases(lui, cmdtable)
563 563
564 564 cmd = args[0]
565 565 try:
566 566 aliases, entry = cmdutil.findcmd(cmd, cmdtable,
567 567 lui.configbool("ui", "strict"))
568 568 except (error.AmbiguousCommand, error.UnknownCommand):
569 569 restorecommands()
570 570 return
571 571
572 572 cmd = aliases[0]
573 573 fn = entry[0]
574 574
575 575 if cmd and util.safehasattr(fn, 'shell'):
576 576 d = lambda: fn(ui, *args[1:])
577 577 return lambda: runcommand(lui, None, cmd, args[:1], ui, options, d,
578 578 [], {})
579 579
580 580 restorecommands()
581 581
582 582 _loaded = set()
583 583 def _dispatch(req):
584 584 args = req.args
585 585 ui = req.ui
586 586
587 587 # read --config before doing anything else
588 588 # (e.g. to change trust settings for reading .hg/hgrc)
589 589 cfgs = _parseconfig(ui, _earlygetopt(['--config'], args))
590 590
591 591 # check for cwd
592 592 cwd = _earlygetopt(['--cwd'], args)
593 593 if cwd:
594 594 os.chdir(cwd[-1])
595 595
596 596 rpath = _earlygetopt(["-R", "--repository", "--repo"], args)
597 597 path, lui = _getlocal(ui, rpath)
598 598
599 599 # Now that we're operating in the right directory/repository with
600 600 # the right config settings, check for shell aliases
601 601 shellaliasfn = _checkshellalias(lui, ui, args)
602 602 if shellaliasfn:
603 603 return shellaliasfn()
604 604
605 605 # Configure extensions in phases: uisetup, extsetup, cmdtable, and
606 606 # reposetup. Programs like TortoiseHg will call _dispatch several
607 607 # times so we keep track of configured extensions in _loaded.
608 608 extensions.loadall(lui)
609 609 exts = [ext for ext in extensions.extensions() if ext[0] not in _loaded]
610 610 # Propagate any changes to lui.__class__ by extensions
611 611 ui.__class__ = lui.__class__
612 612
613 613 # (uisetup and extsetup are handled in extensions.loadall)
614 614
615 615 for name, module in exts:
616 616 cmdtable = getattr(module, 'cmdtable', {})
617 617 overrides = [cmd for cmd in cmdtable if cmd in commands.table]
618 618 if overrides:
619 619 ui.warn(_("extension '%s' overrides commands: %s\n")
620 620 % (name, " ".join(overrides)))
621 621 commands.table.update(cmdtable)
622 622 _loaded.add(name)
623 623
624 624 # (reposetup is handled in hg.repository)
625 625
626 626 addaliases(lui, commands.table)
627 627
628 628 # check for fallback encoding
629 629 fallback = lui.config('ui', 'fallbackencoding')
630 630 if fallback:
631 631 encoding.fallbackencoding = fallback
632 632
633 633 fullargs = args
634 634 cmd, func, args, options, cmdoptions = _parse(lui, args)
635 635
636 636 if options["config"]:
637 637 raise util.Abort(_("option --config may not be abbreviated!"))
638 638 if options["cwd"]:
639 639 raise util.Abort(_("option --cwd may not be abbreviated!"))
640 640 if options["repository"]:
641 641 raise util.Abort(_(
642 642 "option -R has to be separated from other options (e.g. not -qR) "
643 643 "and --repository may only be abbreviated as --repo!"))
644 644
645 645 if options["encoding"]:
646 646 encoding.encoding = options["encoding"]
647 647 if options["encodingmode"]:
648 648 encoding.encodingmode = options["encodingmode"]
649 649 if options["time"]:
650 650 def get_times():
651 651 t = os.times()
652 652 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
653 653 t = (t[0], t[1], t[2], t[3], time.clock())
654 654 return t
655 655 s = get_times()
656 656 def print_time():
657 657 t = get_times()
658 658 ui.warn(_("time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
659 659 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
660 660 atexit.register(print_time)
661 661
662 662 uis = set([ui, lui])
663 663
664 664 if req.repo:
665 665 uis.add(req.repo.ui)
666 666
667 667 # copy configs that were passed on the cmdline (--config) to the repo ui
668 668 for cfg in cfgs:
669 669 req.repo.ui.setconfig(*cfg)
670 670
671 671 if options['verbose'] or options['debug'] or options['quiet']:
672 672 for opt in ('verbose', 'debug', 'quiet'):
673 673 val = str(bool(options[opt]))
674 674 for ui_ in uis:
675 675 ui_.setconfig('ui', opt, val)
676 676
677 677 if options['traceback']:
678 678 for ui_ in uis:
679 679 ui_.setconfig('ui', 'traceback', 'on')
680 680
681 681 if options['noninteractive']:
682 682 for ui_ in uis:
683 683 ui_.setconfig('ui', 'interactive', 'off')
684 684
685 685 if cmdoptions.get('insecure', False):
686 686 for ui_ in uis:
687 687 ui_.setconfig('web', 'cacerts', '')
688 688
689 689 if options['version']:
690 690 return commands.version_(ui)
691 691 if options['help']:
692 692 return commands.help_(ui, cmd)
693 693 elif not cmd:
694 694 return commands.help_(ui, 'shortlist')
695 695
696 696 repo = None
697 697 cmdpats = args[:]
698 698 if cmd not in commands.norepo.split():
699 699 # use the repo from the request only if we don't have -R
700 700 if not rpath and not cwd:
701 701 repo = req.repo
702 702
703 703 if repo:
704 704 # set the descriptors of the repo ui to those of ui
705 705 repo.ui.fin = ui.fin
706 706 repo.ui.fout = ui.fout
707 707 repo.ui.ferr = ui.ferr
708 708 else:
709 709 try:
710 710 repo = hg.repository(ui, path=path)
711 711 if not repo.local():
712 712 raise util.Abort(_("repository '%s' is not local") % path)
713 if not options['hidden']:
714 repo = repo.filtered('hidden')
715 else:
716 repo = repo.unfiltered()
713 717 repo.ui.setconfig("bundle", "mainreporoot", repo.root)
714 718 except error.RequirementError:
715 719 raise
716 720 except error.RepoError:
717 721 if cmd not in commands.optionalrepo.split():
718 722 if (cmd in commands.inferrepo.split() and
719 723 args and not path): # try to infer -R from command args
720 724 repos = map(cmdutil.findrepo, args)
721 725 guess = repos[0]
722 726 if guess and repos.count(guess) == len(repos):
723 727 req.args = ['--repository', guess] + fullargs
724 728 return _dispatch(req)
725 729 if not path:
726 730 raise error.RepoError(_("no repository found in '%s'"
727 731 " (.hg not found)")
728 732 % os.getcwd())
729 733 raise
730 734 if repo:
731 735 ui = repo.ui
732 736 args.insert(0, repo)
733 737 elif rpath:
734 738 ui.warn(_("warning: --repository ignored\n"))
735 739
736 740 msg = ' '.join(' ' in a and repr(a) or a for a in fullargs)
737 741 ui.log("command", msg + "\n")
738 742 d = lambda: util.checksignature(func)(ui, *args, **cmdoptions)
739 743 try:
740 744 return runcommand(lui, repo, cmd, fullargs, ui, options, d,
741 745 cmdpats, cmdoptions)
742 746 finally:
743 747 if repo and repo != req.repo:
744 748 repo.close()
745 749
746 750 def lsprofile(ui, func, fp):
747 751 format = ui.config('profiling', 'format', default='text')
748 752 field = ui.config('profiling', 'sort', default='inlinetime')
749 753 climit = ui.configint('profiling', 'nested', default=5)
750 754
751 755 if format not in ['text', 'kcachegrind']:
752 756 ui.warn(_("unrecognized profiling format '%s'"
753 757 " - Ignored\n") % format)
754 758 format = 'text'
755 759
756 760 try:
757 761 from mercurial import lsprof
758 762 except ImportError:
759 763 raise util.Abort(_(
760 764 'lsprof not available - install from '
761 765 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
762 766 p = lsprof.Profiler()
763 767 p.enable(subcalls=True)
764 768 try:
765 769 return func()
766 770 finally:
767 771 p.disable()
768 772
769 773 if format == 'kcachegrind':
770 774 import lsprofcalltree
771 775 calltree = lsprofcalltree.KCacheGrind(p)
772 776 calltree.output(fp)
773 777 else:
774 778 # format == 'text'
775 779 stats = lsprof.Stats(p.getstats())
776 780 stats.sort(field)
777 781 stats.pprint(limit=30, file=fp, climit=climit)
778 782
779 783 def statprofile(ui, func, fp):
780 784 try:
781 785 import statprof
782 786 except ImportError:
783 787 raise util.Abort(_(
784 788 'statprof not available - install using "easy_install statprof"'))
785 789
786 790 freq = ui.configint('profiling', 'freq', default=1000)
787 791 if freq > 0:
788 792 statprof.reset(freq)
789 793 else:
790 794 ui.warn(_("invalid sampling frequency '%s' - ignoring\n") % freq)
791 795
792 796 statprof.start()
793 797 try:
794 798 return func()
795 799 finally:
796 800 statprof.stop()
797 801 statprof.display(fp)
798 802
799 803 def _runcommand(ui, options, cmd, cmdfunc):
800 804 def checkargs():
801 805 try:
802 806 return cmdfunc()
803 807 except error.SignatureError:
804 808 raise error.CommandError(cmd, _("invalid arguments"))
805 809
806 810 if options['profile']:
807 811 profiler = os.getenv('HGPROF')
808 812 if profiler is None:
809 813 profiler = ui.config('profiling', 'type', default='ls')
810 814 if profiler not in ('ls', 'stat'):
811 815 ui.warn(_("unrecognized profiler '%s' - ignored\n") % profiler)
812 816 profiler = 'ls'
813 817
814 818 output = ui.config('profiling', 'output')
815 819
816 820 if output:
817 821 path = ui.expandpath(output)
818 822 fp = open(path, 'wb')
819 823 else:
820 824 fp = sys.stderr
821 825
822 826 try:
823 827 if profiler == 'ls':
824 828 return lsprofile(ui, checkargs, fp)
825 829 else:
826 830 return statprofile(ui, checkargs, fp)
827 831 finally:
828 832 if output:
829 833 fp.close()
830 834 else:
831 835 return checkargs()
@@ -1,521 +1,521 b''
1 1 $ hg init
2 2
3 3 Setup:
4 4
5 5 $ echo a >> a
6 6 $ hg ci -Am 'base'
7 7 adding a
8 8
9 9 Refuse to amend public csets:
10 10
11 11 $ hg phase -r . -p
12 12 $ hg ci --amend
13 13 abort: cannot amend public changesets
14 14 [255]
15 15 $ hg phase -r . -f -d
16 16
17 17 $ echo a >> a
18 18 $ hg ci -Am 'base1'
19 19
20 20 Nothing to amend:
21 21
22 22 $ hg ci --amend
23 23 nothing changed
24 24 [1]
25 25
26 26 $ cat >> $HGRCPATH <<EOF
27 27 > [hooks]
28 28 > pretxncommit.foo = sh -c "echo \\"pretxncommit \$HG_NODE\\"; hg id -r \$HG_NODE"
29 29 > EOF
30 30
31 31 Amending changeset with changes in working dir:
32 32 (and check that --message does not trigger an editor)
33 33
34 34 $ echo a >> a
35 35 $ HGEDITOR="\"sh\" \"`pwd`/editor.sh\"" hg commit --amend -m 'amend base1'
36 36 pretxncommit 43f1ba15f28a50abf0aae529cf8a16bfced7b149
37 37 43f1ba15f28a tip
38 38 saved backup bundle to $TESTTMP/.hg/strip-backup/489edb5b847d-amend-backup.hg (glob)
39 39 $ echo 'pretxncommit.foo = ' >> $HGRCPATH
40 40 $ hg diff -c .
41 41 diff -r ad120869acf0 -r 43f1ba15f28a a
42 42 --- a/a Thu Jan 01 00:00:00 1970 +0000
43 43 +++ b/a Thu Jan 01 00:00:00 1970 +0000
44 44 @@ -1,1 +1,3 @@
45 45 a
46 46 +a
47 47 +a
48 48 $ hg log
49 49 changeset: 1:43f1ba15f28a
50 50 tag: tip
51 51 user: test
52 52 date: Thu Jan 01 00:00:00 1970 +0000
53 53 summary: amend base1
54 54
55 55 changeset: 0:ad120869acf0
56 56 user: test
57 57 date: Thu Jan 01 00:00:00 1970 +0000
58 58 summary: base
59 59
60 60
61 61 Check proper abort for empty message
62 62
63 63 $ cat > editor.sh << '__EOF__'
64 64 > #!/bin/sh
65 65 > echo "" > "$1"
66 66 > __EOF__
67 67 $ echo b > b
68 68 $ hg add b
69 69 $ hg summary
70 70 parent: 1:43f1ba15f28a tip
71 71 amend base1
72 72 branch: default
73 73 commit: 1 added, 1 unknown
74 74 update: (current)
75 75 $ HGEDITOR="\"sh\" \"`pwd`/editor.sh\"" hg commit --amend
76 76 transaction abort!
77 77 rollback completed
78 78 abort: empty commit message
79 79 [255]
80 80 $ hg summary
81 81 parent: 1:43f1ba15f28a tip
82 82 amend base1
83 83 branch: default
84 84 commit: 1 added, 1 unknown
85 85 update: (current)
86 86
87 87 Add new file:
88 88 $ hg ci --amend -m 'amend base1 new file'
89 89 saved backup bundle to $TESTTMP/.hg/strip-backup/43f1ba15f28a-amend-backup.hg (glob)
90 90
91 91 Remove file that was added in amended commit:
92 92 (and test logfile option)
93 93 (and test that logfile option do not trigger an editor)
94 94
95 95 $ hg rm b
96 96 $ echo 'amend base1 remove new file' > ../logfile
97 97 $ HGEDITOR="\"sh\" \"`pwd`/editor.sh\"" hg ci --amend --logfile ../logfile
98 98 saved backup bundle to $TESTTMP/.hg/strip-backup/b8e3cb2b3882-amend-backup.hg (glob)
99 99
100 100 $ hg cat b
101 101 b: no such file in rev 74609c7f506e
102 102 [1]
103 103
104 104 No changes, just a different message:
105 105
106 106 $ hg ci -v --amend -m 'no changes, new message'
107 107 amending changeset 74609c7f506e
108 108 copying changeset 74609c7f506e to ad120869acf0
109 109 a
110 110 stripping amended changeset 74609c7f506e
111 111 1 changesets found
112 112 saved backup bundle to $TESTTMP/.hg/strip-backup/74609c7f506e-amend-backup.hg (glob)
113 113 1 changesets found
114 114 adding branch
115 115 adding changesets
116 116 adding manifests
117 117 adding file changes
118 118 added 1 changesets with 1 changes to 1 files
119 119 committed changeset 1:1cd866679df8
120 120 $ hg diff -c .
121 121 diff -r ad120869acf0 -r 1cd866679df8 a
122 122 --- a/a Thu Jan 01 00:00:00 1970 +0000
123 123 +++ b/a Thu Jan 01 00:00:00 1970 +0000
124 124 @@ -1,1 +1,3 @@
125 125 a
126 126 +a
127 127 +a
128 128 $ hg log
129 129 changeset: 1:1cd866679df8
130 130 tag: tip
131 131 user: test
132 132 date: Thu Jan 01 00:00:00 1970 +0000
133 133 summary: no changes, new message
134 134
135 135 changeset: 0:ad120869acf0
136 136 user: test
137 137 date: Thu Jan 01 00:00:00 1970 +0000
138 138 summary: base
139 139
140 140
141 141 Disable default date on commit so when -d isn't given, the old date is preserved:
142 142
143 143 $ echo '[defaults]' >> $HGRCPATH
144 144 $ echo 'commit=' >> $HGRCPATH
145 145
146 146 Test -u/-d:
147 147
148 148 $ hg ci --amend -u foo -d '1 0'
149 149 saved backup bundle to $TESTTMP/.hg/strip-backup/1cd866679df8-amend-backup.hg (glob)
150 150 $ echo a >> a
151 151 $ hg ci --amend -u foo -d '1 0'
152 152 saved backup bundle to $TESTTMP/.hg/strip-backup/780e6f23e03d-amend-backup.hg (glob)
153 153 $ hg log -r .
154 154 changeset: 1:5f357c7560ab
155 155 tag: tip
156 156 user: foo
157 157 date: Thu Jan 01 00:00:01 1970 +0000
158 158 summary: no changes, new message
159 159
160 160
161 161 Open editor with old commit message if a message isn't given otherwise:
162 162
163 163 $ cat > editor.sh << '__EOF__'
164 164 > #!/bin/sh
165 165 > cat $1
166 166 > echo "another precious commit message" > "$1"
167 167 > __EOF__
168 168 $ HGEDITOR="\"sh\" \"`pwd`/editor.sh\"" hg commit --amend -v
169 169 amending changeset 5f357c7560ab
170 170 copying changeset 5f357c7560ab to ad120869acf0
171 171 no changes, new message
172 172
173 173
174 174 HG: Enter commit message. Lines beginning with 'HG:' are removed.
175 175 HG: Leave message empty to abort commit.
176 176 HG: --
177 177 HG: user: foo
178 178 HG: branch 'default'
179 179 HG: changed a
180 180 a
181 181 stripping amended changeset 5f357c7560ab
182 182 1 changesets found
183 183 saved backup bundle to $TESTTMP/.hg/strip-backup/5f357c7560ab-amend-backup.hg (glob)
184 184 1 changesets found
185 185 adding branch
186 186 adding changesets
187 187 adding manifests
188 188 adding file changes
189 189 added 1 changesets with 1 changes to 1 files
190 190 committed changeset 1:7ab3bf440b54
191 191
192 192 Same, but with changes in working dir (different code path):
193 193
194 194 $ echo a >> a
195 195 $ HGEDITOR="\"sh\" \"`pwd`/editor.sh\"" hg commit --amend -v
196 196 amending changeset 7ab3bf440b54
197 197 a
198 198 copying changeset a0ea9b1a4c8c to ad120869acf0
199 199 another precious commit message
200 200
201 201
202 202 HG: Enter commit message. Lines beginning with 'HG:' are removed.
203 203 HG: Leave message empty to abort commit.
204 204 HG: --
205 205 HG: user: foo
206 206 HG: branch 'default'
207 207 HG: changed a
208 208 a
209 209 stripping intermediate changeset a0ea9b1a4c8c
210 210 stripping amended changeset 7ab3bf440b54
211 211 2 changesets found
212 212 saved backup bundle to $TESTTMP/.hg/strip-backup/7ab3bf440b54-amend-backup.hg (glob)
213 213 1 changesets found
214 214 adding branch
215 215 adding changesets
216 216 adding manifests
217 217 adding file changes
218 218 added 1 changesets with 1 changes to 1 files
219 219 committed changeset 1:ea22a388757c
220 220
221 221 $ rm editor.sh
222 222 $ hg log -r .
223 223 changeset: 1:ea22a388757c
224 224 tag: tip
225 225 user: foo
226 226 date: Thu Jan 01 00:00:01 1970 +0000
227 227 summary: another precious commit message
228 228
229 229
230 230 Moving bookmarks, preserve active bookmark:
231 231
232 232 $ hg book book1
233 233 $ hg book book2
234 234 $ hg ci --amend -m 'move bookmarks'
235 235 saved backup bundle to $TESTTMP/.hg/strip-backup/ea22a388757c-amend-backup.hg (glob)
236 236 $ hg book
237 237 book1 1:6cec5aa930e2
238 238 * book2 1:6cec5aa930e2
239 239 $ echo a >> a
240 240 $ hg ci --amend -m 'move bookmarks'
241 241 saved backup bundle to $TESTTMP/.hg/strip-backup/6cec5aa930e2-amend-backup.hg (glob)
242 242 $ hg book
243 243 book1 1:48bb6e53a15f
244 244 * book2 1:48bb6e53a15f
245 245
246 246 abort does not loose bookmarks
247 247
248 248 $ cat > editor.sh << '__EOF__'
249 249 > #!/bin/sh
250 250 > echo "" > "$1"
251 251 > __EOF__
252 252 $ echo a >> a
253 253 $ HGEDITOR="\"sh\" \"`pwd`/editor.sh\"" hg commit --amend
254 254 transaction abort!
255 255 rollback completed
256 256 abort: empty commit message
257 257 [255]
258 258 $ hg book
259 259 book1 1:48bb6e53a15f
260 260 * book2 1:48bb6e53a15f
261 261 $ hg revert -Caq
262 262 $ rm editor.sh
263 263
264 264 $ echo '[defaults]' >> $HGRCPATH
265 265 $ echo "commit=-d '0 0'" >> $HGRCPATH
266 266
267 267 Moving branches:
268 268
269 269 $ hg branch foo
270 270 marked working directory as branch foo
271 271 (branches are permanent and global, did you want a bookmark?)
272 272 $ echo a >> a
273 273 $ hg ci -m 'branch foo'
274 274 $ hg branch default -f
275 275 marked working directory as branch default
276 276 (branches are permanent and global, did you want a bookmark?)
277 277 $ hg ci --amend -m 'back to default'
278 278 saved backup bundle to $TESTTMP/.hg/strip-backup/8ac881fbf49d-amend-backup.hg (glob)
279 279 $ hg branches
280 280 default 2:ce12b0b57d46
281 281
282 282 Close branch:
283 283
284 284 $ hg up -q 0
285 285 $ echo b >> b
286 286 $ hg branch foo
287 287 marked working directory as branch foo
288 288 (branches are permanent and global, did you want a bookmark?)
289 289 $ hg ci -Am 'fork'
290 290 adding b
291 291 $ echo b >> b
292 292 $ hg ci -mb
293 293 $ hg ci --amend --close-branch -m 'closing branch foo'
294 294 saved backup bundle to $TESTTMP/.hg/strip-backup/c962248fa264-amend-backup.hg (glob)
295 295
296 296 Same thing, different code path:
297 297
298 298 $ echo b >> b
299 299 $ hg ci -m 'reopen branch'
300 300 reopening closed branch head 4
301 301 $ echo b >> b
302 302 $ hg ci --amend --close-branch
303 303 saved backup bundle to $TESTTMP/.hg/strip-backup/027371728205-amend-backup.hg (glob)
304 304 $ hg branches
305 305 default 2:ce12b0b57d46
306 306
307 307 Refuse to amend merges:
308 308
309 309 $ hg up -q default
310 310 $ hg merge foo
311 311 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
312 312 (branch merge, don't forget to commit)
313 313 $ hg ci --amend
314 314 abort: cannot amend while merging
315 315 [255]
316 316 $ hg ci -m 'merge'
317 317 $ hg ci --amend
318 318 abort: cannot amend merge changesets
319 319 [255]
320 320
321 321 Follow copies/renames:
322 322
323 323 $ hg mv b c
324 324 $ hg ci -m 'b -> c'
325 325 $ hg mv c d
326 326 $ hg ci --amend -m 'b -> d'
327 327 saved backup bundle to $TESTTMP/.hg/strip-backup/b8c6eac7f12e-amend-backup.hg (glob)
328 328 $ hg st --rev '.^' --copies d
329 329 A d
330 330 b
331 331 $ hg cp d e
332 332 $ hg ci -m 'e = d'
333 333 $ hg cp e f
334 334 $ hg ci --amend -m 'f = d'
335 335 saved backup bundle to $TESTTMP/.hg/strip-backup/7f9761d65613-amend-backup.hg (glob)
336 336 $ hg st --rev '.^' --copies f
337 337 A f
338 338 d
339 339
340 340 $ mv f f.orig
341 341 $ hg rm -A f
342 342 $ hg ci -m removef
343 343 $ hg cp a f
344 344 $ mv f.orig f
345 345 $ hg ci --amend -m replacef
346 346 saved backup bundle to $TESTTMP/.hg/strip-backup/9e8c5f7e3d95-amend-backup.hg (glob)
347 347 $ hg st --change . --copies
348 348 $ hg log -r . --template "{file_copies}\n"
349 349
350 350
351 351 Move added file (issue3410):
352 352
353 353 $ echo g >> g
354 354 $ hg ci -Am g
355 355 adding g
356 356 $ hg mv g h
357 357 $ hg ci --amend
358 358 saved backup bundle to $TESTTMP/.hg/strip-backup/24aa8eacce2b-amend-backup.hg (glob)
359 359 $ hg st --change . --copies h
360 360 A h
361 361 $ hg log -r . --template "{file_copies}\n"
362 362
363 363
364 364 Can't rollback an amend:
365 365
366 366 $ hg rollback
367 367 no rollback information available
368 368 [1]
369 369
370 370 Preserve extra dict (issue3430):
371 371
372 372 $ hg branch a
373 373 marked working directory as branch a
374 374 (branches are permanent and global, did you want a bookmark?)
375 375 $ echo a >> a
376 376 $ hg ci -ma
377 377 $ hg ci --amend -m "a'"
378 378 saved backup bundle to $TESTTMP/.hg/strip-backup/3837aa2a2fdb-amend-backup.hg (glob)
379 379 $ hg log -r . --template "{branch}\n"
380 380 a
381 381 $ hg ci --amend -m "a''"
382 382 saved backup bundle to $TESTTMP/.hg/strip-backup/c05c06be7514-amend-backup.hg (glob)
383 383 $ hg log -r . --template "{branch}\n"
384 384 a
385 385
386 386 Also preserve other entries in the dict that are in the old commit,
387 387 first graft something so there's an additional entry:
388 388
389 389 $ hg up 0 -q
390 390 $ echo z > z
391 391 $ hg ci -Am 'fork'
392 392 adding z
393 393 created new head
394 394 $ hg up 11
395 395 5 files updated, 0 files merged, 1 files removed, 0 files unresolved
396 396 $ hg graft 12
397 397 grafting revision 12
398 398 $ hg ci --amend -m 'graft amend'
399 399 saved backup bundle to $TESTTMP/.hg/strip-backup/bd010aea3f39-amend-backup.hg (glob)
400 400 $ hg log -r . --debug | grep extra
401 401 extra: amend_source=bd010aea3f39f3fb2a2f884b9ccb0471cd77398e
402 402 extra: branch=a
403 403 extra: source=2647734878ef0236dda712fae9c1651cf694ea8a
404 404
405 405 Preserve phase
406 406
407 407 $ hg phase '.^::.'
408 408 11: draft
409 409 13: draft
410 410 $ hg phase --secret --force .
411 411 $ hg phase '.^::.'
412 412 11: draft
413 413 13: secret
414 414 $ hg commit --amend -m 'amend for phase' -q
415 415 $ hg phase '.^::.'
416 416 11: draft
417 417 13: secret
418 418
419 419 Test amend with obsolete
420 420 ---------------------------
421 421
422 422 Enable obsolete
423 423
424 424 $ cat > ${TESTTMP}/obs.py << EOF
425 425 > import mercurial.obsolete
426 426 > mercurial.obsolete._enabled = True
427 427 > EOF
428 428 $ echo '[extensions]' >> $HGRCPATH
429 429 $ echo "obs=${TESTTMP}/obs.py" >> $HGRCPATH
430 430
431 431
432 432 Amend with no files changes
433 433
434 434 $ hg id -n
435 435 13
436 436 $ hg ci --amend -m 'babar'
437 437 $ hg id -n
438 438 14
439 439 $ hg log -Gl 3 --style=compact
440 440 @ 14[tip]:11 b650e6ee8614 1970-01-01 00:00 +0000 test
441 441 | babar
442 442 |
443 443 | o 12:0 2647734878ef 1970-01-01 00:00 +0000 test
444 444 | | fork
445 445 | |
446 446 o | 11 3334b7925910 1970-01-01 00:00 +0000 test
447 447 | | a''
448 448 | |
449 449 $ hg log -Gl 4 --hidden --style=compact
450 450 @ 14[tip]:11 b650e6ee8614 1970-01-01 00:00 +0000 test
451 451 | babar
452 452 |
453 453 | x 13:11 68ff8ff97044 1970-01-01 00:00 +0000 test
454 454 |/ amend for phase
455 455 |
456 456 | o 12:0 2647734878ef 1970-01-01 00:00 +0000 test
457 457 | | fork
458 458 | |
459 459 o | 11 3334b7925910 1970-01-01 00:00 +0000 test
460 460 | | a''
461 461 | |
462 462
463 463 Amend with files changes
464 464
465 465 (note: the extra commit over 15 is a temporary junk I would be happy to get
466 466 ride of)
467 467
468 468 $ echo 'babar' >> a
469 469 $ hg commit --amend
470 470 $ hg log -Gl 6 --hidden --style=compact
471 471 @ 16[tip]:11 9f9e9bccf56c 1970-01-01 00:00 +0000 test
472 472 | babar
473 473 |
474 474 | x 15 90fef497c56f 1970-01-01 00:00 +0000 test
475 475 | | temporary amend commit for b650e6ee8614
476 476 | |
477 477 | x 14:11 b650e6ee8614 1970-01-01 00:00 +0000 test
478 478 |/ babar
479 479 |
480 480 | x 13:11 68ff8ff97044 1970-01-01 00:00 +0000 test
481 481 |/ amend for phase
482 482 |
483 483 | o 12:0 2647734878ef 1970-01-01 00:00 +0000 test
484 484 | | fork
485 485 | |
486 486 o | 11 3334b7925910 1970-01-01 00:00 +0000 test
487 487 | | a''
488 488 | |
489 489
490 490
491 491 Test that amend does not make it easy to create obsoletescence cycle
492 492 ---------------------------------------------------------------------
493 493
494 494
495 $ hg id -r 14
495 $ hg id -r 14 --hidden
496 496 b650e6ee8614 (a)
497 $ hg revert -ar 14
497 $ hg revert -ar 14 --hidden
498 498 reverting a
499 499 $ hg commit --amend
500 500 $ hg id
501 501 b99e5df575f7 (a) tip
502 502
503 503 Test ui.prevent-unstable
504 504 ---------------------------------------------------------------------
505 505
506 506 $ hg up '.^'
507 507 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
508 508 $ echo 'b' >> a
509 509 $ hg log --style compact -r 'children(.)'
510 510 18[tip]:11 b99e5df575f7 1970-01-01 00:00 +0000 test
511 511 babar
512 512
513 513 $ hg commit --amend
514 514 $ hg log -r 'unstable()'
515 515 changeset: 18:b99e5df575f7
516 516 branch: a
517 517 parent: 11:3334b7925910
518 518 user: test
519 519 date: Thu Jan 01 00:00:00 1970 +0000
520 520 summary: babar
521 521
@@ -1,278 +1,280 b''
1 1 Show all commands except debug commands
2 2 $ hg debugcomplete
3 3 add
4 4 addremove
5 5 annotate
6 6 archive
7 7 backout
8 8 bisect
9 9 bookmarks
10 10 branch
11 11 branches
12 12 bundle
13 13 cat
14 14 clone
15 15 commit
16 16 copy
17 17 diff
18 18 export
19 19 forget
20 20 graft
21 21 grep
22 22 heads
23 23 help
24 24 identify
25 25 import
26 26 incoming
27 27 init
28 28 locate
29 29 log
30 30 manifest
31 31 merge
32 32 outgoing
33 33 parents
34 34 paths
35 35 phase
36 36 pull
37 37 push
38 38 recover
39 39 remove
40 40 rename
41 41 resolve
42 42 revert
43 43 rollback
44 44 root
45 45 serve
46 46 showconfig
47 47 status
48 48 summary
49 49 tag
50 50 tags
51 51 tip
52 52 unbundle
53 53 update
54 54 verify
55 55 version
56 56
57 57 Show all commands that start with "a"
58 58 $ hg debugcomplete a
59 59 add
60 60 addremove
61 61 annotate
62 62 archive
63 63
64 64 Do not show debug commands if there are other candidates
65 65 $ hg debugcomplete d
66 66 diff
67 67
68 68 Show debug commands if there are no other candidates
69 69 $ hg debugcomplete debug
70 70 debugancestor
71 71 debugbuilddag
72 72 debugbundle
73 73 debugcheckstate
74 74 debugcommands
75 75 debugcomplete
76 76 debugconfig
77 77 debugdag
78 78 debugdata
79 79 debugdate
80 80 debugdiscovery
81 81 debugfileset
82 82 debugfsinfo
83 83 debuggetbundle
84 84 debugignore
85 85 debugindex
86 86 debugindexdot
87 87 debuginstall
88 88 debugknown
89 89 debugobsolete
90 90 debugpushkey
91 91 debugpvec
92 92 debugrebuildstate
93 93 debugrename
94 94 debugrevlog
95 95 debugrevspec
96 96 debugsetparents
97 97 debugstate
98 98 debugsub
99 99 debugsuccessorssets
100 100 debugwalk
101 101 debugwireargs
102 102
103 103 Do not show the alias of a debug command if there are other candidates
104 104 (this should hide rawcommit)
105 105 $ hg debugcomplete r
106 106 recover
107 107 remove
108 108 rename
109 109 resolve
110 110 revert
111 111 rollback
112 112 root
113 113 Show the alias of a debug command if there are no other candidates
114 114 $ hg debugcomplete rawc
115 115
116 116
117 117 Show the global options
118 118 $ hg debugcomplete --options | sort
119 119 --config
120 120 --cwd
121 121 --debug
122 122 --debugger
123 123 --encoding
124 124 --encodingmode
125 125 --help
126 --hidden
126 127 --noninteractive
127 128 --profile
128 129 --quiet
129 130 --repository
130 131 --time
131 132 --traceback
132 133 --verbose
133 134 --version
134 135 -R
135 136 -h
136 137 -q
137 138 -v
138 139 -y
139 140
140 141 Show the options for the "serve" command
141 142 $ hg debugcomplete --options serve | sort
142 143 --accesslog
143 144 --address
144 145 --certificate
145 146 --cmdserver
146 147 --config
147 148 --cwd
148 149 --daemon
149 150 --daemon-pipefds
150 151 --debug
151 152 --debugger
152 153 --encoding
153 154 --encodingmode
154 155 --errorlog
155 156 --help
157 --hidden
156 158 --ipv6
157 159 --name
158 160 --noninteractive
159 161 --pid-file
160 162 --port
161 163 --prefix
162 164 --profile
163 165 --quiet
164 166 --repository
165 167 --stdio
166 168 --style
167 169 --templates
168 170 --time
169 171 --traceback
170 172 --verbose
171 173 --version
172 174 --web-conf
173 175 -6
174 176 -A
175 177 -E
176 178 -R
177 179 -a
178 180 -d
179 181 -h
180 182 -n
181 183 -p
182 184 -q
183 185 -t
184 186 -v
185 187 -y
186 188
187 189 Show an error if we use --options with an ambiguous abbreviation
188 190 $ hg debugcomplete --options s
189 191 hg: command 's' is ambiguous:
190 192 serve showconfig status summary
191 193 [255]
192 194
193 195 Show all commands + options
194 196 $ hg debugcommands
195 197 add: include, exclude, subrepos, dry-run
196 198 annotate: rev, follow, no-follow, text, user, file, date, number, changeset, line-number, ignore-all-space, ignore-space-change, ignore-blank-lines, include, exclude
197 199 clone: noupdate, updaterev, rev, branch, pull, uncompressed, ssh, remotecmd, insecure
198 200 commit: addremove, close-branch, amend, include, exclude, message, logfile, date, user, subrepos
199 201 diff: rev, change, text, git, nodates, show-function, reverse, ignore-all-space, ignore-space-change, ignore-blank-lines, unified, stat, include, exclude, subrepos
200 202 export: output, switch-parent, rev, text, git, nodates
201 203 forget: include, exclude
202 204 init: ssh, remotecmd, insecure
203 log: follow, follow-first, date, copies, keyword, rev, removed, only-merges, user, only-branch, branch, prune, hidden, patch, git, limit, no-merges, stat, graph, style, template, include, exclude
205 log: follow, follow-first, date, copies, keyword, rev, removed, only-merges, user, only-branch, branch, prune, patch, git, limit, no-merges, stat, graph, style, template, include, exclude
204 206 merge: force, rev, preview, tool
205 207 pull: update, force, rev, bookmark, branch, ssh, remotecmd, insecure
206 208 push: force, rev, bookmark, branch, new-branch, ssh, remotecmd, insecure
207 209 remove: after, force, include, exclude
208 210 serve: accesslog, daemon, daemon-pipefds, errorlog, port, address, prefix, name, web-conf, webdir-conf, pid-file, stdio, cmdserver, templates, style, ipv6, certificate
209 211 status: all, modified, added, removed, deleted, clean, unknown, ignored, no-status, copies, print0, rev, change, include, exclude, subrepos
210 212 summary: remote
211 213 update: clean, check, date, rev
212 214 addremove: similarity, include, exclude, dry-run
213 215 archive: no-decode, prefix, rev, type, subrepos, include, exclude
214 216 backout: merge, parent, rev, tool, include, exclude, message, logfile, date, user
215 217 bisect: reset, good, bad, skip, extend, command, noupdate
216 218 bookmarks: force, rev, delete, rename, inactive
217 219 branch: force, clean
218 220 branches: active, closed
219 221 bundle: force, rev, branch, base, all, type, ssh, remotecmd, insecure
220 222 cat: output, rev, decode, include, exclude
221 223 copy: after, force, include, exclude, dry-run
222 224 debugancestor:
223 225 debugbuilddag: mergeable-file, overwritten-file, new-file
224 226 debugbundle: all
225 227 debugcheckstate:
226 228 debugcommands:
227 229 debugcomplete: options
228 230 debugdag: tags, branches, dots, spaces
229 231 debugdata: changelog, manifest
230 232 debugdate: extended
231 233 debugdiscovery: old, nonheads, ssh, remotecmd, insecure
232 234 debugfileset: rev
233 235 debugfsinfo:
234 236 debuggetbundle: head, common, type
235 237 debugignore:
236 238 debugindex: changelog, manifest, format
237 239 debugindexdot:
238 240 debuginstall:
239 241 debugknown:
240 242 debugobsolete: flags, date, user
241 243 debugpushkey:
242 244 debugpvec:
243 245 debugrebuildstate: rev
244 246 debugrename: rev
245 247 debugrevlog: changelog, manifest, dump
246 248 debugrevspec:
247 249 debugsetparents:
248 250 debugstate: nodates, datesort
249 251 debugsub: rev
250 252 debugsuccessorssets:
251 253 debugwalk: include, exclude
252 254 debugwireargs: three, four, five, ssh, remotecmd, insecure
253 255 graft: rev, continue, edit, log, currentdate, currentuser, date, user, tool, dry-run
254 256 grep: print0, all, text, follow, ignore-case, files-with-matches, line-number, rev, user, date, include, exclude
255 257 heads: rev, topo, active, closed, style, template
256 258 help: extension, command, keyword
257 259 identify: rev, num, id, branch, tags, bookmarks, ssh, remotecmd, insecure
258 260 import: strip, base, edit, force, no-commit, bypass, exact, import-branch, message, logfile, date, user, similarity
259 261 incoming: force, newest-first, bundle, rev, bookmarks, branch, patch, git, limit, no-merges, stat, graph, style, template, ssh, remotecmd, insecure, subrepos
260 262 locate: rev, print0, fullpath, include, exclude
261 263 manifest: rev, all
262 264 outgoing: force, rev, newest-first, bookmarks, branch, patch, git, limit, no-merges, stat, graph, style, template, ssh, remotecmd, insecure, subrepos
263 265 parents: rev, style, template
264 266 paths:
265 267 phase: public, draft, secret, force, rev
266 268 recover:
267 269 rename: after, force, include, exclude, dry-run
268 270 resolve: all, list, mark, unmark, no-status, tool, include, exclude
269 271 revert: all, date, rev, no-backup, include, exclude, dry-run
270 272 rollback: dry-run, force
271 273 root:
272 274 showconfig: untrusted
273 275 tag: force, local, rev, remove, edit, message, date, user
274 276 tags:
275 277 tip: patch, git, style, template
276 278 unbundle: update
277 279 verify:
278 280 version:
@@ -1,571 +1,573 b''
1 1 Test basic extension support
2 2
3 3 $ cat > foobar.py <<EOF
4 4 > import os
5 5 > from mercurial import commands
6 6 >
7 7 > def uisetup(ui):
8 8 > ui.write("uisetup called\\n")
9 9 >
10 10 > def reposetup(ui, repo):
11 11 > ui.write("reposetup called for %s\\n" % os.path.basename(repo.root))
12 12 > ui.write("ui %s= repo.ui\\n" % (ui == repo.ui and "=" or "!"))
13 13 >
14 14 > def foo(ui, *args, **kwargs):
15 15 > ui.write("Foo\\n")
16 16 >
17 17 > def bar(ui, *args, **kwargs):
18 18 > ui.write("Bar\\n")
19 19 >
20 20 > cmdtable = {
21 21 > "foo": (foo, [], "hg foo"),
22 22 > "bar": (bar, [], "hg bar"),
23 23 > }
24 24 >
25 25 > commands.norepo += ' bar'
26 26 > EOF
27 27 $ abspath=`pwd`/foobar.py
28 28
29 29 $ mkdir barfoo
30 30 $ cp foobar.py barfoo/__init__.py
31 31 $ barfoopath=`pwd`/barfoo
32 32
33 33 $ hg init a
34 34 $ cd a
35 35 $ echo foo > file
36 36 $ hg add file
37 37 $ hg commit -m 'add file'
38 38
39 39 $ echo '[extensions]' >> $HGRCPATH
40 40 $ echo "foobar = $abspath" >> $HGRCPATH
41 41 $ hg foo
42 42 uisetup called
43 43 reposetup called for a
44 44 ui == repo.ui
45 45 Foo
46 46
47 47 $ cd ..
48 48 $ hg clone a b
49 49 uisetup called
50 50 reposetup called for a
51 51 ui == repo.ui
52 52 reposetup called for b
53 53 ui == repo.ui
54 54 updating to branch default
55 55 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
56 56
57 57 $ hg bar
58 58 uisetup called
59 59 Bar
60 60 $ echo 'foobar = !' >> $HGRCPATH
61 61
62 62 module/__init__.py-style
63 63
64 64 $ echo "barfoo = $barfoopath" >> $HGRCPATH
65 65 $ cd a
66 66 $ hg foo
67 67 uisetup called
68 68 reposetup called for a
69 69 ui == repo.ui
70 70 Foo
71 71 $ echo 'barfoo = !' >> $HGRCPATH
72 72
73 73 Check that extensions are loaded in phases:
74 74
75 75 $ cat > foo.py <<EOF
76 76 > import os
77 77 > name = os.path.basename(__file__).rsplit('.', 1)[0]
78 78 > print "1) %s imported" % name
79 79 > def uisetup(ui):
80 80 > print "2) %s uisetup" % name
81 81 > def extsetup():
82 82 > print "3) %s extsetup" % name
83 83 > def reposetup(ui, repo):
84 84 > print "4) %s reposetup" % name
85 85 > EOF
86 86
87 87 $ cp foo.py bar.py
88 88 $ echo 'foo = foo.py' >> $HGRCPATH
89 89 $ echo 'bar = bar.py' >> $HGRCPATH
90 90
91 91 Command with no output, we just want to see the extensions loaded:
92 92
93 93 $ hg paths
94 94 1) foo imported
95 95 1) bar imported
96 96 2) foo uisetup
97 97 2) bar uisetup
98 98 3) foo extsetup
99 99 3) bar extsetup
100 100 4) foo reposetup
101 101 4) bar reposetup
102 102
103 103 Check hgweb's load order:
104 104
105 105 $ cat > hgweb.cgi <<EOF
106 106 > #!/usr/bin/env python
107 107 > from mercurial import demandimport; demandimport.enable()
108 108 > from mercurial.hgweb import hgweb
109 109 > from mercurial.hgweb import wsgicgi
110 110 >
111 111 > application = hgweb('.', 'test repo')
112 112 > wsgicgi.launch(application)
113 113 > EOF
114 114
115 115 $ SCRIPT_NAME='/' SERVER_PORT='80' SERVER_NAME='localhost' python hgweb.cgi \
116 116 > | grep '^[0-9]) ' # ignores HTML output
117 117 1) foo imported
118 118 1) bar imported
119 119 2) foo uisetup
120 120 2) bar uisetup
121 121 3) foo extsetup
122 122 3) bar extsetup
123 123 4) foo reposetup
124 124 4) bar reposetup
125 125 4) foo reposetup
126 126 4) bar reposetup
127 127
128 128 $ echo 'foo = !' >> $HGRCPATH
129 129 $ echo 'bar = !' >> $HGRCPATH
130 130
131 131 $ cd ..
132 132
133 133 hide outer repo
134 134 $ hg init
135 135
136 136 $ cat > empty.py <<EOF
137 137 > '''empty cmdtable
138 138 > '''
139 139 > cmdtable = {}
140 140 > EOF
141 141 $ emptypath=`pwd`/empty.py
142 142 $ echo "empty = $emptypath" >> $HGRCPATH
143 143 $ hg help empty
144 144 empty extension - empty cmdtable
145 145
146 146 no commands defined
147 147
148 148 $ echo 'empty = !' >> $HGRCPATH
149 149
150 150 $ cat > debugextension.py <<EOF
151 151 > '''only debugcommands
152 152 > '''
153 153 > def debugfoobar(ui, repo, *args, **opts):
154 154 > "yet another debug command"
155 155 > pass
156 156 >
157 157 > def foo(ui, repo, *args, **opts):
158 158 > """yet another foo command
159 159 >
160 160 > This command has been DEPRECATED since forever.
161 161 > """
162 162 > pass
163 163 >
164 164 > cmdtable = {
165 165 > "debugfoobar": (debugfoobar, (), "hg debugfoobar"),
166 166 > "foo": (foo, (), "hg foo")
167 167 > }
168 168 > EOF
169 169 $ debugpath=`pwd`/debugextension.py
170 170 $ echo "debugextension = $debugpath" >> $HGRCPATH
171 171
172 172 $ hg help debugextension
173 173 debugextension extension - only debugcommands
174 174
175 175 no commands defined
176 176
177 177 $ hg --verbose help debugextension
178 178 debugextension extension - only debugcommands
179 179
180 180 list of commands:
181 181
182 182 foo yet another foo command
183 183
184 184 global options:
185 185
186 186 -R --repository REPO repository root directory or name of overlay bundle
187 187 file
188 188 --cwd DIR change working directory
189 189 -y --noninteractive do not prompt, automatically pick the first choice for
190 190 all prompts
191 191 -q --quiet suppress output
192 192 -v --verbose enable additional output
193 193 --config CONFIG [+] set/override config option (use 'section.name=value')
194 194 --debug enable debugging output
195 195 --debugger start debugger
196 196 --encoding ENCODE set the charset encoding (default: ascii)
197 197 --encodingmode MODE set the charset encoding mode (default: strict)
198 198 --traceback always print a traceback on exception
199 199 --time time how long the command takes
200 200 --profile print command execution profile
201 201 --version output version information and exit
202 202 -h --help display help and exit
203 --hidden consider hidden changesets
203 204
204 205 [+] marked option can be specified multiple times
205 206
206 207 $ hg --debug help debugextension
207 208 debugextension extension - only debugcommands
208 209
209 210 list of commands:
210 211
211 212 debugfoobar yet another debug command
212 213 foo yet another foo command
213 214
214 215 global options:
215 216
216 217 -R --repository REPO repository root directory or name of overlay bundle
217 218 file
218 219 --cwd DIR change working directory
219 220 -y --noninteractive do not prompt, automatically pick the first choice for
220 221 all prompts
221 222 -q --quiet suppress output
222 223 -v --verbose enable additional output
223 224 --config CONFIG [+] set/override config option (use 'section.name=value')
224 225 --debug enable debugging output
225 226 --debugger start debugger
226 227 --encoding ENCODE set the charset encoding (default: ascii)
227 228 --encodingmode MODE set the charset encoding mode (default: strict)
228 229 --traceback always print a traceback on exception
229 230 --time time how long the command takes
230 231 --profile print command execution profile
231 232 --version output version information and exit
232 233 -h --help display help and exit
234 --hidden consider hidden changesets
233 235
234 236 [+] marked option can be specified multiple times
235 237 $ echo 'debugextension = !' >> $HGRCPATH
236 238
237 239 Extension module help vs command help:
238 240
239 241 $ echo 'extdiff =' >> $HGRCPATH
240 242 $ hg help extdiff
241 243 hg extdiff [OPT]... [FILE]...
242 244
243 245 use external program to diff repository (or selected files)
244 246
245 247 Show differences between revisions for the specified files, using an
246 248 external program. The default program used is diff, with default options
247 249 "-Npru".
248 250
249 251 To select a different program, use the -p/--program option. The program
250 252 will be passed the names of two directories to compare. To pass additional
251 253 options to the program, use -o/--option. These will be passed before the
252 254 names of the directories to compare.
253 255
254 256 When two revision arguments are given, then changes are shown between
255 257 those revisions. If only one revision is specified then that revision is
256 258 compared to the working directory, and, when no revisions are specified,
257 259 the working directory files are compared to its parent.
258 260
259 261 use "hg help -e extdiff" to show help for the extdiff extension
260 262
261 263 options:
262 264
263 265 -p --program CMD comparison program to run
264 266 -o --option OPT [+] pass option to comparison program
265 267 -r --rev REV [+] revision
266 268 -c --change REV change made by revision
267 269 -I --include PATTERN [+] include names matching the given patterns
268 270 -X --exclude PATTERN [+] exclude names matching the given patterns
269 271
270 272 [+] marked option can be specified multiple times
271 273
272 274 use "hg -v help extdiff" to show the global options
273 275
274 276 $ hg help --extension extdiff
275 277 extdiff extension - command to allow external programs to compare revisions
276 278
277 279 The extdiff Mercurial extension allows you to use external programs to compare
278 280 revisions, or revision with working directory. The external diff programs are
279 281 called with a configurable set of options and two non-option arguments: paths
280 282 to directories containing snapshots of files to compare.
281 283
282 284 The extdiff extension also allows you to configure new diff commands, so you
283 285 do not need to type "hg extdiff -p kdiff3" always.
284 286
285 287 [extdiff]
286 288 # add new command that runs GNU diff(1) in 'context diff' mode
287 289 cdiff = gdiff -Nprc5
288 290 ## or the old way:
289 291 #cmd.cdiff = gdiff
290 292 #opts.cdiff = -Nprc5
291 293
292 294 # add new command called vdiff, runs kdiff3
293 295 vdiff = kdiff3
294 296
295 297 # add new command called meld, runs meld (no need to name twice)
296 298 meld =
297 299
298 300 # add new command called vimdiff, runs gvimdiff with DirDiff plugin
299 301 # (see http://www.vim.org/scripts/script.php?script_id=102) Non
300 302 # English user, be sure to put "let g:DirDiffDynamicDiffText = 1" in
301 303 # your .vimrc
302 304 vimdiff = gvim -f "+next" \
303 305 "+execute 'DirDiff' fnameescape(argv(0)) fnameescape(argv(1))"
304 306
305 307 Tool arguments can include variables that are expanded at runtime:
306 308
307 309 $parent1, $plabel1 - filename, descriptive label of first parent
308 310 $child, $clabel - filename, descriptive label of child revision
309 311 $parent2, $plabel2 - filename, descriptive label of second parent
310 312 $root - repository root
311 313 $parent is an alias for $parent1.
312 314
313 315 The extdiff extension will look in your [diff-tools] and [merge-tools]
314 316 sections for diff tool arguments, when none are specified in [extdiff].
315 317
316 318 [extdiff]
317 319 kdiff3 =
318 320
319 321 [diff-tools]
320 322 kdiff3.diffargs=--L1 '$plabel1' --L2 '$clabel' $parent $child
321 323
322 324 You can use -I/-X and list of file or directory names like normal "hg diff"
323 325 command. The extdiff extension makes snapshots of only needed files, so
324 326 running the external diff program will actually be pretty fast (at least
325 327 faster than having to compare the entire tree).
326 328
327 329 list of commands:
328 330
329 331 extdiff use external program to diff repository (or selected files)
330 332
331 333 use "hg -v help extdiff" to show builtin aliases and global options
332 334
333 335 $ echo 'extdiff = !' >> $HGRCPATH
334 336
335 337 Test help topic with same name as extension
336 338
337 339 $ cat > multirevs.py <<EOF
338 340 > from mercurial import commands
339 341 > """multirevs extension
340 342 > Big multi-line module docstring."""
341 343 > def multirevs(ui, repo, arg, *args, **opts):
342 344 > """multirevs command"""
343 345 > pass
344 346 > cmdtable = {
345 347 > "multirevs": (multirevs, [], 'ARG')
346 348 > }
347 349 > commands.norepo += ' multirevs'
348 350 > EOF
349 351 $ echo "multirevs = multirevs.py" >> $HGRCPATH
350 352
351 353 $ hg help multirevs
352 354 Specifying Multiple Revisions
353 355
354 356 When Mercurial accepts more than one revision, they may be specified
355 357 individually, or provided as a topologically continuous range, separated
356 358 by the ":" character.
357 359
358 360 The syntax of range notation is [BEGIN]:[END], where BEGIN and END are
359 361 revision identifiers. Both BEGIN and END are optional. If BEGIN is not
360 362 specified, it defaults to revision number 0. If END is not specified, it
361 363 defaults to the tip. The range ":" thus means "all revisions".
362 364
363 365 If BEGIN is greater than END, revisions are treated in reverse order.
364 366
365 367 A range acts as a closed interval. This means that a range of 3:5 gives 3,
366 368 4 and 5. Similarly, a range of 9:6 gives 9, 8, 7, and 6.
367 369
368 370 use "hg help -c multirevs" to see help for the multirevs command
369 371
370 372 $ hg help -c multirevs
371 373 hg multirevs ARG
372 374
373 375 multirevs command
374 376
375 377 use "hg -v help multirevs" to show the global options
376 378
377 379 $ hg multirevs
378 380 hg multirevs: invalid arguments
379 381 hg multirevs ARG
380 382
381 383 multirevs command
382 384
383 385 use "hg help multirevs" to show the full help text
384 386 [255]
385 387
386 388 $ echo "multirevs = !" >> $HGRCPATH
387 389
388 390 Issue811: Problem loading extensions twice (by site and by user)
389 391
390 392 $ debugpath=`pwd`/debugissue811.py
391 393 $ cat > debugissue811.py <<EOF
392 394 > '''show all loaded extensions
393 395 > '''
394 396 > from mercurial import extensions, commands
395 397 >
396 398 > def debugextensions(ui):
397 399 > "yet another debug command"
398 400 > ui.write("%s\n" % '\n'.join([x for x, y in extensions.extensions()]))
399 401 >
400 402 > cmdtable = {"debugextensions": (debugextensions, (), "hg debugextensions")}
401 403 > commands.norepo += " debugextensions"
402 404 > EOF
403 405 $ echo "debugissue811 = $debugpath" >> $HGRCPATH
404 406 $ echo "mq=" >> $HGRCPATH
405 407 $ echo "hgext.mq=" >> $HGRCPATH
406 408 $ echo "hgext/mq=" >> $HGRCPATH
407 409
408 410 Show extensions:
409 411
410 412 $ hg debugextensions
411 413 debugissue811
412 414 mq
413 415
414 416 Disabled extension commands:
415 417
416 418 $ HGRCPATH=
417 419 $ export HGRCPATH
418 420 $ hg help email
419 421 'email' is provided by the following extension:
420 422
421 423 patchbomb command to send changesets as (a series of) patch emails
422 424
423 425 use "hg help extensions" for information on enabling extensions
424 426 $ hg qdel
425 427 hg: unknown command 'qdel'
426 428 'qdelete' is provided by the following extension:
427 429
428 430 mq manage a stack of patches
429 431
430 432 use "hg help extensions" for information on enabling extensions
431 433 [255]
432 434 $ hg churn
433 435 hg: unknown command 'churn'
434 436 'churn' is provided by the following extension:
435 437
436 438 churn command to display statistics about repository history
437 439
438 440 use "hg help extensions" for information on enabling extensions
439 441 [255]
440 442
441 443 Disabled extensions:
442 444
443 445 $ hg help churn
444 446 churn extension - command to display statistics about repository history
445 447
446 448 use "hg help extensions" for information on enabling extensions
447 449 $ hg help patchbomb
448 450 patchbomb extension - command to send changesets as (a series of) patch emails
449 451
450 452 use "hg help extensions" for information on enabling extensions
451 453
452 454 Broken disabled extension and command:
453 455
454 456 $ mkdir hgext
455 457 $ echo > hgext/__init__.py
456 458 $ cat > hgext/broken.py <<EOF
457 459 > "broken extension'
458 460 > EOF
459 461 $ cat > path.py <<EOF
460 462 > import os, sys
461 463 > sys.path.insert(0, os.environ['HGEXTPATH'])
462 464 > EOF
463 465 $ HGEXTPATH=`pwd`
464 466 $ export HGEXTPATH
465 467
466 468 $ hg --config extensions.path=./path.py help broken
467 469 broken extension - (no help text available)
468 470
469 471 use "hg help extensions" for information on enabling extensions
470 472
471 473 $ cat > hgext/forest.py <<EOF
472 474 > cmdtable = None
473 475 > EOF
474 476 $ hg --config extensions.path=./path.py help foo > /dev/null
475 477 warning: error finding commands in $TESTTMP/hgext/forest.py (glob)
476 478 hg: unknown command 'foo'
477 479 warning: error finding commands in $TESTTMP/hgext/forest.py (glob)
478 480 [255]
479 481
480 482 $ cat > throw.py <<EOF
481 483 > from mercurial import cmdutil, commands
482 484 > cmdtable = {}
483 485 > command = cmdutil.command(cmdtable)
484 486 > class Bogon(Exception): pass
485 487 >
486 488 > @command('throw', [], 'hg throw')
487 489 > def throw(ui, **opts):
488 490 > """throws an exception"""
489 491 > raise Bogon()
490 492 > commands.norepo += " throw"
491 493 > EOF
492 494 No declared supported version, extension complains:
493 495 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
494 496 ** Unknown exception encountered with possibly-broken third-party extension throw
495 497 ** which supports versions unknown of Mercurial.
496 498 ** Please disable throw and try your action again.
497 499 ** If that fixes the bug please report it to the extension author.
498 500 ** Python * (glob)
499 501 ** Mercurial Distributed SCM * (glob)
500 502 ** Extensions loaded: throw
501 503 empty declaration of supported version, extension complains:
502 504 $ echo "testedwith = ''" >> throw.py
503 505 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
504 506 ** Unknown exception encountered with possibly-broken third-party extension throw
505 507 ** which supports versions unknown of Mercurial.
506 508 ** Please disable throw and try your action again.
507 509 ** If that fixes the bug please report it to the extension author.
508 510 ** Python * (glob)
509 511 ** Mercurial Distributed SCM (*) (glob)
510 512 ** Extensions loaded: throw
511 513 If the extension specifies a buglink, show that:
512 514 $ echo 'buglink = "http://example.com/bts"' >> throw.py
513 515 $ rm -f throw.pyc throw.pyo
514 516 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
515 517 ** Unknown exception encountered with possibly-broken third-party extension throw
516 518 ** which supports versions unknown of Mercurial.
517 519 ** Please disable throw and try your action again.
518 520 ** If that fixes the bug please report it to http://example.com/bts
519 521 ** Python * (glob)
520 522 ** Mercurial Distributed SCM (*) (glob)
521 523 ** Extensions loaded: throw
522 524 If the extensions declare outdated versions, accuse the older extension first:
523 525 $ echo "from mercurial import util" >> older.py
524 526 $ echo "util.version = lambda:'2.2'" >> older.py
525 527 $ echo "testedwith = '1.9.3'" >> older.py
526 528 $ echo "testedwith = '2.1.1'" >> throw.py
527 529 $ rm -f throw.pyc throw.pyo
528 530 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
529 531 > throw 2>&1 | egrep '^\*\*'
530 532 ** Unknown exception encountered with possibly-broken third-party extension older
531 533 ** which supports versions 1.9.3 of Mercurial.
532 534 ** Please disable older and try your action again.
533 535 ** If that fixes the bug please report it to the extension author.
534 536 ** Python * (glob)
535 537 ** Mercurial Distributed SCM (version 2.2)
536 538 ** Extensions loaded: throw, older
537 539 One extension only tested with older, one only with newer versions:
538 540 $ echo "util.version = lambda:'2.1.0'" >> older.py
539 541 $ rm -f older.pyc older.pyo
540 542 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
541 543 > throw 2>&1 | egrep '^\*\*'
542 544 ** Unknown exception encountered with possibly-broken third-party extension older
543 545 ** which supports versions 1.9.3 of Mercurial.
544 546 ** Please disable older and try your action again.
545 547 ** If that fixes the bug please report it to the extension author.
546 548 ** Python * (glob)
547 549 ** Mercurial Distributed SCM (version 2.1.0)
548 550 ** Extensions loaded: throw, older
549 551 Older extension is tested with current version, the other only with newer:
550 552 $ echo "util.version = lambda:'1.9.3'" >> older.py
551 553 $ rm -f older.pyc older.pyo
552 554 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
553 555 > throw 2>&1 | egrep '^\*\*'
554 556 ** Unknown exception encountered with possibly-broken third-party extension throw
555 557 ** which supports versions 2.1.1 of Mercurial.
556 558 ** Please disable throw and try your action again.
557 559 ** If that fixes the bug please report it to http://example.com/bts
558 560 ** Python * (glob)
559 561 ** Mercurial Distributed SCM (version 1.9.3)
560 562 ** Extensions loaded: throw, older
561 563
562 564 Declare the version as supporting this hg version, show regular bts link:
563 565 $ hgver=`python -c 'from mercurial import util; print util.version().split("+")[0]'`
564 566 $ echo 'testedwith = """'"$hgver"'"""' >> throw.py
565 567 $ rm -f throw.pyc throw.pyo
566 568 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
567 569 ** unknown exception encountered, please report by visiting
568 570 ** http://mercurial.selenic.com/wiki/BugTracker
569 571 ** Python * (glob)
570 572 ** Mercurial Distributed SCM (*) (glob)
571 573 ** Extensions loaded: throw
@@ -1,875 +1,877 b''
1 1 Short help:
2 2
3 3 $ hg
4 4 Mercurial Distributed SCM
5 5
6 6 basic commands:
7 7
8 8 add add the specified files on the next commit
9 9 annotate show changeset information by line for each file
10 10 clone make a copy of an existing repository
11 11 commit commit the specified files or all outstanding changes
12 12 diff diff repository (or selected files)
13 13 export dump the header and diffs for one or more changesets
14 14 forget forget the specified files on the next commit
15 15 init create a new repository in the given directory
16 16 log show revision history of entire repository or files
17 17 merge merge working directory with another revision
18 18 pull pull changes from the specified source
19 19 push push changes to the specified destination
20 20 remove remove the specified files on the next commit
21 21 serve start stand-alone webserver
22 22 status show changed files in the working directory
23 23 summary summarize working directory state
24 24 update update working directory (or switch revisions)
25 25
26 26 use "hg help" for the full list of commands or "hg -v" for details
27 27
28 28 $ hg -q
29 29 add add the specified files on the next commit
30 30 annotate show changeset information by line for each file
31 31 clone make a copy of an existing repository
32 32 commit commit the specified files or all outstanding changes
33 33 diff diff repository (or selected files)
34 34 export dump the header and diffs for one or more changesets
35 35 forget forget the specified files on the next commit
36 36 init create a new repository in the given directory
37 37 log show revision history of entire repository or files
38 38 merge merge working directory with another revision
39 39 pull pull changes from the specified source
40 40 push push changes to the specified destination
41 41 remove remove the specified files on the next commit
42 42 serve start stand-alone webserver
43 43 status show changed files in the working directory
44 44 summary summarize working directory state
45 45 update update working directory (or switch revisions)
46 46
47 47 $ hg help
48 48 Mercurial Distributed SCM
49 49
50 50 list of commands:
51 51
52 52 add add the specified files on the next commit
53 53 addremove add all new files, delete all missing files
54 54 annotate show changeset information by line for each file
55 55 archive create an unversioned archive of a repository revision
56 56 backout reverse effect of earlier changeset
57 57 bisect subdivision search of changesets
58 58 bookmarks track a line of development with movable markers
59 59 branch set or show the current branch name
60 60 branches list repository named branches
61 61 bundle create a changegroup file
62 62 cat output the current or given revision of files
63 63 clone make a copy of an existing repository
64 64 commit commit the specified files or all outstanding changes
65 65 copy mark files as copied for the next commit
66 66 diff diff repository (or selected files)
67 67 export dump the header and diffs for one or more changesets
68 68 forget forget the specified files on the next commit
69 69 graft copy changes from other branches onto the current branch
70 70 grep search for a pattern in specified files and revisions
71 71 heads show current repository heads or show branch heads
72 72 help show help for a given topic or a help overview
73 73 identify identify the working copy or specified revision
74 74 import import an ordered set of patches
75 75 incoming show new changesets found in source
76 76 init create a new repository in the given directory
77 77 locate locate files matching specific patterns
78 78 log show revision history of entire repository or files
79 79 manifest output the current or given revision of the project manifest
80 80 merge merge working directory with another revision
81 81 outgoing show changesets not found in the destination
82 82 parents show the parents of the working directory or revision
83 83 paths show aliases for remote repositories
84 84 phase set or show the current phase name
85 85 pull pull changes from the specified source
86 86 push push changes to the specified destination
87 87 recover roll back an interrupted transaction
88 88 remove remove the specified files on the next commit
89 89 rename rename files; equivalent of copy + remove
90 90 resolve redo merges or set/view the merge status of files
91 91 revert restore files to their checkout state
92 92 rollback roll back the last transaction (dangerous)
93 93 root print the root (top) of the current working directory
94 94 serve start stand-alone webserver
95 95 showconfig show combined config settings from all hgrc files
96 96 status show changed files in the working directory
97 97 summary summarize working directory state
98 98 tag add one or more tags for the current or given revision
99 99 tags list repository tags
100 100 tip show the tip revision
101 101 unbundle apply one or more changegroup files
102 102 update update working directory (or switch revisions)
103 103 verify verify the integrity of the repository
104 104 version output version and copyright information
105 105
106 106 additional help topics:
107 107
108 108 config Configuration Files
109 109 dates Date Formats
110 110 diffs Diff Formats
111 111 environment Environment Variables
112 112 extensions Using Additional Features
113 113 filesets Specifying File Sets
114 114 glossary Glossary
115 115 hgignore Syntax for Mercurial Ignore Files
116 116 hgweb Configuring hgweb
117 117 merge-tools Merge Tools
118 118 multirevs Specifying Multiple Revisions
119 119 patterns File Name Patterns
120 120 phases Working with Phases
121 121 revisions Specifying Single Revisions
122 122 revsets Specifying Revision Sets
123 123 subrepos Subrepositories
124 124 templating Template Usage
125 125 urls URL Paths
126 126
127 127 use "hg -v help" to show builtin aliases and global options
128 128
129 129 $ hg -q help
130 130 add add the specified files on the next commit
131 131 addremove add all new files, delete all missing files
132 132 annotate show changeset information by line for each file
133 133 archive create an unversioned archive of a repository revision
134 134 backout reverse effect of earlier changeset
135 135 bisect subdivision search of changesets
136 136 bookmarks track a line of development with movable markers
137 137 branch set or show the current branch name
138 138 branches list repository named branches
139 139 bundle create a changegroup file
140 140 cat output the current or given revision of files
141 141 clone make a copy of an existing repository
142 142 commit commit the specified files or all outstanding changes
143 143 copy mark files as copied for the next commit
144 144 diff diff repository (or selected files)
145 145 export dump the header and diffs for one or more changesets
146 146 forget forget the specified files on the next commit
147 147 graft copy changes from other branches onto the current branch
148 148 grep search for a pattern in specified files and revisions
149 149 heads show current repository heads or show branch heads
150 150 help show help for a given topic or a help overview
151 151 identify identify the working copy or specified revision
152 152 import import an ordered set of patches
153 153 incoming show new changesets found in source
154 154 init create a new repository in the given directory
155 155 locate locate files matching specific patterns
156 156 log show revision history of entire repository or files
157 157 manifest output the current or given revision of the project manifest
158 158 merge merge working directory with another revision
159 159 outgoing show changesets not found in the destination
160 160 parents show the parents of the working directory or revision
161 161 paths show aliases for remote repositories
162 162 phase set or show the current phase name
163 163 pull pull changes from the specified source
164 164 push push changes to the specified destination
165 165 recover roll back an interrupted transaction
166 166 remove remove the specified files on the next commit
167 167 rename rename files; equivalent of copy + remove
168 168 resolve redo merges or set/view the merge status of files
169 169 revert restore files to their checkout state
170 170 rollback roll back the last transaction (dangerous)
171 171 root print the root (top) of the current working directory
172 172 serve start stand-alone webserver
173 173 showconfig show combined config settings from all hgrc files
174 174 status show changed files in the working directory
175 175 summary summarize working directory state
176 176 tag add one or more tags for the current or given revision
177 177 tags list repository tags
178 178 tip show the tip revision
179 179 unbundle apply one or more changegroup files
180 180 update update working directory (or switch revisions)
181 181 verify verify the integrity of the repository
182 182 version output version and copyright information
183 183
184 184 additional help topics:
185 185
186 186 config Configuration Files
187 187 dates Date Formats
188 188 diffs Diff Formats
189 189 environment Environment Variables
190 190 extensions Using Additional Features
191 191 filesets Specifying File Sets
192 192 glossary Glossary
193 193 hgignore Syntax for Mercurial Ignore Files
194 194 hgweb Configuring hgweb
195 195 merge-tools Merge Tools
196 196 multirevs Specifying Multiple Revisions
197 197 patterns File Name Patterns
198 198 phases Working with Phases
199 199 revisions Specifying Single Revisions
200 200 revsets Specifying Revision Sets
201 201 subrepos Subrepositories
202 202 templating Template Usage
203 203 urls URL Paths
204 204
205 205 Test short command list with verbose option
206 206
207 207 $ hg -v help shortlist
208 208 Mercurial Distributed SCM
209 209
210 210 basic commands:
211 211
212 212 add add the specified files on the next commit
213 213 annotate, blame
214 214 show changeset information by line for each file
215 215 clone make a copy of an existing repository
216 216 commit, ci commit the specified files or all outstanding changes
217 217 diff diff repository (or selected files)
218 218 export dump the header and diffs for one or more changesets
219 219 forget forget the specified files on the next commit
220 220 init create a new repository in the given directory
221 221 log, history show revision history of entire repository or files
222 222 merge merge working directory with another revision
223 223 pull pull changes from the specified source
224 224 push push changes to the specified destination
225 225 remove, rm remove the specified files on the next commit
226 226 serve start stand-alone webserver
227 227 status, st show changed files in the working directory
228 228 summary, sum summarize working directory state
229 229 update, up, checkout, co
230 230 update working directory (or switch revisions)
231 231
232 232 global options:
233 233
234 234 -R --repository REPO repository root directory or name of overlay bundle
235 235 file
236 236 --cwd DIR change working directory
237 237 -y --noninteractive do not prompt, automatically pick the first choice for
238 238 all prompts
239 239 -q --quiet suppress output
240 240 -v --verbose enable additional output
241 241 --config CONFIG [+] set/override config option (use 'section.name=value')
242 242 --debug enable debugging output
243 243 --debugger start debugger
244 244 --encoding ENCODE set the charset encoding (default: ascii)
245 245 --encodingmode MODE set the charset encoding mode (default: strict)
246 246 --traceback always print a traceback on exception
247 247 --time time how long the command takes
248 248 --profile print command execution profile
249 249 --version output version information and exit
250 250 -h --help display help and exit
251 --hidden consider hidden changesets
251 252
252 253 [+] marked option can be specified multiple times
253 254
254 255 use "hg help" for the full list of commands
255 256
256 257 $ hg add -h
257 258 hg add [OPTION]... [FILE]...
258 259
259 260 add the specified files on the next commit
260 261
261 262 Schedule files to be version controlled and added to the repository.
262 263
263 264 The files will be added to the repository at the next commit. To undo an
264 265 add before that, see "hg forget".
265 266
266 267 If no names are given, add all files to the repository.
267 268
268 269 Returns 0 if all files are successfully added.
269 270
270 271 options:
271 272
272 273 -I --include PATTERN [+] include names matching the given patterns
273 274 -X --exclude PATTERN [+] exclude names matching the given patterns
274 275 -S --subrepos recurse into subrepositories
275 276 -n --dry-run do not perform actions, just print output
276 277
277 278 [+] marked option can be specified multiple times
278 279
279 280 use "hg -v help add" to show more complete help and the global options
280 281
281 282 Verbose help for add
282 283
283 284 $ hg add -hv
284 285 hg add [OPTION]... [FILE]...
285 286
286 287 add the specified files on the next commit
287 288
288 289 Schedule files to be version controlled and added to the repository.
289 290
290 291 The files will be added to the repository at the next commit. To undo an
291 292 add before that, see "hg forget".
292 293
293 294 If no names are given, add all files to the repository.
294 295
295 296 An example showing how new (unknown) files are added automatically by "hg
296 297 add":
297 298
298 299 $ ls
299 300 foo.c
300 301 $ hg status
301 302 ? foo.c
302 303 $ hg add
303 304 adding foo.c
304 305 $ hg status
305 306 A foo.c
306 307
307 308 Returns 0 if all files are successfully added.
308 309
309 310 options:
310 311
311 312 -I --include PATTERN [+] include names matching the given patterns
312 313 -X --exclude PATTERN [+] exclude names matching the given patterns
313 314 -S --subrepos recurse into subrepositories
314 315 -n --dry-run do not perform actions, just print output
315 316
316 317 [+] marked option can be specified multiple times
317 318
318 319 global options:
319 320
320 321 -R --repository REPO repository root directory or name of overlay bundle
321 322 file
322 323 --cwd DIR change working directory
323 324 -y --noninteractive do not prompt, automatically pick the first choice for
324 325 all prompts
325 326 -q --quiet suppress output
326 327 -v --verbose enable additional output
327 328 --config CONFIG [+] set/override config option (use 'section.name=value')
328 329 --debug enable debugging output
329 330 --debugger start debugger
330 331 --encoding ENCODE set the charset encoding (default: ascii)
331 332 --encodingmode MODE set the charset encoding mode (default: strict)
332 333 --traceback always print a traceback on exception
333 334 --time time how long the command takes
334 335 --profile print command execution profile
335 336 --version output version information and exit
336 337 -h --help display help and exit
338 --hidden consider hidden changesets
337 339
338 340 [+] marked option can be specified multiple times
339 341
340 342 Test help option with version option
341 343
342 344 $ hg add -h --version
343 345 Mercurial Distributed SCM (version *) (glob)
344 346 (see http://mercurial.selenic.com for more information)
345 347
346 348 Copyright (C) 2005-2012 Matt Mackall and others
347 349 This is free software; see the source for copying conditions. There is NO
348 350 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
349 351
350 352 $ hg add --skjdfks
351 353 hg add: option --skjdfks not recognized
352 354 hg add [OPTION]... [FILE]...
353 355
354 356 add the specified files on the next commit
355 357
356 358 options:
357 359
358 360 -I --include PATTERN [+] include names matching the given patterns
359 361 -X --exclude PATTERN [+] exclude names matching the given patterns
360 362 -S --subrepos recurse into subrepositories
361 363 -n --dry-run do not perform actions, just print output
362 364
363 365 [+] marked option can be specified multiple times
364 366
365 367 use "hg help add" to show the full help text
366 368 [255]
367 369
368 370 Test ambiguous command help
369 371
370 372 $ hg help ad
371 373 list of commands:
372 374
373 375 add add the specified files on the next commit
374 376 addremove add all new files, delete all missing files
375 377
376 378 use "hg -v help ad" to show builtin aliases and global options
377 379
378 380 Test command without options
379 381
380 382 $ hg help verify
381 383 hg verify
382 384
383 385 verify the integrity of the repository
384 386
385 387 Verify the integrity of the current repository.
386 388
387 389 This will perform an extensive check of the repository's integrity,
388 390 validating the hashes and checksums of each entry in the changelog,
389 391 manifest, and tracked files, as well as the integrity of their crosslinks
390 392 and indices.
391 393
392 394 Please see http://mercurial.selenic.com/wiki/RepositoryCorruption for more
393 395 information about recovery from corruption of the repository.
394 396
395 397 Returns 0 on success, 1 if errors are encountered.
396 398
397 399 use "hg -v help verify" to show the global options
398 400
399 401 $ hg help diff
400 402 hg diff [OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...
401 403
402 404 diff repository (or selected files)
403 405
404 406 Show differences between revisions for the specified files.
405 407
406 408 Differences between files are shown using the unified diff format.
407 409
408 410 Note:
409 411 diff may generate unexpected results for merges, as it will default to
410 412 comparing against the working directory's first parent changeset if no
411 413 revisions are specified.
412 414
413 415 When two revision arguments are given, then changes are shown between
414 416 those revisions. If only one revision is specified then that revision is
415 417 compared to the working directory, and, when no revisions are specified,
416 418 the working directory files are compared to its parent.
417 419
418 420 Alternatively you can specify -c/--change with a revision to see the
419 421 changes in that changeset relative to its first parent.
420 422
421 423 Without the -a/--text option, diff will avoid generating diffs of files it
422 424 detects as binary. With -a, diff will generate a diff anyway, probably
423 425 with undesirable results.
424 426
425 427 Use the -g/--git option to generate diffs in the git extended diff format.
426 428 For more information, read "hg help diffs".
427 429
428 430 Returns 0 on success.
429 431
430 432 options:
431 433
432 434 -r --rev REV [+] revision
433 435 -c --change REV change made by revision
434 436 -a --text treat all files as text
435 437 -g --git use git extended diff format
436 438 --nodates omit dates from diff headers
437 439 -p --show-function show which function each change is in
438 440 --reverse produce a diff that undoes the changes
439 441 -w --ignore-all-space ignore white space when comparing lines
440 442 -b --ignore-space-change ignore changes in the amount of white space
441 443 -B --ignore-blank-lines ignore changes whose lines are all blank
442 444 -U --unified NUM number of lines of context to show
443 445 --stat output diffstat-style summary of changes
444 446 -I --include PATTERN [+] include names matching the given patterns
445 447 -X --exclude PATTERN [+] exclude names matching the given patterns
446 448 -S --subrepos recurse into subrepositories
447 449
448 450 [+] marked option can be specified multiple times
449 451
450 452 use "hg -v help diff" to show more complete help and the global options
451 453
452 454 $ hg help status
453 455 hg status [OPTION]... [FILE]...
454 456
455 457 aliases: st
456 458
457 459 show changed files in the working directory
458 460
459 461 Show status of files in the repository. If names are given, only files
460 462 that match are shown. Files that are clean or ignored or the source of a
461 463 copy/move operation, are not listed unless -c/--clean, -i/--ignored,
462 464 -C/--copies or -A/--all are given. Unless options described with "show
463 465 only ..." are given, the options -mardu are used.
464 466
465 467 Option -q/--quiet hides untracked (unknown and ignored) files unless
466 468 explicitly requested with -u/--unknown or -i/--ignored.
467 469
468 470 Note:
469 471 status may appear to disagree with diff if permissions have changed or
470 472 a merge has occurred. The standard diff format does not report
471 473 permission changes and diff only reports changes relative to one merge
472 474 parent.
473 475
474 476 If one revision is given, it is used as the base revision. If two
475 477 revisions are given, the differences between them are shown. The --change
476 478 option can also be used as a shortcut to list the changed files of a
477 479 revision from its first parent.
478 480
479 481 The codes used to show the status of files are:
480 482
481 483 M = modified
482 484 A = added
483 485 R = removed
484 486 C = clean
485 487 ! = missing (deleted by non-hg command, but still tracked)
486 488 ? = not tracked
487 489 I = ignored
488 490 = origin of the previous file listed as A (added)
489 491
490 492 Returns 0 on success.
491 493
492 494 options:
493 495
494 496 -A --all show status of all files
495 497 -m --modified show only modified files
496 498 -a --added show only added files
497 499 -r --removed show only removed files
498 500 -d --deleted show only deleted (but tracked) files
499 501 -c --clean show only files without changes
500 502 -u --unknown show only unknown (not tracked) files
501 503 -i --ignored show only ignored files
502 504 -n --no-status hide status prefix
503 505 -C --copies show source of copied files
504 506 -0 --print0 end filenames with NUL, for use with xargs
505 507 --rev REV [+] show difference from revision
506 508 --change REV list the changed files of a revision
507 509 -I --include PATTERN [+] include names matching the given patterns
508 510 -X --exclude PATTERN [+] exclude names matching the given patterns
509 511 -S --subrepos recurse into subrepositories
510 512
511 513 [+] marked option can be specified multiple times
512 514
513 515 use "hg -v help status" to show more complete help and the global options
514 516
515 517 $ hg -q help status
516 518 hg status [OPTION]... [FILE]...
517 519
518 520 show changed files in the working directory
519 521
520 522 $ hg help foo
521 523 hg: unknown command 'foo'
522 524 Mercurial Distributed SCM
523 525
524 526 basic commands:
525 527
526 528 add add the specified files on the next commit
527 529 annotate show changeset information by line for each file
528 530 clone make a copy of an existing repository
529 531 commit commit the specified files or all outstanding changes
530 532 diff diff repository (or selected files)
531 533 export dump the header and diffs for one or more changesets
532 534 forget forget the specified files on the next commit
533 535 init create a new repository in the given directory
534 536 log show revision history of entire repository or files
535 537 merge merge working directory with another revision
536 538 pull pull changes from the specified source
537 539 push push changes to the specified destination
538 540 remove remove the specified files on the next commit
539 541 serve start stand-alone webserver
540 542 status show changed files in the working directory
541 543 summary summarize working directory state
542 544 update update working directory (or switch revisions)
543 545
544 546 use "hg help" for the full list of commands or "hg -v" for details
545 547 [255]
546 548
547 549 $ hg skjdfks
548 550 hg: unknown command 'skjdfks'
549 551 Mercurial Distributed SCM
550 552
551 553 basic commands:
552 554
553 555 add add the specified files on the next commit
554 556 annotate show changeset information by line for each file
555 557 clone make a copy of an existing repository
556 558 commit commit the specified files or all outstanding changes
557 559 diff diff repository (or selected files)
558 560 export dump the header and diffs for one or more changesets
559 561 forget forget the specified files on the next commit
560 562 init create a new repository in the given directory
561 563 log show revision history of entire repository or files
562 564 merge merge working directory with another revision
563 565 pull pull changes from the specified source
564 566 push push changes to the specified destination
565 567 remove remove the specified files on the next commit
566 568 serve start stand-alone webserver
567 569 status show changed files in the working directory
568 570 summary summarize working directory state
569 571 update update working directory (or switch revisions)
570 572
571 573 use "hg help" for the full list of commands or "hg -v" for details
572 574 [255]
573 575
574 576 $ cat > helpext.py <<EOF
575 577 > import os
576 578 > from mercurial import commands
577 579 >
578 580 > def nohelp(ui, *args, **kwargs):
579 581 > pass
580 582 >
581 583 > cmdtable = {
582 584 > "nohelp": (nohelp, [], "hg nohelp"),
583 585 > }
584 586 >
585 587 > commands.norepo += ' nohelp'
586 588 > EOF
587 589 $ echo '[extensions]' >> $HGRCPATH
588 590 $ echo "helpext = `pwd`/helpext.py" >> $HGRCPATH
589 591
590 592 Test command with no help text
591 593
592 594 $ hg help nohelp
593 595 hg nohelp
594 596
595 597 (no help text available)
596 598
597 599 use "hg -v help nohelp" to show the global options
598 600
599 601 $ hg help -k nohelp
600 602 Commands:
601 603
602 604 nohelp hg nohelp
603 605
604 606 Extension Commands:
605 607
606 608 nohelp (no help text available)
607 609
608 610 Test that default list of commands omits extension commands
609 611
610 612 $ hg help
611 613 Mercurial Distributed SCM
612 614
613 615 list of commands:
614 616
615 617 add add the specified files on the next commit
616 618 addremove add all new files, delete all missing files
617 619 annotate show changeset information by line for each file
618 620 archive create an unversioned archive of a repository revision
619 621 backout reverse effect of earlier changeset
620 622 bisect subdivision search of changesets
621 623 bookmarks track a line of development with movable markers
622 624 branch set or show the current branch name
623 625 branches list repository named branches
624 626 bundle create a changegroup file
625 627 cat output the current or given revision of files
626 628 clone make a copy of an existing repository
627 629 commit commit the specified files or all outstanding changes
628 630 copy mark files as copied for the next commit
629 631 diff diff repository (or selected files)
630 632 export dump the header and diffs for one or more changesets
631 633 forget forget the specified files on the next commit
632 634 graft copy changes from other branches onto the current branch
633 635 grep search for a pattern in specified files and revisions
634 636 heads show current repository heads or show branch heads
635 637 help show help for a given topic or a help overview
636 638 identify identify the working copy or specified revision
637 639 import import an ordered set of patches
638 640 incoming show new changesets found in source
639 641 init create a new repository in the given directory
640 642 locate locate files matching specific patterns
641 643 log show revision history of entire repository or files
642 644 manifest output the current or given revision of the project manifest
643 645 merge merge working directory with another revision
644 646 outgoing show changesets not found in the destination
645 647 parents show the parents of the working directory or revision
646 648 paths show aliases for remote repositories
647 649 phase set or show the current phase name
648 650 pull pull changes from the specified source
649 651 push push changes to the specified destination
650 652 recover roll back an interrupted transaction
651 653 remove remove the specified files on the next commit
652 654 rename rename files; equivalent of copy + remove
653 655 resolve redo merges or set/view the merge status of files
654 656 revert restore files to their checkout state
655 657 rollback roll back the last transaction (dangerous)
656 658 root print the root (top) of the current working directory
657 659 serve start stand-alone webserver
658 660 showconfig show combined config settings from all hgrc files
659 661 status show changed files in the working directory
660 662 summary summarize working directory state
661 663 tag add one or more tags for the current or given revision
662 664 tags list repository tags
663 665 tip show the tip revision
664 666 unbundle apply one or more changegroup files
665 667 update update working directory (or switch revisions)
666 668 verify verify the integrity of the repository
667 669 version output version and copyright information
668 670
669 671 enabled extensions:
670 672
671 673 helpext (no help text available)
672 674
673 675 additional help topics:
674 676
675 677 config Configuration Files
676 678 dates Date Formats
677 679 diffs Diff Formats
678 680 environment Environment Variables
679 681 extensions Using Additional Features
680 682 filesets Specifying File Sets
681 683 glossary Glossary
682 684 hgignore Syntax for Mercurial Ignore Files
683 685 hgweb Configuring hgweb
684 686 merge-tools Merge Tools
685 687 multirevs Specifying Multiple Revisions
686 688 patterns File Name Patterns
687 689 phases Working with Phases
688 690 revisions Specifying Single Revisions
689 691 revsets Specifying Revision Sets
690 692 subrepos Subrepositories
691 693 templating Template Usage
692 694 urls URL Paths
693 695
694 696 use "hg -v help" to show builtin aliases and global options
695 697
696 698
697 699
698 700 Test list of commands with command with no help text
699 701
700 702 $ hg help helpext
701 703 helpext extension - no help text available
702 704
703 705 list of commands:
704 706
705 707 nohelp (no help text available)
706 708
707 709 use "hg -v help helpext" to show builtin aliases and global options
708 710
709 711 Test a help topic
710 712
711 713 $ hg help revs
712 714 Specifying Single Revisions
713 715
714 716 Mercurial supports several ways to specify individual revisions.
715 717
716 718 A plain integer is treated as a revision number. Negative integers are
717 719 treated as sequential offsets from the tip, with -1 denoting the tip, -2
718 720 denoting the revision prior to the tip, and so forth.
719 721
720 722 A 40-digit hexadecimal string is treated as a unique revision identifier.
721 723
722 724 A hexadecimal string less than 40 characters long is treated as a unique
723 725 revision identifier and is referred to as a short-form identifier. A
724 726 short-form identifier is only valid if it is the prefix of exactly one
725 727 full-length identifier.
726 728
727 729 Any other string is treated as a bookmark, tag, or branch name. A bookmark
728 730 is a movable pointer to a revision. A tag is a permanent name associated
729 731 with a revision. A branch name denotes the tipmost revision of that
730 732 branch. Bookmark, tag, and branch names must not contain the ":"
731 733 character.
732 734
733 735 The reserved name "tip" always identifies the most recent revision.
734 736
735 737 The reserved name "null" indicates the null revision. This is the revision
736 738 of an empty repository, and the parent of revision 0.
737 739
738 740 The reserved name "." indicates the working directory parent. If no
739 741 working directory is checked out, it is equivalent to null. If an
740 742 uncommitted merge is in progress, "." is the revision of the first parent.
741 743
742 744 Test templating help
743 745
744 746 $ hg help templating | egrep '(desc|diffstat|firstline|nonempty) '
745 747 desc String. The text of the changeset description.
746 748 diffstat String. Statistics of changes with the following format:
747 749 firstline Any text. Returns the first line of text.
748 750 nonempty Any text. Returns '(none)' if the string is empty.
749 751
750 752 Test help hooks
751 753
752 754 $ cat > helphook1.py <<EOF
753 755 > from mercurial import help
754 756 >
755 757 > def rewrite(topic, doc):
756 758 > return doc + '\nhelphook1\n'
757 759 >
758 760 > def extsetup(ui):
759 761 > help.addtopichook('revsets', rewrite)
760 762 > EOF
761 763 $ cat > helphook2.py <<EOF
762 764 > from mercurial import help
763 765 >
764 766 > def rewrite(topic, doc):
765 767 > return doc + '\nhelphook2\n'
766 768 >
767 769 > def extsetup(ui):
768 770 > help.addtopichook('revsets', rewrite)
769 771 > EOF
770 772 $ echo '[extensions]' >> $HGRCPATH
771 773 $ echo "helphook1 = `pwd`/helphook1.py" >> $HGRCPATH
772 774 $ echo "helphook2 = `pwd`/helphook2.py" >> $HGRCPATH
773 775 $ hg help revsets | grep helphook
774 776 helphook1
775 777 helphook2
776 778
777 779 Test keyword search help
778 780
779 781 $ hg help -k clone
780 782 Topics:
781 783
782 784 config Configuration Files
783 785 extensions Using Additional Features
784 786 glossary Glossary
785 787 phases Working with Phases
786 788 subrepos Subrepositories
787 789 urls URL Paths
788 790
789 791 Commands:
790 792
791 793 clone make a copy of an existing repository
792 794 paths show aliases for remote repositories
793 795 update update working directory (or switch revisions)
794 796
795 797 Extensions:
796 798
797 799 relink recreates hardlinks between repository clones
798 800
799 801 Extension Commands:
800 802
801 803 qclone clone main and patch repository at same time
802 804
803 805 Test omit indicating for help
804 806
805 807 $ cat > addverboseitems.py <<EOF
806 808 > '''extension to test omit indicating.
807 809 >
808 810 > This paragraph is never omitted (for extension)
809 811 >
810 812 > .. container:: verbose
811 813 >
812 814 > This paragraph is omitted,
813 815 > if :hg:\`help\` is invoked witout \`\`-v\`\` (for extension)
814 816 >
815 817 > This paragraph is never omitted, too (for extension)
816 818 > '''
817 819 >
818 820 > from mercurial import help, commands
819 821 > testtopic = """This paragraph is never omitted (for topic).
820 822 >
821 823 > .. container:: verbose
822 824 >
823 825 > This paragraph is omitted,
824 826 > if :hg:\`help\` is invoked witout \`\`-v\`\` (for topic)
825 827 >
826 828 > This paragraph is never omitted, too (for topic)
827 829 > """
828 830 > def extsetup(ui):
829 831 > help.helptable.append((["topic-containing-verbose"],
830 832 > "This is the topic to test omit indicating.",
831 833 > lambda : testtopic))
832 834 > EOF
833 835 $ echo '[extensions]' >> $HGRCPATH
834 836 $ echo "addverboseitems = `pwd`/addverboseitems.py" >> $HGRCPATH
835 837 $ hg help addverboseitems
836 838 addverboseitems extension - extension to test omit indicating.
837 839
838 840 This paragraph is never omitted (for extension)
839 841
840 842 This paragraph is never omitted, too (for extension)
841 843
842 844 use "hg help -v addverboseitems" to show more complete help
843 845
844 846 no commands defined
845 847 $ hg help -v addverboseitems
846 848 addverboseitems extension - extension to test omit indicating.
847 849
848 850 This paragraph is never omitted (for extension)
849 851
850 852 This paragraph is omitted, if "hg help" is invoked witout "-v" (for extension)
851 853
852 854 This paragraph is never omitted, too (for extension)
853 855
854 856 no commands defined
855 857 $ hg help topic-containing-verbose
856 858 This is the topic to test omit indicating.
857 859
858 860 This paragraph is never omitted (for topic).
859 861
860 862 This paragraph is never omitted, too (for topic)
861 863
862 864 use "hg help -v topic-containing-verbose" to show more complete help
863 865 $ hg help -v topic-containing-verbose
864 866 This is the topic to test omit indicating.
865 867
866 868 This paragraph is never omitted (for topic).
867 869
868 870 This paragraph is omitted, if "hg help" is invoked witout "-v" (for topic)
869 871
870 872 This paragraph is never omitted, too (for topic)
871 873
872 874 Test usage of section marks in help documents
873 875
874 876 $ cd "$TESTDIR"/../doc
875 877 $ python check-seclevel.py
@@ -1,1157 +1,1155 b''
1 1 $ cat <<EOF >> $HGRCPATH
2 2 > [extensions]
3 3 > keyword =
4 4 > mq =
5 5 > notify =
6 6 > record =
7 7 > transplant =
8 8 > [ui]
9 9 > interactive = true
10 10 > EOF
11 11
12 12 hide outer repo
13 13 $ hg init
14 14
15 15 Run kwdemo before [keyword] files are set up
16 16 as it would succeed without uisetup otherwise
17 17
18 18 $ hg --quiet kwdemo
19 19 [extensions]
20 20 keyword =
21 21 [keyword]
22 22 demo.txt =
23 23 [keywordset]
24 24 svn = False
25 25 [keywordmaps]
26 26 Author = {author|user}
27 27 Date = {date|utcdate}
28 28 Header = {root}/{file},v {node|short} {date|utcdate} {author|user}
29 29 Id = {file|basename},v {node|short} {date|utcdate} {author|user}
30 30 RCSFile = {file|basename},v
31 31 RCSfile = {file|basename},v
32 32 Revision = {node|short}
33 33 Source = {root}/{file},v
34 34 $Author: test $
35 35 $Date: ????/??/?? ??:??:?? $ (glob)
36 36 $Header: */demo.txt,v ???????????? ????/??/?? ??:??:?? test $ (glob)
37 37 $Id: demo.txt,v ???????????? ????/??/?? ??:??:?? test $ (glob)
38 38 $RCSFile: demo.txt,v $
39 39 $RCSfile: demo.txt,v $
40 40 $Revision: ???????????? $ (glob)
41 41 $Source: */demo.txt,v $ (glob)
42 42
43 43 $ hg --quiet kwdemo "Branch = {branches}"
44 44 [extensions]
45 45 keyword =
46 46 [keyword]
47 47 demo.txt =
48 48 [keywordset]
49 49 svn = False
50 50 [keywordmaps]
51 51 Branch = {branches}
52 52 $Branch: demobranch $
53 53
54 54 $ cat <<EOF >> $HGRCPATH
55 55 > [keyword]
56 56 > ** =
57 57 > b = ignore
58 58 > i = ignore
59 59 > [hooks]
60 60 > EOF
61 61 $ cp $HGRCPATH $HGRCPATH.nohooks
62 62 > cat <<EOF >> $HGRCPATH
63 63 > commit=
64 64 > commit.test=cp a hooktest
65 65 > EOF
66 66
67 67 $ hg init Test-bndl
68 68 $ cd Test-bndl
69 69
70 70 kwshrink should exit silently in empty/invalid repo
71 71
72 72 $ hg kwshrink
73 73
74 74 Symlinks cannot be created on Windows.
75 75 A bundle to test this was made with:
76 76 hg init t
77 77 cd t
78 78 echo a > a
79 79 ln -s a sym
80 80 hg add sym
81 81 hg ci -m addsym -u mercurial
82 82 hg bundle --base null ../test-keyword.hg
83 83
84 84 $ hg pull -u "$TESTDIR"/bundles/test-keyword.hg
85 85 pulling from *test-keyword.hg (glob)
86 86 requesting all changes
87 87 adding changesets
88 88 adding manifests
89 89 adding file changes
90 90 added 1 changesets with 1 changes to 1 files
91 91 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
92 92
93 93 $ echo 'expand $Id$' > a
94 94 $ echo 'do not process $Id:' >> a
95 95 $ echo 'xxx $' >> a
96 96 $ echo 'ignore $Id$' > b
97 97
98 98 Output files as they were created
99 99
100 100 $ cat a b
101 101 expand $Id$
102 102 do not process $Id:
103 103 xxx $
104 104 ignore $Id$
105 105
106 106 no kwfiles
107 107
108 108 $ hg kwfiles
109 109
110 110 untracked candidates
111 111
112 112 $ hg -v kwfiles --unknown
113 113 k a
114 114
115 115 Add files and check status
116 116
117 117 $ hg addremove
118 118 adding a
119 119 adding b
120 120 $ hg status
121 121 A a
122 122 A b
123 123
124 124
125 125 Default keyword expansion including commit hook
126 126 Interrupted commit should not change state or run commit hook
127 127
128 128 $ hg --debug commit
129 129 abort: empty commit message
130 130 [255]
131 131 $ hg status
132 132 A a
133 133 A b
134 134
135 135 Commit with several checks
136 136
137 137 $ hg --debug commit -mabsym -u 'User Name <user@example.com>'
138 138 a
139 139 b
140 140 overwriting a expanding keywords
141 141 running hook commit.test: cp a hooktest
142 142 committed changeset 1:ef63ca68695bc9495032c6fda1350c71e6d256e9
143 143 $ hg status
144 144 ? hooktest
145 145 $ hg debugrebuildstate
146 146 $ hg --quiet identify
147 147 ef63ca68695b
148 148
149 149 cat files in working directory with keywords expanded
150 150
151 151 $ cat a b
152 152 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
153 153 do not process $Id:
154 154 xxx $
155 155 ignore $Id$
156 156
157 157 hg cat files and symlink, no expansion
158 158
159 159 $ hg cat sym a b && echo
160 160 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
161 161 do not process $Id:
162 162 xxx $
163 163 ignore $Id$
164 164 a
165 165
166 166 $ diff a hooktest
167 167
168 168 $ cp $HGRCPATH.nohooks $HGRCPATH
169 169 $ rm hooktest
170 170
171 171 hg status of kw-ignored binary file starting with '\1\n'
172 172
173 173 >>> open("i", "wb").write("\1\nfoo")
174 174 $ hg -q commit -Am metasep i
175 175 $ hg status
176 176 >>> open("i", "wb").write("\1\nbar")
177 177 $ hg status
178 178 M i
179 179 $ hg -q commit -m "modify metasep" i
180 180 $ hg status --rev 2:3
181 181 M i
182 182 $ touch empty
183 183 $ hg -q commit -A -m "another file"
184 184 $ hg status -A --rev 3:4 i
185 185 C i
186 186
187 187 $ hg -q strip -n 2
188 188
189 189 Test hook execution
190 190
191 191 bundle
192 192
193 193 $ hg bundle --base null ../kw.hg
194 194 2 changesets found
195 195 $ cd ..
196 196 $ hg init Test
197 197 $ cd Test
198 198
199 199 Notify on pull to check whether keywords stay as is in email
200 200 ie. if patch.diff wrapper acts as it should
201 201
202 202 $ cat <<EOF >> $HGRCPATH
203 203 > [hooks]
204 204 > incoming.notify = python:hgext.notify.hook
205 205 > [notify]
206 206 > sources = pull
207 207 > diffstat = False
208 208 > maxsubject = 15
209 209 > [reposubs]
210 210 > * = Test
211 211 > EOF
212 212
213 213 Pull from bundle and trigger notify
214 214
215 215 $ hg pull -u ../kw.hg
216 216 pulling from ../kw.hg
217 217 requesting all changes
218 218 adding changesets
219 219 adding manifests
220 220 adding file changes
221 221 added 2 changesets with 3 changes to 3 files
222 222 Content-Type: text/plain; charset="us-ascii"
223 223 MIME-Version: 1.0
224 224 Content-Transfer-Encoding: 7bit
225 225 Date: * (glob)
226 226 Subject: changeset in...
227 227 From: mercurial
228 228 X-Hg-Notification: changeset a2392c293916
229 229 Message-Id: <hg.a2392c293916*> (glob)
230 230 To: Test
231 231
232 232 changeset a2392c293916 in $TESTTMP/Test (glob)
233 233 details: $TESTTMP/Test?cmd=changeset;node=a2392c293916
234 234 description:
235 235 addsym
236 236
237 237 diffs (6 lines):
238 238
239 239 diff -r 000000000000 -r a2392c293916 sym
240 240 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
241 241 +++ b/sym Sat Feb 09 20:25:47 2008 +0100
242 242 @@ -0,0 +1,1 @@
243 243 +a
244 244 \ No newline at end of file
245 245 Content-Type: text/plain; charset="us-ascii"
246 246 MIME-Version: 1.0
247 247 Content-Transfer-Encoding: 7bit
248 248 Date:* (glob)
249 249 Subject: changeset in...
250 250 From: User Name <user@example.com>
251 251 X-Hg-Notification: changeset ef63ca68695b
252 252 Message-Id: <hg.ef63ca68695b*> (glob)
253 253 To: Test
254 254
255 255 changeset ef63ca68695b in $TESTTMP/Test (glob)
256 256 details: $TESTTMP/Test?cmd=changeset;node=ef63ca68695b
257 257 description:
258 258 absym
259 259
260 260 diffs (12 lines):
261 261
262 262 diff -r a2392c293916 -r ef63ca68695b a
263 263 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
264 264 +++ b/a Thu Jan 01 00:00:00 1970 +0000
265 265 @@ -0,0 +1,3 @@
266 266 +expand $Id$
267 267 +do not process $Id:
268 268 +xxx $
269 269 diff -r a2392c293916 -r ef63ca68695b b
270 270 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
271 271 +++ b/b Thu Jan 01 00:00:00 1970 +0000
272 272 @@ -0,0 +1,1 @@
273 273 +ignore $Id$
274 274 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
275 275
276 276 $ cp $HGRCPATH.nohooks $HGRCPATH
277 277
278 278 Touch files and check with status
279 279
280 280 $ touch a b
281 281 $ hg status
282 282
283 283 Update and expand
284 284
285 285 $ rm sym a b
286 286 $ hg update -C
287 287 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
288 288 $ cat a b
289 289 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
290 290 do not process $Id:
291 291 xxx $
292 292 ignore $Id$
293 293
294 294 Check whether expansion is filewise and file mode is preserved
295 295
296 296 $ echo '$Id$' > c
297 297 $ echo 'tests for different changenodes' >> c
298 298 #if unix-permissions
299 299 $ chmod 600 c
300 300 $ ls -l c | cut -b 1-10
301 301 -rw-------
302 302 #endif
303 303
304 304 commit file c
305 305
306 306 $ hg commit -A -mcndiff -d '1 0' -u 'User Name <user@example.com>'
307 307 adding c
308 308 #if unix-permissions
309 309 $ ls -l c | cut -b 1-10
310 310 -rw-------
311 311 #endif
312 312
313 313 force expansion
314 314
315 315 $ hg -v kwexpand
316 316 overwriting a expanding keywords
317 317 overwriting c expanding keywords
318 318
319 319 compare changenodes in a and c
320 320
321 321 $ cat a c
322 322 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
323 323 do not process $Id:
324 324 xxx $
325 325 $Id: c,v 40a904bbbe4c 1970/01/01 00:00:01 user $
326 326 tests for different changenodes
327 327
328 328 record
329 329
330 330 $ echo '$Id$' > r
331 331 $ hg add r
332 332
333 333 record chunk
334 334
335 335 >>> lines = open('a', 'rb').readlines()
336 336 >>> lines.insert(1, 'foo\n')
337 337 >>> lines.append('bar\n')
338 338 >>> open('a', 'wb').writelines(lines)
339 339 $ hg record -d '10 1' -m rectest a<<EOF
340 340 > y
341 341 > y
342 342 > n
343 343 > EOF
344 344 diff --git a/a b/a
345 345 2 hunks, 2 lines changed
346 346 examine changes to 'a'? [Ynesfdaq?]
347 347 @@ -1,3 +1,4 @@
348 348 expand $Id$
349 349 +foo
350 350 do not process $Id:
351 351 xxx $
352 352 record change 1/2 to 'a'? [Ynesfdaq?]
353 353 @@ -2,2 +3,3 @@
354 354 do not process $Id:
355 355 xxx $
356 356 +bar
357 357 record change 2/2 to 'a'? [Ynesfdaq?]
358 358
359 359 $ hg identify
360 360 5f5eb23505c3+ tip
361 361 $ hg status
362 362 M a
363 363 A r
364 364
365 365 Cat modified file a
366 366
367 367 $ cat a
368 368 expand $Id: a,v 5f5eb23505c3 1970/01/01 00:00:10 test $
369 369 foo
370 370 do not process $Id:
371 371 xxx $
372 372 bar
373 373
374 374 Diff remaining chunk
375 375
376 376 $ hg diff a
377 377 diff -r 5f5eb23505c3 a
378 378 --- a/a Thu Jan 01 00:00:09 1970 -0000
379 379 +++ b/a * (glob)
380 380 @@ -2,3 +2,4 @@
381 381 foo
382 382 do not process $Id:
383 383 xxx $
384 384 +bar
385 385
386 386 $ hg rollback
387 387 repository tip rolled back to revision 2 (undo commit)
388 388 working directory now based on revision 2
389 389
390 390 Record all chunks in file a
391 391
392 392 $ echo foo > msg
393 393
394 394 - do not use "hg record -m" here!
395 395
396 396 $ hg record -l msg -d '11 1' a<<EOF
397 397 > y
398 398 > y
399 399 > y
400 400 > EOF
401 401 diff --git a/a b/a
402 402 2 hunks, 2 lines changed
403 403 examine changes to 'a'? [Ynesfdaq?]
404 404 @@ -1,3 +1,4 @@
405 405 expand $Id$
406 406 +foo
407 407 do not process $Id:
408 408 xxx $
409 409 record change 1/2 to 'a'? [Ynesfdaq?]
410 410 @@ -2,2 +3,3 @@
411 411 do not process $Id:
412 412 xxx $
413 413 +bar
414 414 record change 2/2 to 'a'? [Ynesfdaq?]
415 415
416 416 File a should be clean
417 417
418 418 $ hg status -A a
419 419 C a
420 420
421 421 rollback and revert expansion
422 422
423 423 $ cat a
424 424 expand $Id: a,v 78e0a02d76aa 1970/01/01 00:00:11 test $
425 425 foo
426 426 do not process $Id:
427 427 xxx $
428 428 bar
429 429 $ hg --verbose rollback
430 430 repository tip rolled back to revision 2 (undo commit)
431 431 working directory now based on revision 2
432 432 overwriting a expanding keywords
433 433 $ hg status a
434 434 M a
435 435 $ cat a
436 436 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
437 437 foo
438 438 do not process $Id:
439 439 xxx $
440 440 bar
441 441 $ echo '$Id$' > y
442 442 $ echo '$Id$' > z
443 443 $ hg add y
444 444 $ hg commit -Am "rollback only" z
445 445 $ cat z
446 446 $Id: z,v 45a5d3adce53 1970/01/01 00:00:00 test $
447 447 $ hg --verbose rollback
448 448 repository tip rolled back to revision 2 (undo commit)
449 449 working directory now based on revision 2
450 450 overwriting z shrinking keywords
451 451
452 452 Only z should be overwritten
453 453
454 454 $ hg status a y z
455 455 M a
456 456 A y
457 457 A z
458 458 $ cat z
459 459 $Id$
460 460 $ hg forget y z
461 461 $ rm y z
462 462
463 463 record added file alone
464 464
465 465 $ hg -v record -l msg -d '12 2' r<<EOF
466 466 > y
467 467 > EOF
468 468 diff --git a/r b/r
469 469 new file mode 100644
470 470 examine changes to 'r'? [Ynesfdaq?]
471 471 r
472 472 committed changeset 3:82a2f715724d
473 473 overwriting r expanding keywords
474 474 - status call required for dirstate.normallookup() check
475 475 $ hg status r
476 476 $ hg --verbose rollback
477 477 repository tip rolled back to revision 2 (undo commit)
478 478 working directory now based on revision 2
479 479 overwriting r shrinking keywords
480 480 $ hg forget r
481 481 $ rm msg r
482 482 $ hg update -C
483 483 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
484 484
485 485 record added keyword ignored file
486 486
487 487 $ echo '$Id$' > i
488 488 $ hg add i
489 489 $ hg --verbose record -d '13 1' -m recignored<<EOF
490 490 > y
491 491 > EOF
492 492 diff --git a/i b/i
493 493 new file mode 100644
494 494 examine changes to 'i'? [Ynesfdaq?]
495 495 i
496 496 committed changeset 3:9f40ceb5a072
497 497 $ cat i
498 498 $Id$
499 499 $ hg -q rollback
500 500 $ hg forget i
501 501 $ rm i
502 502
503 503 amend
504 504
505 505 $ echo amend >> a
506 506 $ echo amend >> b
507 507 $ hg -q commit -d '14 1' -m 'prepare amend'
508 508
509 509 $ hg --debug commit --amend -d '15 1' -m 'amend without changes' | grep keywords
510 510 overwriting a expanding keywords
511 511 $ hg -q id
512 512 67d8c481a6be
513 513 $ head -1 a
514 514 expand $Id: a,v 67d8c481a6be 1970/01/01 00:00:15 test $
515 515
516 516 $ hg -q strip -n tip
517 517
518 518 Test patch queue repo
519 519
520 520 $ hg init --mq
521 521 $ hg qimport -r tip -n mqtest.diff
522 522 $ hg commit --mq -m mqtest
523 523
524 524 Keywords should not be expanded in patch
525 525
526 526 $ cat .hg/patches/mqtest.diff
527 527 # HG changeset patch
528 528 # User User Name <user@example.com>
529 529 # Date 1 0
530 530 # Node ID 40a904bbbe4cd4ab0a1f28411e35db26341a40ad
531 531 # Parent ef63ca68695bc9495032c6fda1350c71e6d256e9
532 532 cndiff
533 533
534 534 diff -r ef63ca68695b -r 40a904bbbe4c c
535 535 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
536 536 +++ b/c Thu Jan 01 00:00:01 1970 +0000
537 537 @@ -0,0 +1,2 @@
538 538 +$Id$
539 539 +tests for different changenodes
540 540
541 541 $ hg qpop
542 542 popping mqtest.diff
543 543 patch queue now empty
544 544
545 545 qgoto, implying qpush, should expand
546 546
547 547 $ hg qgoto mqtest.diff
548 548 applying mqtest.diff
549 549 now at: mqtest.diff
550 550 $ cat c
551 551 $Id: c,v 40a904bbbe4c 1970/01/01 00:00:01 user $
552 552 tests for different changenodes
553 553 $ hg cat c
554 554 $Id: c,v 40a904bbbe4c 1970/01/01 00:00:01 user $
555 555 tests for different changenodes
556 556
557 557 Keywords should not be expanded in filelog
558 558
559 559 $ hg --config 'extensions.keyword=!' cat c
560 560 $Id$
561 561 tests for different changenodes
562 562
563 563 qpop and move on
564 564
565 565 $ hg qpop
566 566 popping mqtest.diff
567 567 patch queue now empty
568 568
569 569 Copy and show added kwfiles
570 570
571 571 $ hg cp a c
572 572 $ hg kwfiles
573 573 a
574 574 c
575 575
576 576 Commit and show expansion in original and copy
577 577
578 578 $ hg --debug commit -ma2c -d '1 0' -u 'User Name <user@example.com>'
579 invalid branchheads cache (unserved): tip differs
579 580 c
580 581 c: copy a:0045e12f6c5791aac80ca6cbfd97709a88307292
581 582 overwriting c expanding keywords
582 583 committed changeset 2:25736cf2f5cbe41f6be4e6784ef6ecf9f3bbcc7d
583 584 $ cat a c
584 585 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
585 586 do not process $Id:
586 587 xxx $
587 588 expand $Id: c,v 25736cf2f5cb 1970/01/01 00:00:01 user $
588 589 do not process $Id:
589 590 xxx $
590 591
591 592 Touch copied c and check its status
592 593
593 594 $ touch c
594 595 $ hg status
595 596
596 597 Copy kwfile to keyword ignored file unexpanding keywords
597 598
598 599 $ hg --verbose copy a i
599 600 copying a to i
600 601 overwriting i shrinking keywords
601 602 $ head -n 1 i
602 603 expand $Id$
603 604 $ hg forget i
604 605 $ rm i
605 606
606 607 Copy ignored file to ignored file: no overwriting
607 608
608 609 $ hg --verbose copy b i
609 610 copying b to i
610 611 $ hg forget i
611 612 $ rm i
612 613
613 614 cp symlink file; hg cp -A symlink file (part1)
614 615 - copied symlink points to kwfile: overwrite
615 616
616 617 #if symlink
617 618 $ cp sym i
618 619 $ ls -l i
619 620 -rw-r--r--* (glob)
620 621 $ head -1 i
621 622 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
622 623 $ hg copy --after --verbose sym i
623 624 copying sym to i
624 625 overwriting i shrinking keywords
625 626 $ head -1 i
626 627 expand $Id$
627 628 $ hg forget i
628 629 $ rm i
629 630 #endif
630 631
631 632 Test different options of hg kwfiles
632 633
633 634 $ hg kwfiles
634 635 a
635 636 c
636 637 $ hg -v kwfiles --ignore
637 638 I b
638 639 I sym
639 640 $ hg kwfiles --all
640 641 K a
641 642 K c
642 643 I b
643 644 I sym
644 645
645 646 Diff specific revision
646 647
647 648 $ hg diff --rev 1
648 649 diff -r ef63ca68695b c
649 650 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
650 651 +++ b/c * (glob)
651 652 @@ -0,0 +1,3 @@
652 653 +expand $Id$
653 654 +do not process $Id:
654 655 +xxx $
655 656
656 657 Status after rollback:
657 658
658 659 $ hg rollback
659 660 repository tip rolled back to revision 1 (undo commit)
660 661 working directory now based on revision 1
661 662 $ hg status
662 663 A c
663 664 $ hg update --clean
664 665 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
665 666
666 667 #if symlink
667 668
668 669 cp symlink file; hg cp -A symlink file (part2)
669 670 - copied symlink points to kw ignored file: do not overwrite
670 671
671 672 $ cat a > i
672 673 $ ln -s i symignored
673 674 $ hg commit -Am 'fake expansion in ignored and symlink' i symignored
674 675 $ cp symignored x
675 676 $ hg copy --after --verbose symignored x
676 677 copying symignored to x
677 678 $ head -n 1 x
678 679 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
679 680 $ hg forget x
680 681 $ rm x
681 682
682 683 $ hg rollback
683 684 repository tip rolled back to revision 1 (undo commit)
684 685 working directory now based on revision 1
685 686 $ hg update --clean
686 687 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
687 688 $ rm i symignored
688 689
689 690 #endif
690 691
691 692 Custom keywordmaps as argument to kwdemo
692 693
693 694 $ hg --quiet kwdemo "Xinfo = {author}: {desc}"
694 695 [extensions]
695 696 keyword =
696 697 [keyword]
697 698 ** =
698 699 b = ignore
699 700 demo.txt =
700 701 i = ignore
701 702 [keywordset]
702 703 svn = False
703 704 [keywordmaps]
704 705 Xinfo = {author}: {desc}
705 706 $Xinfo: test: hg keyword configuration and expansion example $
706 707
707 708 Configure custom keywordmaps
708 709
709 710 $ cat <<EOF >>$HGRCPATH
710 711 > [keywordmaps]
711 712 > Id = {file} {node|short} {date|rfc822date} {author|user}
712 713 > Xinfo = {author}: {desc}
713 714 > EOF
714 715
715 716 Cat and hg cat files before custom expansion
716 717
717 718 $ cat a b
718 719 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
719 720 do not process $Id:
720 721 xxx $
721 722 ignore $Id$
722 723 $ hg cat sym a b && echo
723 724 expand $Id: a ef63ca68695b Thu, 01 Jan 1970 00:00:00 +0000 user $
724 725 do not process $Id:
725 726 xxx $
726 727 ignore $Id$
727 728 a
728 729
729 730 Write custom keyword and prepare multi-line commit message
730 731
731 732 $ echo '$Xinfo$' >> a
732 733 $ cat <<EOF >> log
733 734 > firstline
734 735 > secondline
735 736 > EOF
736 737
737 738 Interrupted commit should not change state
738 739
739 740 $ hg commit
740 741 abort: empty commit message
741 742 [255]
742 743 $ hg status
743 744 M a
744 745 ? c
745 746 ? log
746 747
747 748 Commit with multi-line message and custom expansion
748 749
749 750 |Note:
750 751 |
751 752 | After the last rollback, the "unserved" branchheads cache became invalid, but
752 753 | all changeset in the repo were public. So filtering wise:
753 754 | "mutable" == "unserved" == ø.
754 755 |
755 756 | As the "unserved" cache is invalid, we fall back to "mutable" cache. But not
756 757 | update is needed between "mutable" and "unserved" cache and the "unserved"
757 758 | cache is not updated on disk. The on disk version therefor stay invalid for
758 759 | some time. This explains why the "unserved" branchheads cache is detect
759 760 | invalid here.
760 761
761 762 $ hg --debug commit -l log -d '2 0' -u 'User Name <user@example.com>'
762 invalid branchheads cache: tip differs
763 763 invalid branchheads cache (unserved): tip differs
764 764 a
765 765 invalid branchheads cache: tip differs
766 766 invalid branchheads cache (unserved): tip differs
767 767 overwriting a expanding keywords
768 768 committed changeset 2:bb948857c743469b22bbf51f7ec8112279ca5d83
769 769 $ rm log
770 770
771 771 Stat, verify and show custom expansion (firstline)
772 772
773 773 $ hg status
774 774 ? c
775 775 $ hg verify
776 776 checking changesets
777 777 checking manifests
778 778 crosschecking files in changesets and manifests
779 779 checking files
780 780 3 files, 3 changesets, 4 total revisions
781 781 $ cat a b
782 782 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
783 783 do not process $Id:
784 784 xxx $
785 785 $Xinfo: User Name <user@example.com>: firstline $
786 786 ignore $Id$
787 787 $ hg cat sym a b && echo
788 788 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
789 789 do not process $Id:
790 790 xxx $
791 791 $Xinfo: User Name <user@example.com>: firstline $
792 792 ignore $Id$
793 793 a
794 794
795 795 annotate
796 796
797 797 $ hg annotate a
798 798 1: expand $Id$
799 799 1: do not process $Id:
800 800 1: xxx $
801 801 2: $Xinfo$
802 802
803 803 remove with status checks
804 804
805 805 $ hg debugrebuildstate
806 806 $ hg remove a
807 807 $ hg --debug commit -m rma
808 808 invalid branchheads cache: tip differs
809 invalid branchheads cache: tip differs
810 809 committed changeset 3:d14c712653769de926994cf7fbb06c8fbd68f012
811 810 $ hg status
812 811 ? c
813 812
814 813 Rollback, revert, and check expansion
815 814
816 815 $ hg rollback
817 816 repository tip rolled back to revision 2 (undo commit)
818 817 working directory now based on revision 2
819 818 $ hg status
820 819 R a
821 820 ? c
822 821 $ hg revert --no-backup --rev tip a
823 822 $ cat a
824 823 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
825 824 do not process $Id:
826 825 xxx $
827 826 $Xinfo: User Name <user@example.com>: firstline $
828 827
829 828 Clone to test global and local configurations
830 829
831 830 $ cd ..
832 831
833 832 Expansion in destination with global configuration
834 833
835 834 $ hg --quiet clone Test globalconf
836 835 $ cat globalconf/a
837 836 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
838 837 do not process $Id:
839 838 xxx $
840 839 $Xinfo: User Name <user@example.com>: firstline $
841 840
842 841 No expansion in destination with local configuration in origin only
843 842
844 843 $ hg --quiet --config 'keyword.**=ignore' clone Test localconf
845 844 $ cat localconf/a
846 845 expand $Id$
847 846 do not process $Id:
848 847 xxx $
849 848 $Xinfo$
850 849
851 850 Clone to test incoming
852 851
853 852 $ hg clone -r1 Test Test-a
854 853 adding changesets
855 854 adding manifests
856 855 adding file changes
857 856 added 2 changesets with 3 changes to 3 files
858 857 updating to branch default
859 858 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
860 859 $ cd Test-a
861 860 $ cat <<EOF >> .hg/hgrc
862 861 > [paths]
863 862 > default = ../Test
864 863 > EOF
865 864 $ hg incoming
866 865 comparing with $TESTTMP/Test (glob)
867 866 searching for changes
868 867 changeset: 2:bb948857c743
869 868 tag: tip
870 869 user: User Name <user@example.com>
871 870 date: Thu Jan 01 00:00:02 1970 +0000
872 871 summary: firstline
873 872
874 873 Imported patch should not be rejected
875 874
876 875 >>> import re
877 876 >>> text = re.sub(r'(Id.*)', r'\1 rejecttest', open('a').read())
878 877 >>> open('a', 'wb').write(text)
879 878 $ hg --debug commit -m'rejects?' -d '3 0' -u 'User Name <user@example.com>'
880 879 a
881 880 overwriting a expanding keywords
882 881 committed changeset 2:85e279d709ffc28c9fdd1b868570985fc3d87082
883 882 $ hg export -o ../rejecttest.diff tip
884 883 $ cd ../Test
885 884 $ hg import ../rejecttest.diff
886 885 applying ../rejecttest.diff
887 886 $ cat a b
888 887 expand $Id: a 4e0994474d25 Thu, 01 Jan 1970 00:00:03 +0000 user $ rejecttest
889 888 do not process $Id: rejecttest
890 889 xxx $
891 890 $Xinfo: User Name <user@example.com>: rejects? $
892 891 ignore $Id$
893 892
894 893 $ hg rollback
895 894 repository tip rolled back to revision 2 (undo import)
896 895 working directory now based on revision 2
897 896 $ hg update --clean
898 897 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
899 898
900 899 kwexpand/kwshrink on selected files
901 900
902 901 $ mkdir x
903 902 $ hg copy a x/a
904 903 $ hg --verbose kwshrink a
905 904 overwriting a shrinking keywords
906 905 - sleep required for dirstate.normal() check
907 906 $ sleep 1
908 907 $ hg status a
909 908 $ hg --verbose kwexpand a
910 909 overwriting a expanding keywords
911 910 $ hg status a
912 911
913 912 kwexpand x/a should abort
914 913
915 914 $ hg --verbose kwexpand x/a
916 915 abort: outstanding uncommitted changes
917 916 [255]
918 917 $ cd x
919 918 $ hg --debug commit -m xa -d '3 0' -u 'User Name <user@example.com>'
920 invalid branchheads cache: tip differs
921 919 x/a
922 920 x/a: copy a:779c764182ce5d43e2b1eb66ce06d7b47bfe342e
923 921 invalid branchheads cache: tip differs
924 922 overwriting x/a expanding keywords
925 923 committed changeset 3:b4560182a3f9a358179fd2d835c15e9da379c1e4
926 924 $ cat a
927 925 expand $Id: x/a b4560182a3f9 Thu, 01 Jan 1970 00:00:03 +0000 user $
928 926 do not process $Id:
929 927 xxx $
930 928 $Xinfo: User Name <user@example.com>: xa $
931 929
932 930 kwshrink a inside directory x
933 931
934 932 $ hg --verbose kwshrink a
935 933 overwriting x/a shrinking keywords
936 934 $ cat a
937 935 expand $Id$
938 936 do not process $Id:
939 937 xxx $
940 938 $Xinfo$
941 939 $ cd ..
942 940
943 941 kwexpand nonexistent
944 942
945 943 $ hg kwexpand nonexistent
946 944 nonexistent:* (glob)
947 945
948 946
949 947 #if serve
950 948 hg serve
951 949 - expand with hgweb file
952 950 - no expansion with hgweb annotate/changeset/filediff
953 951 - check errors
954 952
955 953 $ hg serve -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
956 954 $ cat hg.pid >> $DAEMON_PIDS
957 955 $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT 'file/tip/a/?style=raw'
958 956 200 Script output follows
959 957
960 958 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
961 959 do not process $Id:
962 960 xxx $
963 961 $Xinfo: User Name <user@example.com>: firstline $
964 962 $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT 'annotate/tip/a/?style=raw'
965 963 200 Script output follows
966 964
967 965
968 966 user@1: expand $Id$
969 967 user@1: do not process $Id:
970 968 user@1: xxx $
971 969 user@2: $Xinfo$
972 970
973 971
974 972
975 973
976 974 $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT 'rev/tip/?style=raw'
977 975 200 Script output follows
978 976
979 977
980 978 # HG changeset patch
981 979 # User User Name <user@example.com>
982 980 # Date 3 0
983 981 # Node ID b4560182a3f9a358179fd2d835c15e9da379c1e4
984 982 # Parent bb948857c743469b22bbf51f7ec8112279ca5d83
985 983 xa
986 984
987 985 diff -r bb948857c743 -r b4560182a3f9 x/a
988 986 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
989 987 +++ b/x/a Thu Jan 01 00:00:03 1970 +0000
990 988 @@ -0,0 +1,4 @@
991 989 +expand $Id$
992 990 +do not process $Id:
993 991 +xxx $
994 992 +$Xinfo$
995 993
996 994 $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT 'diff/bb948857c743/a?style=raw'
997 995 200 Script output follows
998 996
999 997
1000 998 diff -r ef63ca68695b -r bb948857c743 a
1001 999 --- a/a Thu Jan 01 00:00:00 1970 +0000
1002 1000 +++ b/a Thu Jan 01 00:00:02 1970 +0000
1003 1001 @@ -1,3 +1,4 @@
1004 1002 expand $Id$
1005 1003 do not process $Id:
1006 1004 xxx $
1007 1005 +$Xinfo$
1008 1006
1009 1007
1010 1008
1011 1009
1012 1010 $ cat errors.log
1013 1011 #endif
1014 1012
1015 1013 Prepare merge and resolve tests
1016 1014
1017 1015 $ echo '$Id$' > m
1018 1016 $ hg add m
1019 1017 $ hg commit -m 4kw
1020 1018 $ echo foo >> m
1021 1019 $ hg commit -m 5foo
1022 1020
1023 1021 simplemerge
1024 1022
1025 1023 $ hg update 4
1026 1024 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1027 1025 $ echo foo >> m
1028 1026 $ hg commit -m 6foo
1029 1027 created new head
1030 1028 $ hg merge
1031 1029 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1032 1030 (branch merge, don't forget to commit)
1033 1031 $ hg commit -m simplemerge
1034 1032 $ cat m
1035 1033 $Id: m 27d48ee14f67 Thu, 01 Jan 1970 00:00:00 +0000 test $
1036 1034 foo
1037 1035
1038 1036 conflict: keyword should stay outside conflict zone
1039 1037
1040 1038 $ hg update 4
1041 1039 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1042 1040 $ echo bar >> m
1043 1041 $ hg commit -m 8bar
1044 1042 created new head
1045 1043 $ hg merge
1046 1044 merging m
1047 1045 warning: conflicts during merge.
1048 1046 merging m incomplete! (edit conflicts, then use 'hg resolve --mark')
1049 1047 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
1050 1048 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
1051 1049 [1]
1052 1050 $ cat m
1053 1051 $Id$
1054 1052 <<<<<<< local
1055 1053 bar
1056 1054 =======
1057 1055 foo
1058 1056 >>>>>>> other
1059 1057
1060 1058 resolve to local
1061 1059
1062 1060 $ HGMERGE=internal:local hg resolve -a
1063 1061 $ hg commit -m localresolve
1064 1062 $ cat m
1065 1063 $Id: m 800511b3a22d Thu, 01 Jan 1970 00:00:00 +0000 test $
1066 1064 bar
1067 1065
1068 1066 Test restricted mode with transplant -b
1069 1067
1070 1068 $ hg update 6
1071 1069 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1072 1070 $ hg branch foo
1073 1071 marked working directory as branch foo
1074 1072 (branches are permanent and global, did you want a bookmark?)
1075 1073 $ mv a a.bak
1076 1074 $ echo foobranch > a
1077 1075 $ cat a.bak >> a
1078 1076 $ rm a.bak
1079 1077 $ hg commit -m 9foobranch
1080 1078 $ hg update default
1081 1079 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1082 1080 $ hg -y transplant -b foo tip
1083 1081 applying 4aa30d025d50
1084 1082 4aa30d025d50 transplanted to e00abbf63521
1085 1083
1086 1084 Expansion in changeset but not in file
1087 1085
1088 1086 $ hg tip -p
1089 1087 changeset: 11:e00abbf63521
1090 1088 tag: tip
1091 1089 parent: 9:800511b3a22d
1092 1090 user: test
1093 1091 date: Thu Jan 01 00:00:00 1970 +0000
1094 1092 summary: 9foobranch
1095 1093
1096 1094 diff -r 800511b3a22d -r e00abbf63521 a
1097 1095 --- a/a Thu Jan 01 00:00:00 1970 +0000
1098 1096 +++ b/a Thu Jan 01 00:00:00 1970 +0000
1099 1097 @@ -1,3 +1,4 @@
1100 1098 +foobranch
1101 1099 expand $Id$
1102 1100 do not process $Id:
1103 1101 xxx $
1104 1102
1105 1103 $ head -n 2 a
1106 1104 foobranch
1107 1105 expand $Id: a e00abbf63521 Thu, 01 Jan 1970 00:00:00 +0000 test $
1108 1106
1109 1107 Turn off expansion
1110 1108
1111 1109 $ hg -q rollback
1112 1110 $ hg -q update -C
1113 1111
1114 1112 kwshrink with unknown file u
1115 1113
1116 1114 $ cp a u
1117 1115 $ hg --verbose kwshrink
1118 1116 overwriting a shrinking keywords
1119 1117 overwriting m shrinking keywords
1120 1118 overwriting x/a shrinking keywords
1121 1119
1122 1120 Keywords shrunk in working directory, but not yet disabled
1123 1121 - cat shows unexpanded keywords
1124 1122 - hg cat shows expanded keywords
1125 1123
1126 1124 $ cat a b
1127 1125 expand $Id$
1128 1126 do not process $Id:
1129 1127 xxx $
1130 1128 $Xinfo$
1131 1129 ignore $Id$
1132 1130 $ hg cat sym a b && echo
1133 1131 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
1134 1132 do not process $Id:
1135 1133 xxx $
1136 1134 $Xinfo: User Name <user@example.com>: firstline $
1137 1135 ignore $Id$
1138 1136 a
1139 1137
1140 1138 Now disable keyword expansion
1141 1139
1142 1140 $ rm "$HGRCPATH"
1143 1141 $ cat a b
1144 1142 expand $Id$
1145 1143 do not process $Id:
1146 1144 xxx $
1147 1145 $Xinfo$
1148 1146 ignore $Id$
1149 1147 $ hg cat sym a b && echo
1150 1148 expand $Id$
1151 1149 do not process $Id:
1152 1150 xxx $
1153 1151 $Xinfo$
1154 1152 ignore $Id$
1155 1153 a
1156 1154
1157 1155 $ cd ..
@@ -1,1273 +1,1273 b''
1 1 The g is crafted to have 2 filelog topological heads in a linear
2 2 changeset graph
3 3
4 4 $ hg init a
5 5 $ cd a
6 6 $ echo a > a
7 7 $ echo f > f
8 8 $ hg ci -Ama -d '1 0'
9 9 adding a
10 10 adding f
11 11
12 12 $ hg cp a b
13 13 $ hg cp f g
14 14 $ hg ci -mb -d '2 0'
15 15
16 16 $ mkdir dir
17 17 $ hg mv b dir
18 18 $ echo g >> g
19 19 $ echo f >> f
20 20 $ hg ci -mc -d '3 0'
21 21
22 22 $ hg mv a b
23 23 $ hg cp -f f g
24 24 $ echo a > d
25 25 $ hg add d
26 26 $ hg ci -md -d '4 0'
27 27
28 28 $ hg mv dir/b e
29 29 $ hg ci -me -d '5 0'
30 30
31 31 $ hg log a
32 32 changeset: 0:9161b9aeaf16
33 33 user: test
34 34 date: Thu Jan 01 00:00:01 1970 +0000
35 35 summary: a
36 36
37 37
38 38 -f, directory
39 39
40 40 $ hg log -f dir
41 41 abort: cannot follow file not in parent revision: "dir"
42 42 [255]
43 43
44 44 -f, but no args
45 45
46 46 $ hg log -f
47 47 changeset: 4:7e4639b4691b
48 48 tag: tip
49 49 user: test
50 50 date: Thu Jan 01 00:00:05 1970 +0000
51 51 summary: e
52 52
53 53 changeset: 3:2ca5ba701980
54 54 user: test
55 55 date: Thu Jan 01 00:00:04 1970 +0000
56 56 summary: d
57 57
58 58 changeset: 2:f8954cd4dc1f
59 59 user: test
60 60 date: Thu Jan 01 00:00:03 1970 +0000
61 61 summary: c
62 62
63 63 changeset: 1:d89b0a12d229
64 64 user: test
65 65 date: Thu Jan 01 00:00:02 1970 +0000
66 66 summary: b
67 67
68 68 changeset: 0:9161b9aeaf16
69 69 user: test
70 70 date: Thu Jan 01 00:00:01 1970 +0000
71 71 summary: a
72 72
73 73
74 74 one rename
75 75
76 76 $ hg up -q 2
77 77 $ hg log -vf a
78 78 changeset: 0:9161b9aeaf16
79 79 user: test
80 80 date: Thu Jan 01 00:00:01 1970 +0000
81 81 files: a f
82 82 description:
83 83 a
84 84
85 85
86 86
87 87 many renames
88 88
89 89 $ hg up -q tip
90 90 $ hg log -vf e
91 91 changeset: 4:7e4639b4691b
92 92 tag: tip
93 93 user: test
94 94 date: Thu Jan 01 00:00:05 1970 +0000
95 95 files: dir/b e
96 96 description:
97 97 e
98 98
99 99
100 100 changeset: 2:f8954cd4dc1f
101 101 user: test
102 102 date: Thu Jan 01 00:00:03 1970 +0000
103 103 files: b dir/b f g
104 104 description:
105 105 c
106 106
107 107
108 108 changeset: 1:d89b0a12d229
109 109 user: test
110 110 date: Thu Jan 01 00:00:02 1970 +0000
111 111 files: b g
112 112 description:
113 113 b
114 114
115 115
116 116 changeset: 0:9161b9aeaf16
117 117 user: test
118 118 date: Thu Jan 01 00:00:01 1970 +0000
119 119 files: a f
120 120 description:
121 121 a
122 122
123 123
124 124
125 125
126 126 log -pf dir/b
127 127
128 128 $ hg up -q 3
129 129 $ hg log -pf dir/b
130 130 changeset: 2:f8954cd4dc1f
131 131 user: test
132 132 date: Thu Jan 01 00:00:03 1970 +0000
133 133 summary: c
134 134
135 135 diff -r d89b0a12d229 -r f8954cd4dc1f dir/b
136 136 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
137 137 +++ b/dir/b Thu Jan 01 00:00:03 1970 +0000
138 138 @@ -0,0 +1,1 @@
139 139 +a
140 140
141 141 changeset: 1:d89b0a12d229
142 142 user: test
143 143 date: Thu Jan 01 00:00:02 1970 +0000
144 144 summary: b
145 145
146 146 diff -r 9161b9aeaf16 -r d89b0a12d229 b
147 147 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
148 148 +++ b/b Thu Jan 01 00:00:02 1970 +0000
149 149 @@ -0,0 +1,1 @@
150 150 +a
151 151
152 152 changeset: 0:9161b9aeaf16
153 153 user: test
154 154 date: Thu Jan 01 00:00:01 1970 +0000
155 155 summary: a
156 156
157 157 diff -r 000000000000 -r 9161b9aeaf16 a
158 158 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
159 159 +++ b/a Thu Jan 01 00:00:01 1970 +0000
160 160 @@ -0,0 +1,1 @@
161 161 +a
162 162
163 163
164 164 log -vf dir/b
165 165
166 166 $ hg log -vf dir/b
167 167 changeset: 2:f8954cd4dc1f
168 168 user: test
169 169 date: Thu Jan 01 00:00:03 1970 +0000
170 170 files: b dir/b f g
171 171 description:
172 172 c
173 173
174 174
175 175 changeset: 1:d89b0a12d229
176 176 user: test
177 177 date: Thu Jan 01 00:00:02 1970 +0000
178 178 files: b g
179 179 description:
180 180 b
181 181
182 182
183 183 changeset: 0:9161b9aeaf16
184 184 user: test
185 185 date: Thu Jan 01 00:00:01 1970 +0000
186 186 files: a f
187 187 description:
188 188 a
189 189
190 190
191 191
192 192
193 193 -f and multiple filelog heads
194 194
195 195 $ hg up -q 2
196 196 $ hg log -f g --template '{rev}\n'
197 197 2
198 198 1
199 199 0
200 200 $ hg up -q tip
201 201 $ hg log -f g --template '{rev}\n'
202 202 3
203 203 2
204 204 0
205 205
206 206
207 207 log copies with --copies
208 208
209 209 $ hg log -vC --template '{rev} {file_copies}\n'
210 210 4 e (dir/b)
211 211 3 b (a)g (f)
212 212 2 dir/b (b)
213 213 1 b (a)g (f)
214 214 0
215 215
216 216 log copies switch without --copies, with old filecopy template
217 217
218 218 $ hg log -v --template '{rev} {file_copies_switch%filecopy}\n'
219 219 4
220 220 3
221 221 2
222 222 1
223 223 0
224 224
225 225 log copies switch with --copies
226 226
227 227 $ hg log -vC --template '{rev} {file_copies_switch}\n'
228 228 4 e (dir/b)
229 229 3 b (a)g (f)
230 230 2 dir/b (b)
231 231 1 b (a)g (f)
232 232 0
233 233
234 234
235 235 log copies with hardcoded style and with --style=default
236 236
237 237 $ hg log -vC -r4
238 238 changeset: 4:7e4639b4691b
239 239 tag: tip
240 240 user: test
241 241 date: Thu Jan 01 00:00:05 1970 +0000
242 242 files: dir/b e
243 243 copies: e (dir/b)
244 244 description:
245 245 e
246 246
247 247
248 248 $ hg log -vC -r4 --style=default
249 249 changeset: 4:7e4639b4691b
250 250 tag: tip
251 251 user: test
252 252 date: Thu Jan 01 00:00:05 1970 +0000
253 253 files: dir/b e
254 254 copies: e (dir/b)
255 255 description:
256 256 e
257 257
258 258
259 259
260 260
261 261 log copies, non-linear manifest
262 262
263 263 $ hg up -C 3
264 264 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
265 265 $ hg mv dir/b e
266 266 $ echo foo > foo
267 267 $ hg ci -Ame2 -d '6 0'
268 268 adding foo
269 269 created new head
270 270 $ hg log -v --template '{rev} {file_copies}\n' -r 5
271 271 5 e (dir/b)
272 272
273 273
274 274 log copies, execute bit set
275 275
276 276 #if execbit
277 277 $ chmod +x e
278 278 $ hg ci -me3 -d '7 0'
279 279 $ hg log -v --template '{rev} {file_copies}\n' -r 6
280 280 6
281 281 #endif
282 282
283 283
284 284 log -p d
285 285
286 286 $ hg log -pv d
287 287 changeset: 3:2ca5ba701980
288 288 user: test
289 289 date: Thu Jan 01 00:00:04 1970 +0000
290 290 files: a b d g
291 291 description:
292 292 d
293 293
294 294
295 295 diff -r f8954cd4dc1f -r 2ca5ba701980 d
296 296 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
297 297 +++ b/d Thu Jan 01 00:00:04 1970 +0000
298 298 @@ -0,0 +1,1 @@
299 299 +a
300 300
301 301
302 302
303 303 log --removed file
304 304
305 305 $ hg log --removed -v a
306 306 changeset: 3:2ca5ba701980
307 307 user: test
308 308 date: Thu Jan 01 00:00:04 1970 +0000
309 309 files: a b d g
310 310 description:
311 311 d
312 312
313 313
314 314 changeset: 0:9161b9aeaf16
315 315 user: test
316 316 date: Thu Jan 01 00:00:01 1970 +0000
317 317 files: a f
318 318 description:
319 319 a
320 320
321 321
322 322
323 323 log --removed revrange file
324 324
325 325 $ hg log --removed -v -r0:2 a
326 326 changeset: 0:9161b9aeaf16
327 327 user: test
328 328 date: Thu Jan 01 00:00:01 1970 +0000
329 329 files: a f
330 330 description:
331 331 a
332 332
333 333
334 334 $ cd ..
335 335
336 336 log --follow tests
337 337
338 338 $ hg init follow
339 339 $ cd follow
340 340
341 341 $ echo base > base
342 342 $ hg ci -Ambase -d '1 0'
343 343 adding base
344 344
345 345 $ echo r1 >> base
346 346 $ hg ci -Amr1 -d '1 0'
347 347 $ echo r2 >> base
348 348 $ hg ci -Amr2 -d '1 0'
349 349
350 350 $ hg up -C 1
351 351 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
352 352 $ echo b1 > b1
353 353 $ hg ci -Amb1 -d '1 0'
354 354 adding b1
355 355 created new head
356 356
357 357
358 358 log -f
359 359
360 360 $ hg log -f
361 361 changeset: 3:e62f78d544b4
362 362 tag: tip
363 363 parent: 1:3d5bf5654eda
364 364 user: test
365 365 date: Thu Jan 01 00:00:01 1970 +0000
366 366 summary: b1
367 367
368 368 changeset: 1:3d5bf5654eda
369 369 user: test
370 370 date: Thu Jan 01 00:00:01 1970 +0000
371 371 summary: r1
372 372
373 373 changeset: 0:67e992f2c4f3
374 374 user: test
375 375 date: Thu Jan 01 00:00:01 1970 +0000
376 376 summary: base
377 377
378 378
379 379
380 380 log -f -r 1:tip
381 381
382 382 $ hg up -C 0
383 383 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
384 384 $ echo b2 > b2
385 385 $ hg ci -Amb2 -d '1 0'
386 386 adding b2
387 387 created new head
388 388 $ hg log -f -r 1:tip
389 389 changeset: 1:3d5bf5654eda
390 390 user: test
391 391 date: Thu Jan 01 00:00:01 1970 +0000
392 392 summary: r1
393 393
394 394 changeset: 2:60c670bf5b30
395 395 user: test
396 396 date: Thu Jan 01 00:00:01 1970 +0000
397 397 summary: r2
398 398
399 399 changeset: 3:e62f78d544b4
400 400 parent: 1:3d5bf5654eda
401 401 user: test
402 402 date: Thu Jan 01 00:00:01 1970 +0000
403 403 summary: b1
404 404
405 405
406 406
407 407 log -r . with two parents
408 408
409 409 $ hg up -C 3
410 410 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
411 411 $ hg merge tip
412 412 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
413 413 (branch merge, don't forget to commit)
414 414 $ hg log -r .
415 415 changeset: 3:e62f78d544b4
416 416 parent: 1:3d5bf5654eda
417 417 user: test
418 418 date: Thu Jan 01 00:00:01 1970 +0000
419 419 summary: b1
420 420
421 421
422 422
423 423 log -r . with one parent
424 424
425 425 $ hg ci -mm12 -d '1 0'
426 426 $ hg log -r .
427 427 changeset: 5:302e9dd6890d
428 428 tag: tip
429 429 parent: 3:e62f78d544b4
430 430 parent: 4:ddb82e70d1a1
431 431 user: test
432 432 date: Thu Jan 01 00:00:01 1970 +0000
433 433 summary: m12
434 434
435 435
436 436 $ echo postm >> b1
437 437 $ hg ci -Amb1.1 -d'1 0'
438 438
439 439
440 440 log --follow-first
441 441
442 442 $ hg log --follow-first
443 443 changeset: 6:2404bbcab562
444 444 tag: tip
445 445 user: test
446 446 date: Thu Jan 01 00:00:01 1970 +0000
447 447 summary: b1.1
448 448
449 449 changeset: 5:302e9dd6890d
450 450 parent: 3:e62f78d544b4
451 451 parent: 4:ddb82e70d1a1
452 452 user: test
453 453 date: Thu Jan 01 00:00:01 1970 +0000
454 454 summary: m12
455 455
456 456 changeset: 3:e62f78d544b4
457 457 parent: 1:3d5bf5654eda
458 458 user: test
459 459 date: Thu Jan 01 00:00:01 1970 +0000
460 460 summary: b1
461 461
462 462 changeset: 1:3d5bf5654eda
463 463 user: test
464 464 date: Thu Jan 01 00:00:01 1970 +0000
465 465 summary: r1
466 466
467 467 changeset: 0:67e992f2c4f3
468 468 user: test
469 469 date: Thu Jan 01 00:00:01 1970 +0000
470 470 summary: base
471 471
472 472
473 473
474 474 log -P 2
475 475
476 476 $ hg log -P 2
477 477 changeset: 6:2404bbcab562
478 478 tag: tip
479 479 user: test
480 480 date: Thu Jan 01 00:00:01 1970 +0000
481 481 summary: b1.1
482 482
483 483 changeset: 5:302e9dd6890d
484 484 parent: 3:e62f78d544b4
485 485 parent: 4:ddb82e70d1a1
486 486 user: test
487 487 date: Thu Jan 01 00:00:01 1970 +0000
488 488 summary: m12
489 489
490 490 changeset: 4:ddb82e70d1a1
491 491 parent: 0:67e992f2c4f3
492 492 user: test
493 493 date: Thu Jan 01 00:00:01 1970 +0000
494 494 summary: b2
495 495
496 496 changeset: 3:e62f78d544b4
497 497 parent: 1:3d5bf5654eda
498 498 user: test
499 499 date: Thu Jan 01 00:00:01 1970 +0000
500 500 summary: b1
501 501
502 502
503 503
504 504 log -r tip -p --git
505 505
506 506 $ hg log -r tip -p --git
507 507 changeset: 6:2404bbcab562
508 508 tag: tip
509 509 user: test
510 510 date: Thu Jan 01 00:00:01 1970 +0000
511 511 summary: b1.1
512 512
513 513 diff --git a/b1 b/b1
514 514 --- a/b1
515 515 +++ b/b1
516 516 @@ -1,1 +1,2 @@
517 517 b1
518 518 +postm
519 519
520 520
521 521
522 522 log -r ""
523 523
524 524 $ hg log -r ''
525 525 hg: parse error: empty query
526 526 [255]
527 527
528 528 log -r <some unknown node id>
529 529
530 530 $ hg log -r 1000000000000000000000000000000000000000
531 531 abort: unknown revision '1000000000000000000000000000000000000000'!
532 532 [255]
533 533
534 534 log -k r1
535 535
536 536 $ hg log -k r1
537 537 changeset: 1:3d5bf5654eda
538 538 user: test
539 539 date: Thu Jan 01 00:00:01 1970 +0000
540 540 summary: r1
541 541
542 542 log -p -l2 --color=always
543 543
544 544 $ hg --config extensions.color= --config color.mode=ansi \
545 545 > log -p -l2 --color=always
546 546 \x1b[0;33mchangeset: 6:2404bbcab562\x1b[0m (esc)
547 547 tag: tip
548 548 user: test
549 549 date: Thu Jan 01 00:00:01 1970 +0000
550 550 summary: b1.1
551 551
552 552 \x1b[0;1mdiff -r 302e9dd6890d -r 2404bbcab562 b1\x1b[0m (esc)
553 553 \x1b[0;31;1m--- a/b1 Thu Jan 01 00:00:01 1970 +0000\x1b[0m (esc)
554 554 \x1b[0;32;1m+++ b/b1 Thu Jan 01 00:00:01 1970 +0000\x1b[0m (esc)
555 555 \x1b[0;35m@@ -1,1 +1,2 @@\x1b[0m (esc)
556 556 b1
557 557 \x1b[0;32m+postm\x1b[0m (esc)
558 558
559 559 \x1b[0;33mchangeset: 5:302e9dd6890d\x1b[0m (esc)
560 560 parent: 3:e62f78d544b4
561 561 parent: 4:ddb82e70d1a1
562 562 user: test
563 563 date: Thu Jan 01 00:00:01 1970 +0000
564 564 summary: m12
565 565
566 566 \x1b[0;1mdiff -r e62f78d544b4 -r 302e9dd6890d b2\x1b[0m (esc)
567 567 \x1b[0;31;1m--- /dev/null Thu Jan 01 00:00:00 1970 +0000\x1b[0m (esc)
568 568 \x1b[0;32;1m+++ b/b2 Thu Jan 01 00:00:01 1970 +0000\x1b[0m (esc)
569 569 \x1b[0;35m@@ -0,0 +1,1 @@\x1b[0m (esc)
570 570 \x1b[0;32m+b2\x1b[0m (esc)
571 571
572 572
573 573
574 574 log -r tip --stat
575 575
576 576 $ hg log -r tip --stat
577 577 changeset: 6:2404bbcab562
578 578 tag: tip
579 579 user: test
580 580 date: Thu Jan 01 00:00:01 1970 +0000
581 581 summary: b1.1
582 582
583 583 b1 | 1 +
584 584 1 files changed, 1 insertions(+), 0 deletions(-)
585 585
586 586
587 587 $ cd ..
588 588
589 589
590 590 User
591 591
592 592 $ hg init usertest
593 593 $ cd usertest
594 594
595 595 $ echo a > a
596 596 $ hg ci -A -m "a" -u "User One <user1@example.org>"
597 597 adding a
598 598 $ echo b > b
599 599 $ hg ci -A -m "b" -u "User Two <user2@example.org>"
600 600 adding b
601 601
602 602 $ hg log -u "User One <user1@example.org>"
603 603 changeset: 0:29a4c94f1924
604 604 user: User One <user1@example.org>
605 605 date: Thu Jan 01 00:00:00 1970 +0000
606 606 summary: a
607 607
608 608 $ hg log -u "user1" -u "user2"
609 609 changeset: 1:e834b5e69c0e
610 610 tag: tip
611 611 user: User Two <user2@example.org>
612 612 date: Thu Jan 01 00:00:00 1970 +0000
613 613 summary: b
614 614
615 615 changeset: 0:29a4c94f1924
616 616 user: User One <user1@example.org>
617 617 date: Thu Jan 01 00:00:00 1970 +0000
618 618 summary: a
619 619
620 620 $ hg log -u "user3"
621 621
622 622 $ cd ..
623 623
624 624 $ hg init branches
625 625 $ cd branches
626 626
627 627 $ echo a > a
628 628 $ hg ci -A -m "commit on default"
629 629 adding a
630 630 $ hg branch test
631 631 marked working directory as branch test
632 632 (branches are permanent and global, did you want a bookmark?)
633 633 $ echo b > b
634 634 $ hg ci -A -m "commit on test"
635 635 adding b
636 636
637 637 $ hg up default
638 638 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
639 639 $ echo c > c
640 640 $ hg ci -A -m "commit on default"
641 641 adding c
642 642 $ hg up test
643 643 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
644 644 $ echo c > c
645 645 $ hg ci -A -m "commit on test"
646 646 adding c
647 647
648 648
649 649 log -b default
650 650
651 651 $ hg log -b default
652 652 changeset: 2:c3a4f03cc9a7
653 653 parent: 0:24427303d56f
654 654 user: test
655 655 date: Thu Jan 01 00:00:00 1970 +0000
656 656 summary: commit on default
657 657
658 658 changeset: 0:24427303d56f
659 659 user: test
660 660 date: Thu Jan 01 00:00:00 1970 +0000
661 661 summary: commit on default
662 662
663 663
664 664
665 665 log -b test
666 666
667 667 $ hg log -b test
668 668 changeset: 3:f5d8de11c2e2
669 669 branch: test
670 670 tag: tip
671 671 parent: 1:d32277701ccb
672 672 user: test
673 673 date: Thu Jan 01 00:00:00 1970 +0000
674 674 summary: commit on test
675 675
676 676 changeset: 1:d32277701ccb
677 677 branch: test
678 678 user: test
679 679 date: Thu Jan 01 00:00:00 1970 +0000
680 680 summary: commit on test
681 681
682 682
683 683
684 684 log -b dummy
685 685
686 686 $ hg log -b dummy
687 687 abort: unknown revision 'dummy'!
688 688 [255]
689 689
690 690
691 691 log -b .
692 692
693 693 $ hg log -b .
694 694 changeset: 3:f5d8de11c2e2
695 695 branch: test
696 696 tag: tip
697 697 parent: 1:d32277701ccb
698 698 user: test
699 699 date: Thu Jan 01 00:00:00 1970 +0000
700 700 summary: commit on test
701 701
702 702 changeset: 1:d32277701ccb
703 703 branch: test
704 704 user: test
705 705 date: Thu Jan 01 00:00:00 1970 +0000
706 706 summary: commit on test
707 707
708 708
709 709
710 710 log -b default -b test
711 711
712 712 $ hg log -b default -b test
713 713 changeset: 3:f5d8de11c2e2
714 714 branch: test
715 715 tag: tip
716 716 parent: 1:d32277701ccb
717 717 user: test
718 718 date: Thu Jan 01 00:00:00 1970 +0000
719 719 summary: commit on test
720 720
721 721 changeset: 2:c3a4f03cc9a7
722 722 parent: 0:24427303d56f
723 723 user: test
724 724 date: Thu Jan 01 00:00:00 1970 +0000
725 725 summary: commit on default
726 726
727 727 changeset: 1:d32277701ccb
728 728 branch: test
729 729 user: test
730 730 date: Thu Jan 01 00:00:00 1970 +0000
731 731 summary: commit on test
732 732
733 733 changeset: 0:24427303d56f
734 734 user: test
735 735 date: Thu Jan 01 00:00:00 1970 +0000
736 736 summary: commit on default
737 737
738 738
739 739
740 740 log -b default -b .
741 741
742 742 $ hg log -b default -b .
743 743 changeset: 3:f5d8de11c2e2
744 744 branch: test
745 745 tag: tip
746 746 parent: 1:d32277701ccb
747 747 user: test
748 748 date: Thu Jan 01 00:00:00 1970 +0000
749 749 summary: commit on test
750 750
751 751 changeset: 2:c3a4f03cc9a7
752 752 parent: 0:24427303d56f
753 753 user: test
754 754 date: Thu Jan 01 00:00:00 1970 +0000
755 755 summary: commit on default
756 756
757 757 changeset: 1:d32277701ccb
758 758 branch: test
759 759 user: test
760 760 date: Thu Jan 01 00:00:00 1970 +0000
761 761 summary: commit on test
762 762
763 763 changeset: 0:24427303d56f
764 764 user: test
765 765 date: Thu Jan 01 00:00:00 1970 +0000
766 766 summary: commit on default
767 767
768 768
769 769
770 770 log -b . -b test
771 771
772 772 $ hg log -b . -b test
773 773 changeset: 3:f5d8de11c2e2
774 774 branch: test
775 775 tag: tip
776 776 parent: 1:d32277701ccb
777 777 user: test
778 778 date: Thu Jan 01 00:00:00 1970 +0000
779 779 summary: commit on test
780 780
781 781 changeset: 1:d32277701ccb
782 782 branch: test
783 783 user: test
784 784 date: Thu Jan 01 00:00:00 1970 +0000
785 785 summary: commit on test
786 786
787 787
788 788
789 789 log -b 2
790 790
791 791 $ hg log -b 2
792 792 changeset: 2:c3a4f03cc9a7
793 793 parent: 0:24427303d56f
794 794 user: test
795 795 date: Thu Jan 01 00:00:00 1970 +0000
796 796 summary: commit on default
797 797
798 798 changeset: 0:24427303d56f
799 799 user: test
800 800 date: Thu Jan 01 00:00:00 1970 +0000
801 801 summary: commit on default
802 802
803 803
804 804
805 805 log -p --cwd dir (in subdir)
806 806
807 807 $ mkdir dir
808 808 $ hg log -p --cwd dir
809 809 changeset: 3:f5d8de11c2e2
810 810 branch: test
811 811 tag: tip
812 812 parent: 1:d32277701ccb
813 813 user: test
814 814 date: Thu Jan 01 00:00:00 1970 +0000
815 815 summary: commit on test
816 816
817 817 diff -r d32277701ccb -r f5d8de11c2e2 c
818 818 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
819 819 +++ b/c Thu Jan 01 00:00:00 1970 +0000
820 820 @@ -0,0 +1,1 @@
821 821 +c
822 822
823 823 changeset: 2:c3a4f03cc9a7
824 824 parent: 0:24427303d56f
825 825 user: test
826 826 date: Thu Jan 01 00:00:00 1970 +0000
827 827 summary: commit on default
828 828
829 829 diff -r 24427303d56f -r c3a4f03cc9a7 c
830 830 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
831 831 +++ b/c Thu Jan 01 00:00:00 1970 +0000
832 832 @@ -0,0 +1,1 @@
833 833 +c
834 834
835 835 changeset: 1:d32277701ccb
836 836 branch: test
837 837 user: test
838 838 date: Thu Jan 01 00:00:00 1970 +0000
839 839 summary: commit on test
840 840
841 841 diff -r 24427303d56f -r d32277701ccb b
842 842 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
843 843 +++ b/b Thu Jan 01 00:00:00 1970 +0000
844 844 @@ -0,0 +1,1 @@
845 845 +b
846 846
847 847 changeset: 0:24427303d56f
848 848 user: test
849 849 date: Thu Jan 01 00:00:00 1970 +0000
850 850 summary: commit on default
851 851
852 852 diff -r 000000000000 -r 24427303d56f a
853 853 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
854 854 +++ b/a Thu Jan 01 00:00:00 1970 +0000
855 855 @@ -0,0 +1,1 @@
856 856 +a
857 857
858 858
859 859
860 860 log -p -R repo
861 861
862 862 $ cd dir
863 863 $ hg log -p -R .. ../a
864 864 changeset: 0:24427303d56f
865 865 user: test
866 866 date: Thu Jan 01 00:00:00 1970 +0000
867 867 summary: commit on default
868 868
869 869 diff -r 000000000000 -r 24427303d56f a
870 870 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
871 871 +++ b/a Thu Jan 01 00:00:00 1970 +0000
872 872 @@ -0,0 +1,1 @@
873 873 +a
874 874
875 875
876 876 $ cd ../..
877 877
878 878 $ hg init follow2
879 879 $ cd follow2
880 880
881 881 # Build the following history:
882 882 # tip - o - x - o - x - x
883 883 # \ /
884 884 # o - o - o - x
885 885 # \ /
886 886 # o
887 887 #
888 888 # Where "o" is a revision containing "foo" and
889 889 # "x" is a revision without "foo"
890 890
891 891 $ touch init
892 892 $ hg ci -A -m "init, unrelated"
893 893 adding init
894 894 $ echo 'foo' > init
895 895 $ hg ci -m "change, unrelated"
896 896 $ echo 'foo' > foo
897 897 $ hg ci -A -m "add unrelated old foo"
898 898 adding foo
899 899 $ hg rm foo
900 900 $ hg ci -m "delete foo, unrelated"
901 901 $ echo 'related' > foo
902 902 $ hg ci -A -m "add foo, related"
903 903 adding foo
904 904
905 905 $ hg up 0
906 906 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
907 907 $ touch branch
908 908 $ hg ci -A -m "first branch, unrelated"
909 909 adding branch
910 910 created new head
911 911 $ touch foo
912 912 $ hg ci -A -m "create foo, related"
913 913 adding foo
914 914 $ echo 'change' > foo
915 915 $ hg ci -m "change foo, related"
916 916
917 917 $ hg up 6
918 918 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
919 919 $ echo 'change foo in branch' > foo
920 920 $ hg ci -m "change foo in branch, related"
921 921 created new head
922 922 $ hg merge 7
923 923 merging foo
924 924 warning: conflicts during merge.
925 925 merging foo incomplete! (edit conflicts, then use 'hg resolve --mark')
926 926 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
927 927 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
928 928 [1]
929 929 $ echo 'merge 1' > foo
930 930 $ hg resolve -m foo
931 931 $ hg ci -m "First merge, related"
932 932
933 933 $ hg merge 4
934 934 merging foo
935 935 warning: conflicts during merge.
936 936 merging foo incomplete! (edit conflicts, then use 'hg resolve --mark')
937 937 1 files updated, 0 files merged, 0 files removed, 1 files unresolved
938 938 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
939 939 [1]
940 940 $ echo 'merge 2' > foo
941 941 $ hg resolve -m foo
942 942 $ hg ci -m "Last merge, related"
943 943
944 944 $ hg log --graph
945 945 @ changeset: 10:4dae8563d2c5
946 946 |\ tag: tip
947 947 | | parent: 9:7b35701b003e
948 948 | | parent: 4:88176d361b69
949 949 | | user: test
950 950 | | date: Thu Jan 01 00:00:00 1970 +0000
951 951 | | summary: Last merge, related
952 952 | |
953 953 | o changeset: 9:7b35701b003e
954 954 | |\ parent: 8:e5416ad8a855
955 955 | | | parent: 7:87fe3144dcfa
956 956 | | | user: test
957 957 | | | date: Thu Jan 01 00:00:00 1970 +0000
958 958 | | | summary: First merge, related
959 959 | | |
960 960 | | o changeset: 8:e5416ad8a855
961 961 | | | parent: 6:dc6c325fe5ee
962 962 | | | user: test
963 963 | | | date: Thu Jan 01 00:00:00 1970 +0000
964 964 | | | summary: change foo in branch, related
965 965 | | |
966 966 | o | changeset: 7:87fe3144dcfa
967 967 | |/ user: test
968 968 | | date: Thu Jan 01 00:00:00 1970 +0000
969 969 | | summary: change foo, related
970 970 | |
971 971 | o changeset: 6:dc6c325fe5ee
972 972 | | user: test
973 973 | | date: Thu Jan 01 00:00:00 1970 +0000
974 974 | | summary: create foo, related
975 975 | |
976 976 | o changeset: 5:73db34516eb9
977 977 | | parent: 0:e87515fd044a
978 978 | | user: test
979 979 | | date: Thu Jan 01 00:00:00 1970 +0000
980 980 | | summary: first branch, unrelated
981 981 | |
982 982 o | changeset: 4:88176d361b69
983 983 | | user: test
984 984 | | date: Thu Jan 01 00:00:00 1970 +0000
985 985 | | summary: add foo, related
986 986 | |
987 987 o | changeset: 3:dd78ae4afb56
988 988 | | user: test
989 989 | | date: Thu Jan 01 00:00:00 1970 +0000
990 990 | | summary: delete foo, unrelated
991 991 | |
992 992 o | changeset: 2:c4c64aedf0f7
993 993 | | user: test
994 994 | | date: Thu Jan 01 00:00:00 1970 +0000
995 995 | | summary: add unrelated old foo
996 996 | |
997 997 o | changeset: 1:e5faa7440653
998 998 |/ user: test
999 999 | date: Thu Jan 01 00:00:00 1970 +0000
1000 1000 | summary: change, unrelated
1001 1001 |
1002 1002 o changeset: 0:e87515fd044a
1003 1003 user: test
1004 1004 date: Thu Jan 01 00:00:00 1970 +0000
1005 1005 summary: init, unrelated
1006 1006
1007 1007
1008 1008 $ hg --traceback log -f foo
1009 1009 changeset: 10:4dae8563d2c5
1010 1010 tag: tip
1011 1011 parent: 9:7b35701b003e
1012 1012 parent: 4:88176d361b69
1013 1013 user: test
1014 1014 date: Thu Jan 01 00:00:00 1970 +0000
1015 1015 summary: Last merge, related
1016 1016
1017 1017 changeset: 9:7b35701b003e
1018 1018 parent: 8:e5416ad8a855
1019 1019 parent: 7:87fe3144dcfa
1020 1020 user: test
1021 1021 date: Thu Jan 01 00:00:00 1970 +0000
1022 1022 summary: First merge, related
1023 1023
1024 1024 changeset: 8:e5416ad8a855
1025 1025 parent: 6:dc6c325fe5ee
1026 1026 user: test
1027 1027 date: Thu Jan 01 00:00:00 1970 +0000
1028 1028 summary: change foo in branch, related
1029 1029
1030 1030 changeset: 7:87fe3144dcfa
1031 1031 user: test
1032 1032 date: Thu Jan 01 00:00:00 1970 +0000
1033 1033 summary: change foo, related
1034 1034
1035 1035 changeset: 6:dc6c325fe5ee
1036 1036 user: test
1037 1037 date: Thu Jan 01 00:00:00 1970 +0000
1038 1038 summary: create foo, related
1039 1039
1040 1040 changeset: 4:88176d361b69
1041 1041 user: test
1042 1042 date: Thu Jan 01 00:00:00 1970 +0000
1043 1043 summary: add foo, related
1044 1044
1045 1045
1046 1046 Also check when maxrev < lastrevfilelog
1047 1047
1048 1048 $ hg --traceback log -f -r4 foo
1049 1049 changeset: 4:88176d361b69
1050 1050 user: test
1051 1051 date: Thu Jan 01 00:00:00 1970 +0000
1052 1052 summary: add foo, related
1053 1053
1054 1054 $ cd ..
1055 1055
1056 1056 Issue2383: hg log showing _less_ differences than hg diff
1057 1057
1058 1058 $ hg init issue2383
1059 1059 $ cd issue2383
1060 1060
1061 1061 Create a test repo:
1062 1062
1063 1063 $ echo a > a
1064 1064 $ hg ci -Am0
1065 1065 adding a
1066 1066 $ echo b > b
1067 1067 $ hg ci -Am1
1068 1068 adding b
1069 1069 $ hg co 0
1070 1070 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1071 1071 $ echo b > a
1072 1072 $ hg ci -m2
1073 1073 created new head
1074 1074
1075 1075 Merge:
1076 1076
1077 1077 $ hg merge
1078 1078 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1079 1079 (branch merge, don't forget to commit)
1080 1080
1081 1081 Make sure there's a file listed in the merge to trigger the bug:
1082 1082
1083 1083 $ echo c > a
1084 1084 $ hg ci -m3
1085 1085
1086 1086 Two files shown here in diff:
1087 1087
1088 1088 $ hg diff --rev 2:3
1089 1089 diff -r b09be438c43a -r 8e07aafe1edc a
1090 1090 --- a/a Thu Jan 01 00:00:00 1970 +0000
1091 1091 +++ b/a Thu Jan 01 00:00:00 1970 +0000
1092 1092 @@ -1,1 +1,1 @@
1093 1093 -b
1094 1094 +c
1095 1095 diff -r b09be438c43a -r 8e07aafe1edc b
1096 1096 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1097 1097 +++ b/b Thu Jan 01 00:00:00 1970 +0000
1098 1098 @@ -0,0 +1,1 @@
1099 1099 +b
1100 1100
1101 1101 Diff here should be the same:
1102 1102
1103 1103 $ hg log -vpr 3
1104 1104 changeset: 3:8e07aafe1edc
1105 1105 tag: tip
1106 1106 parent: 2:b09be438c43a
1107 1107 parent: 1:925d80f479bb
1108 1108 user: test
1109 1109 date: Thu Jan 01 00:00:00 1970 +0000
1110 1110 files: a
1111 1111 description:
1112 1112 3
1113 1113
1114 1114
1115 1115 diff -r b09be438c43a -r 8e07aafe1edc a
1116 1116 --- a/a Thu Jan 01 00:00:00 1970 +0000
1117 1117 +++ b/a Thu Jan 01 00:00:00 1970 +0000
1118 1118 @@ -1,1 +1,1 @@
1119 1119 -b
1120 1120 +c
1121 1121 diff -r b09be438c43a -r 8e07aafe1edc b
1122 1122 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1123 1123 +++ b/b Thu Jan 01 00:00:00 1970 +0000
1124 1124 @@ -0,0 +1,1 @@
1125 1125 +b
1126 1126
1127 1127 $ cd ..
1128 1128
1129 1129 'hg log -r rev fn' when last(filelog(fn)) != rev
1130 1130
1131 1131 $ hg init simplelog
1132 1132 $ cd simplelog
1133 1133 $ echo f > a
1134 1134 $ hg ci -Am'a' -d '0 0'
1135 1135 adding a
1136 1136 $ echo f >> a
1137 1137 $ hg ci -Am'a bis' -d '1 0'
1138 1138
1139 1139 $ hg log -r0 a
1140 1140 changeset: 0:9f758d63dcde
1141 1141 user: test
1142 1142 date: Thu Jan 01 00:00:00 1970 +0000
1143 1143 summary: a
1144 1144
1145 1145 enable obsolete to test hidden feature
1146 1146
1147 1147 $ cat > ${TESTTMP}/obs.py << EOF
1148 1148 > import mercurial.obsolete
1149 1149 > mercurial.obsolete._enabled = True
1150 1150 > EOF
1151 1151 $ echo '[extensions]' >> $HGRCPATH
1152 1152 $ echo "obs=${TESTTMP}/obs.py" >> $HGRCPATH
1153 1153
1154 1154 $ hg log --template='{rev}:{node}\n'
1155 1155 1:a765632148dc55d38c35c4f247c618701886cb2f
1156 1156 0:9f758d63dcde62d547ebfb08e1e7ee96535f2b05
1157 1157 $ hg debugobsolete a765632148dc55d38c35c4f247c618701886cb2f
1158 1158 $ hg up null -q
1159 1159 $ hg log --template='{rev}:{node}\n'
1160 1160 0:9f758d63dcde62d547ebfb08e1e7ee96535f2b05
1161 1161 $ hg log --template='{rev}:{node}\n' --hidden
1162 1162 1:a765632148dc55d38c35c4f247c618701886cb2f
1163 1163 0:9f758d63dcde62d547ebfb08e1e7ee96535f2b05
1164 1164
1165 1165 test that parent prevent a changeset to be hidden
1166 1166
1167 $ hg up 1 -q
1167 $ hg up 1 -q --hidden
1168 1168 $ hg log --template='{rev}:{node}\n'
1169 1169 1:a765632148dc55d38c35c4f247c618701886cb2f
1170 1170 0:9f758d63dcde62d547ebfb08e1e7ee96535f2b05
1171 1171
1172 1172 test that second parent prevent a changeset to be hidden too
1173 1173
1174 1174 $ hg debugsetparents 0 1 # nothing suitable to merge here
1175 1175 $ hg log --template='{rev}:{node}\n'
1176 1176 1:a765632148dc55d38c35c4f247c618701886cb2f
1177 1177 0:9f758d63dcde62d547ebfb08e1e7ee96535f2b05
1178 1178
1179 1179 clear extensions configuration
1180 1180 $ echo '[extensions]' >> $HGRCPATH
1181 1181 $ echo "obs=!" >> $HGRCPATH
1182 1182 $ cd ..
1183 1183
1184 1184 test -u/-k for problematic encoding
1185 1185 # unicode: cp932:
1186 1186 # u30A2 0x83 0x41(= 'A')
1187 1187 # u30C2 0x83 0x61(= 'a')
1188 1188
1189 1189 $ hg init problematicencoding
1190 1190 $ cd problematicencoding
1191 1191
1192 1192 $ python > setup.sh <<EOF
1193 1193 > print u'''
1194 1194 > echo a > text
1195 1195 > hg add text
1196 1196 > hg --encoding utf-8 commit -u '\u30A2' -m none
1197 1197 > echo b > text
1198 1198 > hg --encoding utf-8 commit -u '\u30C2' -m none
1199 1199 > echo c > text
1200 1200 > hg --encoding utf-8 commit -u none -m '\u30A2'
1201 1201 > echo d > text
1202 1202 > hg --encoding utf-8 commit -u none -m '\u30C2'
1203 1203 > '''.encode('utf-8')
1204 1204 > EOF
1205 1205 $ sh < setup.sh
1206 1206
1207 1207 test in problematic encoding
1208 1208 $ python > test.sh <<EOF
1209 1209 > print u'''
1210 1210 > hg --encoding cp932 log --template '{rev}\\n' -u '\u30A2'
1211 1211 > echo ====
1212 1212 > hg --encoding cp932 log --template '{rev}\\n' -u '\u30C2'
1213 1213 > echo ====
1214 1214 > hg --encoding cp932 log --template '{rev}\\n' -k '\u30A2'
1215 1215 > echo ====
1216 1216 > hg --encoding cp932 log --template '{rev}\\n' -k '\u30C2'
1217 1217 > '''.encode('cp932')
1218 1218 > EOF
1219 1219 $ sh < test.sh
1220 1220 0
1221 1221 ====
1222 1222 1
1223 1223 ====
1224 1224 2
1225 1225 0
1226 1226 ====
1227 1227 3
1228 1228 1
1229 1229
1230 1230 $ cd ..
1231 1231
1232 1232 test hg log on non-existent files and on directories
1233 1233 $ hg init issue1340
1234 1234 $ cd issue1340
1235 1235 $ mkdir d1; mkdir D2; mkdir D3.i; mkdir d4.hg; mkdir d5.d; mkdir .d6
1236 1236 $ echo 1 > d1/f1
1237 1237 $ echo 1 > D2/f1
1238 1238 $ echo 1 > D3.i/f1
1239 1239 $ echo 1 > d4.hg/f1
1240 1240 $ echo 1 > d5.d/f1
1241 1241 $ echo 1 > .d6/f1
1242 1242 $ hg -q add .
1243 1243 $ hg commit -m "a bunch of weird directories"
1244 1244 $ hg log -l1 d1/f1 | grep changeset
1245 1245 changeset: 0:65624cd9070a
1246 1246 $ hg log -l1 f1
1247 1247 $ hg log -l1 . | grep changeset
1248 1248 changeset: 0:65624cd9070a
1249 1249 $ hg log -l1 ./ | grep changeset
1250 1250 changeset: 0:65624cd9070a
1251 1251 $ hg log -l1 d1 | grep changeset
1252 1252 changeset: 0:65624cd9070a
1253 1253 $ hg log -l1 D2 | grep changeset
1254 1254 changeset: 0:65624cd9070a
1255 1255 $ hg log -l1 D2/f1 | grep changeset
1256 1256 changeset: 0:65624cd9070a
1257 1257 $ hg log -l1 D3.i | grep changeset
1258 1258 changeset: 0:65624cd9070a
1259 1259 $ hg log -l1 D3.i/f1 | grep changeset
1260 1260 changeset: 0:65624cd9070a
1261 1261 $ hg log -l1 d4.hg | grep changeset
1262 1262 changeset: 0:65624cd9070a
1263 1263 $ hg log -l1 d4.hg/f1 | grep changeset
1264 1264 changeset: 0:65624cd9070a
1265 1265 $ hg log -l1 d5.d | grep changeset
1266 1266 changeset: 0:65624cd9070a
1267 1267 $ hg log -l1 d5.d/f1 | grep changeset
1268 1268 changeset: 0:65624cd9070a
1269 1269 $ hg log -l1 .d6 | grep changeset
1270 1270 changeset: 0:65624cd9070a
1271 1271 $ hg log -l1 .d6/f1 | grep changeset
1272 1272 changeset: 0:65624cd9070a
1273 1273 $ cd ..
@@ -1,345 +1,344 b''
1 1 $ branchcache=.hg/cache/branchheads
2 2
3 3 $ listbranchcaches() {
4 4 > for f in .hg/cache/branchheads*;
5 5 > do echo === $f ===;
6 6 > cat $f;
7 7 > done;
8 8 > }
9 9 $ purgebranchcaches() {
10 10 > rm .hg/cache/branchheads*
11 11 > }
12 12
13 13 $ hg init t
14 14 $ cd t
15 15
16 16 $ hg branches
17 17 $ echo foo > a
18 18 $ hg add a
19 19 $ hg ci -m "initial"
20 20 $ hg branch foo
21 21 marked working directory as branch foo
22 22 (branches are permanent and global, did you want a bookmark?)
23 23 $ hg branch
24 24 foo
25 25 $ hg ci -m "add branch name"
26 26 $ hg branch bar
27 27 marked working directory as branch bar
28 28 (branches are permanent and global, did you want a bookmark?)
29 29 $ hg ci -m "change branch name"
30 30
31 31 Branch shadowing:
32 32
33 33 $ hg branch default
34 34 abort: a branch of the same name already exists
35 35 (use 'hg update' to switch to it)
36 36 [255]
37 37
38 38 $ hg branch -f default
39 39 marked working directory as branch default
40 40 (branches are permanent and global, did you want a bookmark?)
41 41
42 42 $ hg ci -m "clear branch name"
43 43 created new head
44 44
45 45 There should be only one default branch head
46 46
47 47 $ hg heads .
48 48 changeset: 3:1c28f494dae6
49 49 tag: tip
50 50 user: test
51 51 date: Thu Jan 01 00:00:00 1970 +0000
52 52 summary: clear branch name
53 53
54 54
55 55 $ hg co foo
56 56 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
57 57 $ hg branch
58 58 foo
59 59 $ echo bleah > a
60 60 $ hg ci -m "modify a branch"
61 61
62 62 $ hg merge default
63 63 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
64 64 (branch merge, don't forget to commit)
65 65
66 66 $ hg branch
67 67 foo
68 68 $ hg ci -m "merge"
69 69
70 70 $ hg log
71 71 changeset: 5:530046499edf
72 72 branch: foo
73 73 tag: tip
74 74 parent: 4:adf1a74a7f7b
75 75 parent: 3:1c28f494dae6
76 76 user: test
77 77 date: Thu Jan 01 00:00:00 1970 +0000
78 78 summary: merge
79 79
80 80 changeset: 4:adf1a74a7f7b
81 81 branch: foo
82 82 parent: 1:6c0e42da283a
83 83 user: test
84 84 date: Thu Jan 01 00:00:00 1970 +0000
85 85 summary: modify a branch
86 86
87 87 changeset: 3:1c28f494dae6
88 88 user: test
89 89 date: Thu Jan 01 00:00:00 1970 +0000
90 90 summary: clear branch name
91 91
92 92 changeset: 2:c21617b13b22
93 93 branch: bar
94 94 user: test
95 95 date: Thu Jan 01 00:00:00 1970 +0000
96 96 summary: change branch name
97 97
98 98 changeset: 1:6c0e42da283a
99 99 branch: foo
100 100 user: test
101 101 date: Thu Jan 01 00:00:00 1970 +0000
102 102 summary: add branch name
103 103
104 104 changeset: 0:db01e8ea3388
105 105 user: test
106 106 date: Thu Jan 01 00:00:00 1970 +0000
107 107 summary: initial
108 108
109 109 $ hg branches
110 110 foo 5:530046499edf
111 111 default 3:1c28f494dae6 (inactive)
112 112 bar 2:c21617b13b22 (inactive)
113 113
114 114 $ hg branches -q
115 115 foo
116 116 default
117 117 bar
118 118
119 119 Test for invalid branch cache:
120 120
121 121 $ hg rollback
122 122 repository tip rolled back to revision 4 (undo commit)
123 123 working directory now based on revisions 4 and 3
124 124
125 125 $ cp ${branchcache}-unserved .hg/bc-invalid
126 126
127 127 $ hg log -r foo
128 128 changeset: 4:adf1a74a7f7b
129 129 branch: foo
130 130 tag: tip
131 131 parent: 1:6c0e42da283a
132 132 user: test
133 133 date: Thu Jan 01 00:00:00 1970 +0000
134 134 summary: modify a branch
135 135
136 136 $ cp .hg/bc-invalid $branchcache
137 137
138 138 $ hg --debug log -r foo
139 invalid branchheads cache: tip differs
140 139 changeset: 4:adf1a74a7f7b4cd193d12992f5d0d6a004ed21d6
141 140 branch: foo
142 141 tag: tip
143 142 phase: draft
144 143 parent: 1:6c0e42da283a56b5edc5b4fadb491365ec7f5fa8
145 144 parent: -1:0000000000000000000000000000000000000000
146 145 manifest: 1:8c342a37dfba0b3d3ce073562a00d8a813c54ffe
147 146 user: test
148 147 date: Thu Jan 01 00:00:00 1970 +0000
149 148 files: a
150 149 extra: branch=foo
151 150 description:
152 151 modify a branch
153 152
154 153
155 154 $ purgebranchcaches
156 155 $ echo corrupted > $branchcache
157 156
158 157 $ hg log -qr foo
159 158 4:adf1a74a7f7b
160 159
161 160 $ listbranchcaches
162 161 === .hg/cache/branchheads ===
163 162 corrupted
164 163 === .hg/cache/branchheads-unserved ===
165 164 adf1a74a7f7b4cd193d12992f5d0d6a004ed21d6 4
166 165 1c28f494dae69a2f8fc815059d257eccf3fcfe75 default
167 166 adf1a74a7f7b4cd193d12992f5d0d6a004ed21d6 foo
168 167 c21617b13b220988e7a2e26290fbe4325ffa7139 bar
169 168
170 169 Push should update the branch cache:
171 170
172 171 $ hg init ../target
173 172
174 173 Pushing just rev 0:
175 174
176 175 $ hg push -qr 0 ../target
177 176
178 177 $ (cd ../target/; listbranchcaches)
179 178 === .hg/cache/branchheads-impactable ===
180 179 db01e8ea3388fd3c7c94e1436ea2bd6a53d581c5 0
181 180 db01e8ea3388fd3c7c94e1436ea2bd6a53d581c5 default
182 181
183 182 Pushing everything:
184 183
185 184 $ hg push -qf ../target
186 185
187 186 $ (cd ../target/; listbranchcaches)
188 187 === .hg/cache/branchheads-impactable ===
189 188 adf1a74a7f7b4cd193d12992f5d0d6a004ed21d6 4
190 189 1c28f494dae69a2f8fc815059d257eccf3fcfe75 default
191 190 adf1a74a7f7b4cd193d12992f5d0d6a004ed21d6 foo
192 191 c21617b13b220988e7a2e26290fbe4325ffa7139 bar
193 192
194 193 Update with no arguments: tipmost revision of the current branch:
195 194
196 195 $ hg up -q -C 0
197 196 $ hg up -q
198 197 $ hg id
199 198 1c28f494dae6
200 199
201 200 $ hg up -q 1
202 201 $ hg up -q
203 202 $ hg id
204 203 adf1a74a7f7b (foo) tip
205 204
206 205 $ hg branch foobar
207 206 marked working directory as branch foobar
208 207 (branches are permanent and global, did you want a bookmark?)
209 208
210 209 $ hg up
211 210 abort: branch foobar not found
212 211 [255]
213 212
214 213 Fastforward merge:
215 214
216 215 $ hg branch ff
217 216 marked working directory as branch ff
218 217 (branches are permanent and global, did you want a bookmark?)
219 218
220 219 $ echo ff > ff
221 220 $ hg ci -Am'fast forward'
222 221 adding ff
223 222
224 223 $ hg up foo
225 224 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
226 225
227 226 $ hg merge ff
228 227 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
229 228 (branch merge, don't forget to commit)
230 229
231 230 $ hg branch
232 231 foo
233 232 $ hg commit -m'Merge ff into foo'
234 233 $ hg parents
235 234 changeset: 6:185ffbfefa30
236 235 branch: foo
237 236 tag: tip
238 237 parent: 4:adf1a74a7f7b
239 238 parent: 5:1a3c27dc5e11
240 239 user: test
241 240 date: Thu Jan 01 00:00:00 1970 +0000
242 241 summary: Merge ff into foo
243 242
244 243 $ hg manifest
245 244 a
246 245 ff
247 246
248 247
249 248 Test merging, add 3 default heads and one test head:
250 249
251 250 $ cd ..
252 251 $ hg init merges
253 252 $ cd merges
254 253 $ echo a > a
255 254 $ hg ci -Ama
256 255 adding a
257 256
258 257 $ echo b > b
259 258 $ hg ci -Amb
260 259 adding b
261 260
262 261 $ hg up 0
263 262 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
264 263 $ echo c > c
265 264 $ hg ci -Amc
266 265 adding c
267 266 created new head
268 267
269 268 $ hg up 0
270 269 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
271 270 $ echo d > d
272 271 $ hg ci -Amd
273 272 adding d
274 273 created new head
275 274
276 275 $ hg up 0
277 276 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
278 277 $ hg branch test
279 278 marked working directory as branch test
280 279 (branches are permanent and global, did you want a bookmark?)
281 280 $ echo e >> e
282 281 $ hg ci -Ame
283 282 adding e
284 283
285 284 $ hg log
286 285 changeset: 4:3a1e01ed1df4
287 286 branch: test
288 287 tag: tip
289 288 parent: 0:cb9a9f314b8b
290 289 user: test
291 290 date: Thu Jan 01 00:00:00 1970 +0000
292 291 summary: e
293 292
294 293 changeset: 3:980f7dc84c29
295 294 parent: 0:cb9a9f314b8b
296 295 user: test
297 296 date: Thu Jan 01 00:00:00 1970 +0000
298 297 summary: d
299 298
300 299 changeset: 2:d36c0562f908
301 300 parent: 0:cb9a9f314b8b
302 301 user: test
303 302 date: Thu Jan 01 00:00:00 1970 +0000
304 303 summary: c
305 304
306 305 changeset: 1:d2ae7f538514
307 306 user: test
308 307 date: Thu Jan 01 00:00:00 1970 +0000
309 308 summary: b
310 309
311 310 changeset: 0:cb9a9f314b8b
312 311 user: test
313 312 date: Thu Jan 01 00:00:00 1970 +0000
314 313 summary: a
315 314
316 315 Implicit merge with test branch as parent:
317 316
318 317 $ hg merge
319 318 abort: branch 'test' has one head - please merge with an explicit rev
320 319 (run 'hg heads' to see all heads)
321 320 [255]
322 321 $ hg up -C default
323 322 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
324 323
325 324 Implicit merge with default branch as parent:
326 325
327 326 $ hg merge
328 327 abort: branch 'default' has 3 heads - please merge with an explicit rev
329 328 (run 'hg heads .' to see heads)
330 329 [255]
331 330
332 331 3 branch heads, explicit merge required:
333 332
334 333 $ hg merge 2
335 334 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
336 335 (branch merge, don't forget to commit)
337 336 $ hg ci -m merge
338 337
339 338 2 branch heads, implicit merge works:
340 339
341 340 $ hg merge
342 341 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
343 342 (branch merge, don't forget to commit)
344 343
345 344 $ cd ..
@@ -1,447 +1,447 b''
1 1 Test file dedicated to testing the divergent troubles from obsolete changeset.
2 2
3 3 This is the most complexe troubles from far so we isolate it in a dedicated
4 4 file.
5 5
6 6 Enable obsolete
7 7
8 8 $ cat > obs.py << EOF
9 9 > import mercurial.obsolete
10 10 > mercurial.obsolete._enabled = True
11 11 > EOF
12 12 $ cat >> $HGRCPATH << EOF
13 13 > [ui]
14 14 > logtemplate = {rev}:{node|short} {desc}\n
15 15 > [extensions]
16 16 > obs=${TESTTMP}/obs.py
17 17 > [alias]
18 18 > debugobsolete = debugobsolete -d '0 0'
19 19 > [phases]
20 20 > publish=False
21 21 > EOF
22 22
23 23
24 24 $ mkcommit() {
25 25 > echo "$1" > "$1"
26 26 > hg add "$1"
27 27 > hg ci -m "$1"
28 28 > }
29 29 $ getid() {
30 > hg id --debug -ir "desc('$1')"
30 > hg id --debug --hidden -ir "desc('$1')"
31 31 > }
32 32
33 33 setup repo
34 34
35 35 $ hg init reference
36 36 $ cd reference
37 37 $ mkcommit base
38 38 $ mkcommit A_0
39 39 $ hg up 0
40 40 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
41 41 $ mkcommit A_1
42 42 created new head
43 43 $ hg up 0
44 44 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
45 45 $ mkcommit A_2
46 46 created new head
47 47 $ hg up 0
48 48 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
49 49 $ cd ..
50 50
51 51
52 52 $ newcase() {
53 53 > hg clone -u 0 -q reference $1
54 54 > cd $1
55 55 > }
56 56
57 57 direct divergence
58 58 -----------------
59 59
60 60 A_1 have two direct and divergent successors A_1 and A_1
61 61
62 62 $ newcase direct
63 63 $ hg debugobsolete `getid A_0` `getid A_1`
64 64 $ hg debugobsolete `getid A_0` `getid A_2`
65 65 invalid branchheads cache (unserved): tip differs
66 66 $ hg log -G --hidden
67 67 o 3:392fd25390da A_2
68 68 |
69 69 | o 2:82623d38b9ba A_1
70 70 |/
71 71 | x 1:007dc284c1f8 A_0
72 72 |/
73 73 @ 0:d20a80d4def3 base
74 74
75 $ hg debugsuccessorssets 'all()'
75 $ hg debugsuccessorssets --hidden 'all()'
76 76 d20a80d4def3
77 77 d20a80d4def3
78 78 007dc284c1f8
79 79 392fd25390da
80 80 82623d38b9ba
81 81 82623d38b9ba
82 82 82623d38b9ba
83 83 392fd25390da
84 84 392fd25390da
85 85 $ hg log -r 'divergent()'
86 86 2:82623d38b9ba A_1
87 87 3:392fd25390da A_2
88 88
89 89 check that mercurial refuse to push
90 90
91 91 $ hg init ../other
92 92 $ hg push ../other
93 93 pushing to ../other
94 94 searching for changes
95 95 abort: push includes divergent changeset: 392fd25390da!
96 96 [255]
97 97
98 98 $ cd ..
99 99
100 100
101 101 indirect divergence with known changeset
102 102 -------------------------------------------
103 103
104 104 $ newcase indirect_known
105 105 $ hg debugobsolete `getid A_0` `getid A_1`
106 106 $ hg debugobsolete `getid A_0` `getid A_2`
107 107 invalid branchheads cache (unserved): tip differs
108 108 $ mkcommit A_3
109 109 created new head
110 110 $ hg debugobsolete `getid A_2` `getid A_3`
111 111 $ hg log -G --hidden
112 112 @ 4:01f36c5a8fda A_3
113 113 |
114 114 | x 3:392fd25390da A_2
115 115 |/
116 116 | o 2:82623d38b9ba A_1
117 117 |/
118 118 | x 1:007dc284c1f8 A_0
119 119 |/
120 120 o 0:d20a80d4def3 base
121 121
122 $ hg debugsuccessorssets 'all()'
122 $ hg debugsuccessorssets --hidden 'all()'
123 123 d20a80d4def3
124 124 d20a80d4def3
125 125 007dc284c1f8
126 126 01f36c5a8fda
127 127 82623d38b9ba
128 128 82623d38b9ba
129 129 82623d38b9ba
130 130 392fd25390da
131 131 01f36c5a8fda
132 132 01f36c5a8fda
133 133 01f36c5a8fda
134 134 $ hg log -r 'divergent()'
135 135 2:82623d38b9ba A_1
136 136 4:01f36c5a8fda A_3
137 137 $ cd ..
138 138
139 139
140 140 indirect divergence with known changeset
141 141 -------------------------------------------
142 142
143 143 $ newcase indirect_unknown
144 144 $ hg debugobsolete `getid A_0` aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
145 145 $ hg debugobsolete aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa `getid A_1`
146 146 invalid branchheads cache (unserved): tip differs
147 147 $ hg debugobsolete `getid A_0` `getid A_2`
148 148 $ hg log -G --hidden
149 149 o 3:392fd25390da A_2
150 150 |
151 151 | o 2:82623d38b9ba A_1
152 152 |/
153 153 | x 1:007dc284c1f8 A_0
154 154 |/
155 155 @ 0:d20a80d4def3 base
156 156
157 $ hg debugsuccessorssets 'all()'
157 $ hg debugsuccessorssets --hidden 'all()'
158 158 d20a80d4def3
159 159 d20a80d4def3
160 160 007dc284c1f8
161 161 392fd25390da
162 162 82623d38b9ba
163 163 82623d38b9ba
164 164 82623d38b9ba
165 165 392fd25390da
166 166 392fd25390da
167 167 $ hg log -r 'divergent()'
168 168 2:82623d38b9ba A_1
169 169 3:392fd25390da A_2
170 170 $ cd ..
171 171
172 172 do not take unknown node in account if they are final
173 173 -----------------------------------------------------
174 174
175 175 $ newcase final-unknown
176 176 $ hg debugobsolete `getid A_0` `getid A_1`
177 177 $ hg debugobsolete `getid A_1` `getid A_2`
178 178 invalid branchheads cache (unserved): tip differs
179 179 $ hg debugobsolete `getid A_0` bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
180 180 $ hg debugobsolete bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb cccccccccccccccccccccccccccccccccccccccc
181 181 $ hg debugobsolete `getid A_1` dddddddddddddddddddddddddddddddddddddddd
182 182
183 $ hg debugsuccessorssets 'desc('A_0')'
183 $ hg debugsuccessorssets --hidden 'desc('A_0')'
184 184 007dc284c1f8
185 185 392fd25390da
186 186
187 187 $ cd ..
188 188
189 189 divergence that converge again is not divergence anymore
190 190 -----------------------------------------------------
191 191
192 192 $ newcase converged_divergence
193 193 $ hg debugobsolete `getid A_0` `getid A_1`
194 194 $ hg debugobsolete `getid A_0` `getid A_2`
195 195 invalid branchheads cache (unserved): tip differs
196 196 $ mkcommit A_3
197 197 created new head
198 198 $ hg debugobsolete `getid A_1` `getid A_3`
199 199 $ hg debugobsolete `getid A_2` `getid A_3`
200 200 $ hg log -G --hidden
201 201 @ 4:01f36c5a8fda A_3
202 202 |
203 203 | x 3:392fd25390da A_2
204 204 |/
205 205 | x 2:82623d38b9ba A_1
206 206 |/
207 207 | x 1:007dc284c1f8 A_0
208 208 |/
209 209 o 0:d20a80d4def3 base
210 210
211 $ hg debugsuccessorssets 'all()'
211 $ hg debugsuccessorssets --hidden 'all()'
212 212 d20a80d4def3
213 213 d20a80d4def3
214 214 007dc284c1f8
215 215 01f36c5a8fda
216 216 82623d38b9ba
217 217 01f36c5a8fda
218 218 392fd25390da
219 219 01f36c5a8fda
220 220 01f36c5a8fda
221 221 01f36c5a8fda
222 222 $ hg log -r 'divergent()'
223 223 $ cd ..
224 224
225 225 split is not divergences
226 226 -----------------------------
227 227
228 228 $ newcase split
229 229 $ hg debugobsolete `getid A_0` `getid A_1` `getid A_2`
230 230 $ hg log -G --hidden
231 231 o 3:392fd25390da A_2
232 232 |
233 233 | o 2:82623d38b9ba A_1
234 234 |/
235 235 | x 1:007dc284c1f8 A_0
236 236 |/
237 237 @ 0:d20a80d4def3 base
238 238
239 $ hg debugsuccessorssets 'all()'
239 $ hg debugsuccessorssets --hidden 'all()'
240 240 d20a80d4def3
241 241 d20a80d4def3
242 242 007dc284c1f8
243 243 82623d38b9ba 392fd25390da
244 244 82623d38b9ba
245 245 82623d38b9ba
246 246 392fd25390da
247 247 392fd25390da
248 248 $ hg log -r 'divergent()'
249 249
250 250 Even when subsequente rewriting happen
251 251
252 252 $ mkcommit A_3
253 253 created new head
254 254 $ hg debugobsolete `getid A_1` `getid A_3`
255 255 $ hg up 0
256 256 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
257 257 $ mkcommit A_4
258 258 created new head
259 259 $ hg debugobsolete `getid A_2` `getid A_4`
260 260 $ hg up 0
261 261 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
262 262 $ mkcommit A_5
263 263 created new head
264 264 $ hg debugobsolete `getid A_4` `getid A_5`
265 265 $ hg log -G --hidden
266 266 @ 6:e442cfc57690 A_5
267 267 |
268 268 | x 5:6a411f0d7a0a A_4
269 269 |/
270 270 | o 4:01f36c5a8fda A_3
271 271 |/
272 272 | x 3:392fd25390da A_2
273 273 |/
274 274 | x 2:82623d38b9ba A_1
275 275 |/
276 276 | x 1:007dc284c1f8 A_0
277 277 |/
278 278 o 0:d20a80d4def3 base
279 279
280 $ hg debugsuccessorssets 'all()'
280 $ hg debugsuccessorssets --hidden 'all()'
281 281 d20a80d4def3
282 282 d20a80d4def3
283 283 007dc284c1f8
284 284 01f36c5a8fda e442cfc57690
285 285 82623d38b9ba
286 286 01f36c5a8fda
287 287 392fd25390da
288 288 e442cfc57690
289 289 01f36c5a8fda
290 290 01f36c5a8fda
291 291 6a411f0d7a0a
292 292 e442cfc57690
293 293 e442cfc57690
294 294 e442cfc57690
295 295 $ hg log -r 'divergent()'
296 296
297 297 Check more complexe obsolescence graft (with divergence)
298 298
299 299 $ mkcommit B_0; hg up 0
300 300 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
301 301 $ hg debugobsolete `getid B_0` `getid A_2`
302 302 $ mkcommit A_7; hg up 0
303 303 created new head
304 304 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
305 305 $ mkcommit A_8; hg up 0
306 306 created new head
307 307 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
308 308 $ hg debugobsolete `getid A_5` `getid A_7` `getid A_8`
309 309 $ mkcommit A_9; hg up 0
310 310 created new head
311 311 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
312 312 $ hg debugobsolete `getid A_5` `getid A_9`
313 313 $ hg log -G --hidden
314 314 o 10:bed64f5d2f5a A_9
315 315 |
316 316 | o 9:14608b260df8 A_8
317 317 |/
318 318 | o 8:7ae126973a96 A_7
319 319 |/
320 320 | x 7:3750ebee865d B_0
321 321 | |
322 322 | x 6:e442cfc57690 A_5
323 323 |/
324 324 | x 5:6a411f0d7a0a A_4
325 325 |/
326 326 | o 4:01f36c5a8fda A_3
327 327 |/
328 328 | x 3:392fd25390da A_2
329 329 |/
330 330 | x 2:82623d38b9ba A_1
331 331 |/
332 332 | x 1:007dc284c1f8 A_0
333 333 |/
334 334 @ 0:d20a80d4def3 base
335 335
336 $ hg debugsuccessorssets 'all()'
336 $ hg debugsuccessorssets --hidden 'all()'
337 337 d20a80d4def3
338 338 d20a80d4def3
339 339 007dc284c1f8
340 340 01f36c5a8fda bed64f5d2f5a
341 341 01f36c5a8fda 7ae126973a96 14608b260df8
342 342 82623d38b9ba
343 343 01f36c5a8fda
344 344 392fd25390da
345 345 bed64f5d2f5a
346 346 7ae126973a96 14608b260df8
347 347 01f36c5a8fda
348 348 01f36c5a8fda
349 349 6a411f0d7a0a
350 350 bed64f5d2f5a
351 351 7ae126973a96 14608b260df8
352 352 e442cfc57690
353 353 bed64f5d2f5a
354 354 7ae126973a96 14608b260df8
355 355 3750ebee865d
356 356 bed64f5d2f5a
357 357 7ae126973a96 14608b260df8
358 358 7ae126973a96
359 359 7ae126973a96
360 360 14608b260df8
361 361 14608b260df8
362 362 bed64f5d2f5a
363 363 bed64f5d2f5a
364 364 $ hg log -r 'divergent()'
365 365 4:01f36c5a8fda A_3
366 366 8:7ae126973a96 A_7
367 367 9:14608b260df8 A_8
368 368 10:bed64f5d2f5a A_9
369 369
370 370 fix the divergence
371 371
372 372 $ mkcommit A_A; hg up 0
373 373 created new head
374 374 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
375 375 $ hg debugobsolete `getid A_9` `getid A_A`
376 376 $ hg debugobsolete `getid A_7` `getid A_A`
377 377 $ hg debugobsolete `getid A_8` `getid A_A`
378 378 $ hg log -G --hidden
379 379 o 11:a139f71be9da A_A
380 380 |
381 381 | x 10:bed64f5d2f5a A_9
382 382 |/
383 383 | x 9:14608b260df8 A_8
384 384 |/
385 385 | x 8:7ae126973a96 A_7
386 386 |/
387 387 | x 7:3750ebee865d B_0
388 388 | |
389 389 | x 6:e442cfc57690 A_5
390 390 |/
391 391 | x 5:6a411f0d7a0a A_4
392 392 |/
393 393 | o 4:01f36c5a8fda A_3
394 394 |/
395 395 | x 3:392fd25390da A_2
396 396 |/
397 397 | x 2:82623d38b9ba A_1
398 398 |/
399 399 | x 1:007dc284c1f8 A_0
400 400 |/
401 401 @ 0:d20a80d4def3 base
402 402
403 $ hg debugsuccessorssets 'all()'
403 $ hg debugsuccessorssets --hidden 'all()'
404 404 d20a80d4def3
405 405 d20a80d4def3
406 406 007dc284c1f8
407 407 01f36c5a8fda a139f71be9da
408 408 82623d38b9ba
409 409 01f36c5a8fda
410 410 392fd25390da
411 411 a139f71be9da
412 412 01f36c5a8fda
413 413 01f36c5a8fda
414 414 6a411f0d7a0a
415 415 a139f71be9da
416 416 e442cfc57690
417 417 a139f71be9da
418 418 3750ebee865d
419 419 a139f71be9da
420 420 7ae126973a96
421 421 a139f71be9da
422 422 14608b260df8
423 423 a139f71be9da
424 424 bed64f5d2f5a
425 425 a139f71be9da
426 426 a139f71be9da
427 427 a139f71be9da
428 428 $ hg log -r 'divergent()'
429 429
430 430 $ cd ..
431 431
432 432
433 433 Subset does not diverge
434 434 ------------------------------
435 435
436 436 Do not report divergent successors-set if it is a subset of another
437 437 successors-set. (report [A,B] not [A] + [A,B])
438 438
439 439 $ newcase subset
440 440 $ hg debugobsolete `getid A_0` `getid A_2`
441 441 $ hg debugobsolete `getid A_0` `getid A_1` `getid A_2`
442 442 invalid branchheads cache (unserved): tip differs
443 $ hg debugsuccessorssets 'desc('A_0')'
443 $ hg debugsuccessorssets --hidden 'desc('A_0')'
444 444 007dc284c1f8
445 445 82623d38b9ba 392fd25390da
446 446
447 447 $ cd ..
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
General Comments 0
You need to be logged in to leave comments. Login now