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