##// END OF EJS Templates
Don't abort when backup files already exist. Don't allow alternative names....
Thomas Arendsen Hein -
r2036:c9226bcc default
parent child Browse files
Show More
@@ -1,3418 +1,3402 b''
1 1 # commands.py - command processing for mercurial
2 2 #
3 3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms
6 6 # of the GNU General Public License, incorporated herein by reference.
7 7
8 8 from demandload import demandload
9 9 from node import *
10 10 from i18n import gettext as _
11 11 demandload(globals(), "os re sys signal shutil imp urllib pdb")
12 12 demandload(globals(), "fancyopts ui hg util lock revlog templater bundlerepo")
13 13 demandload(globals(), "fnmatch hgweb mdiff random signal tempfile time")
14 14 demandload(globals(), "traceback errno socket version struct atexit sets bz2")
15 15 demandload(globals(), "changegroup")
16 16
17 17 class UnknownCommand(Exception):
18 18 """Exception raised if command is not in the command table."""
19 19 class AmbiguousCommand(Exception):
20 20 """Exception raised if command shortcut matches more than one command."""
21 21
22 22 def filterfiles(filters, files):
23 23 l = [x for x in files if x in filters]
24 24
25 25 for t in filters:
26 26 if t and t[-1] != "/":
27 27 t += "/"
28 28 l += [x for x in files if x.startswith(t)]
29 29 return l
30 30
31 31 def relpath(repo, args):
32 32 cwd = repo.getcwd()
33 33 if cwd:
34 34 return [util.normpath(os.path.join(cwd, x)) for x in args]
35 35 return args
36 36
37 37 def matchpats(repo, pats=[], opts={}, head=''):
38 38 cwd = repo.getcwd()
39 39 if not pats and cwd:
40 40 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
41 41 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
42 42 cwd = ''
43 43 return util.cmdmatcher(repo.root, cwd, pats or ['.'], opts.get('include'),
44 44 opts.get('exclude'), head)
45 45
46 46 def makewalk(repo, pats, opts, node=None, head='', badmatch=None):
47 47 files, matchfn, anypats = matchpats(repo, pats, opts, head)
48 48 exact = dict(zip(files, files))
49 49 def walk():
50 50 for src, fn in repo.walk(node=node, files=files, match=matchfn,
51 51 badmatch=None):
52 52 yield src, fn, util.pathto(repo.getcwd(), fn), fn in exact
53 53 return files, matchfn, walk()
54 54
55 55 def walk(repo, pats, opts, node=None, head='', badmatch=None):
56 56 files, matchfn, results = makewalk(repo, pats, opts, node, head, badmatch)
57 57 for r in results:
58 58 yield r
59 59
60 60 def walkchangerevs(ui, repo, pats, opts):
61 61 '''Iterate over files and the revs they changed in.
62 62
63 63 Callers most commonly need to iterate backwards over the history
64 64 it is interested in. Doing so has awful (quadratic-looking)
65 65 performance, so we use iterators in a "windowed" way.
66 66
67 67 We walk a window of revisions in the desired order. Within the
68 68 window, we first walk forwards to gather data, then in the desired
69 69 order (usually backwards) to display it.
70 70
71 71 This function returns an (iterator, getchange, matchfn) tuple. The
72 72 getchange function returns the changelog entry for a numeric
73 73 revision. The iterator yields 3-tuples. They will be of one of
74 74 the following forms:
75 75
76 76 "window", incrementing, lastrev: stepping through a window,
77 77 positive if walking forwards through revs, last rev in the
78 78 sequence iterated over - use to reset state for the current window
79 79
80 80 "add", rev, fns: out-of-order traversal of the given file names
81 81 fns, which changed during revision rev - use to gather data for
82 82 possible display
83 83
84 84 "iter", rev, None: in-order traversal of the revs earlier iterated
85 85 over with "add" - use to display data'''
86 86
87 87 def increasing_windows(start, end, windowsize=8, sizelimit=512):
88 88 if start < end:
89 89 while start < end:
90 90 yield start, min(windowsize, end-start)
91 91 start += windowsize
92 92 if windowsize < sizelimit:
93 93 windowsize *= 2
94 94 else:
95 95 while start > end:
96 96 yield start, min(windowsize, start-end-1)
97 97 start -= windowsize
98 98 if windowsize < sizelimit:
99 99 windowsize *= 2
100 100
101 101
102 102 files, matchfn, anypats = matchpats(repo, pats, opts)
103 103
104 104 if repo.changelog.count() == 0:
105 105 return [], False, matchfn
106 106
107 107 revs = map(int, revrange(ui, repo, opts['rev'] or ['tip:0']))
108 108 wanted = {}
109 109 slowpath = anypats
110 110 fncache = {}
111 111
112 112 chcache = {}
113 113 def getchange(rev):
114 114 ch = chcache.get(rev)
115 115 if ch is None:
116 116 chcache[rev] = ch = repo.changelog.read(repo.lookup(str(rev)))
117 117 return ch
118 118
119 119 if not slowpath and not files:
120 120 # No files, no patterns. Display all revs.
121 121 wanted = dict(zip(revs, revs))
122 122 if not slowpath:
123 123 # Only files, no patterns. Check the history of each file.
124 124 def filerevgen(filelog):
125 125 for i, window in increasing_windows(filelog.count()-1, -1):
126 126 revs = []
127 127 for j in xrange(i - window, i + 1):
128 128 revs.append(filelog.linkrev(filelog.node(j)))
129 129 revs.reverse()
130 130 for rev in revs:
131 131 yield rev
132 132
133 133 minrev, maxrev = min(revs), max(revs)
134 134 for file_ in files:
135 135 filelog = repo.file(file_)
136 136 # A zero count may be a directory or deleted file, so
137 137 # try to find matching entries on the slow path.
138 138 if filelog.count() == 0:
139 139 slowpath = True
140 140 break
141 141 for rev in filerevgen(filelog):
142 142 if rev <= maxrev:
143 143 if rev < minrev:
144 144 break
145 145 fncache.setdefault(rev, [])
146 146 fncache[rev].append(file_)
147 147 wanted[rev] = 1
148 148 if slowpath:
149 149 # The slow path checks files modified in every changeset.
150 150 def changerevgen():
151 151 for i, window in increasing_windows(repo.changelog.count()-1, -1):
152 152 for j in xrange(i - window, i + 1):
153 153 yield j, getchange(j)[3]
154 154
155 155 for rev, changefiles in changerevgen():
156 156 matches = filter(matchfn, changefiles)
157 157 if matches:
158 158 fncache[rev] = matches
159 159 wanted[rev] = 1
160 160
161 161 def iterate():
162 162 for i, window in increasing_windows(0, len(revs)):
163 163 yield 'window', revs[0] < revs[-1], revs[-1]
164 164 nrevs = [rev for rev in revs[i:i+window]
165 165 if rev in wanted]
166 166 srevs = list(nrevs)
167 167 srevs.sort()
168 168 for rev in srevs:
169 169 fns = fncache.get(rev) or filter(matchfn, getchange(rev)[3])
170 170 yield 'add', rev, fns
171 171 for rev in nrevs:
172 172 yield 'iter', rev, None
173 173 return iterate(), getchange, matchfn
174 174
175 175 revrangesep = ':'
176 176
177 177 def revrange(ui, repo, revs, revlog=None):
178 178 """Yield revision as strings from a list of revision specifications."""
179 179 if revlog is None:
180 180 revlog = repo.changelog
181 181 revcount = revlog.count()
182 182 def fix(val, defval):
183 183 if not val:
184 184 return defval
185 185 try:
186 186 num = int(val)
187 187 if str(num) != val:
188 188 raise ValueError
189 189 if num < 0:
190 190 num += revcount
191 191 if num < 0:
192 192 num = 0
193 193 elif num >= revcount:
194 194 raise ValueError
195 195 except ValueError:
196 196 try:
197 197 num = repo.changelog.rev(repo.lookup(val))
198 198 except KeyError:
199 199 try:
200 200 num = revlog.rev(revlog.lookup(val))
201 201 except KeyError:
202 202 raise util.Abort(_('invalid revision identifier %s'), val)
203 203 return num
204 204 seen = {}
205 205 for spec in revs:
206 206 if spec.find(revrangesep) >= 0:
207 207 start, end = spec.split(revrangesep, 1)
208 208 start = fix(start, 0)
209 209 end = fix(end, revcount - 1)
210 210 step = start > end and -1 or 1
211 211 for rev in xrange(start, end+step, step):
212 212 if rev in seen:
213 213 continue
214 214 seen[rev] = 1
215 215 yield str(rev)
216 216 else:
217 217 rev = fix(spec, None)
218 218 if rev in seen:
219 219 continue
220 220 seen[rev] = 1
221 221 yield str(rev)
222 222
223 223 def make_filename(repo, r, pat, node=None,
224 224 total=None, seqno=None, revwidth=None, pathname=None):
225 225 node_expander = {
226 226 'H': lambda: hex(node),
227 227 'R': lambda: str(r.rev(node)),
228 228 'h': lambda: short(node),
229 229 }
230 230 expander = {
231 231 '%': lambda: '%',
232 232 'b': lambda: os.path.basename(repo.root),
233 233 }
234 234
235 235 try:
236 236 if node:
237 237 expander.update(node_expander)
238 238 if node and revwidth is not None:
239 239 expander['r'] = lambda: str(r.rev(node)).zfill(revwidth)
240 240 if total is not None:
241 241 expander['N'] = lambda: str(total)
242 242 if seqno is not None:
243 243 expander['n'] = lambda: str(seqno)
244 244 if total is not None and seqno is not None:
245 245 expander['n'] = lambda:str(seqno).zfill(len(str(total)))
246 246 if pathname is not None:
247 247 expander['s'] = lambda: os.path.basename(pathname)
248 248 expander['d'] = lambda: os.path.dirname(pathname) or '.'
249 249 expander['p'] = lambda: pathname
250 250
251 251 newname = []
252 252 patlen = len(pat)
253 253 i = 0
254 254 while i < patlen:
255 255 c = pat[i]
256 256 if c == '%':
257 257 i += 1
258 258 c = pat[i]
259 259 c = expander[c]()
260 260 newname.append(c)
261 261 i += 1
262 262 return ''.join(newname)
263 263 except KeyError, inst:
264 264 raise util.Abort(_("invalid format spec '%%%s' in output file name"),
265 265 inst.args[0])
266 266
267 267 def make_file(repo, r, pat, node=None,
268 268 total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
269 269 if not pat or pat == '-':
270 270 return 'w' in mode and sys.stdout or sys.stdin
271 271 if hasattr(pat, 'write') and 'w' in mode:
272 272 return pat
273 273 if hasattr(pat, 'read') and 'r' in mode:
274 274 return pat
275 275 return open(make_filename(repo, r, pat, node, total, seqno, revwidth,
276 276 pathname),
277 277 mode)
278 278
279 279 def write_bundle(cg, filename=None, compress=True):
280 280 """Write a bundle file and return its filename.
281 281
282 282 Existing files will not be overwritten.
283 283 If no filename is specified, a temporary file is created.
284 284 bz2 compression can be turned off.
285 285 The bundle file will be deleted in case of errors.
286 286 """
287 287 class nocompress(object):
288 288 def compress(self, x):
289 289 return x
290 290 def flush(self):
291 291 return ""
292 292
293 293 fh = None
294 294 cleanup = None
295 295 try:
296 296 if filename:
297 297 if os.path.exists(filename):
298 298 raise util.Abort(_("file '%s' already exists"), filename)
299 299 fh = open(filename, "wb")
300 300 else:
301 301 fd, filename = tempfile.mkstemp(suffix=".hg", prefix="hg-bundle-")
302 302 fh = os.fdopen(fd, "wb")
303 303 cleanup = filename
304 304
305 305 if compress:
306 306 fh.write("HG10")
307 307 z = bz2.BZ2Compressor(9)
308 308 else:
309 309 fh.write("HG10UN")
310 310 z = nocompress()
311 311 # parse the changegroup data, otherwise we will block
312 312 # in case of sshrepo because we don't know the end of the stream
313 313
314 314 # an empty chunkiter is the end of the changegroup
315 315 empty = False
316 316 while not empty:
317 317 empty = True
318 318 for chunk in changegroup.chunkiter(cg):
319 319 empty = False
320 320 fh.write(z.compress(changegroup.genchunk(chunk)))
321 321 fh.write(z.compress(changegroup.closechunk()))
322 322 fh.write(z.flush())
323 323 cleanup = None
324 324 return filename
325 325 finally:
326 326 if fh is not None:
327 327 fh.close()
328 328 if cleanup is not None:
329 329 os.unlink(cleanup)
330 330
331 331 def dodiff(fp, ui, repo, node1, node2, files=None, match=util.always,
332 332 changes=None, text=False, opts={}):
333 333 if not node1:
334 334 node1 = repo.dirstate.parents()[0]
335 335 # reading the data for node1 early allows it to play nicely
336 336 # with repo.changes and the revlog cache.
337 337 change = repo.changelog.read(node1)
338 338 mmap = repo.manifest.read(change[0])
339 339 date1 = util.datestr(change[2])
340 340
341 341 if not changes:
342 342 changes = repo.changes(node1, node2, files, match=match)
343 343 modified, added, removed, deleted, unknown = changes
344 344 if files:
345 345 modified, added, removed = map(lambda x: filterfiles(files, x),
346 346 (modified, added, removed))
347 347
348 348 if not modified and not added and not removed:
349 349 return
350 350
351 351 if node2:
352 352 change = repo.changelog.read(node2)
353 353 mmap2 = repo.manifest.read(change[0])
354 354 date2 = util.datestr(change[2])
355 355 def read(f):
356 356 return repo.file(f).read(mmap2[f])
357 357 else:
358 358 date2 = util.datestr()
359 359 def read(f):
360 360 return repo.wread(f)
361 361
362 362 if ui.quiet:
363 363 r = None
364 364 else:
365 365 hexfunc = ui.verbose and hex or short
366 366 r = [hexfunc(node) for node in [node1, node2] if node]
367 367
368 368 diffopts = ui.diffopts()
369 369 showfunc = opts.get('show_function') or diffopts['showfunc']
370 370 ignorews = opts.get('ignore_all_space') or diffopts['ignorews']
371 371 for f in modified:
372 372 to = None
373 373 if f in mmap:
374 374 to = repo.file(f).read(mmap[f])
375 375 tn = read(f)
376 376 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text,
377 377 showfunc=showfunc, ignorews=ignorews))
378 378 for f in added:
379 379 to = None
380 380 tn = read(f)
381 381 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text,
382 382 showfunc=showfunc, ignorews=ignorews))
383 383 for f in removed:
384 384 to = repo.file(f).read(mmap[f])
385 385 tn = None
386 386 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text,
387 387 showfunc=showfunc, ignorews=ignorews))
388 388
389 389 def trimuser(ui, name, rev, revcache):
390 390 """trim the name of the user who committed a change"""
391 391 user = revcache.get(rev)
392 392 if user is None:
393 393 user = revcache[rev] = ui.shortuser(name)
394 394 return user
395 395
396 396 class changeset_templater(object):
397 397 '''use templater module to format changeset information.'''
398 398
399 399 def __init__(self, ui, repo, mapfile):
400 400 self.t = templater.templater(mapfile, templater.common_filters,
401 401 cache={'parent': '{rev}:{node|short} ',
402 402 'manifest': '{rev}:{node|short}'})
403 403 self.ui = ui
404 404 self.repo = repo
405 405
406 406 def use_template(self, t):
407 407 '''set template string to use'''
408 408 self.t.cache['changeset'] = t
409 409
410 410 def write(self, thing):
411 411 '''write expanded template.
412 412 uses in-order recursive traverse of iterators.'''
413 413 for t in thing:
414 414 if hasattr(t, '__iter__'):
415 415 self.write(t)
416 416 else:
417 417 self.ui.write(t)
418 418
419 419 def show(self, rev=0, changenode=None, brinfo=None):
420 420 '''show a single changeset or file revision'''
421 421 log = self.repo.changelog
422 422 if changenode is None:
423 423 changenode = log.node(rev)
424 424 elif not rev:
425 425 rev = log.rev(changenode)
426 426
427 427 changes = log.read(changenode)
428 428
429 429 def showlist(name, values, plural=None, **args):
430 430 '''expand set of values.
431 431 name is name of key in template map.
432 432 values is list of strings or dicts.
433 433 plural is plural of name, if not simply name + 's'.
434 434
435 435 expansion works like this, given name 'foo'.
436 436
437 437 if values is empty, expand 'no_foos'.
438 438
439 439 if 'foo' not in template map, return values as a string,
440 440 joined by space.
441 441
442 442 expand 'start_foos'.
443 443
444 444 for each value, expand 'foo'. if 'last_foo' in template
445 445 map, expand it instead of 'foo' for last key.
446 446
447 447 expand 'end_foos'.
448 448 '''
449 449 if plural: names = plural
450 450 else: names = name + 's'
451 451 if not values:
452 452 noname = 'no_' + names
453 453 if noname in self.t:
454 454 yield self.t(noname, **args)
455 455 return
456 456 if name not in self.t:
457 457 if isinstance(values[0], str):
458 458 yield ' '.join(values)
459 459 else:
460 460 for v in values:
461 461 yield dict(v, **args)
462 462 return
463 463 startname = 'start_' + names
464 464 if startname in self.t:
465 465 yield self.t(startname, **args)
466 466 vargs = args.copy()
467 467 def one(v, tag=name):
468 468 try:
469 469 vargs.update(v)
470 470 except (AttributeError, ValueError):
471 471 try:
472 472 for a, b in v:
473 473 vargs[a] = b
474 474 except ValueError:
475 475 vargs[name] = v
476 476 return self.t(tag, **vargs)
477 477 lastname = 'last_' + name
478 478 if lastname in self.t:
479 479 last = values.pop()
480 480 else:
481 481 last = None
482 482 for v in values:
483 483 yield one(v)
484 484 if last is not None:
485 485 yield one(last, tag=lastname)
486 486 endname = 'end_' + names
487 487 if endname in self.t:
488 488 yield self.t(endname, **args)
489 489
490 490 if brinfo:
491 491 def showbranches(**args):
492 492 if changenode in brinfo:
493 493 for x in showlist('branch', brinfo[changenode],
494 494 plural='branches', **args):
495 495 yield x
496 496 else:
497 497 showbranches = ''
498 498
499 499 if self.ui.debugflag:
500 500 def showmanifest(**args):
501 501 args = args.copy()
502 502 args.update(dict(rev=self.repo.manifest.rev(changes[0]),
503 503 node=hex(changes[0])))
504 504 yield self.t('manifest', **args)
505 505 else:
506 506 showmanifest = ''
507 507
508 508 def showparents(**args):
509 509 parents = [[('rev', log.rev(p)), ('node', hex(p))]
510 510 for p in log.parents(changenode)
511 511 if self.ui.debugflag or p != nullid]
512 512 if (not self.ui.debugflag and len(parents) == 1 and
513 513 parents[0][0][1] == rev - 1):
514 514 return
515 515 for x in showlist('parent', parents, **args):
516 516 yield x
517 517
518 518 def showtags(**args):
519 519 for x in showlist('tag', self.repo.nodetags(changenode), **args):
520 520 yield x
521 521
522 522 if self.ui.debugflag:
523 523 files = self.repo.changes(log.parents(changenode)[0], changenode)
524 524 def showfiles(**args):
525 525 for x in showlist('file', files[0], **args): yield x
526 526 def showadds(**args):
527 527 for x in showlist('file_add', files[1], **args): yield x
528 528 def showdels(**args):
529 529 for x in showlist('file_del', files[2], **args): yield x
530 530 else:
531 531 def showfiles(**args):
532 532 for x in showlist('file', changes[3], **args): yield x
533 533 showadds = ''
534 534 showdels = ''
535 535
536 536 props = {
537 537 'author': changes[1],
538 538 'branches': showbranches,
539 539 'date': changes[2],
540 540 'desc': changes[4],
541 541 'file_adds': showadds,
542 542 'file_dels': showdels,
543 543 'files': showfiles,
544 544 'manifest': showmanifest,
545 545 'node': hex(changenode),
546 546 'parents': showparents,
547 547 'rev': rev,
548 548 'tags': showtags,
549 549 }
550 550
551 551 try:
552 552 if self.ui.debugflag and 'changeset_debug' in self.t:
553 553 key = 'changeset_debug'
554 554 elif self.ui.quiet and 'changeset_quiet' in self.t:
555 555 key = 'changeset_quiet'
556 556 elif self.ui.verbose and 'changeset_verbose' in self.t:
557 557 key = 'changeset_verbose'
558 558 else:
559 559 key = 'changeset'
560 560 self.write(self.t(key, **props))
561 561 except KeyError, inst:
562 562 raise util.Abort(_("%s: no key named '%s'") % (self.t.mapfile,
563 563 inst.args[0]))
564 564 except SyntaxError, inst:
565 565 raise util.Abort(_('%s: %s') % (self.t.mapfile, inst.args[0]))
566 566
567 567 class changeset_printer(object):
568 568 '''show changeset information when templating not requested.'''
569 569
570 570 def __init__(self, ui, repo):
571 571 self.ui = ui
572 572 self.repo = repo
573 573
574 574 def show(self, rev=0, changenode=None, brinfo=None):
575 575 '''show a single changeset or file revision'''
576 576 log = self.repo.changelog
577 577 if changenode is None:
578 578 changenode = log.node(rev)
579 579 elif not rev:
580 580 rev = log.rev(changenode)
581 581
582 582 if self.ui.quiet:
583 583 self.ui.write("%d:%s\n" % (rev, short(changenode)))
584 584 return
585 585
586 586 changes = log.read(changenode)
587 587 date = util.datestr(changes[2])
588 588
589 589 parents = [(log.rev(p), self.ui.verbose and hex(p) or short(p))
590 590 for p in log.parents(changenode)
591 591 if self.ui.debugflag or p != nullid]
592 592 if (not self.ui.debugflag and len(parents) == 1 and
593 593 parents[0][0] == rev-1):
594 594 parents = []
595 595
596 596 if self.ui.verbose:
597 597 self.ui.write(_("changeset: %d:%s\n") % (rev, hex(changenode)))
598 598 else:
599 599 self.ui.write(_("changeset: %d:%s\n") % (rev, short(changenode)))
600 600
601 601 for tag in self.repo.nodetags(changenode):
602 602 self.ui.status(_("tag: %s\n") % tag)
603 603 for parent in parents:
604 604 self.ui.write(_("parent: %d:%s\n") % parent)
605 605
606 606 if brinfo and changenode in brinfo:
607 607 br = brinfo[changenode]
608 608 self.ui.write(_("branch: %s\n") % " ".join(br))
609 609
610 610 self.ui.debug(_("manifest: %d:%s\n") %
611 611 (self.repo.manifest.rev(changes[0]), hex(changes[0])))
612 612 self.ui.status(_("user: %s\n") % changes[1])
613 613 self.ui.status(_("date: %s\n") % date)
614 614
615 615 if self.ui.debugflag:
616 616 files = self.repo.changes(log.parents(changenode)[0], changenode)
617 617 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
618 618 files):
619 619 if value:
620 620 self.ui.note("%-12s %s\n" % (key, " ".join(value)))
621 621 else:
622 622 self.ui.note(_("files: %s\n") % " ".join(changes[3]))
623 623
624 624 description = changes[4].strip()
625 625 if description:
626 626 if self.ui.verbose:
627 627 self.ui.status(_("description:\n"))
628 628 self.ui.status(description)
629 629 self.ui.status("\n\n")
630 630 else:
631 631 self.ui.status(_("summary: %s\n") %
632 632 description.splitlines()[0])
633 633 self.ui.status("\n")
634 634
635 635 def show_changeset(ui, repo, opts):
636 636 '''show one changeset. uses template or regular display. caller
637 637 can pass in 'style' and 'template' options in opts.'''
638 638
639 639 tmpl = opts.get('template')
640 640 if tmpl:
641 641 tmpl = templater.parsestring(tmpl, quoted=False)
642 642 else:
643 643 tmpl = ui.config('ui', 'logtemplate')
644 644 if tmpl: tmpl = templater.parsestring(tmpl)
645 645 mapfile = opts.get('style') or ui.config('ui', 'style')
646 646 if tmpl or mapfile:
647 647 if mapfile:
648 648 if not os.path.isfile(mapfile):
649 649 mapname = templater.templatepath('map-cmdline.' + mapfile)
650 650 if not mapname: mapname = templater.templatepath(mapfile)
651 651 if mapname: mapfile = mapname
652 652 try:
653 653 t = changeset_templater(ui, repo, mapfile)
654 654 except SyntaxError, inst:
655 655 raise util.Abort(inst.args[0])
656 656 if tmpl: t.use_template(tmpl)
657 657 return t
658 658 return changeset_printer(ui, repo)
659 659
660 660 def show_version(ui):
661 661 """output version and copyright information"""
662 662 ui.write(_("Mercurial Distributed SCM (version %s)\n")
663 663 % version.get_version())
664 664 ui.status(_(
665 665 "\nCopyright (C) 2005 Matt Mackall <mpm@selenic.com>\n"
666 666 "This is free software; see the source for copying conditions. "
667 667 "There is NO\nwarranty; "
668 668 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
669 669 ))
670 670
671 671 def help_(ui, cmd=None, with_version=False):
672 672 """show help for a given command or all commands"""
673 673 option_lists = []
674 674 if cmd and cmd != 'shortlist':
675 675 if with_version:
676 676 show_version(ui)
677 677 ui.write('\n')
678 678 aliases, i = find(cmd)
679 679 # synopsis
680 680 ui.write("%s\n\n" % i[2])
681 681
682 682 # description
683 683 doc = i[0].__doc__
684 684 if not doc:
685 685 doc = _("(No help text available)")
686 686 if ui.quiet:
687 687 doc = doc.splitlines(0)[0]
688 688 ui.write("%s\n" % doc.rstrip())
689 689
690 690 if not ui.quiet:
691 691 # aliases
692 692 if len(aliases) > 1:
693 693 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
694 694
695 695 # options
696 696 if i[1]:
697 697 option_lists.append(("options", i[1]))
698 698
699 699 else:
700 700 # program name
701 701 if ui.verbose or with_version:
702 702 show_version(ui)
703 703 else:
704 704 ui.status(_("Mercurial Distributed SCM\n"))
705 705 ui.status('\n')
706 706
707 707 # list of commands
708 708 if cmd == "shortlist":
709 709 ui.status(_('basic commands (use "hg help" '
710 710 'for the full list or option "-v" for details):\n\n'))
711 711 elif ui.verbose:
712 712 ui.status(_('list of commands:\n\n'))
713 713 else:
714 714 ui.status(_('list of commands (use "hg help -v" '
715 715 'to show aliases and global options):\n\n'))
716 716
717 717 h = {}
718 718 cmds = {}
719 719 for c, e in table.items():
720 720 f = c.split("|")[0]
721 721 if cmd == "shortlist" and not f.startswith("^"):
722 722 continue
723 723 f = f.lstrip("^")
724 724 if not ui.debugflag and f.startswith("debug"):
725 725 continue
726 726 doc = e[0].__doc__
727 727 if not doc:
728 728 doc = _("(No help text available)")
729 729 h[f] = doc.splitlines(0)[0].rstrip()
730 730 cmds[f] = c.lstrip("^")
731 731
732 732 fns = h.keys()
733 733 fns.sort()
734 734 m = max(map(len, fns))
735 735 for f in fns:
736 736 if ui.verbose:
737 737 commands = cmds[f].replace("|",", ")
738 738 ui.write(" %s:\n %s\n"%(commands, h[f]))
739 739 else:
740 740 ui.write(' %-*s %s\n' % (m, f, h[f]))
741 741
742 742 # global options
743 743 if ui.verbose:
744 744 option_lists.append(("global options", globalopts))
745 745
746 746 # list all option lists
747 747 opt_output = []
748 748 for title, options in option_lists:
749 749 opt_output.append(("\n%s:\n" % title, None))
750 750 for shortopt, longopt, default, desc in options:
751 751 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
752 752 longopt and " --%s" % longopt),
753 753 "%s%s" % (desc,
754 754 default
755 755 and _(" (default: %s)") % default
756 756 or "")))
757 757
758 758 if opt_output:
759 759 opts_len = max([len(line[0]) for line in opt_output if line[1]])
760 760 for first, second in opt_output:
761 761 if second:
762 762 ui.write(" %-*s %s\n" % (opts_len, first, second))
763 763 else:
764 764 ui.write("%s\n" % first)
765 765
766 766 # Commands start here, listed alphabetically
767 767
768 768 def add(ui, repo, *pats, **opts):
769 769 """add the specified files on the next commit
770 770
771 771 Schedule files to be version controlled and added to the repository.
772 772
773 773 The files will be added to the repository at the next commit.
774 774
775 775 If no names are given, add all files in the repository.
776 776 """
777 777
778 778 names = []
779 779 for src, abs, rel, exact in walk(repo, pats, opts):
780 780 if exact:
781 781 if ui.verbose:
782 782 ui.status(_('adding %s\n') % rel)
783 783 names.append(abs)
784 784 elif repo.dirstate.state(abs) == '?':
785 785 ui.status(_('adding %s\n') % rel)
786 786 names.append(abs)
787 787 repo.add(names)
788 788
789 789 def addremove(ui, repo, *pats, **opts):
790 790 """add all new files, delete all missing files
791 791
792 792 Add all new files and remove all missing files from the repository.
793 793
794 794 New files are ignored if they match any of the patterns in .hgignore. As
795 795 with add, these changes take effect at the next commit.
796 796 """
797 797 return addremove_lock(ui, repo, pats, opts)
798 798
799 799 def addremove_lock(ui, repo, pats, opts, wlock=None):
800 800 add, remove = [], []
801 801 for src, abs, rel, exact in walk(repo, pats, opts):
802 802 if src == 'f' and repo.dirstate.state(abs) == '?':
803 803 add.append(abs)
804 804 if ui.verbose or not exact:
805 805 ui.status(_('adding %s\n') % ((pats and rel) or abs))
806 806 if repo.dirstate.state(abs) != 'r' and not os.path.exists(rel):
807 807 remove.append(abs)
808 808 if ui.verbose or not exact:
809 809 ui.status(_('removing %s\n') % ((pats and rel) or abs))
810 810 repo.add(add, wlock=wlock)
811 811 repo.remove(remove, wlock=wlock)
812 812
813 813 def annotate(ui, repo, *pats, **opts):
814 814 """show changeset information per file line
815 815
816 816 List changes in files, showing the revision id responsible for each line
817 817
818 818 This command is useful to discover who did a change or when a change took
819 819 place.
820 820
821 821 Without the -a option, annotate will avoid processing files it
822 822 detects as binary. With -a, annotate will generate an annotation
823 823 anyway, probably with undesirable results.
824 824 """
825 825 def getnode(rev):
826 826 return short(repo.changelog.node(rev))
827 827
828 828 ucache = {}
829 829 def getname(rev):
830 830 cl = repo.changelog.read(repo.changelog.node(rev))
831 831 return trimuser(ui, cl[1], rev, ucache)
832 832
833 833 dcache = {}
834 834 def getdate(rev):
835 835 datestr = dcache.get(rev)
836 836 if datestr is None:
837 837 cl = repo.changelog.read(repo.changelog.node(rev))
838 838 datestr = dcache[rev] = util.datestr(cl[2])
839 839 return datestr
840 840
841 841 if not pats:
842 842 raise util.Abort(_('at least one file name or pattern required'))
843 843
844 844 opmap = [['user', getname], ['number', str], ['changeset', getnode],
845 845 ['date', getdate]]
846 846 if not opts['user'] and not opts['changeset'] and not opts['date']:
847 847 opts['number'] = 1
848 848
849 849 if opts['rev']:
850 850 node = repo.changelog.lookup(opts['rev'])
851 851 else:
852 852 node = repo.dirstate.parents()[0]
853 853 change = repo.changelog.read(node)
854 854 mmap = repo.manifest.read(change[0])
855 855
856 856 for src, abs, rel, exact in walk(repo, pats, opts, node=node):
857 857 f = repo.file(abs)
858 858 if not opts['text'] and util.binary(f.read(mmap[abs])):
859 859 ui.write(_("%s: binary file\n") % ((pats and rel) or abs))
860 860 continue
861 861
862 862 lines = f.annotate(mmap[abs])
863 863 pieces = []
864 864
865 865 for o, f in opmap:
866 866 if opts[o]:
867 867 l = [f(n) for n, dummy in lines]
868 868 if l:
869 869 m = max(map(len, l))
870 870 pieces.append(["%*s" % (m, x) for x in l])
871 871
872 872 if pieces:
873 873 for p, l in zip(zip(*pieces), lines):
874 874 ui.write("%s: %s" % (" ".join(p), l[1]))
875 875
876 876 def bundle(ui, repo, fname, dest="default-push", **opts):
877 877 """create a changegroup file
878 878
879 879 Generate a compressed changegroup file collecting all changesets
880 880 not found in the other repository.
881 881
882 882 This file can then be transferred using conventional means and
883 883 applied to another repository with the unbundle command. This is
884 884 useful when native push and pull are not available or when
885 885 exporting an entire repository is undesirable. The standard file
886 886 extension is ".hg".
887 887
888 888 Unlike import/export, this exactly preserves all changeset
889 889 contents including permissions, rename data, and revision history.
890 890 """
891 891 dest = ui.expandpath(dest)
892 892 other = hg.repository(ui, dest)
893 893 o = repo.findoutgoing(other, force=opts['force'])
894 894 cg = repo.changegroup(o, 'bundle')
895 895 write_bundle(cg, fname)
896 896
897 897 def cat(ui, repo, file1, *pats, **opts):
898 898 """output the latest or given revisions of files
899 899
900 900 Print the specified files as they were at the given revision.
901 901 If no revision is given then the tip is used.
902 902
903 903 Output may be to a file, in which case the name of the file is
904 904 given using a format string. The formatting rules are the same as
905 905 for the export command, with the following additions:
906 906
907 907 %s basename of file being printed
908 908 %d dirname of file being printed, or '.' if in repo root
909 909 %p root-relative path name of file being printed
910 910 """
911 911 mf = {}
912 912 rev = opts['rev']
913 913 if rev:
914 914 node = repo.lookup(rev)
915 915 else:
916 916 node = repo.changelog.tip()
917 917 change = repo.changelog.read(node)
918 918 mf = repo.manifest.read(change[0])
919 919 for src, abs, rel, exact in walk(repo, (file1,) + pats, opts, node):
920 920 r = repo.file(abs)
921 921 n = mf[abs]
922 922 fp = make_file(repo, r, opts['output'], node=n, pathname=abs)
923 923 fp.write(r.read(n))
924 924
925 925 def clone(ui, source, dest=None, **opts):
926 926 """make a copy of an existing repository
927 927
928 928 Create a copy of an existing repository in a new directory.
929 929
930 930 If no destination directory name is specified, it defaults to the
931 931 basename of the source.
932 932
933 933 The location of the source is added to the new repository's
934 934 .hg/hgrc file, as the default to be used for future pulls.
935 935
936 936 For efficiency, hardlinks are used for cloning whenever the source
937 937 and destination are on the same filesystem. Some filesystems,
938 938 such as AFS, implement hardlinking incorrectly, but do not report
939 939 errors. In these cases, use the --pull option to avoid
940 940 hardlinking.
941 941
942 942 See pull for valid source format details.
943 943 """
944 944 if dest is None:
945 945 dest = os.path.basename(os.path.normpath(source))
946 946
947 947 if os.path.exists(dest):
948 948 raise util.Abort(_("destination '%s' already exists"), dest)
949 949
950 950 dest = os.path.realpath(dest)
951 951
952 952 class Dircleanup(object):
953 953 def __init__(self, dir_):
954 954 self.rmtree = shutil.rmtree
955 955 self.dir_ = dir_
956 956 os.mkdir(dir_)
957 957 def close(self):
958 958 self.dir_ = None
959 959 def __del__(self):
960 960 if self.dir_:
961 961 self.rmtree(self.dir_, True)
962 962
963 963 if opts['ssh']:
964 964 ui.setconfig("ui", "ssh", opts['ssh'])
965 965 if opts['remotecmd']:
966 966 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
967 967
968 968 source = ui.expandpath(source)
969 969
970 970 d = Dircleanup(dest)
971 971 abspath = source
972 972 other = hg.repository(ui, source)
973 973
974 974 copy = False
975 975 if other.dev() != -1:
976 976 abspath = os.path.abspath(source)
977 977 if not opts['pull'] and not opts['rev']:
978 978 copy = True
979 979
980 980 if copy:
981 981 try:
982 982 # we use a lock here because if we race with commit, we
983 983 # can end up with extra data in the cloned revlogs that's
984 984 # not pointed to by changesets, thus causing verify to
985 985 # fail
986 986 l1 = other.lock()
987 987 except lock.LockException:
988 988 copy = False
989 989
990 990 if copy:
991 991 # we lock here to avoid premature writing to the target
992 992 os.mkdir(os.path.join(dest, ".hg"))
993 993 l2 = lock.lock(os.path.join(dest, ".hg", "lock"))
994 994
995 995 files = "data 00manifest.d 00manifest.i 00changelog.d 00changelog.i"
996 996 for f in files.split():
997 997 src = os.path.join(source, ".hg", f)
998 998 dst = os.path.join(dest, ".hg", f)
999 999 try:
1000 1000 util.copyfiles(src, dst)
1001 1001 except OSError, inst:
1002 1002 if inst.errno != errno.ENOENT:
1003 1003 raise
1004 1004
1005 1005 repo = hg.repository(ui, dest)
1006 1006
1007 1007 else:
1008 1008 revs = None
1009 1009 if opts['rev']:
1010 1010 if not other.local():
1011 1011 error = _("clone -r not supported yet for remote repositories.")
1012 1012 raise util.Abort(error)
1013 1013 else:
1014 1014 revs = [other.lookup(rev) for rev in opts['rev']]
1015 1015 repo = hg.repository(ui, dest, create=1)
1016 1016 repo.pull(other, heads = revs)
1017 1017
1018 1018 f = repo.opener("hgrc", "w", text=True)
1019 1019 f.write("[paths]\n")
1020 1020 f.write("default = %s\n" % abspath)
1021 1021 f.close()
1022 1022
1023 1023 if not opts['noupdate']:
1024 1024 update(repo.ui, repo)
1025 1025
1026 1026 d.close()
1027 1027
1028 1028 def commit(ui, repo, *pats, **opts):
1029 1029 """commit the specified files or all outstanding changes
1030 1030
1031 1031 Commit changes to the given files into the repository.
1032 1032
1033 1033 If a list of files is omitted, all changes reported by "hg status"
1034 1034 will be committed.
1035 1035
1036 1036 If no commit message is specified, the editor configured in your hgrc
1037 1037 or in the EDITOR environment variable is started to enter a message.
1038 1038 """
1039 1039 message = opts['message']
1040 1040 logfile = opts['logfile']
1041 1041
1042 1042 if message and logfile:
1043 1043 raise util.Abort(_('options --message and --logfile are mutually '
1044 1044 'exclusive'))
1045 1045 if not message and logfile:
1046 1046 try:
1047 1047 if logfile == '-':
1048 1048 message = sys.stdin.read()
1049 1049 else:
1050 1050 message = open(logfile).read()
1051 1051 except IOError, inst:
1052 1052 raise util.Abort(_("can't read commit message '%s': %s") %
1053 1053 (logfile, inst.strerror))
1054 1054
1055 1055 if opts['addremove']:
1056 1056 addremove(ui, repo, *pats, **opts)
1057 1057 fns, match, anypats = matchpats(repo, pats, opts)
1058 1058 if pats:
1059 1059 modified, added, removed, deleted, unknown = (
1060 1060 repo.changes(files=fns, match=match))
1061 1061 files = modified + added + removed
1062 1062 else:
1063 1063 files = []
1064 1064 try:
1065 1065 repo.commit(files, message, opts['user'], opts['date'], match)
1066 1066 except ValueError, inst:
1067 1067 raise util.Abort(str(inst))
1068 1068
1069 1069 def docopy(ui, repo, pats, opts, wlock):
1070 1070 # called with the repo lock held
1071 1071 cwd = repo.getcwd()
1072 1072 errors = 0
1073 1073 copied = []
1074 1074 targets = {}
1075 1075
1076 1076 def okaytocopy(abs, rel, exact):
1077 1077 reasons = {'?': _('is not managed'),
1078 1078 'a': _('has been marked for add'),
1079 1079 'r': _('has been marked for remove')}
1080 1080 state = repo.dirstate.state(abs)
1081 1081 reason = reasons.get(state)
1082 1082 if reason:
1083 1083 if state == 'a':
1084 1084 origsrc = repo.dirstate.copied(abs)
1085 1085 if origsrc is not None:
1086 1086 return origsrc
1087 1087 if exact:
1088 1088 ui.warn(_('%s: not copying - file %s\n') % (rel, reason))
1089 1089 else:
1090 1090 return abs
1091 1091
1092 1092 def copy(origsrc, abssrc, relsrc, target, exact):
1093 1093 abstarget = util.canonpath(repo.root, cwd, target)
1094 1094 reltarget = util.pathto(cwd, abstarget)
1095 1095 prevsrc = targets.get(abstarget)
1096 1096 if prevsrc is not None:
1097 1097 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
1098 1098 (reltarget, abssrc, prevsrc))
1099 1099 return
1100 1100 if (not opts['after'] and os.path.exists(reltarget) or
1101 1101 opts['after'] and repo.dirstate.state(abstarget) not in '?r'):
1102 1102 if not opts['force']:
1103 1103 ui.warn(_('%s: not overwriting - file exists\n') %
1104 1104 reltarget)
1105 1105 return
1106 1106 if not opts['after']:
1107 1107 os.unlink(reltarget)
1108 1108 if opts['after']:
1109 1109 if not os.path.exists(reltarget):
1110 1110 return
1111 1111 else:
1112 1112 targetdir = os.path.dirname(reltarget) or '.'
1113 1113 if not os.path.isdir(targetdir):
1114 1114 os.makedirs(targetdir)
1115 1115 try:
1116 1116 restore = repo.dirstate.state(abstarget) == 'r'
1117 1117 if restore:
1118 1118 repo.undelete([abstarget], wlock)
1119 1119 try:
1120 1120 shutil.copyfile(relsrc, reltarget)
1121 1121 shutil.copymode(relsrc, reltarget)
1122 1122 restore = False
1123 1123 finally:
1124 1124 if restore:
1125 1125 repo.remove([abstarget], wlock)
1126 1126 except shutil.Error, inst:
1127 1127 raise util.Abort(str(inst))
1128 1128 except IOError, inst:
1129 1129 if inst.errno == errno.ENOENT:
1130 1130 ui.warn(_('%s: deleted in working copy\n') % relsrc)
1131 1131 else:
1132 1132 ui.warn(_('%s: cannot copy - %s\n') %
1133 1133 (relsrc, inst.strerror))
1134 1134 errors += 1
1135 1135 return
1136 1136 if ui.verbose or not exact:
1137 1137 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
1138 1138 targets[abstarget] = abssrc
1139 1139 if abstarget != origsrc:
1140 1140 repo.copy(origsrc, abstarget, wlock)
1141 1141 copied.append((abssrc, relsrc, exact))
1142 1142
1143 1143 def targetpathfn(pat, dest, srcs):
1144 1144 if os.path.isdir(pat):
1145 1145 abspfx = util.canonpath(repo.root, cwd, pat)
1146 1146 if destdirexists:
1147 1147 striplen = len(os.path.split(abspfx)[0])
1148 1148 else:
1149 1149 striplen = len(abspfx)
1150 1150 if striplen:
1151 1151 striplen += len(os.sep)
1152 1152 res = lambda p: os.path.join(dest, p[striplen:])
1153 1153 elif destdirexists:
1154 1154 res = lambda p: os.path.join(dest, os.path.basename(p))
1155 1155 else:
1156 1156 res = lambda p: dest
1157 1157 return res
1158 1158
1159 1159 def targetpathafterfn(pat, dest, srcs):
1160 1160 if util.patkind(pat, None)[0]:
1161 1161 # a mercurial pattern
1162 1162 res = lambda p: os.path.join(dest, os.path.basename(p))
1163 1163 else:
1164 1164 abspfx = util.canonpath(repo.root, cwd, pat)
1165 1165 if len(abspfx) < len(srcs[0][0]):
1166 1166 # A directory. Either the target path contains the last
1167 1167 # component of the source path or it does not.
1168 1168 def evalpath(striplen):
1169 1169 score = 0
1170 1170 for s in srcs:
1171 1171 t = os.path.join(dest, s[0][striplen:])
1172 1172 if os.path.exists(t):
1173 1173 score += 1
1174 1174 return score
1175 1175
1176 1176 striplen = len(abspfx)
1177 1177 if striplen:
1178 1178 striplen += len(os.sep)
1179 1179 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
1180 1180 score = evalpath(striplen)
1181 1181 striplen1 = len(os.path.split(abspfx)[0])
1182 1182 if striplen1:
1183 1183 striplen1 += len(os.sep)
1184 1184 if evalpath(striplen1) > score:
1185 1185 striplen = striplen1
1186 1186 res = lambda p: os.path.join(dest, p[striplen:])
1187 1187 else:
1188 1188 # a file
1189 1189 if destdirexists:
1190 1190 res = lambda p: os.path.join(dest, os.path.basename(p))
1191 1191 else:
1192 1192 res = lambda p: dest
1193 1193 return res
1194 1194
1195 1195
1196 1196 pats = list(pats)
1197 1197 if not pats:
1198 1198 raise util.Abort(_('no source or destination specified'))
1199 1199 if len(pats) == 1:
1200 1200 raise util.Abort(_('no destination specified'))
1201 1201 dest = pats.pop()
1202 1202 destdirexists = os.path.isdir(dest)
1203 1203 if (len(pats) > 1 or util.patkind(pats[0], None)[0]) and not destdirexists:
1204 1204 raise util.Abort(_('with multiple sources, destination must be an '
1205 1205 'existing directory'))
1206 1206 if opts['after']:
1207 1207 tfn = targetpathafterfn
1208 1208 else:
1209 1209 tfn = targetpathfn
1210 1210 copylist = []
1211 1211 for pat in pats:
1212 1212 srcs = []
1213 1213 for tag, abssrc, relsrc, exact in walk(repo, [pat], opts):
1214 1214 origsrc = okaytocopy(abssrc, relsrc, exact)
1215 1215 if origsrc:
1216 1216 srcs.append((origsrc, abssrc, relsrc, exact))
1217 1217 if not srcs:
1218 1218 continue
1219 1219 copylist.append((tfn(pat, dest, srcs), srcs))
1220 1220 if not copylist:
1221 1221 raise util.Abort(_('no files to copy'))
1222 1222
1223 1223 for targetpath, srcs in copylist:
1224 1224 for origsrc, abssrc, relsrc, exact in srcs:
1225 1225 copy(origsrc, abssrc, relsrc, targetpath(abssrc), exact)
1226 1226
1227 1227 if errors:
1228 1228 ui.warn(_('(consider using --after)\n'))
1229 1229 return errors, copied
1230 1230
1231 1231 def copy(ui, repo, *pats, **opts):
1232 1232 """mark files as copied for the next commit
1233 1233
1234 1234 Mark dest as having copies of source files. If dest is a
1235 1235 directory, copies are put in that directory. If dest is a file,
1236 1236 there can only be one source.
1237 1237
1238 1238 By default, this command copies the contents of files as they
1239 1239 stand in the working directory. If invoked with --after, the
1240 1240 operation is recorded, but no copying is performed.
1241 1241
1242 1242 This command takes effect in the next commit.
1243 1243
1244 1244 NOTE: This command should be treated as experimental. While it
1245 1245 should properly record copied files, this information is not yet
1246 1246 fully used by merge, nor fully reported by log.
1247 1247 """
1248 1248 wlock = repo.wlock(0)
1249 1249 errs, copied = docopy(ui, repo, pats, opts, wlock)
1250 1250 return errs
1251 1251
1252 1252 def debugancestor(ui, index, rev1, rev2):
1253 1253 """find the ancestor revision of two revisions in a given index"""
1254 1254 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index, "")
1255 1255 a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
1256 1256 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1257 1257
1258 1258 def debugcomplete(ui, cmd):
1259 1259 """returns the completion list associated with the given command"""
1260 1260 clist = findpossible(cmd).keys()
1261 1261 clist.sort()
1262 1262 ui.write("%s\n" % " ".join(clist))
1263 1263
1264 1264 def debugrebuildstate(ui, repo, rev=None):
1265 1265 """rebuild the dirstate as it would look like for the given revision"""
1266 1266 if not rev:
1267 1267 rev = repo.changelog.tip()
1268 1268 else:
1269 1269 rev = repo.lookup(rev)
1270 1270 change = repo.changelog.read(rev)
1271 1271 n = change[0]
1272 1272 files = repo.manifest.readflags(n)
1273 1273 wlock = repo.wlock()
1274 1274 repo.dirstate.rebuild(rev, files.iteritems())
1275 1275
1276 1276 def debugcheckstate(ui, repo):
1277 1277 """validate the correctness of the current dirstate"""
1278 1278 parent1, parent2 = repo.dirstate.parents()
1279 1279 repo.dirstate.read()
1280 1280 dc = repo.dirstate.map
1281 1281 keys = dc.keys()
1282 1282 keys.sort()
1283 1283 m1n = repo.changelog.read(parent1)[0]
1284 1284 m2n = repo.changelog.read(parent2)[0]
1285 1285 m1 = repo.manifest.read(m1n)
1286 1286 m2 = repo.manifest.read(m2n)
1287 1287 errors = 0
1288 1288 for f in dc:
1289 1289 state = repo.dirstate.state(f)
1290 1290 if state in "nr" and f not in m1:
1291 1291 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1292 1292 errors += 1
1293 1293 if state in "a" and f in m1:
1294 1294 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1295 1295 errors += 1
1296 1296 if state in "m" and f not in m1 and f not in m2:
1297 1297 ui.warn(_("%s in state %s, but not in either manifest\n") %
1298 1298 (f, state))
1299 1299 errors += 1
1300 1300 for f in m1:
1301 1301 state = repo.dirstate.state(f)
1302 1302 if state not in "nrm":
1303 1303 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1304 1304 errors += 1
1305 1305 if errors:
1306 1306 error = _(".hg/dirstate inconsistent with current parent's manifest")
1307 1307 raise util.Abort(error)
1308 1308
1309 1309 def debugconfig(ui, repo):
1310 1310 """show combined config settings from all hgrc files"""
1311 1311 for section, name, value in ui.walkconfig():
1312 1312 ui.write('%s.%s=%s\n' % (section, name, value))
1313 1313
1314 1314 def debugsetparents(ui, repo, rev1, rev2=None):
1315 1315 """manually set the parents of the current working directory
1316 1316
1317 1317 This is useful for writing repository conversion tools, but should
1318 1318 be used with care.
1319 1319 """
1320 1320
1321 1321 if not rev2:
1322 1322 rev2 = hex(nullid)
1323 1323
1324 1324 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
1325 1325
1326 1326 def debugstate(ui, repo):
1327 1327 """show the contents of the current dirstate"""
1328 1328 repo.dirstate.read()
1329 1329 dc = repo.dirstate.map
1330 1330 keys = dc.keys()
1331 1331 keys.sort()
1332 1332 for file_ in keys:
1333 1333 ui.write("%c %3o %10d %s %s\n"
1334 1334 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
1335 1335 time.strftime("%x %X",
1336 1336 time.localtime(dc[file_][3])), file_))
1337 1337 for f in repo.dirstate.copies:
1338 1338 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copies[f], f))
1339 1339
1340 1340 def debugdata(ui, file_, rev):
1341 1341 """dump the contents of an data file revision"""
1342 1342 r = revlog.revlog(util.opener(os.getcwd(), audit=False),
1343 1343 file_[:-2] + ".i", file_)
1344 1344 try:
1345 1345 ui.write(r.revision(r.lookup(rev)))
1346 1346 except KeyError:
1347 1347 raise util.Abort(_('invalid revision identifier %s'), rev)
1348 1348
1349 1349 def debugindex(ui, file_):
1350 1350 """dump the contents of an index file"""
1351 1351 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "")
1352 1352 ui.write(" rev offset length base linkrev" +
1353 1353 " nodeid p1 p2\n")
1354 1354 for i in range(r.count()):
1355 1355 e = r.index[i]
1356 1356 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1357 1357 i, e[0], e[1], e[2], e[3],
1358 1358 short(e[6]), short(e[4]), short(e[5])))
1359 1359
1360 1360 def debugindexdot(ui, file_):
1361 1361 """dump an index DAG as a .dot file"""
1362 1362 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "")
1363 1363 ui.write("digraph G {\n")
1364 1364 for i in range(r.count()):
1365 1365 e = r.index[i]
1366 1366 ui.write("\t%d -> %d\n" % (r.rev(e[4]), i))
1367 1367 if e[5] != nullid:
1368 1368 ui.write("\t%d -> %d\n" % (r.rev(e[5]), i))
1369 1369 ui.write("}\n")
1370 1370
1371 1371 def debugrename(ui, repo, file, rev=None):
1372 1372 """dump rename information"""
1373 1373 r = repo.file(relpath(repo, [file])[0])
1374 1374 if rev:
1375 1375 try:
1376 1376 # assume all revision numbers are for changesets
1377 1377 n = repo.lookup(rev)
1378 1378 change = repo.changelog.read(n)
1379 1379 m = repo.manifest.read(change[0])
1380 1380 n = m[relpath(repo, [file])[0]]
1381 1381 except (hg.RepoError, KeyError):
1382 1382 n = r.lookup(rev)
1383 1383 else:
1384 1384 n = r.tip()
1385 1385 m = r.renamed(n)
1386 1386 if m:
1387 1387 ui.write(_("renamed from %s:%s\n") % (m[0], hex(m[1])))
1388 1388 else:
1389 1389 ui.write(_("not renamed\n"))
1390 1390
1391 1391 def debugwalk(ui, repo, *pats, **opts):
1392 1392 """show how files match on given patterns"""
1393 1393 items = list(walk(repo, pats, opts))
1394 1394 if not items:
1395 1395 return
1396 1396 fmt = '%%s %%-%ds %%-%ds %%s' % (
1397 1397 max([len(abs) for (src, abs, rel, exact) in items]),
1398 1398 max([len(rel) for (src, abs, rel, exact) in items]))
1399 1399 for src, abs, rel, exact in items:
1400 1400 line = fmt % (src, abs, rel, exact and 'exact' or '')
1401 1401 ui.write("%s\n" % line.rstrip())
1402 1402
1403 1403 def diff(ui, repo, *pats, **opts):
1404 1404 """diff repository (or selected files)
1405 1405
1406 1406 Show differences between revisions for the specified files.
1407 1407
1408 1408 Differences between files are shown using the unified diff format.
1409 1409
1410 1410 When two revision arguments are given, then changes are shown
1411 1411 between those revisions. If only one revision is specified then
1412 1412 that revision is compared to the working directory, and, when no
1413 1413 revisions are specified, the working directory files are compared
1414 1414 to its parent.
1415 1415
1416 1416 Without the -a option, diff will avoid generating diffs of files
1417 1417 it detects as binary. With -a, diff will generate a diff anyway,
1418 1418 probably with undesirable results.
1419 1419 """
1420 1420 node1, node2 = None, None
1421 1421 revs = [repo.lookup(x) for x in opts['rev']]
1422 1422
1423 1423 if len(revs) > 0:
1424 1424 node1 = revs[0]
1425 1425 if len(revs) > 1:
1426 1426 node2 = revs[1]
1427 1427 if len(revs) > 2:
1428 1428 raise util.Abort(_("too many revisions to diff"))
1429 1429
1430 1430 fns, matchfn, anypats = matchpats(repo, pats, opts)
1431 1431
1432 1432 dodiff(sys.stdout, ui, repo, node1, node2, fns, match=matchfn,
1433 1433 text=opts['text'], opts=opts)
1434 1434
1435 1435 def doexport(ui, repo, changeset, seqno, total, revwidth, opts):
1436 1436 node = repo.lookup(changeset)
1437 1437 parents = [p for p in repo.changelog.parents(node) if p != nullid]
1438 1438 if opts['switch_parent']:
1439 1439 parents.reverse()
1440 1440 prev = (parents and parents[0]) or nullid
1441 1441 change = repo.changelog.read(node)
1442 1442
1443 1443 fp = make_file(repo, repo.changelog, opts['output'],
1444 1444 node=node, total=total, seqno=seqno,
1445 1445 revwidth=revwidth)
1446 1446 if fp != sys.stdout:
1447 1447 ui.note("%s\n" % fp.name)
1448 1448
1449 1449 fp.write("# HG changeset patch\n")
1450 1450 fp.write("# User %s\n" % change[1])
1451 1451 fp.write("# Node ID %s\n" % hex(node))
1452 1452 fp.write("# Parent %s\n" % hex(prev))
1453 1453 if len(parents) > 1:
1454 1454 fp.write("# Parent %s\n" % hex(parents[1]))
1455 1455 fp.write(change[4].rstrip())
1456 1456 fp.write("\n\n")
1457 1457
1458 1458 dodiff(fp, ui, repo, prev, node, text=opts['text'])
1459 1459 if fp != sys.stdout:
1460 1460 fp.close()
1461 1461
1462 1462 def export(ui, repo, *changesets, **opts):
1463 1463 """dump the header and diffs for one or more changesets
1464 1464
1465 1465 Print the changeset header and diffs for one or more revisions.
1466 1466
1467 1467 The information shown in the changeset header is: author,
1468 1468 changeset hash, parent and commit comment.
1469 1469
1470 1470 Output may be to a file, in which case the name of the file is
1471 1471 given using a format string. The formatting rules are as follows:
1472 1472
1473 1473 %% literal "%" character
1474 1474 %H changeset hash (40 bytes of hexadecimal)
1475 1475 %N number of patches being generated
1476 1476 %R changeset revision number
1477 1477 %b basename of the exporting repository
1478 1478 %h short-form changeset hash (12 bytes of hexadecimal)
1479 1479 %n zero-padded sequence number, starting at 1
1480 1480 %r zero-padded changeset revision number
1481 1481
1482 1482 Without the -a option, export will avoid generating diffs of files
1483 1483 it detects as binary. With -a, export will generate a diff anyway,
1484 1484 probably with undesirable results.
1485 1485
1486 1486 With the --switch-parent option, the diff will be against the second
1487 1487 parent. It can be useful to review a merge.
1488 1488 """
1489 1489 if not changesets:
1490 1490 raise util.Abort(_("export requires at least one changeset"))
1491 1491 seqno = 0
1492 1492 revs = list(revrange(ui, repo, changesets))
1493 1493 total = len(revs)
1494 1494 revwidth = max(map(len, revs))
1495 1495 msg = len(revs) > 1 and _("Exporting patches:\n") or _("Exporting patch:\n")
1496 1496 ui.note(msg)
1497 1497 for cset in revs:
1498 1498 seqno += 1
1499 1499 doexport(ui, repo, cset, seqno, total, revwidth, opts)
1500 1500
1501 1501 def forget(ui, repo, *pats, **opts):
1502 1502 """don't add the specified files on the next commit
1503 1503
1504 1504 Undo an 'hg add' scheduled for the next commit.
1505 1505 """
1506 1506 forget = []
1507 1507 for src, abs, rel, exact in walk(repo, pats, opts):
1508 1508 if repo.dirstate.state(abs) == 'a':
1509 1509 forget.append(abs)
1510 1510 if ui.verbose or not exact:
1511 1511 ui.status(_('forgetting %s\n') % ((pats and rel) or abs))
1512 1512 repo.forget(forget)
1513 1513
1514 1514 def grep(ui, repo, pattern, *pats, **opts):
1515 1515 """search for a pattern in specified files and revisions
1516 1516
1517 1517 Search revisions of files for a regular expression.
1518 1518
1519 1519 This command behaves differently than Unix grep. It only accepts
1520 1520 Python/Perl regexps. It searches repository history, not the
1521 1521 working directory. It always prints the revision number in which
1522 1522 a match appears.
1523 1523
1524 1524 By default, grep only prints output for the first revision of a
1525 1525 file in which it finds a match. To get it to print every revision
1526 1526 that contains a change in match status ("-" for a match that
1527 1527 becomes a non-match, or "+" for a non-match that becomes a match),
1528 1528 use the --all flag.
1529 1529 """
1530 1530 reflags = 0
1531 1531 if opts['ignore_case']:
1532 1532 reflags |= re.I
1533 1533 regexp = re.compile(pattern, reflags)
1534 1534 sep, eol = ':', '\n'
1535 1535 if opts['print0']:
1536 1536 sep = eol = '\0'
1537 1537
1538 1538 fcache = {}
1539 1539 def getfile(fn):
1540 1540 if fn not in fcache:
1541 1541 fcache[fn] = repo.file(fn)
1542 1542 return fcache[fn]
1543 1543
1544 1544 def matchlines(body):
1545 1545 begin = 0
1546 1546 linenum = 0
1547 1547 while True:
1548 1548 match = regexp.search(body, begin)
1549 1549 if not match:
1550 1550 break
1551 1551 mstart, mend = match.span()
1552 1552 linenum += body.count('\n', begin, mstart) + 1
1553 1553 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1554 1554 lend = body.find('\n', mend)
1555 1555 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1556 1556 begin = lend + 1
1557 1557
1558 1558 class linestate(object):
1559 1559 def __init__(self, line, linenum, colstart, colend):
1560 1560 self.line = line
1561 1561 self.linenum = linenum
1562 1562 self.colstart = colstart
1563 1563 self.colend = colend
1564 1564 def __eq__(self, other):
1565 1565 return self.line == other.line
1566 1566 def __hash__(self):
1567 1567 return hash(self.line)
1568 1568
1569 1569 matches = {}
1570 1570 def grepbody(fn, rev, body):
1571 1571 matches[rev].setdefault(fn, {})
1572 1572 m = matches[rev][fn]
1573 1573 for lnum, cstart, cend, line in matchlines(body):
1574 1574 s = linestate(line, lnum, cstart, cend)
1575 1575 m[s] = s
1576 1576
1577 1577 # FIXME: prev isn't used, why ?
1578 1578 prev = {}
1579 1579 ucache = {}
1580 1580 def display(fn, rev, states, prevstates):
1581 1581 diff = list(sets.Set(states).symmetric_difference(sets.Set(prevstates)))
1582 1582 diff.sort(lambda x, y: cmp(x.linenum, y.linenum))
1583 1583 counts = {'-': 0, '+': 0}
1584 1584 filerevmatches = {}
1585 1585 for l in diff:
1586 1586 if incrementing or not opts['all']:
1587 1587 change = ((l in prevstates) and '-') or '+'
1588 1588 r = rev
1589 1589 else:
1590 1590 change = ((l in states) and '-') or '+'
1591 1591 r = prev[fn]
1592 1592 cols = [fn, str(rev)]
1593 1593 if opts['line_number']:
1594 1594 cols.append(str(l.linenum))
1595 1595 if opts['all']:
1596 1596 cols.append(change)
1597 1597 if opts['user']:
1598 1598 cols.append(trimuser(ui, getchange(rev)[1], rev,
1599 1599 ucache))
1600 1600 if opts['files_with_matches']:
1601 1601 c = (fn, rev)
1602 1602 if c in filerevmatches:
1603 1603 continue
1604 1604 filerevmatches[c] = 1
1605 1605 else:
1606 1606 cols.append(l.line)
1607 1607 ui.write(sep.join(cols), eol)
1608 1608 counts[change] += 1
1609 1609 return counts['+'], counts['-']
1610 1610
1611 1611 fstate = {}
1612 1612 skip = {}
1613 1613 changeiter, getchange, matchfn = walkchangerevs(ui, repo, pats, opts)
1614 1614 count = 0
1615 1615 incrementing = False
1616 1616 for st, rev, fns in changeiter:
1617 1617 if st == 'window':
1618 1618 incrementing = rev
1619 1619 matches.clear()
1620 1620 elif st == 'add':
1621 1621 change = repo.changelog.read(repo.lookup(str(rev)))
1622 1622 mf = repo.manifest.read(change[0])
1623 1623 matches[rev] = {}
1624 1624 for fn in fns:
1625 1625 if fn in skip:
1626 1626 continue
1627 1627 fstate.setdefault(fn, {})
1628 1628 try:
1629 1629 grepbody(fn, rev, getfile(fn).read(mf[fn]))
1630 1630 except KeyError:
1631 1631 pass
1632 1632 elif st == 'iter':
1633 1633 states = matches[rev].items()
1634 1634 states.sort()
1635 1635 for fn, m in states:
1636 1636 if fn in skip:
1637 1637 continue
1638 1638 if incrementing or not opts['all'] or fstate[fn]:
1639 1639 pos, neg = display(fn, rev, m, fstate[fn])
1640 1640 count += pos + neg
1641 1641 if pos and not opts['all']:
1642 1642 skip[fn] = True
1643 1643 fstate[fn] = m
1644 1644 prev[fn] = rev
1645 1645
1646 1646 if not incrementing:
1647 1647 fstate = fstate.items()
1648 1648 fstate.sort()
1649 1649 for fn, state in fstate:
1650 1650 if fn in skip:
1651 1651 continue
1652 1652 display(fn, rev, {}, state)
1653 1653 return (count == 0 and 1) or 0
1654 1654
1655 1655 def heads(ui, repo, **opts):
1656 1656 """show current repository heads
1657 1657
1658 1658 Show all repository head changesets.
1659 1659
1660 1660 Repository "heads" are changesets that don't have children
1661 1661 changesets. They are where development generally takes place and
1662 1662 are the usual targets for update and merge operations.
1663 1663 """
1664 1664 if opts['rev']:
1665 1665 heads = repo.heads(repo.lookup(opts['rev']))
1666 1666 else:
1667 1667 heads = repo.heads()
1668 1668 br = None
1669 1669 if opts['branches']:
1670 1670 br = repo.branchlookup(heads)
1671 1671 displayer = show_changeset(ui, repo, opts)
1672 1672 for n in heads:
1673 1673 displayer.show(changenode=n, brinfo=br)
1674 1674
1675 1675 def identify(ui, repo):
1676 1676 """print information about the working copy
1677 1677
1678 1678 Print a short summary of the current state of the repo.
1679 1679
1680 1680 This summary identifies the repository state using one or two parent
1681 1681 hash identifiers, followed by a "+" if there are uncommitted changes
1682 1682 in the working directory, followed by a list of tags for this revision.
1683 1683 """
1684 1684 parents = [p for p in repo.dirstate.parents() if p != nullid]
1685 1685 if not parents:
1686 1686 ui.write(_("unknown\n"))
1687 1687 return
1688 1688
1689 1689 hexfunc = ui.verbose and hex or short
1690 1690 modified, added, removed, deleted, unknown = repo.changes()
1691 1691 output = ["%s%s" %
1692 1692 ('+'.join([hexfunc(parent) for parent in parents]),
1693 1693 (modified or added or removed or deleted) and "+" or "")]
1694 1694
1695 1695 if not ui.quiet:
1696 1696 # multiple tags for a single parent separated by '/'
1697 1697 parenttags = ['/'.join(tags)
1698 1698 for tags in map(repo.nodetags, parents) if tags]
1699 1699 # tags for multiple parents separated by ' + '
1700 1700 if parenttags:
1701 1701 output.append(' + '.join(parenttags))
1702 1702
1703 1703 ui.write("%s\n" % ' '.join(output))
1704 1704
1705 1705 def import_(ui, repo, patch1, *patches, **opts):
1706 1706 """import an ordered set of patches
1707 1707
1708 1708 Import a list of patches and commit them individually.
1709 1709
1710 1710 If there are outstanding changes in the working directory, import
1711 1711 will abort unless given the -f flag.
1712 1712
1713 1713 If a patch looks like a mail message (its first line starts with
1714 1714 "From " or looks like an RFC822 header), it will not be applied
1715 1715 unless the -f option is used. The importer neither parses nor
1716 1716 discards mail headers, so use -f only to override the "mailness"
1717 1717 safety check, not to import a real mail message.
1718 1718 """
1719 1719 patches = (patch1,) + patches
1720 1720
1721 1721 if not opts['force']:
1722 1722 modified, added, removed, deleted, unknown = repo.changes()
1723 1723 if modified or added or removed or deleted:
1724 1724 raise util.Abort(_("outstanding uncommitted changes"))
1725 1725
1726 1726 d = opts["base"]
1727 1727 strip = opts["strip"]
1728 1728
1729 1729 mailre = re.compile(r'(?:From |[\w-]+:)')
1730 1730
1731 1731 # attempt to detect the start of a patch
1732 1732 # (this heuristic is borrowed from quilt)
1733 1733 diffre = re.compile(r'(?:Index:[ \t]|diff[ \t]|RCS file: |' +
1734 1734 'retrieving revision [0-9]+(\.[0-9]+)*$|' +
1735 1735 '(---|\*\*\*)[ \t])')
1736 1736
1737 1737 for patch in patches:
1738 1738 ui.status(_("applying %s\n") % patch)
1739 1739 pf = os.path.join(d, patch)
1740 1740
1741 1741 message = []
1742 1742 user = None
1743 1743 hgpatch = False
1744 1744 for line in file(pf):
1745 1745 line = line.rstrip()
1746 1746 if (not message and not hgpatch and
1747 1747 mailre.match(line) and not opts['force']):
1748 1748 if len(line) > 35:
1749 1749 line = line[:32] + '...'
1750 1750 raise util.Abort(_('first line looks like a '
1751 1751 'mail header: ') + line)
1752 1752 if diffre.match(line):
1753 1753 break
1754 1754 elif hgpatch:
1755 1755 # parse values when importing the result of an hg export
1756 1756 if line.startswith("# User "):
1757 1757 user = line[7:]
1758 1758 ui.debug(_('User: %s\n') % user)
1759 1759 elif not line.startswith("# ") and line:
1760 1760 message.append(line)
1761 1761 hgpatch = False
1762 1762 elif line == '# HG changeset patch':
1763 1763 hgpatch = True
1764 1764 message = [] # We may have collected garbage
1765 1765 else:
1766 1766 message.append(line)
1767 1767
1768 1768 # make sure message isn't empty
1769 1769 if not message:
1770 1770 message = _("imported patch %s\n") % patch
1771 1771 else:
1772 1772 message = "%s\n" % '\n'.join(message)
1773 1773 ui.debug(_('message:\n%s\n') % message)
1774 1774
1775 1775 files = util.patch(strip, pf, ui)
1776 1776
1777 1777 if len(files) > 0:
1778 1778 addremove(ui, repo, *files)
1779 1779 repo.commit(files, message, user)
1780 1780
1781 1781 def incoming(ui, repo, source="default", **opts):
1782 1782 """show new changesets found in source
1783 1783
1784 1784 Show new changesets found in the specified path/URL or the default
1785 1785 pull location. These are the changesets that would be pulled if a pull
1786 1786 was requested.
1787 1787
1788 1788 For remote repository, using --bundle avoids downloading the changesets
1789 1789 twice if the incoming is followed by a pull.
1790 1790
1791 1791 See pull for valid source format details.
1792 1792 """
1793 1793 source = ui.expandpath(source)
1794 1794 if opts['ssh']:
1795 1795 ui.setconfig("ui", "ssh", opts['ssh'])
1796 1796 if opts['remotecmd']:
1797 1797 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
1798 1798
1799 1799 other = hg.repository(ui, source)
1800 1800 incoming = repo.findincoming(other, force=opts["force"])
1801 1801 if not incoming:
1802 1802 ui.status(_("no changes found\n"))
1803 1803 return
1804 1804
1805 1805 cleanup = None
1806 1806 try:
1807 1807 fname = opts["bundle"]
1808 1808 if fname or not other.local():
1809 1809 # create a bundle (uncompressed if other repo is not local)
1810 1810 cg = other.changegroup(incoming, "incoming")
1811 1811 fname = cleanup = write_bundle(cg, fname, compress=other.local())
1812 1812 # keep written bundle?
1813 1813 if opts["bundle"]:
1814 1814 cleanup = None
1815 1815 if not other.local():
1816 1816 # use the created uncompressed bundlerepo
1817 1817 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1818 1818
1819 1819 o = other.changelog.nodesbetween(incoming)[0]
1820 1820 if opts['newest_first']:
1821 1821 o.reverse()
1822 1822 displayer = show_changeset(ui, other, opts)
1823 1823 for n in o:
1824 1824 parents = [p for p in other.changelog.parents(n) if p != nullid]
1825 1825 if opts['no_merges'] and len(parents) == 2:
1826 1826 continue
1827 1827 displayer.show(changenode=n)
1828 1828 if opts['patch']:
1829 1829 prev = (parents and parents[0]) or nullid
1830 1830 dodiff(ui, ui, other, prev, n)
1831 1831 ui.write("\n")
1832 1832 finally:
1833 1833 if hasattr(other, 'close'):
1834 1834 other.close()
1835 1835 if cleanup:
1836 1836 os.unlink(cleanup)
1837 1837
1838 1838 def init(ui, dest="."):
1839 1839 """create a new repository in the given directory
1840 1840
1841 1841 Initialize a new repository in the given directory. If the given
1842 1842 directory does not exist, it is created.
1843 1843
1844 1844 If no directory is given, the current directory is used.
1845 1845 """
1846 1846 if not os.path.exists(dest):
1847 1847 os.mkdir(dest)
1848 1848 hg.repository(ui, dest, create=1)
1849 1849
1850 1850 def locate(ui, repo, *pats, **opts):
1851 1851 """locate files matching specific patterns
1852 1852
1853 1853 Print all files under Mercurial control whose names match the
1854 1854 given patterns.
1855 1855
1856 1856 This command searches the current directory and its
1857 1857 subdirectories. To search an entire repository, move to the root
1858 1858 of the repository.
1859 1859
1860 1860 If no patterns are given to match, this command prints all file
1861 1861 names.
1862 1862
1863 1863 If you want to feed the output of this command into the "xargs"
1864 1864 command, use the "-0" option to both this command and "xargs".
1865 1865 This will avoid the problem of "xargs" treating single filenames
1866 1866 that contain white space as multiple filenames.
1867 1867 """
1868 1868 end = opts['print0'] and '\0' or '\n'
1869 1869 rev = opts['rev']
1870 1870 if rev:
1871 1871 node = repo.lookup(rev)
1872 1872 else:
1873 1873 node = None
1874 1874
1875 1875 for src, abs, rel, exact in walk(repo, pats, opts, node=node,
1876 1876 head='(?:.*/|)'):
1877 1877 if not node and repo.dirstate.state(abs) == '?':
1878 1878 continue
1879 1879 if opts['fullpath']:
1880 1880 ui.write(os.path.join(repo.root, abs), end)
1881 1881 else:
1882 1882 ui.write(((pats and rel) or abs), end)
1883 1883
1884 1884 def log(ui, repo, *pats, **opts):
1885 1885 """show revision history of entire repository or files
1886 1886
1887 1887 Print the revision history of the specified files or the entire project.
1888 1888
1889 1889 By default this command outputs: changeset id and hash, tags,
1890 1890 non-trivial parents, user, date and time, and a summary for each
1891 1891 commit. When the -v/--verbose switch is used, the list of changed
1892 1892 files and full commit message is shown.
1893 1893 """
1894 1894 class dui(object):
1895 1895 # Implement and delegate some ui protocol. Save hunks of
1896 1896 # output for later display in the desired order.
1897 1897 def __init__(self, ui):
1898 1898 self.ui = ui
1899 1899 self.hunk = {}
1900 1900 def bump(self, rev):
1901 1901 self.rev = rev
1902 1902 self.hunk[rev] = []
1903 1903 def note(self, *args):
1904 1904 if self.verbose:
1905 1905 self.write(*args)
1906 1906 def status(self, *args):
1907 1907 if not self.quiet:
1908 1908 self.write(*args)
1909 1909 def write(self, *args):
1910 1910 self.hunk[self.rev].append(args)
1911 1911 def debug(self, *args):
1912 1912 if self.debugflag:
1913 1913 self.write(*args)
1914 1914 def __getattr__(self, key):
1915 1915 return getattr(self.ui, key)
1916 1916
1917 1917 changeiter, getchange, matchfn = walkchangerevs(ui, repo, pats, opts)
1918 1918
1919 1919 if opts['limit']:
1920 1920 try:
1921 1921 limit = int(opts['limit'])
1922 1922 except ValueError:
1923 1923 raise util.Abort(_('limit must be a positive integer'))
1924 1924 if limit <= 0: raise util.Abort(_('limit must be positive'))
1925 1925 else:
1926 1926 limit = sys.maxint
1927 1927 count = 0
1928 1928
1929 1929 displayer = show_changeset(ui, repo, opts)
1930 1930 for st, rev, fns in changeiter:
1931 1931 if st == 'window':
1932 1932 du = dui(ui)
1933 1933 displayer.ui = du
1934 1934 elif st == 'add':
1935 1935 du.bump(rev)
1936 1936 changenode = repo.changelog.node(rev)
1937 1937 parents = [p for p in repo.changelog.parents(changenode)
1938 1938 if p != nullid]
1939 1939 if opts['no_merges'] and len(parents) == 2:
1940 1940 continue
1941 1941 if opts['only_merges'] and len(parents) != 2:
1942 1942 continue
1943 1943
1944 1944 if opts['keyword']:
1945 1945 changes = getchange(rev)
1946 1946 miss = 0
1947 1947 for k in [kw.lower() for kw in opts['keyword']]:
1948 1948 if not (k in changes[1].lower() or
1949 1949 k in changes[4].lower() or
1950 1950 k in " ".join(changes[3][:20]).lower()):
1951 1951 miss = 1
1952 1952 break
1953 1953 if miss:
1954 1954 continue
1955 1955
1956 1956 br = None
1957 1957 if opts['branches']:
1958 1958 br = repo.branchlookup([repo.changelog.node(rev)])
1959 1959
1960 1960 displayer.show(rev, brinfo=br)
1961 1961 if opts['patch']:
1962 1962 prev = (parents and parents[0]) or nullid
1963 1963 dodiff(du, du, repo, prev, changenode, match=matchfn)
1964 1964 du.write("\n\n")
1965 1965 elif st == 'iter':
1966 1966 if count == limit: break
1967 1967 if du.hunk[rev]:
1968 1968 count += 1
1969 1969 for args in du.hunk[rev]:
1970 1970 ui.write(*args)
1971 1971
1972 1972 def manifest(ui, repo, rev=None):
1973 1973 """output the latest or given revision of the project manifest
1974 1974
1975 1975 Print a list of version controlled files for the given revision.
1976 1976
1977 1977 The manifest is the list of files being version controlled. If no revision
1978 1978 is given then the tip is used.
1979 1979 """
1980 1980 if rev:
1981 1981 try:
1982 1982 # assume all revision numbers are for changesets
1983 1983 n = repo.lookup(rev)
1984 1984 change = repo.changelog.read(n)
1985 1985 n = change[0]
1986 1986 except hg.RepoError:
1987 1987 n = repo.manifest.lookup(rev)
1988 1988 else:
1989 1989 n = repo.manifest.tip()
1990 1990 m = repo.manifest.read(n)
1991 1991 mf = repo.manifest.readflags(n)
1992 1992 files = m.keys()
1993 1993 files.sort()
1994 1994
1995 1995 for f in files:
1996 1996 ui.write("%40s %3s %s\n" % (hex(m[f]), mf[f] and "755" or "644", f))
1997 1997
1998 1998 def merge(ui, repo, node=None, **opts):
1999 1999 """Merge working directory with another revision
2000 2000
2001 2001 Merge the contents of the current working directory and the
2002 2002 requested revision. Files that changed between either parent are
2003 2003 marked as changed for the next commit and a commit must be
2004 2004 performed before any further updates are allowed.
2005 2005 """
2006 2006 return update(ui, repo, node=node, merge=True, **opts)
2007 2007
2008 2008 def outgoing(ui, repo, dest="default-push", **opts):
2009 2009 """show changesets not found in destination
2010 2010
2011 2011 Show changesets not found in the specified destination repository or
2012 2012 the default push location. These are the changesets that would be pushed
2013 2013 if a push was requested.
2014 2014
2015 2015 See pull for valid destination format details.
2016 2016 """
2017 2017 dest = ui.expandpath(dest)
2018 2018 if opts['ssh']:
2019 2019 ui.setconfig("ui", "ssh", opts['ssh'])
2020 2020 if opts['remotecmd']:
2021 2021 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
2022 2022
2023 2023 other = hg.repository(ui, dest)
2024 2024 o = repo.findoutgoing(other, force=opts['force'])
2025 2025 if not o:
2026 2026 ui.status(_("no changes found\n"))
2027 2027 return
2028 2028 o = repo.changelog.nodesbetween(o)[0]
2029 2029 if opts['newest_first']:
2030 2030 o.reverse()
2031 2031 displayer = show_changeset(ui, repo, opts)
2032 2032 for n in o:
2033 2033 parents = [p for p in repo.changelog.parents(n) if p != nullid]
2034 2034 if opts['no_merges'] and len(parents) == 2:
2035 2035 continue
2036 2036 displayer.show(changenode=n)
2037 2037 if opts['patch']:
2038 2038 prev = (parents and parents[0]) or nullid
2039 2039 dodiff(ui, ui, repo, prev, n)
2040 2040 ui.write("\n")
2041 2041
2042 2042 def parents(ui, repo, rev=None, branches=None, **opts):
2043 2043 """show the parents of the working dir or revision
2044 2044
2045 2045 Print the working directory's parent revisions.
2046 2046 """
2047 2047 if rev:
2048 2048 p = repo.changelog.parents(repo.lookup(rev))
2049 2049 else:
2050 2050 p = repo.dirstate.parents()
2051 2051
2052 2052 br = None
2053 2053 if branches is not None:
2054 2054 br = repo.branchlookup(p)
2055 2055 displayer = show_changeset(ui, repo, opts)
2056 2056 for n in p:
2057 2057 if n != nullid:
2058 2058 displayer.show(changenode=n, brinfo=br)
2059 2059
2060 2060 def paths(ui, repo, search=None):
2061 2061 """show definition of symbolic path names
2062 2062
2063 2063 Show definition of symbolic path name NAME. If no name is given, show
2064 2064 definition of available names.
2065 2065
2066 2066 Path names are defined in the [paths] section of /etc/mercurial/hgrc
2067 2067 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
2068 2068 """
2069 2069 if search:
2070 2070 for name, path in ui.configitems("paths"):
2071 2071 if name == search:
2072 2072 ui.write("%s\n" % path)
2073 2073 return
2074 2074 ui.warn(_("not found!\n"))
2075 2075 return 1
2076 2076 else:
2077 2077 for name, path in ui.configitems("paths"):
2078 2078 ui.write("%s = %s\n" % (name, path))
2079 2079
2080 2080 def postincoming(ui, repo, modheads, optupdate):
2081 2081 if modheads == 0:
2082 2082 return
2083 2083 if optupdate:
2084 2084 if modheads == 1:
2085 2085 return update(ui, repo)
2086 2086 else:
2087 2087 ui.status(_("not updating, since new heads added\n"))
2088 2088 if modheads > 1:
2089 2089 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2090 2090 else:
2091 2091 ui.status(_("(run 'hg update' to get a working copy)\n"))
2092 2092
2093 2093 def pull(ui, repo, source="default", **opts):
2094 2094 """pull changes from the specified source
2095 2095
2096 2096 Pull changes from a remote repository to a local one.
2097 2097
2098 2098 This finds all changes from the repository at the specified path
2099 2099 or URL and adds them to the local repository. By default, this
2100 2100 does not update the copy of the project in the working directory.
2101 2101
2102 2102 Valid URLs are of the form:
2103 2103
2104 2104 local/filesystem/path
2105 2105 http://[user@]host[:port][/path]
2106 2106 https://[user@]host[:port][/path]
2107 2107 ssh://[user@]host[:port][/path]
2108 2108
2109 2109 Some notes about using SSH with Mercurial:
2110 2110 - SSH requires an accessible shell account on the destination machine
2111 2111 and a copy of hg in the remote path or specified with as remotecmd.
2112 2112 - /path is relative to the remote user's home directory by default.
2113 2113 Use two slashes at the start of a path to specify an absolute path.
2114 2114 - Mercurial doesn't use its own compression via SSH; the right thing
2115 2115 to do is to configure it in your ~/.ssh/ssh_config, e.g.:
2116 2116 Host *.mylocalnetwork.example.com
2117 2117 Compression off
2118 2118 Host *
2119 2119 Compression on
2120 2120 Alternatively specify "ssh -C" as your ssh command in your hgrc or
2121 2121 with the --ssh command line option.
2122 2122 """
2123 2123 source = ui.expandpath(source)
2124 2124 ui.status(_('pulling from %s\n') % (source))
2125 2125
2126 2126 if opts['ssh']:
2127 2127 ui.setconfig("ui", "ssh", opts['ssh'])
2128 2128 if opts['remotecmd']:
2129 2129 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
2130 2130
2131 2131 other = hg.repository(ui, source)
2132 2132 revs = None
2133 2133 if opts['rev'] and not other.local():
2134 2134 raise util.Abort(_("pull -r doesn't work for remote repositories yet"))
2135 2135 elif opts['rev']:
2136 2136 revs = [other.lookup(rev) for rev in opts['rev']]
2137 2137 modheads = repo.pull(other, heads=revs, force=opts['force'])
2138 2138 return postincoming(ui, repo, modheads, opts['update'])
2139 2139
2140 2140 def push(ui, repo, dest="default-push", **opts):
2141 2141 """push changes to the specified destination
2142 2142
2143 2143 Push changes from the local repository to the given destination.
2144 2144
2145 2145 This is the symmetrical operation for pull. It helps to move
2146 2146 changes from the current repository to a different one. If the
2147 2147 destination is local this is identical to a pull in that directory
2148 2148 from the current one.
2149 2149
2150 2150 By default, push will refuse to run if it detects the result would
2151 2151 increase the number of remote heads. This generally indicates the
2152 2152 the client has forgotten to sync and merge before pushing.
2153 2153
2154 2154 Valid URLs are of the form:
2155 2155
2156 2156 local/filesystem/path
2157 2157 ssh://[user@]host[:port][/path]
2158 2158
2159 2159 Look at the help text for the pull command for important details
2160 2160 about ssh:// URLs.
2161 2161 """
2162 2162 dest = ui.expandpath(dest)
2163 2163 ui.status('pushing to %s\n' % (dest))
2164 2164
2165 2165 if opts['ssh']:
2166 2166 ui.setconfig("ui", "ssh", opts['ssh'])
2167 2167 if opts['remotecmd']:
2168 2168 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
2169 2169
2170 2170 other = hg.repository(ui, dest)
2171 2171 revs = None
2172 2172 if opts['rev']:
2173 2173 revs = [repo.lookup(rev) for rev in opts['rev']]
2174 2174 r = repo.push(other, opts['force'], revs=revs)
2175 2175 return r == 0
2176 2176
2177 2177 def rawcommit(ui, repo, *flist, **rc):
2178 2178 """raw commit interface (DEPRECATED)
2179 2179
2180 2180 (DEPRECATED)
2181 2181 Lowlevel commit, for use in helper scripts.
2182 2182
2183 2183 This command is not intended to be used by normal users, as it is
2184 2184 primarily useful for importing from other SCMs.
2185 2185
2186 2186 This command is now deprecated and will be removed in a future
2187 2187 release, please use debugsetparents and commit instead.
2188 2188 """
2189 2189
2190 2190 ui.warn(_("(the rawcommit command is deprecated)\n"))
2191 2191
2192 2192 message = rc['message']
2193 2193 if not message and rc['logfile']:
2194 2194 try:
2195 2195 message = open(rc['logfile']).read()
2196 2196 except IOError:
2197 2197 pass
2198 2198 if not message and not rc['logfile']:
2199 2199 raise util.Abort(_("missing commit message"))
2200 2200
2201 2201 files = relpath(repo, list(flist))
2202 2202 if rc['files']:
2203 2203 files += open(rc['files']).read().splitlines()
2204 2204
2205 2205 rc['parent'] = map(repo.lookup, rc['parent'])
2206 2206
2207 2207 try:
2208 2208 repo.rawcommit(files, message, rc['user'], rc['date'], *rc['parent'])
2209 2209 except ValueError, inst:
2210 2210 raise util.Abort(str(inst))
2211 2211
2212 2212 def recover(ui, repo):
2213 2213 """roll back an interrupted transaction
2214 2214
2215 2215 Recover from an interrupted commit or pull.
2216 2216
2217 2217 This command tries to fix the repository status after an interrupted
2218 2218 operation. It should only be necessary when Mercurial suggests it.
2219 2219 """
2220 2220 if repo.recover():
2221 2221 return repo.verify()
2222 2222 return False
2223 2223
2224 2224 def remove(ui, repo, pat, *pats, **opts):
2225 2225 """remove the specified files on the next commit
2226 2226
2227 2227 Schedule the indicated files for removal from the repository.
2228 2228
2229 2229 This command schedules the files to be removed at the next commit.
2230 2230 This only removes files from the current branch, not from the
2231 2231 entire project history. If the files still exist in the working
2232 2232 directory, they will be deleted from it.
2233 2233 """
2234 2234 names = []
2235 2235 def okaytoremove(abs, rel, exact):
2236 2236 modified, added, removed, deleted, unknown = repo.changes(files=[abs])
2237 2237 reason = None
2238 2238 if modified and not opts['force']:
2239 2239 reason = _('is modified')
2240 2240 elif added:
2241 2241 reason = _('has been marked for add')
2242 2242 elif unknown:
2243 2243 reason = _('is not managed')
2244 2244 if reason:
2245 2245 if exact:
2246 2246 ui.warn(_('not removing %s: file %s\n') % (rel, reason))
2247 2247 else:
2248 2248 return True
2249 2249 for src, abs, rel, exact in walk(repo, (pat,) + pats, opts):
2250 2250 if okaytoremove(abs, rel, exact):
2251 2251 if ui.verbose or not exact:
2252 2252 ui.status(_('removing %s\n') % rel)
2253 2253 names.append(abs)
2254 2254 repo.remove(names, unlink=True)
2255 2255
2256 2256 def rename(ui, repo, *pats, **opts):
2257 2257 """rename files; equivalent of copy + remove
2258 2258
2259 2259 Mark dest as copies of sources; mark sources for deletion. If
2260 2260 dest is a directory, copies are put in that directory. If dest is
2261 2261 a file, there can only be one source.
2262 2262
2263 2263 By default, this command copies the contents of files as they
2264 2264 stand in the working directory. If invoked with --after, the
2265 2265 operation is recorded, but no copying is performed.
2266 2266
2267 2267 This command takes effect in the next commit.
2268 2268
2269 2269 NOTE: This command should be treated as experimental. While it
2270 2270 should properly record rename files, this information is not yet
2271 2271 fully used by merge, nor fully reported by log.
2272 2272 """
2273 2273 wlock = repo.wlock(0)
2274 2274 errs, copied = docopy(ui, repo, pats, opts, wlock)
2275 2275 names = []
2276 2276 for abs, rel, exact in copied:
2277 2277 if ui.verbose or not exact:
2278 2278 ui.status(_('removing %s\n') % rel)
2279 2279 names.append(abs)
2280 2280 repo.remove(names, True, wlock)
2281 2281 return errs
2282 2282
2283 2283 def revert(ui, repo, *pats, **opts):
2284 2284 """revert modified files or dirs back to their unmodified states
2285 2285
2286 2286 In its default mode, it reverts any uncommitted modifications made
2287 2287 to the named files or directories. This restores the contents of
2288 2288 the affected files to an unmodified state.
2289 2289
2290 Modified files have backup copies saved before revert. To disable
2291 backups, use --no-backup. To change the name of backup files, use
2292 --backup to give a format string.
2290 Modified files are saved with a .orig suffix before reverting.
2291 To disable these backups, use --no-backup.
2293 2292
2294 2293 Using the -r option, it reverts the given files or directories to
2295 2294 their state as of an earlier revision. This can be helpful to "roll
2296 2295 back" some or all of a change that should not have been committed.
2297 2296
2298 2297 Revert modifies the working directory. It does not commit any
2299 2298 changes, or change the parent of the current working directory.
2300 2299
2301 2300 If a file has been deleted, it is recreated. If the executable
2302 2301 mode of a file was changed, it is reset.
2303 2302
2304 2303 If names are given, all files matching the names are reverted.
2305 2304
2306 2305 If no arguments are given, all files in the repository are reverted.
2307 2306 """
2308 2307 parent = repo.dirstate.parents()[0]
2309 2308 node = opts['rev'] and repo.lookup(opts['rev']) or parent
2310 2309 mf = repo.manifest.read(repo.changelog.read(node)[0])
2311 2310
2312 def backup(name, exact):
2313 bakname = make_filename(repo, repo.changelog,
2314 opts['backup_name'] or '%p.orig',
2315 node=parent, pathname=name)
2316 if os.path.exists(name):
2317 # if backup already exists and is same as backup we want
2318 # to make, do nothing
2319 if os.path.exists(bakname):
2320 if repo.wread(name) == repo.wread(bakname):
2321 return
2322 raise util.Abort(_('cannot save current version of %s - '
2323 '%s exists and differs') %
2324 (name, bakname))
2325 ui.status(('saving current version of %s as %s\n') %
2326 (name, bakname))
2327 shutil.copyfile(name, bakname)
2328 shutil.copymode(name, bakname)
2329
2330 2311 wlock = repo.wlock()
2331 2312
2332 2313 entries = []
2333 2314 names = {}
2334 2315 for src, abs, rel, exact in walk(repo, pats, opts, badmatch=mf.has_key):
2335 2316 names[abs] = True
2336 2317 entries.append((abs, rel, exact))
2337 2318
2338 2319 changes = repo.changes(match=names.has_key, wlock=wlock)
2339 2320 modified, added, removed, deleted, unknown = map(dict.fromkeys, changes)
2340 2321
2341 2322 revert = ([], _('reverting %s\n'))
2342 2323 add = ([], _('adding %s\n'))
2343 2324 remove = ([], _('removing %s\n'))
2344 2325 forget = ([], _('forgetting %s\n'))
2345 2326 undelete = ([], _('undeleting %s\n'))
2346 2327 update = {}
2347 2328
2348 2329 disptable = (
2349 2330 # dispatch table:
2350 2331 # file state
2351 2332 # action if in target manifest
2352 2333 # action if not in target manifest
2353 2334 # make backup if in target manifest
2354 2335 # make backup if not in target manifest
2355 2336 (modified, revert, remove, True, True),
2356 2337 (added, revert, forget, True, True),
2357 2338 (removed, undelete, None, False, False),
2358 2339 (deleted, revert, remove, False, False),
2359 2340 (unknown, add, None, True, False),
2360 2341 )
2361 2342
2362 2343 for abs, rel, exact in entries:
2363 2344 def handle(xlist, dobackup):
2364 2345 xlist[0].append(abs)
2365 if dobackup and not opts['no_backup']:
2366 backup(rel, exact)
2346 if dobackup and not opts['no_backup'] and os.path.exists(rel):
2347 bakname = "%s.orig" % rel
2348 ui.note(_('saving current version of %s as %s\n') %
2349 (rel, bakname))
2350 shutil.copyfile(rel, bakname)
2351 shutil.copymode(rel, bakname)
2367 2352 if ui.verbose or not exact:
2368 2353 ui.status(xlist[1] % rel)
2369 2354 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2370 2355 if abs not in table: continue
2371 2356 # file has changed in dirstate
2372 2357 if abs in mf:
2373 2358 handle(hitlist, backuphit)
2374 2359 elif misslist is not None:
2375 2360 handle(misslist, backupmiss)
2376 2361 else:
2377 2362 if exact: ui.warn(_('file not managed: %s\n' % rel))
2378 2363 break
2379 2364 else:
2380 2365 # file has not changed in dirstate
2381 2366 if node == parent:
2382 2367 if exact: ui.warn(_('no changes needed to %s\n' % rel))
2383 2368 continue
2384 2369 if abs not in mf:
2385 2370 remove[0].append(abs)
2386 2371 update[abs] = True
2387 2372
2388 2373 repo.dirstate.forget(forget[0])
2389 2374 r = repo.update(node, False, True, update.has_key, False, wlock=wlock)
2390 2375 repo.dirstate.update(add[0], 'a')
2391 2376 repo.dirstate.update(undelete[0], 'n')
2392 2377 repo.dirstate.update(remove[0], 'r')
2393 2378 return r
2394 2379
2395 2380 def root(ui, repo):
2396 2381 """print the root (top) of the current working dir
2397 2382
2398 2383 Print the root directory of the current repository.
2399 2384 """
2400 2385 ui.write(repo.root + "\n")
2401 2386
2402 2387 def serve(ui, repo, **opts):
2403 2388 """export the repository via HTTP
2404 2389
2405 2390 Start a local HTTP repository browser and pull server.
2406 2391
2407 2392 By default, the server logs accesses to stdout and errors to
2408 2393 stderr. Use the "-A" and "-E" options to log to files.
2409 2394 """
2410 2395
2411 2396 if opts["stdio"]:
2412 2397 fin, fout = sys.stdin, sys.stdout
2413 2398 sys.stdout = sys.stderr
2414 2399
2415 2400 # Prevent insertion/deletion of CRs
2416 2401 util.set_binary(fin)
2417 2402 util.set_binary(fout)
2418 2403
2419 2404 def getarg():
2420 2405 argline = fin.readline()[:-1]
2421 2406 arg, l = argline.split()
2422 2407 val = fin.read(int(l))
2423 2408 return arg, val
2424 2409 def respond(v):
2425 2410 fout.write("%d\n" % len(v))
2426 2411 fout.write(v)
2427 2412 fout.flush()
2428 2413
2429 2414 lock = None
2430 2415
2431 2416 while 1:
2432 2417 cmd = fin.readline()[:-1]
2433 2418 if cmd == '':
2434 2419 return
2435 2420 if cmd == "heads":
2436 2421 h = repo.heads()
2437 2422 respond(" ".join(map(hex, h)) + "\n")
2438 2423 if cmd == "lock":
2439 2424 lock = repo.lock()
2440 2425 respond("")
2441 2426 if cmd == "unlock":
2442 2427 if lock:
2443 2428 lock.release()
2444 2429 lock = None
2445 2430 respond("")
2446 2431 elif cmd == "branches":
2447 2432 arg, nodes = getarg()
2448 2433 nodes = map(bin, nodes.split(" "))
2449 2434 r = []
2450 2435 for b in repo.branches(nodes):
2451 2436 r.append(" ".join(map(hex, b)) + "\n")
2452 2437 respond("".join(r))
2453 2438 elif cmd == "between":
2454 2439 arg, pairs = getarg()
2455 2440 pairs = [map(bin, p.split("-")) for p in pairs.split(" ")]
2456 2441 r = []
2457 2442 for b in repo.between(pairs):
2458 2443 r.append(" ".join(map(hex, b)) + "\n")
2459 2444 respond("".join(r))
2460 2445 elif cmd == "changegroup":
2461 2446 nodes = []
2462 2447 arg, roots = getarg()
2463 2448 nodes = map(bin, roots.split(" "))
2464 2449
2465 2450 cg = repo.changegroup(nodes, 'serve')
2466 2451 while 1:
2467 2452 d = cg.read(4096)
2468 2453 if not d:
2469 2454 break
2470 2455 fout.write(d)
2471 2456
2472 2457 fout.flush()
2473 2458
2474 2459 elif cmd == "addchangegroup":
2475 2460 if not lock:
2476 2461 respond("not locked")
2477 2462 continue
2478 2463 respond("")
2479 2464
2480 2465 r = repo.addchangegroup(fin)
2481 2466 respond(str(r))
2482 2467
2483 2468 optlist = "name templates style address port ipv6 accesslog errorlog"
2484 2469 for o in optlist.split():
2485 2470 if opts[o]:
2486 2471 ui.setconfig("web", o, opts[o])
2487 2472
2488 2473 if opts['daemon'] and not opts['daemon_pipefds']:
2489 2474 rfd, wfd = os.pipe()
2490 2475 args = sys.argv[:]
2491 2476 args.append('--daemon-pipefds=%d,%d' % (rfd, wfd))
2492 2477 pid = os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
2493 2478 args[0], args)
2494 2479 os.close(wfd)
2495 2480 os.read(rfd, 1)
2496 2481 os._exit(0)
2497 2482
2498 2483 try:
2499 2484 httpd = hgweb.create_server(repo)
2500 2485 except socket.error, inst:
2501 2486 raise util.Abort(_('cannot start server: ') + inst.args[1])
2502 2487
2503 2488 if ui.verbose:
2504 2489 addr, port = httpd.socket.getsockname()
2505 2490 if addr == '0.0.0.0':
2506 2491 addr = socket.gethostname()
2507 2492 else:
2508 2493 try:
2509 2494 addr = socket.gethostbyaddr(addr)[0]
2510 2495 except socket.error:
2511 2496 pass
2512 2497 if port != 80:
2513 2498 ui.status(_('listening at http://%s:%d/\n') % (addr, port))
2514 2499 else:
2515 2500 ui.status(_('listening at http://%s/\n') % addr)
2516 2501
2517 2502 if opts['pid_file']:
2518 2503 fp = open(opts['pid_file'], 'w')
2519 2504 fp.write(str(os.getpid()))
2520 2505 fp.close()
2521 2506
2522 2507 if opts['daemon_pipefds']:
2523 2508 rfd, wfd = [int(x) for x in opts['daemon_pipefds'].split(',')]
2524 2509 os.close(rfd)
2525 2510 os.write(wfd, 'y')
2526 2511 os.close(wfd)
2527 2512 sys.stdout.flush()
2528 2513 sys.stderr.flush()
2529 2514 fd = os.open(util.nulldev, os.O_RDWR)
2530 2515 if fd != 0: os.dup2(fd, 0)
2531 2516 if fd != 1: os.dup2(fd, 1)
2532 2517 if fd != 2: os.dup2(fd, 2)
2533 2518 if fd not in (0, 1, 2): os.close(fd)
2534 2519
2535 2520 httpd.serve_forever()
2536 2521
2537 2522 def status(ui, repo, *pats, **opts):
2538 2523 """show changed files in the working directory
2539 2524
2540 2525 Show changed files in the repository. If names are
2541 2526 given, only files that match are shown.
2542 2527
2543 2528 The codes used to show the status of files are:
2544 2529 M = modified
2545 2530 A = added
2546 2531 R = removed
2547 2532 ! = deleted, but still tracked
2548 2533 ? = not tracked
2549 2534 I = ignored (not shown by default)
2550 2535 """
2551 2536
2552 2537 show_ignored = opts['ignored'] and True or False
2553 2538 files, matchfn, anypats = matchpats(repo, pats, opts)
2554 2539 cwd = (pats and repo.getcwd()) or ''
2555 2540 modified, added, removed, deleted, unknown, ignored = [
2556 2541 [util.pathto(cwd, x) for x in n]
2557 2542 for n in repo.changes(files=files, match=matchfn,
2558 2543 show_ignored=show_ignored)]
2559 2544
2560 2545 changetypes = [('modified', 'M', modified),
2561 2546 ('added', 'A', added),
2562 2547 ('removed', 'R', removed),
2563 2548 ('deleted', '!', deleted),
2564 2549 ('unknown', '?', unknown),
2565 2550 ('ignored', 'I', ignored)]
2566 2551
2567 2552 end = opts['print0'] and '\0' or '\n'
2568 2553
2569 2554 for opt, char, changes in ([ct for ct in changetypes if opts[ct[0]]]
2570 2555 or changetypes):
2571 2556 if opts['no_status']:
2572 2557 format = "%%s%s" % end
2573 2558 else:
2574 2559 format = "%s %%s%s" % (char, end)
2575 2560
2576 2561 for f in changes:
2577 2562 ui.write(format % f)
2578 2563
2579 2564 def tag(ui, repo, name, rev_=None, **opts):
2580 2565 """add a tag for the current tip or a given revision
2581 2566
2582 2567 Name a particular revision using <name>.
2583 2568
2584 2569 Tags are used to name particular revisions of the repository and are
2585 2570 very useful to compare different revision, to go back to significant
2586 2571 earlier versions or to mark branch points as releases, etc.
2587 2572
2588 2573 If no revision is given, the tip is used.
2589 2574
2590 2575 To facilitate version control, distribution, and merging of tags,
2591 2576 they are stored as a file named ".hgtags" which is managed
2592 2577 similarly to other project files and can be hand-edited if
2593 2578 necessary. The file '.hg/localtags' is used for local tags (not
2594 2579 shared among repositories).
2595 2580 """
2596 2581 if name == "tip":
2597 2582 raise util.Abort(_("the name 'tip' is reserved"))
2598 2583 if rev_ is not None:
2599 2584 ui.warn(_("use of 'hg tag NAME [REV]' is deprecated, "
2600 2585 "please use 'hg tag [-r REV] NAME' instead\n"))
2601 2586 if opts['rev']:
2602 2587 raise util.Abort(_("use only one form to specify the revision"))
2603 2588 if opts['rev']:
2604 2589 rev_ = opts['rev']
2605 2590 if rev_:
2606 2591 r = hex(repo.lookup(rev_))
2607 2592 else:
2608 2593 r = hex(repo.changelog.tip())
2609 2594
2610 2595 disallowed = (revrangesep, '\r', '\n')
2611 2596 for c in disallowed:
2612 2597 if name.find(c) >= 0:
2613 2598 raise util.Abort(_("%s cannot be used in a tag name") % repr(c))
2614 2599
2615 2600 repo.hook('pretag', throw=True, node=r, tag=name,
2616 2601 local=int(not not opts['local']))
2617 2602
2618 2603 if opts['local']:
2619 2604 repo.opener("localtags", "a").write("%s %s\n" % (r, name))
2620 2605 repo.hook('tag', node=r, tag=name, local=1)
2621 2606 return
2622 2607
2623 2608 for x in repo.changes():
2624 2609 if ".hgtags" in x:
2625 2610 raise util.Abort(_("working copy of .hgtags is changed "
2626 2611 "(please commit .hgtags manually)"))
2627 2612
2628 2613 repo.wfile(".hgtags", "ab").write("%s %s\n" % (r, name))
2629 2614 if repo.dirstate.state(".hgtags") == '?':
2630 2615 repo.add([".hgtags"])
2631 2616
2632 2617 message = (opts['message'] or
2633 2618 _("Added tag %s for changeset %s") % (name, r))
2634 2619 try:
2635 2620 repo.commit([".hgtags"], message, opts['user'], opts['date'])
2636 2621 repo.hook('tag', node=r, tag=name, local=0)
2637 2622 except ValueError, inst:
2638 2623 raise util.Abort(str(inst))
2639 2624
2640 2625 def tags(ui, repo):
2641 2626 """list repository tags
2642 2627
2643 2628 List the repository tags.
2644 2629
2645 2630 This lists both regular and local tags.
2646 2631 """
2647 2632
2648 2633 l = repo.tagslist()
2649 2634 l.reverse()
2650 2635 for t, n in l:
2651 2636 try:
2652 2637 r = "%5d:%s" % (repo.changelog.rev(n), hex(n))
2653 2638 except KeyError:
2654 2639 r = " ?:?"
2655 2640 ui.write("%-30s %s\n" % (t, r))
2656 2641
2657 2642 def tip(ui, repo, **opts):
2658 2643 """show the tip revision
2659 2644
2660 2645 Show the tip revision.
2661 2646 """
2662 2647 n = repo.changelog.tip()
2663 2648 br = None
2664 2649 if opts['branches']:
2665 2650 br = repo.branchlookup([n])
2666 2651 show_changeset(ui, repo, opts).show(changenode=n, brinfo=br)
2667 2652 if opts['patch']:
2668 2653 dodiff(ui, ui, repo, repo.changelog.parents(n)[0], n)
2669 2654
2670 2655 def unbundle(ui, repo, fname, **opts):
2671 2656 """apply a changegroup file
2672 2657
2673 2658 Apply a compressed changegroup file generated by the bundle
2674 2659 command.
2675 2660 """
2676 2661 f = urllib.urlopen(fname)
2677 2662
2678 2663 header = f.read(6)
2679 2664 if not header.startswith("HG"):
2680 2665 raise util.Abort(_("%s: not a Mercurial bundle file") % fname)
2681 2666 elif not header.startswith("HG10"):
2682 2667 raise util.Abort(_("%s: unknown bundle version") % fname)
2683 2668 elif header == "HG10BZ":
2684 2669 def generator(f):
2685 2670 zd = bz2.BZ2Decompressor()
2686 2671 zd.decompress("BZ")
2687 2672 for chunk in f:
2688 2673 yield zd.decompress(chunk)
2689 2674 elif header == "HG10UN":
2690 2675 def generator(f):
2691 2676 for chunk in f:
2692 2677 yield chunk
2693 2678 else:
2694 2679 raise util.Abort(_("%s: unknown bundle compression type")
2695 2680 % fname)
2696 2681 gen = generator(util.filechunkiter(f, 4096))
2697 2682 modheads = repo.addchangegroup(util.chunkbuffer(gen))
2698 2683 return postincoming(ui, repo, modheads, opts['update'])
2699 2684
2700 2685 def undo(ui, repo):
2701 2686 """undo the last commit or pull
2702 2687
2703 2688 Roll back the last pull or commit transaction on the
2704 2689 repository, restoring the project to its earlier state.
2705 2690
2706 2691 This command should be used with care. There is only one level of
2707 2692 undo and there is no redo.
2708 2693
2709 2694 This command is not intended for use on public repositories. Once
2710 2695 a change is visible for pull by other users, undoing it locally is
2711 2696 ineffective.
2712 2697 """
2713 2698 repo.undo()
2714 2699
2715 2700 def update(ui, repo, node=None, merge=False, clean=False, force=None,
2716 2701 branch=None, **opts):
2717 2702 """update or merge working directory
2718 2703
2719 2704 Update the working directory to the specified revision.
2720 2705
2721 2706 If there are no outstanding changes in the working directory and
2722 2707 there is a linear relationship between the current version and the
2723 2708 requested version, the result is the requested version.
2724 2709
2725 2710 Otherwise the result is a merge between the contents of the
2726 2711 current working directory and the requested version. Files that
2727 2712 changed between either parent are marked as changed for the next
2728 2713 commit and a commit must be performed before any further updates
2729 2714 are allowed.
2730 2715
2731 2716 By default, update will refuse to run if doing so would require
2732 2717 merging or discarding local changes.
2733 2718 """
2734 2719 if branch:
2735 2720 br = repo.branchlookup(branch=branch)
2736 2721 found = []
2737 2722 for x in br:
2738 2723 if branch in br[x]:
2739 2724 found.append(x)
2740 2725 if len(found) > 1:
2741 2726 ui.warn(_("Found multiple heads for %s\n") % branch)
2742 2727 for x in found:
2743 2728 show_changeset(ui, repo, opts).show(changenode=x, brinfo=br)
2744 2729 return 1
2745 2730 if len(found) == 1:
2746 2731 node = found[0]
2747 2732 ui.warn(_("Using head %s for branch %s\n") % (short(node), branch))
2748 2733 else:
2749 2734 ui.warn(_("branch %s not found\n") % (branch))
2750 2735 return 1
2751 2736 else:
2752 2737 node = node and repo.lookup(node) or repo.changelog.tip()
2753 2738 return repo.update(node, allow=merge, force=clean, forcemerge=force)
2754 2739
2755 2740 def verify(ui, repo):
2756 2741 """verify the integrity of the repository
2757 2742
2758 2743 Verify the integrity of the current repository.
2759 2744
2760 2745 This will perform an extensive check of the repository's
2761 2746 integrity, validating the hashes and checksums of each entry in
2762 2747 the changelog, manifest, and tracked files, as well as the
2763 2748 integrity of their crosslinks and indices.
2764 2749 """
2765 2750 return repo.verify()
2766 2751
2767 2752 # Command options and aliases are listed here, alphabetically
2768 2753
2769 2754 table = {
2770 2755 "^add":
2771 2756 (add,
2772 2757 [('I', 'include', [], _('include names matching the given patterns')),
2773 2758 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2774 2759 _('hg add [OPTION]... [FILE]...')),
2775 2760 "addremove":
2776 2761 (addremove,
2777 2762 [('I', 'include', [], _('include names matching the given patterns')),
2778 2763 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2779 2764 _('hg addremove [OPTION]... [FILE]...')),
2780 2765 "^annotate":
2781 2766 (annotate,
2782 2767 [('r', 'rev', '', _('annotate the specified revision')),
2783 2768 ('a', 'text', None, _('treat all files as text')),
2784 2769 ('u', 'user', None, _('list the author')),
2785 2770 ('d', 'date', None, _('list the date')),
2786 2771 ('n', 'number', None, _('list the revision number (default)')),
2787 2772 ('c', 'changeset', None, _('list the changeset')),
2788 2773 ('I', 'include', [], _('include names matching the given patterns')),
2789 2774 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2790 2775 _('hg annotate [-r REV] [-a] [-u] [-d] [-n] [-c] FILE...')),
2791 2776 "bundle":
2792 2777 (bundle,
2793 2778 [('f', 'force', None,
2794 2779 _('run even when remote repository is unrelated'))],
2795 2780 _('hg bundle FILE DEST')),
2796 2781 "cat":
2797 2782 (cat,
2798 2783 [('o', 'output', '', _('print output to file with formatted name')),
2799 2784 ('r', 'rev', '', _('print the given revision')),
2800 2785 ('I', 'include', [], _('include names matching the given patterns')),
2801 2786 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2802 2787 _('hg cat [OPTION]... FILE...')),
2803 2788 "^clone":
2804 2789 (clone,
2805 2790 [('U', 'noupdate', None, _('do not update the new working directory')),
2806 2791 ('r', 'rev', [],
2807 2792 _('a changeset you would like to have after cloning')),
2808 2793 ('', 'pull', None, _('use pull protocol to copy metadata')),
2809 2794 ('e', 'ssh', '', _('specify ssh command to use')),
2810 2795 ('', 'remotecmd', '',
2811 2796 _('specify hg command to run on the remote side'))],
2812 2797 _('hg clone [OPTION]... SOURCE [DEST]')),
2813 2798 "^commit|ci":
2814 2799 (commit,
2815 2800 [('A', 'addremove', None, _('run addremove during commit')),
2816 2801 ('m', 'message', '', _('use <text> as commit message')),
2817 2802 ('l', 'logfile', '', _('read the commit message from <file>')),
2818 2803 ('d', 'date', '', _('record datecode as commit date')),
2819 2804 ('u', 'user', '', _('record user as commiter')),
2820 2805 ('I', 'include', [], _('include names matching the given patterns')),
2821 2806 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2822 2807 _('hg commit [OPTION]... [FILE]...')),
2823 2808 "copy|cp":
2824 2809 (copy,
2825 2810 [('A', 'after', None, _('record a copy that has already occurred')),
2826 2811 ('f', 'force', None,
2827 2812 _('forcibly copy over an existing managed file')),
2828 2813 ('I', 'include', [], _('include names matching the given patterns')),
2829 2814 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2830 2815 _('hg copy [OPTION]... [SOURCE]... DEST')),
2831 2816 "debugancestor": (debugancestor, [], _('debugancestor INDEX REV1 REV2')),
2832 2817 "debugcomplete": (debugcomplete, [], _('debugcomplete CMD')),
2833 2818 "debugrebuildstate":
2834 2819 (debugrebuildstate,
2835 2820 [('r', 'rev', '', _('revision to rebuild to'))],
2836 2821 _('debugrebuildstate [-r REV] [REV]')),
2837 2822 "debugcheckstate": (debugcheckstate, [], _('debugcheckstate')),
2838 2823 "debugconfig": (debugconfig, [], _('debugconfig')),
2839 2824 "debugsetparents": (debugsetparents, [], _('debugsetparents REV1 [REV2]')),
2840 2825 "debugstate": (debugstate, [], _('debugstate')),
2841 2826 "debugdata": (debugdata, [], _('debugdata FILE REV')),
2842 2827 "debugindex": (debugindex, [], _('debugindex FILE')),
2843 2828 "debugindexdot": (debugindexdot, [], _('debugindexdot FILE')),
2844 2829 "debugrename": (debugrename, [], _('debugrename FILE [REV]')),
2845 2830 "debugwalk":
2846 2831 (debugwalk,
2847 2832 [('I', 'include', [], _('include names matching the given patterns')),
2848 2833 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2849 2834 _('debugwalk [OPTION]... [FILE]...')),
2850 2835 "^diff":
2851 2836 (diff,
2852 2837 [('r', 'rev', [], _('revision')),
2853 2838 ('a', 'text', None, _('treat all files as text')),
2854 2839 ('p', 'show-function', None,
2855 2840 _('show which function each change is in')),
2856 2841 ('w', 'ignore-all-space', None,
2857 2842 _('ignore white space when comparing lines')),
2858 2843 ('I', 'include', [], _('include names matching the given patterns')),
2859 2844 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2860 2845 _('hg diff [-a] [-I] [-X] [-r REV1 [-r REV2]] [FILE]...')),
2861 2846 "^export":
2862 2847 (export,
2863 2848 [('o', 'output', '', _('print output to file with formatted name')),
2864 2849 ('a', 'text', None, _('treat all files as text')),
2865 2850 ('', 'switch-parent', None, _('diff against the second parent'))],
2866 2851 _('hg export [-a] [-o OUTFILESPEC] REV...')),
2867 2852 "forget":
2868 2853 (forget,
2869 2854 [('I', 'include', [], _('include names matching the given patterns')),
2870 2855 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2871 2856 _('hg forget [OPTION]... FILE...')),
2872 2857 "grep":
2873 2858 (grep,
2874 2859 [('0', 'print0', None, _('end fields with NUL')),
2875 2860 ('', 'all', None, _('print all revisions that match')),
2876 2861 ('i', 'ignore-case', None, _('ignore case when matching')),
2877 2862 ('l', 'files-with-matches', None,
2878 2863 _('print only filenames and revs that match')),
2879 2864 ('n', 'line-number', None, _('print matching line numbers')),
2880 2865 ('r', 'rev', [], _('search in given revision range')),
2881 2866 ('u', 'user', None, _('print user who committed change')),
2882 2867 ('I', 'include', [], _('include names matching the given patterns')),
2883 2868 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2884 2869 _('hg grep [OPTION]... PATTERN [FILE]...')),
2885 2870 "heads":
2886 2871 (heads,
2887 2872 [('b', 'branches', None, _('show branches')),
2888 2873 ('', 'style', '', _('display using template map file')),
2889 2874 ('r', 'rev', '', _('show only heads which are descendants of rev')),
2890 2875 ('', 'template', '', _('display with template'))],
2891 2876 _('hg heads [-b] [-r <rev>]')),
2892 2877 "help": (help_, [], _('hg help [COMMAND]')),
2893 2878 "identify|id": (identify, [], _('hg identify')),
2894 2879 "import|patch":
2895 2880 (import_,
2896 2881 [('p', 'strip', 1,
2897 2882 _('directory strip option for patch. This has the same\n') +
2898 2883 _('meaning as the corresponding patch option')),
2899 2884 ('b', 'base', '', _('base path')),
2900 2885 ('f', 'force', None,
2901 2886 _('skip check for outstanding uncommitted changes'))],
2902 2887 _('hg import [-p NUM] [-b BASE] [-f] PATCH...')),
2903 2888 "incoming|in": (incoming,
2904 2889 [('M', 'no-merges', None, _('do not show merges')),
2905 2890 ('f', 'force', None,
2906 2891 _('run even when remote repository is unrelated')),
2907 2892 ('', 'style', '', _('display using template map file')),
2908 2893 ('n', 'newest-first', None, _('show newest record first')),
2909 2894 ('', 'bundle', '', _('file to store the bundles into')),
2910 2895 ('p', 'patch', None, _('show patch')),
2911 2896 ('', 'template', '', _('display with template')),
2912 2897 ('e', 'ssh', '', _('specify ssh command to use')),
2913 2898 ('', 'remotecmd', '',
2914 2899 _('specify hg command to run on the remote side'))],
2915 2900 _('hg incoming [-p] [-n] [-M] [--bundle FILENAME] [SOURCE]')),
2916 2901 "^init": (init, [], _('hg init [DEST]')),
2917 2902 "locate":
2918 2903 (locate,
2919 2904 [('r', 'rev', '', _('search the repository as it stood at rev')),
2920 2905 ('0', 'print0', None,
2921 2906 _('end filenames with NUL, for use with xargs')),
2922 2907 ('f', 'fullpath', None,
2923 2908 _('print complete paths from the filesystem root')),
2924 2909 ('I', 'include', [], _('include names matching the given patterns')),
2925 2910 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2926 2911 _('hg locate [OPTION]... [PATTERN]...')),
2927 2912 "^log|history":
2928 2913 (log,
2929 2914 [('b', 'branches', None, _('show branches')),
2930 2915 ('k', 'keyword', [], _('search for a keyword')),
2931 2916 ('l', 'limit', '', _('limit number of changes displayed')),
2932 2917 ('r', 'rev', [], _('show the specified revision or range')),
2933 2918 ('M', 'no-merges', None, _('do not show merges')),
2934 2919 ('', 'style', '', _('display using template map file')),
2935 2920 ('m', 'only-merges', None, _('show only merges')),
2936 2921 ('p', 'patch', None, _('show patch')),
2937 2922 ('', 'template', '', _('display with template')),
2938 2923 ('I', 'include', [], _('include names matching the given patterns')),
2939 2924 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2940 2925 _('hg log [OPTION]... [FILE]')),
2941 2926 "manifest": (manifest, [], _('hg manifest [REV]')),
2942 2927 "merge":
2943 2928 (merge,
2944 2929 [('b', 'branch', '', _('merge with head of a specific branch')),
2945 2930 ('', 'style', '', _('display using template map file')),
2946 2931 ('f', 'force', None, _('force a merge with outstanding changes')),
2947 2932 ('', 'template', '', _('display with template'))],
2948 2933 _('hg merge [-b TAG] [-f] [REV]')),
2949 2934 "outgoing|out": (outgoing,
2950 2935 [('M', 'no-merges', None, _('do not show merges')),
2951 2936 ('f', 'force', None,
2952 2937 _('run even when remote repository is unrelated')),
2953 2938 ('p', 'patch', None, _('show patch')),
2954 2939 ('', 'style', '', _('display using template map file')),
2955 2940 ('n', 'newest-first', None, _('show newest record first')),
2956 2941 ('', 'template', '', _('display with template')),
2957 2942 ('e', 'ssh', '', _('specify ssh command to use')),
2958 2943 ('', 'remotecmd', '',
2959 2944 _('specify hg command to run on the remote side'))],
2960 2945 _('hg outgoing [-M] [-p] [-n] [DEST]')),
2961 2946 "^parents":
2962 2947 (parents,
2963 2948 [('b', 'branches', None, _('show branches')),
2964 2949 ('', 'style', '', _('display using template map file')),
2965 2950 ('', 'template', '', _('display with template'))],
2966 2951 _('hg parents [-b] [REV]')),
2967 2952 "paths": (paths, [], _('hg paths [NAME]')),
2968 2953 "^pull":
2969 2954 (pull,
2970 2955 [('u', 'update', None,
2971 2956 _('update the working directory to tip after pull')),
2972 2957 ('e', 'ssh', '', _('specify ssh command to use')),
2973 2958 ('f', 'force', None,
2974 2959 _('run even when remote repository is unrelated')),
2975 2960 ('r', 'rev', [], _('a specific revision you would like to pull')),
2976 2961 ('', 'remotecmd', '',
2977 2962 _('specify hg command to run on the remote side'))],
2978 2963 _('hg pull [-u] [-e FILE] [-r REV]... [--remotecmd FILE] [SOURCE]')),
2979 2964 "^push":
2980 2965 (push,
2981 2966 [('f', 'force', None, _('force push')),
2982 2967 ('e', 'ssh', '', _('specify ssh command to use')),
2983 2968 ('r', 'rev', [], _('a specific revision you would like to push')),
2984 2969 ('', 'remotecmd', '',
2985 2970 _('specify hg command to run on the remote side'))],
2986 2971 _('hg push [-f] [-e FILE] [-r REV]... [--remotecmd FILE] [DEST]')),
2987 2972 "debugrawcommit|rawcommit":
2988 2973 (rawcommit,
2989 2974 [('p', 'parent', [], _('parent')),
2990 2975 ('d', 'date', '', _('date code')),
2991 2976 ('u', 'user', '', _('user')),
2992 2977 ('F', 'files', '', _('file list')),
2993 2978 ('m', 'message', '', _('commit message')),
2994 2979 ('l', 'logfile', '', _('commit message file'))],
2995 2980 _('hg debugrawcommit [OPTION]... [FILE]...')),
2996 2981 "recover": (recover, [], _('hg recover')),
2997 2982 "^remove|rm":
2998 2983 (remove,
2999 2984 [('f', 'force', None, _('remove file even if modified')),
3000 2985 ('I', 'include', [], _('include names matching the given patterns')),
3001 2986 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
3002 2987 _('hg remove [OPTION]... FILE...')),
3003 2988 "rename|mv":
3004 2989 (rename,
3005 2990 [('A', 'after', None, _('record a rename that has already occurred')),
3006 2991 ('f', 'force', None,
3007 2992 _('forcibly copy over an existing managed file')),
3008 2993 ('I', 'include', [], _('include names matching the given patterns')),
3009 2994 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
3010 2995 _('hg rename [OPTION]... SOURCE... DEST')),
3011 2996 "^revert":
3012 2997 (revert,
3013 2998 [('r', 'rev', '', _('revision to revert to')),
3014 ('', 'backup-name', '', _('save backup with formatted name')),
3015 2999 ('', 'no-backup', None, _('do not save backup copies of files')),
3016 3000 ('I', 'include', [], _('include names matching given patterns')),
3017 3001 ('X', 'exclude', [], _('exclude names matching given patterns'))],
3018 3002 _('hg revert [-r REV] [NAME]...')),
3019 3003 "root": (root, [], _('hg root')),
3020 3004 "^serve":
3021 3005 (serve,
3022 3006 [('A', 'accesslog', '', _('name of access log file to write to')),
3023 3007 ('d', 'daemon', None, _('run server in background')),
3024 3008 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3025 3009 ('E', 'errorlog', '', _('name of error log file to write to')),
3026 3010 ('p', 'port', 0, _('port to use (default: 8000)')),
3027 3011 ('a', 'address', '', _('address to use')),
3028 3012 ('n', 'name', '',
3029 3013 _('name to show in web pages (default: working dir)')),
3030 3014 ('', 'pid-file', '', _('name of file to write process ID to')),
3031 3015 ('', 'stdio', None, _('for remote clients')),
3032 3016 ('t', 'templates', '', _('web templates to use')),
3033 3017 ('', 'style', '', _('template style to use')),
3034 3018 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4'))],
3035 3019 _('hg serve [OPTION]...')),
3036 3020 "^status|st":
3037 3021 (status,
3038 3022 [('m', 'modified', None, _('show only modified files')),
3039 3023 ('a', 'added', None, _('show only added files')),
3040 3024 ('r', 'removed', None, _('show only removed files')),
3041 3025 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3042 3026 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3043 3027 ('i', 'ignored', None, _('show ignored files')),
3044 3028 ('n', 'no-status', None, _('hide status prefix')),
3045 3029 ('0', 'print0', None,
3046 3030 _('end filenames with NUL, for use with xargs')),
3047 3031 ('I', 'include', [], _('include names matching the given patterns')),
3048 3032 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
3049 3033 _('hg status [OPTION]... [FILE]...')),
3050 3034 "tag":
3051 3035 (tag,
3052 3036 [('l', 'local', None, _('make the tag local')),
3053 3037 ('m', 'message', '', _('message for tag commit log entry')),
3054 3038 ('d', 'date', '', _('record datecode as commit date')),
3055 3039 ('u', 'user', '', _('record user as commiter')),
3056 3040 ('r', 'rev', '', _('revision to tag'))],
3057 3041 _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME')),
3058 3042 "tags": (tags, [], _('hg tags')),
3059 3043 "tip":
3060 3044 (tip,
3061 3045 [('b', 'branches', None, _('show branches')),
3062 3046 ('', 'style', '', _('display using template map file')),
3063 3047 ('p', 'patch', None, _('show patch')),
3064 3048 ('', 'template', '', _('display with template'))],
3065 3049 _('hg tip [-b] [-p]')),
3066 3050 "unbundle":
3067 3051 (unbundle,
3068 3052 [('u', 'update', None,
3069 3053 _('update the working directory to tip after unbundle'))],
3070 3054 _('hg unbundle [-u] FILE')),
3071 3055 "undo": (undo, [], _('hg undo')),
3072 3056 "^update|up|checkout|co":
3073 3057 (update,
3074 3058 [('b', 'branch', '', _('checkout the head of a specific branch')),
3075 3059 ('', 'style', '', _('display using template map file')),
3076 3060 ('m', 'merge', None, _('allow merging of branches')),
3077 3061 ('C', 'clean', None, _('overwrite locally modified files')),
3078 3062 ('f', 'force', None, _('force a merge with outstanding changes')),
3079 3063 ('', 'template', '', _('display with template'))],
3080 3064 _('hg update [-b TAG] [-m] [-C] [-f] [REV]')),
3081 3065 "verify": (verify, [], _('hg verify')),
3082 3066 "version": (show_version, [], _('hg version')),
3083 3067 }
3084 3068
3085 3069 globalopts = [
3086 3070 ('R', 'repository', '',
3087 3071 _('repository root directory or symbolic path name')),
3088 3072 ('', 'cwd', '', _('change working directory')),
3089 3073 ('y', 'noninteractive', None,
3090 3074 _('do not prompt, assume \'yes\' for any required answers')),
3091 3075 ('q', 'quiet', None, _('suppress output')),
3092 3076 ('v', 'verbose', None, _('enable additional output')),
3093 3077 ('', 'debug', None, _('enable debugging output')),
3094 3078 ('', 'debugger', None, _('start debugger')),
3095 3079 ('', 'traceback', None, _('print traceback on exception')),
3096 3080 ('', 'time', None, _('time how long the command takes')),
3097 3081 ('', 'profile', None, _('print command execution profile')),
3098 3082 ('', 'version', None, _('output version information and exit')),
3099 3083 ('h', 'help', None, _('display help and exit')),
3100 3084 ]
3101 3085
3102 3086 norepo = ("clone init version help debugancestor debugcomplete debugdata"
3103 3087 " debugindex debugindexdot")
3104 3088 optionalrepo = ("paths debugconfig")
3105 3089
3106 3090 def findpossible(cmd):
3107 3091 """
3108 3092 Return cmd -> (aliases, command table entry)
3109 3093 for each matching command
3110 3094 """
3111 3095 choice = {}
3112 3096 debugchoice = {}
3113 3097 for e in table.keys():
3114 3098 aliases = e.lstrip("^").split("|")
3115 3099 if cmd in aliases:
3116 3100 choice[cmd] = (aliases, table[e])
3117 3101 continue
3118 3102 for a in aliases:
3119 3103 if a.startswith(cmd):
3120 3104 if aliases[0].startswith("debug"):
3121 3105 debugchoice[a] = (aliases, table[e])
3122 3106 else:
3123 3107 choice[a] = (aliases, table[e])
3124 3108 break
3125 3109
3126 3110 if not choice and debugchoice:
3127 3111 choice = debugchoice
3128 3112
3129 3113 return choice
3130 3114
3131 3115 def find(cmd):
3132 3116 """Return (aliases, command table entry) for command string."""
3133 3117 choice = findpossible(cmd)
3134 3118
3135 3119 if choice.has_key(cmd):
3136 3120 return choice[cmd]
3137 3121
3138 3122 if len(choice) > 1:
3139 3123 clist = choice.keys()
3140 3124 clist.sort()
3141 3125 raise AmbiguousCommand(cmd, clist)
3142 3126
3143 3127 if choice:
3144 3128 return choice.values()[0]
3145 3129
3146 3130 raise UnknownCommand(cmd)
3147 3131
3148 3132 class SignalInterrupt(Exception):
3149 3133 """Exception raised on SIGTERM and SIGHUP."""
3150 3134
3151 3135 def catchterm(*args):
3152 3136 raise SignalInterrupt
3153 3137
3154 3138 def run():
3155 3139 sys.exit(dispatch(sys.argv[1:]))
3156 3140
3157 3141 class ParseError(Exception):
3158 3142 """Exception raised on errors in parsing the command line."""
3159 3143
3160 3144 def parse(ui, args):
3161 3145 options = {}
3162 3146 cmdoptions = {}
3163 3147
3164 3148 try:
3165 3149 args = fancyopts.fancyopts(args, globalopts, options)
3166 3150 except fancyopts.getopt.GetoptError, inst:
3167 3151 raise ParseError(None, inst)
3168 3152
3169 3153 if args:
3170 3154 cmd, args = args[0], args[1:]
3171 3155 aliases, i = find(cmd)
3172 3156 cmd = aliases[0]
3173 3157 defaults = ui.config("defaults", cmd)
3174 3158 if defaults:
3175 3159 args = defaults.split() + args
3176 3160 c = list(i[1])
3177 3161 else:
3178 3162 cmd = None
3179 3163 c = []
3180 3164
3181 3165 # combine global options into local
3182 3166 for o in globalopts:
3183 3167 c.append((o[0], o[1], options[o[1]], o[3]))
3184 3168
3185 3169 try:
3186 3170 args = fancyopts.fancyopts(args, c, cmdoptions)
3187 3171 except fancyopts.getopt.GetoptError, inst:
3188 3172 raise ParseError(cmd, inst)
3189 3173
3190 3174 # separate global options back out
3191 3175 for o in globalopts:
3192 3176 n = o[1]
3193 3177 options[n] = cmdoptions[n]
3194 3178 del cmdoptions[n]
3195 3179
3196 3180 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
3197 3181
3198 3182 def dispatch(args):
3199 3183 signal.signal(signal.SIGTERM, catchterm)
3200 3184 try:
3201 3185 signal.signal(signal.SIGHUP, catchterm)
3202 3186 except AttributeError:
3203 3187 pass
3204 3188
3205 3189 try:
3206 3190 u = ui.ui()
3207 3191 except util.Abort, inst:
3208 3192 sys.stderr.write(_("abort: %s\n") % inst)
3209 3193 sys.exit(1)
3210 3194
3211 3195 external = []
3212 3196 for x in u.extensions():
3213 3197 def on_exception(exc, inst):
3214 3198 u.warn(_("*** failed to import extension %s\n") % x[1])
3215 3199 u.warn("%s\n" % inst)
3216 3200 if "--traceback" in sys.argv[1:]:
3217 3201 traceback.print_exc()
3218 3202 if x[1]:
3219 3203 try:
3220 3204 mod = imp.load_source(x[0], x[1])
3221 3205 except Exception, inst:
3222 3206 on_exception(Exception, inst)
3223 3207 continue
3224 3208 else:
3225 3209 def importh(name):
3226 3210 mod = __import__(name)
3227 3211 components = name.split('.')
3228 3212 for comp in components[1:]:
3229 3213 mod = getattr(mod, comp)
3230 3214 return mod
3231 3215 try:
3232 3216 try:
3233 3217 mod = importh("hgext." + x[0])
3234 3218 except ImportError:
3235 3219 mod = importh(x[0])
3236 3220 except Exception, inst:
3237 3221 on_exception(Exception, inst)
3238 3222 continue
3239 3223
3240 3224 external.append(mod)
3241 3225 for x in external:
3242 3226 cmdtable = getattr(x, 'cmdtable', {})
3243 3227 for t in cmdtable:
3244 3228 if t in table:
3245 3229 u.warn(_("module %s overrides %s\n") % (x.__name__, t))
3246 3230 table.update(cmdtable)
3247 3231
3248 3232 try:
3249 3233 cmd, func, args, options, cmdoptions = parse(u, args)
3250 3234 if options["time"]:
3251 3235 def get_times():
3252 3236 t = os.times()
3253 3237 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
3254 3238 t = (t[0], t[1], t[2], t[3], time.clock())
3255 3239 return t
3256 3240 s = get_times()
3257 3241 def print_time():
3258 3242 t = get_times()
3259 3243 u.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
3260 3244 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
3261 3245 atexit.register(print_time)
3262 3246
3263 3247 u.updateopts(options["verbose"], options["debug"], options["quiet"],
3264 3248 not options["noninteractive"])
3265 3249
3266 3250 # enter the debugger before command execution
3267 3251 if options['debugger']:
3268 3252 pdb.set_trace()
3269 3253
3270 3254 try:
3271 3255 if options['cwd']:
3272 3256 try:
3273 3257 os.chdir(options['cwd'])
3274 3258 except OSError, inst:
3275 3259 raise util.Abort('%s: %s' %
3276 3260 (options['cwd'], inst.strerror))
3277 3261
3278 3262 path = u.expandpath(options["repository"]) or ""
3279 3263 repo = path and hg.repository(u, path=path) or None
3280 3264
3281 3265 if options['help']:
3282 3266 help_(u, cmd, options['version'])
3283 3267 sys.exit(0)
3284 3268 elif options['version']:
3285 3269 show_version(u)
3286 3270 sys.exit(0)
3287 3271 elif not cmd:
3288 3272 help_(u, 'shortlist')
3289 3273 sys.exit(0)
3290 3274
3291 3275 if cmd not in norepo.split():
3292 3276 try:
3293 3277 if not repo:
3294 3278 repo = hg.repository(u, path=path)
3295 3279 u = repo.ui
3296 3280 for x in external:
3297 3281 if hasattr(x, 'reposetup'):
3298 3282 x.reposetup(u, repo)
3299 3283 except hg.RepoError:
3300 3284 if cmd not in optionalrepo.split():
3301 3285 raise
3302 3286 d = lambda: func(u, repo, *args, **cmdoptions)
3303 3287 else:
3304 3288 d = lambda: func(u, *args, **cmdoptions)
3305 3289
3306 3290 try:
3307 3291 if options['profile']:
3308 3292 import hotshot, hotshot.stats
3309 3293 prof = hotshot.Profile("hg.prof")
3310 3294 try:
3311 3295 try:
3312 3296 return prof.runcall(d)
3313 3297 except:
3314 3298 try:
3315 3299 u.warn(_('exception raised - generating '
3316 3300 'profile anyway\n'))
3317 3301 except:
3318 3302 pass
3319 3303 raise
3320 3304 finally:
3321 3305 prof.close()
3322 3306 stats = hotshot.stats.load("hg.prof")
3323 3307 stats.strip_dirs()
3324 3308 stats.sort_stats('time', 'calls')
3325 3309 stats.print_stats(40)
3326 3310 else:
3327 3311 return d()
3328 3312 finally:
3329 3313 u.flush()
3330 3314 except:
3331 3315 # enter the debugger when we hit an exception
3332 3316 if options['debugger']:
3333 3317 pdb.post_mortem(sys.exc_info()[2])
3334 3318 if options['traceback']:
3335 3319 traceback.print_exc()
3336 3320 raise
3337 3321 except ParseError, inst:
3338 3322 if inst.args[0]:
3339 3323 u.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
3340 3324 help_(u, inst.args[0])
3341 3325 else:
3342 3326 u.warn(_("hg: %s\n") % inst.args[1])
3343 3327 help_(u, 'shortlist')
3344 3328 sys.exit(-1)
3345 3329 except AmbiguousCommand, inst:
3346 3330 u.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
3347 3331 (inst.args[0], " ".join(inst.args[1])))
3348 3332 sys.exit(1)
3349 3333 except UnknownCommand, inst:
3350 3334 u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
3351 3335 help_(u, 'shortlist')
3352 3336 sys.exit(1)
3353 3337 except hg.RepoError, inst:
3354 3338 u.warn(_("abort: "), inst, "!\n")
3355 3339 except lock.LockHeld, inst:
3356 3340 if inst.errno == errno.ETIMEDOUT:
3357 3341 reason = _('timed out waiting for lock held by %s') % inst.locker
3358 3342 else:
3359 3343 reason = _('lock held by %s') % inst.locker
3360 3344 u.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
3361 3345 except lock.LockUnavailable, inst:
3362 3346 u.warn(_("abort: could not lock %s: %s\n") %
3363 3347 (inst.desc or inst.filename, inst.strerror))
3364 3348 except revlog.RevlogError, inst:
3365 3349 u.warn(_("abort: "), inst, "!\n")
3366 3350 except SignalInterrupt:
3367 3351 u.warn(_("killed!\n"))
3368 3352 except KeyboardInterrupt:
3369 3353 try:
3370 3354 u.warn(_("interrupted!\n"))
3371 3355 except IOError, inst:
3372 3356 if inst.errno == errno.EPIPE:
3373 3357 if u.debugflag:
3374 3358 u.warn(_("\nbroken pipe\n"))
3375 3359 else:
3376 3360 raise
3377 3361 except IOError, inst:
3378 3362 if hasattr(inst, "code"):
3379 3363 u.warn(_("abort: %s\n") % inst)
3380 3364 elif hasattr(inst, "reason"):
3381 3365 u.warn(_("abort: error: %s\n") % inst.reason[1])
3382 3366 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
3383 3367 if u.debugflag:
3384 3368 u.warn(_("broken pipe\n"))
3385 3369 elif getattr(inst, "strerror", None):
3386 3370 if getattr(inst, "filename", None):
3387 3371 u.warn(_("abort: %s - %s\n") % (inst.strerror, inst.filename))
3388 3372 else:
3389 3373 u.warn(_("abort: %s\n") % inst.strerror)
3390 3374 else:
3391 3375 raise
3392 3376 except OSError, inst:
3393 3377 if hasattr(inst, "filename"):
3394 3378 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
3395 3379 else:
3396 3380 u.warn(_("abort: %s\n") % inst.strerror)
3397 3381 except util.Abort, inst:
3398 3382 u.warn(_('abort: '), inst.args[0] % inst.args[1:], '\n')
3399 3383 sys.exit(1)
3400 3384 except TypeError, inst:
3401 3385 # was this an argument error?
3402 3386 tb = traceback.extract_tb(sys.exc_info()[2])
3403 3387 if len(tb) > 2: # no
3404 3388 raise
3405 3389 u.debug(inst, "\n")
3406 3390 u.warn(_("%s: invalid arguments\n") % cmd)
3407 3391 help_(u, cmd)
3408 3392 except SystemExit:
3409 3393 # don't catch this in the catch-all below
3410 3394 raise
3411 3395 except:
3412 3396 u.warn(_("** unknown exception encountered, details follow\n"))
3413 3397 u.warn(_("** report bug details to mercurial@selenic.com\n"))
3414 3398 u.warn(_("** Mercurial Distributed SCM (version %s)\n")
3415 3399 % version.get_version())
3416 3400 raise
3417 3401
3418 3402 sys.exit(-1)
@@ -1,25 +1,24 b''
1 1 %%% should show a removed and b added
2 2 A b
3 3 R a
4 4 reverting...
5 saving current version of b as b.orig
6 5 forgetting b
7 6 undeleting a
8 7 %%% should show b unknown and a back to normal
9 8 ? b
10 9 ? b.orig
11 10 merging a
12 11 %%% should show foo-b
13 12 foo-b
14 13 %%% should show a removed and b added
15 14 A b
16 15 R a
17 16 ? b.orig
18 17 reverting...
19 18 forgetting b
20 19 undeleting a
21 20 %%% should show b unknown and a marked modified (merged)
22 21 ? b
23 22 ? b.orig
24 23 %%% should show foo-b
25 24 foo-b
@@ -1,20 +1,19 b''
1 1 1:016807e6fdaf
2 2 0:eb43f19ff115
3 3 016807e6fdaf tip
4 4 eb43f19ff115
5 5 eb43f19ff115+
6 saving current version of file1 as file1.orig
7 6 reverting file1
8 7 ? file1.orig
9 8 eb43f19ff115
10 9 ? file1.orig
11 10 016807e6fdaf tip
12 11 merging file1
13 12 ? file1.orig
14 13 016807e6fdaf tip
15 14 ? file1.orig
16 15 016807e6fdaf tip
17 16 ? file1.orig
18 17 016807e6fdaf tip
19 18 ? file1.orig
20 19 016807e6fdaf tip
@@ -1,43 +1,43 b''
1 1 #!/bin/sh
2 2
3 3 hg init
4 4 echo 123 > a
5 5 echo 123 > c
6 6 echo 123 > e
7 7 hg add a c e
8 8 hg commit -m "first" -d "1000000 0" a c e
9 9 echo 123 > b
10 10 echo %% should show b unknown
11 11 hg status
12 12 echo 12 > c
13 13 echo %% should show b unknown and c modified
14 14 hg status
15 15 hg add b
16 16 echo %% should show b added and c modified
17 17 hg status
18 18 hg rm a
19 19 echo %% should show a removed, b added and c modified
20 20 hg status
21 21 hg revert a
22 22 echo %% should show b added, copy saved, and c modified
23 23 hg status
24 24 hg revert b
25 echo %% should show b unknown, b.bak unknown, and c modified
25 echo %% should show b unknown, b.orig unknown, and c modified
26 26 hg status
27 27 hg revert --no-backup c
28 echo %% should show unknown: b b.bak
28 echo %% should show unknown: b b.orig
29 29 hg status
30 echo %% should show a b b.bak c e
30 echo %% should show a b b.orig c e
31 31 ls
32 echo %% should save backup to e.0
32 echo %% should verbosely save backup to e.orig
33 33 echo z > e
34 hg revert --backup='%p.%R'
34 hg revert -v
35 35 echo %% should say no changes needed
36 36 hg revert a
37 37 echo %% should say file not managed
38 38 echo q > q
39 39 hg revert q
40 40 echo %% should say file not found
41 41 hg revert notfound
42 42
43 43 true
@@ -1,38 +1,40 b''
1 1 %% should show b unknown
2 2 ? b
3 3 %% should show b unknown and c modified
4 4 M c
5 5 ? b
6 6 %% should show b added and c modified
7 7 M c
8 8 A b
9 9 %% should show a removed, b added and c modified
10 10 M c
11 11 A b
12 12 R a
13 13 %% should show b added, copy saved, and c modified
14 14 M c
15 15 A b
16 saving current version of b as b.orig
17 %% should show b unknown, b.bak unknown, and c modified
16 %% should show b unknown, b.orig unknown, and c modified
18 17 M c
19 18 ? b
20 19 ? b.orig
21 %% should show unknown: b b.bak
20 %% should show unknown: b b.orig
22 21 ? b
23 22 ? b.orig
24 %% should show a b b.bak c e
23 %% should show a b b.orig c e
25 24 a
26 25 b
27 26 b.orig
28 27 c
29 28 e
30 %% should save backup to e.0
31 saving current version of e as e.0
29 %% should verbosely save backup to e.orig
30 saving current version of e as e.orig
32 31 reverting e
32 resolving manifests
33 getting e
34 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
33 35 %% should say no changes needed
34 36 no changes needed to a
35 37 %% should say file not managed
36 38 file not managed: q
37 39 %% should say file not found
38 40 notfound: No such file or directory
@@ -1,30 +1,29 b''
1 1 changeset: 0:0acdaf898367
2 2 tag: tip
3 3 user: test
4 4 date: Mon Jan 12 13:46:40 1970 +0000
5 5 summary: test
6 6
7 7 changeset: 1:c5c60883086f
8 8 tag: tip
9 9 user: test
10 10 date: Mon Jan 12 13:46:40 1970 +0000
11 11 summary: Added tag bleah for changeset 0acdaf8983679e0aac16e811534eb49d7ee1f2b4
12 12
13 13 changeset: 0:0acdaf898367
14 14 tag: bleah
15 15 user: test
16 16 date: Mon Jan 12 13:46:40 1970 +0000
17 17 summary: test
18 18
19 19 abort: working copy of .hgtags is changed (please commit .hgtags manually)
20 20 failed
21 21 use of 'hg tag NAME [REV]' is deprecated, please use 'hg tag [-r REV] NAME' instead
22 22 abort: use only one form to specify the revision
23 23 failed
24 saving current version of .hgtags as .hgtags.orig
25 24 use of 'hg tag NAME [REV]' is deprecated, please use 'hg tag [-r REV] NAME' instead
26 25 0acdaf8983679e0aac16e811534eb49d7ee1f2b4 bleah
27 26 0acdaf8983679e0aac16e811534eb49d7ee1f2b4 bleah0
28 27 c5c60883086f5526bd3e36814b94a73a4e75e172 bleah1
29 28 abort: '\n' cannot be used in a tag name
30 29 abort: ':' cannot be used in a tag name
General Comments 0
You need to be logged in to leave comments. Login now