##// END OF EJS Templates
revert: fix corner case found by faheem mitha....
Vadim Gelfer -
r2272:e9a0ed9e default
parent child Browse files
Show More
@@ -1,3440 +1,3446 b''
1 1 # commands.py - command processing for mercurial
2 2 #
3 3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms
6 6 # of the GNU General Public License, incorporated herein by reference.
7 7
8 8 from demandload import demandload
9 9 from node import *
10 10 from i18n import gettext as _
11 11 demandload(globals(), "os re sys signal shutil imp urllib pdb")
12 12 demandload(globals(), "fancyopts ui hg util lock revlog templater bundlerepo")
13 13 demandload(globals(), "fnmatch hgweb mdiff random signal tempfile time")
14 14 demandload(globals(), "traceback errno socket version struct atexit sets bz2")
15 15 demandload(globals(), "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 update(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 update(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 e = r.index[i]
1309 1309 ui.write("\t%d -> %d\n" % (r.rev(e[4]), i))
1310 1310 if e[5] != nullid:
1311 1311 ui.write("\t%d -> %d\n" % (r.rev(e[5]), i))
1312 1312 ui.write("}\n")
1313 1313
1314 1314 def debugrename(ui, repo, file, rev=None):
1315 1315 """dump rename information"""
1316 1316 r = repo.file(relpath(repo, [file])[0])
1317 1317 if rev:
1318 1318 try:
1319 1319 # assume all revision numbers are for changesets
1320 1320 n = repo.lookup(rev)
1321 1321 change = repo.changelog.read(n)
1322 1322 m = repo.manifest.read(change[0])
1323 1323 n = m[relpath(repo, [file])[0]]
1324 1324 except (hg.RepoError, KeyError):
1325 1325 n = r.lookup(rev)
1326 1326 else:
1327 1327 n = r.tip()
1328 1328 m = r.renamed(n)
1329 1329 if m:
1330 1330 ui.write(_("renamed from %s:%s\n") % (m[0], hex(m[1])))
1331 1331 else:
1332 1332 ui.write(_("not renamed\n"))
1333 1333
1334 1334 def debugwalk(ui, repo, *pats, **opts):
1335 1335 """show how files match on given patterns"""
1336 1336 items = list(walk(repo, pats, opts))
1337 1337 if not items:
1338 1338 return
1339 1339 fmt = '%%s %%-%ds %%-%ds %%s' % (
1340 1340 max([len(abs) for (src, abs, rel, exact) in items]),
1341 1341 max([len(rel) for (src, abs, rel, exact) in items]))
1342 1342 for src, abs, rel, exact in items:
1343 1343 line = fmt % (src, abs, rel, exact and 'exact' or '')
1344 1344 ui.write("%s\n" % line.rstrip())
1345 1345
1346 1346 def diff(ui, repo, *pats, **opts):
1347 1347 """diff repository (or selected files)
1348 1348
1349 1349 Show differences between revisions for the specified files.
1350 1350
1351 1351 Differences between files are shown using the unified diff format.
1352 1352
1353 1353 When two revision arguments are given, then changes are shown
1354 1354 between those revisions. If only one revision is specified then
1355 1355 that revision is compared to the working directory, and, when no
1356 1356 revisions are specified, the working directory files are compared
1357 1357 to its parent.
1358 1358
1359 1359 Without the -a option, diff will avoid generating diffs of files
1360 1360 it detects as binary. With -a, diff will generate a diff anyway,
1361 1361 probably with undesirable results.
1362 1362 """
1363 1363 node1, node2 = None, None
1364 1364 revs = [repo.lookup(x) for x in opts['rev']]
1365 1365
1366 1366 if len(revs) > 0:
1367 1367 node1 = revs[0]
1368 1368 if len(revs) > 1:
1369 1369 node2 = revs[1]
1370 1370 if len(revs) > 2:
1371 1371 raise util.Abort(_("too many revisions to diff"))
1372 1372
1373 1373 fns, matchfn, anypats = matchpats(repo, pats, opts)
1374 1374
1375 1375 dodiff(sys.stdout, ui, repo, node1, node2, fns, match=matchfn,
1376 1376 text=opts['text'], opts=opts)
1377 1377
1378 1378 def doexport(ui, repo, changeset, seqno, total, revwidth, opts):
1379 1379 node = repo.lookup(changeset)
1380 1380 parents = [p for p in repo.changelog.parents(node) if p != nullid]
1381 1381 if opts['switch_parent']:
1382 1382 parents.reverse()
1383 1383 prev = (parents and parents[0]) or nullid
1384 1384 change = repo.changelog.read(node)
1385 1385
1386 1386 fp = make_file(repo, repo.changelog, opts['output'],
1387 1387 node=node, total=total, seqno=seqno,
1388 1388 revwidth=revwidth)
1389 1389 if fp != sys.stdout:
1390 1390 ui.note("%s\n" % fp.name)
1391 1391
1392 1392 fp.write("# HG changeset patch\n")
1393 1393 fp.write("# User %s\n" % change[1])
1394 1394 fp.write("# Node ID %s\n" % hex(node))
1395 1395 fp.write("# Parent %s\n" % hex(prev))
1396 1396 if len(parents) > 1:
1397 1397 fp.write("# Parent %s\n" % hex(parents[1]))
1398 1398 fp.write(change[4].rstrip())
1399 1399 fp.write("\n\n")
1400 1400
1401 1401 dodiff(fp, ui, repo, prev, node, text=opts['text'])
1402 1402 if fp != sys.stdout:
1403 1403 fp.close()
1404 1404
1405 1405 def export(ui, repo, *changesets, **opts):
1406 1406 """dump the header and diffs for one or more changesets
1407 1407
1408 1408 Print the changeset header and diffs for one or more revisions.
1409 1409
1410 1410 The information shown in the changeset header is: author,
1411 1411 changeset hash, parent and commit comment.
1412 1412
1413 1413 Output may be to a file, in which case the name of the file is
1414 1414 given using a format string. The formatting rules are as follows:
1415 1415
1416 1416 %% literal "%" character
1417 1417 %H changeset hash (40 bytes of hexadecimal)
1418 1418 %N number of patches being generated
1419 1419 %R changeset revision number
1420 1420 %b basename of the exporting repository
1421 1421 %h short-form changeset hash (12 bytes of hexadecimal)
1422 1422 %n zero-padded sequence number, starting at 1
1423 1423 %r zero-padded changeset revision number
1424 1424
1425 1425 Without the -a option, export will avoid generating diffs of files
1426 1426 it detects as binary. With -a, export will generate a diff anyway,
1427 1427 probably with undesirable results.
1428 1428
1429 1429 With the --switch-parent option, the diff will be against the second
1430 1430 parent. It can be useful to review a merge.
1431 1431 """
1432 1432 if not changesets:
1433 1433 raise util.Abort(_("export requires at least one changeset"))
1434 1434 seqno = 0
1435 1435 revs = list(revrange(ui, repo, changesets))
1436 1436 total = len(revs)
1437 1437 revwidth = max(map(len, revs))
1438 1438 msg = len(revs) > 1 and _("Exporting patches:\n") or _("Exporting patch:\n")
1439 1439 ui.note(msg)
1440 1440 for cset in revs:
1441 1441 seqno += 1
1442 1442 doexport(ui, repo, cset, seqno, total, revwidth, opts)
1443 1443
1444 1444 def forget(ui, repo, *pats, **opts):
1445 1445 """don't add the specified files on the next commit (DEPRECATED)
1446 1446
1447 1447 (DEPRECATED)
1448 1448 Undo an 'hg add' scheduled for the next commit.
1449 1449
1450 1450 This command is now deprecated and will be removed in a future
1451 1451 release. Please use revert instead.
1452 1452 """
1453 1453 ui.warn(_("(the forget command is deprecated; use revert instead)\n"))
1454 1454 forget = []
1455 1455 for src, abs, rel, exact in walk(repo, pats, opts):
1456 1456 if repo.dirstate.state(abs) == 'a':
1457 1457 forget.append(abs)
1458 1458 if ui.verbose or not exact:
1459 1459 ui.status(_('forgetting %s\n') % ((pats and rel) or abs))
1460 1460 repo.forget(forget)
1461 1461
1462 1462 def grep(ui, repo, pattern, *pats, **opts):
1463 1463 """search for a pattern in specified files and revisions
1464 1464
1465 1465 Search revisions of files for a regular expression.
1466 1466
1467 1467 This command behaves differently than Unix grep. It only accepts
1468 1468 Python/Perl regexps. It searches repository history, not the
1469 1469 working directory. It always prints the revision number in which
1470 1470 a match appears.
1471 1471
1472 1472 By default, grep only prints output for the first revision of a
1473 1473 file in which it finds a match. To get it to print every revision
1474 1474 that contains a change in match status ("-" for a match that
1475 1475 becomes a non-match, or "+" for a non-match that becomes a match),
1476 1476 use the --all flag.
1477 1477 """
1478 1478 reflags = 0
1479 1479 if opts['ignore_case']:
1480 1480 reflags |= re.I
1481 1481 regexp = re.compile(pattern, reflags)
1482 1482 sep, eol = ':', '\n'
1483 1483 if opts['print0']:
1484 1484 sep = eol = '\0'
1485 1485
1486 1486 fcache = {}
1487 1487 def getfile(fn):
1488 1488 if fn not in fcache:
1489 1489 fcache[fn] = repo.file(fn)
1490 1490 return fcache[fn]
1491 1491
1492 1492 def matchlines(body):
1493 1493 begin = 0
1494 1494 linenum = 0
1495 1495 while True:
1496 1496 match = regexp.search(body, begin)
1497 1497 if not match:
1498 1498 break
1499 1499 mstart, mend = match.span()
1500 1500 linenum += body.count('\n', begin, mstart) + 1
1501 1501 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1502 1502 lend = body.find('\n', mend)
1503 1503 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1504 1504 begin = lend + 1
1505 1505
1506 1506 class linestate(object):
1507 1507 def __init__(self, line, linenum, colstart, colend):
1508 1508 self.line = line
1509 1509 self.linenum = linenum
1510 1510 self.colstart = colstart
1511 1511 self.colend = colend
1512 1512 def __eq__(self, other):
1513 1513 return self.line == other.line
1514 1514 def __hash__(self):
1515 1515 return hash(self.line)
1516 1516
1517 1517 matches = {}
1518 1518 def grepbody(fn, rev, body):
1519 1519 matches[rev].setdefault(fn, {})
1520 1520 m = matches[rev][fn]
1521 1521 for lnum, cstart, cend, line in matchlines(body):
1522 1522 s = linestate(line, lnum, cstart, cend)
1523 1523 m[s] = s
1524 1524
1525 1525 # FIXME: prev isn't used, why ?
1526 1526 prev = {}
1527 1527 ucache = {}
1528 1528 def display(fn, rev, states, prevstates):
1529 1529 diff = list(sets.Set(states).symmetric_difference(sets.Set(prevstates)))
1530 1530 diff.sort(lambda x, y: cmp(x.linenum, y.linenum))
1531 1531 counts = {'-': 0, '+': 0}
1532 1532 filerevmatches = {}
1533 1533 for l in diff:
1534 1534 if incrementing or not opts['all']:
1535 1535 change = ((l in prevstates) and '-') or '+'
1536 1536 r = rev
1537 1537 else:
1538 1538 change = ((l in states) and '-') or '+'
1539 1539 r = prev[fn]
1540 1540 cols = [fn, str(rev)]
1541 1541 if opts['line_number']:
1542 1542 cols.append(str(l.linenum))
1543 1543 if opts['all']:
1544 1544 cols.append(change)
1545 1545 if opts['user']:
1546 1546 cols.append(trimuser(ui, getchange(rev)[1], rev,
1547 1547 ucache))
1548 1548 if opts['files_with_matches']:
1549 1549 c = (fn, rev)
1550 1550 if c in filerevmatches:
1551 1551 continue
1552 1552 filerevmatches[c] = 1
1553 1553 else:
1554 1554 cols.append(l.line)
1555 1555 ui.write(sep.join(cols), eol)
1556 1556 counts[change] += 1
1557 1557 return counts['+'], counts['-']
1558 1558
1559 1559 fstate = {}
1560 1560 skip = {}
1561 1561 changeiter, getchange, matchfn = walkchangerevs(ui, repo, pats, opts)
1562 1562 count = 0
1563 1563 incrementing = False
1564 1564 for st, rev, fns in changeiter:
1565 1565 if st == 'window':
1566 1566 incrementing = rev
1567 1567 matches.clear()
1568 1568 elif st == 'add':
1569 1569 change = repo.changelog.read(repo.lookup(str(rev)))
1570 1570 mf = repo.manifest.read(change[0])
1571 1571 matches[rev] = {}
1572 1572 for fn in fns:
1573 1573 if fn in skip:
1574 1574 continue
1575 1575 fstate.setdefault(fn, {})
1576 1576 try:
1577 1577 grepbody(fn, rev, getfile(fn).read(mf[fn]))
1578 1578 except KeyError:
1579 1579 pass
1580 1580 elif st == 'iter':
1581 1581 states = matches[rev].items()
1582 1582 states.sort()
1583 1583 for fn, m in states:
1584 1584 if fn in skip:
1585 1585 continue
1586 1586 if incrementing or not opts['all'] or fstate[fn]:
1587 1587 pos, neg = display(fn, rev, m, fstate[fn])
1588 1588 count += pos + neg
1589 1589 if pos and not opts['all']:
1590 1590 skip[fn] = True
1591 1591 fstate[fn] = m
1592 1592 prev[fn] = rev
1593 1593
1594 1594 if not incrementing:
1595 1595 fstate = fstate.items()
1596 1596 fstate.sort()
1597 1597 for fn, state in fstate:
1598 1598 if fn in skip:
1599 1599 continue
1600 1600 display(fn, rev, {}, state)
1601 1601 return (count == 0 and 1) or 0
1602 1602
1603 1603 def heads(ui, repo, **opts):
1604 1604 """show current repository heads
1605 1605
1606 1606 Show all repository head changesets.
1607 1607
1608 1608 Repository "heads" are changesets that don't have children
1609 1609 changesets. They are where development generally takes place and
1610 1610 are the usual targets for update and merge operations.
1611 1611 """
1612 1612 if opts['rev']:
1613 1613 heads = repo.heads(repo.lookup(opts['rev']))
1614 1614 else:
1615 1615 heads = repo.heads()
1616 1616 br = None
1617 1617 if opts['branches']:
1618 1618 br = repo.branchlookup(heads)
1619 1619 displayer = show_changeset(ui, repo, opts)
1620 1620 for n in heads:
1621 1621 displayer.show(changenode=n, brinfo=br)
1622 1622
1623 1623 def identify(ui, repo):
1624 1624 """print information about the working copy
1625 1625
1626 1626 Print a short summary of the current state of the repo.
1627 1627
1628 1628 This summary identifies the repository state using one or two parent
1629 1629 hash identifiers, followed by a "+" if there are uncommitted changes
1630 1630 in the working directory, followed by a list of tags for this revision.
1631 1631 """
1632 1632 parents = [p for p in repo.dirstate.parents() if p != nullid]
1633 1633 if not parents:
1634 1634 ui.write(_("unknown\n"))
1635 1635 return
1636 1636
1637 1637 hexfunc = ui.verbose and hex or short
1638 1638 modified, added, removed, deleted, unknown = repo.changes()
1639 1639 output = ["%s%s" %
1640 1640 ('+'.join([hexfunc(parent) for parent in parents]),
1641 1641 (modified or added or removed or deleted) and "+" or "")]
1642 1642
1643 1643 if not ui.quiet:
1644 1644 # multiple tags for a single parent separated by '/'
1645 1645 parenttags = ['/'.join(tags)
1646 1646 for tags in map(repo.nodetags, parents) if tags]
1647 1647 # tags for multiple parents separated by ' + '
1648 1648 if parenttags:
1649 1649 output.append(' + '.join(parenttags))
1650 1650
1651 1651 ui.write("%s\n" % ' '.join(output))
1652 1652
1653 1653 def import_(ui, repo, patch1, *patches, **opts):
1654 1654 """import an ordered set of patches
1655 1655
1656 1656 Import a list of patches and commit them individually.
1657 1657
1658 1658 If there are outstanding changes in the working directory, import
1659 1659 will abort unless given the -f flag.
1660 1660
1661 1661 If a patch looks like a mail message (its first line starts with
1662 1662 "From " or looks like an RFC822 header), it will not be applied
1663 1663 unless the -f option is used. The importer neither parses nor
1664 1664 discards mail headers, so use -f only to override the "mailness"
1665 1665 safety check, not to import a real mail message.
1666 1666 """
1667 1667 patches = (patch1,) + patches
1668 1668
1669 1669 if not opts['force']:
1670 1670 bail_if_changed(repo)
1671 1671
1672 1672 d = opts["base"]
1673 1673 strip = opts["strip"]
1674 1674
1675 1675 mailre = re.compile(r'(?:From |[\w-]+:)')
1676 1676
1677 1677 # attempt to detect the start of a patch
1678 1678 # (this heuristic is borrowed from quilt)
1679 1679 diffre = re.compile(r'(?:Index:[ \t]|diff[ \t]|RCS file: |' +
1680 1680 'retrieving revision [0-9]+(\.[0-9]+)*$|' +
1681 1681 '(---|\*\*\*)[ \t])')
1682 1682
1683 1683 for patch in patches:
1684 1684 ui.status(_("applying %s\n") % patch)
1685 1685 pf = os.path.join(d, patch)
1686 1686
1687 1687 message = []
1688 1688 user = None
1689 1689 hgpatch = False
1690 1690 for line in file(pf):
1691 1691 line = line.rstrip()
1692 1692 if (not message and not hgpatch and
1693 1693 mailre.match(line) and not opts['force']):
1694 1694 if len(line) > 35:
1695 1695 line = line[:32] + '...'
1696 1696 raise util.Abort(_('first line looks like a '
1697 1697 'mail header: ') + line)
1698 1698 if diffre.match(line):
1699 1699 break
1700 1700 elif hgpatch:
1701 1701 # parse values when importing the result of an hg export
1702 1702 if line.startswith("# User "):
1703 1703 user = line[7:]
1704 1704 ui.debug(_('User: %s\n') % user)
1705 1705 elif not line.startswith("# ") and line:
1706 1706 message.append(line)
1707 1707 hgpatch = False
1708 1708 elif line == '# HG changeset patch':
1709 1709 hgpatch = True
1710 1710 message = [] # We may have collected garbage
1711 1711 else:
1712 1712 message.append(line)
1713 1713
1714 1714 # make sure message isn't empty
1715 1715 if not message:
1716 1716 message = _("imported patch %s\n") % patch
1717 1717 else:
1718 1718 message = "%s\n" % '\n'.join(message)
1719 1719 ui.debug(_('message:\n%s\n') % message)
1720 1720
1721 1721 files = util.patch(strip, pf, ui)
1722 1722
1723 1723 if len(files) > 0:
1724 1724 addremove_lock(ui, repo, files, {})
1725 1725 repo.commit(files, message, user)
1726 1726
1727 1727 def incoming(ui, repo, source="default", **opts):
1728 1728 """show new changesets found in source
1729 1729
1730 1730 Show new changesets found in the specified path/URL or the default
1731 1731 pull location. These are the changesets that would be pulled if a pull
1732 1732 was requested.
1733 1733
1734 1734 For remote repository, using --bundle avoids downloading the changesets
1735 1735 twice if the incoming is followed by a pull.
1736 1736
1737 1737 See pull for valid source format details.
1738 1738 """
1739 1739 source = ui.expandpath(source)
1740 1740 if opts['ssh']:
1741 1741 ui.setconfig("ui", "ssh", opts['ssh'])
1742 1742 if opts['remotecmd']:
1743 1743 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
1744 1744
1745 1745 other = hg.repository(ui, source)
1746 1746 incoming = repo.findincoming(other, force=opts["force"])
1747 1747 if not incoming:
1748 1748 ui.status(_("no changes found\n"))
1749 1749 return
1750 1750
1751 1751 cleanup = None
1752 1752 try:
1753 1753 fname = opts["bundle"]
1754 1754 if fname or not other.local():
1755 1755 # create a bundle (uncompressed if other repo is not local)
1756 1756 cg = other.changegroup(incoming, "incoming")
1757 1757 fname = cleanup = write_bundle(cg, fname, compress=other.local())
1758 1758 # keep written bundle?
1759 1759 if opts["bundle"]:
1760 1760 cleanup = None
1761 1761 if not other.local():
1762 1762 # use the created uncompressed bundlerepo
1763 1763 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1764 1764
1765 1765 o = other.changelog.nodesbetween(incoming)[0]
1766 1766 if opts['newest_first']:
1767 1767 o.reverse()
1768 1768 displayer = show_changeset(ui, other, opts)
1769 1769 for n in o:
1770 1770 parents = [p for p in other.changelog.parents(n) if p != nullid]
1771 1771 if opts['no_merges'] and len(parents) == 2:
1772 1772 continue
1773 1773 displayer.show(changenode=n)
1774 1774 if opts['patch']:
1775 1775 prev = (parents and parents[0]) or nullid
1776 1776 dodiff(ui, ui, other, prev, n)
1777 1777 ui.write("\n")
1778 1778 finally:
1779 1779 if hasattr(other, 'close'):
1780 1780 other.close()
1781 1781 if cleanup:
1782 1782 os.unlink(cleanup)
1783 1783
1784 1784 def init(ui, dest="."):
1785 1785 """create a new repository in the given directory
1786 1786
1787 1787 Initialize a new repository in the given directory. If the given
1788 1788 directory does not exist, it is created.
1789 1789
1790 1790 If no directory is given, the current directory is used.
1791 1791 """
1792 1792 if not os.path.exists(dest):
1793 1793 os.mkdir(dest)
1794 1794 hg.repository(ui, dest, create=1)
1795 1795
1796 1796 def locate(ui, repo, *pats, **opts):
1797 1797 """locate files matching specific patterns
1798 1798
1799 1799 Print all files under Mercurial control whose names match the
1800 1800 given patterns.
1801 1801
1802 1802 This command searches the current directory and its
1803 1803 subdirectories. To search an entire repository, move to the root
1804 1804 of the repository.
1805 1805
1806 1806 If no patterns are given to match, this command prints all file
1807 1807 names.
1808 1808
1809 1809 If you want to feed the output of this command into the "xargs"
1810 1810 command, use the "-0" option to both this command and "xargs".
1811 1811 This will avoid the problem of "xargs" treating single filenames
1812 1812 that contain white space as multiple filenames.
1813 1813 """
1814 1814 end = opts['print0'] and '\0' or '\n'
1815 1815 rev = opts['rev']
1816 1816 if rev:
1817 1817 node = repo.lookup(rev)
1818 1818 else:
1819 1819 node = None
1820 1820
1821 1821 for src, abs, rel, exact in walk(repo, pats, opts, node=node,
1822 1822 head='(?:.*/|)'):
1823 1823 if not node and repo.dirstate.state(abs) == '?':
1824 1824 continue
1825 1825 if opts['fullpath']:
1826 1826 ui.write(os.path.join(repo.root, abs), end)
1827 1827 else:
1828 1828 ui.write(((pats and rel) or abs), end)
1829 1829
1830 1830 def log(ui, repo, *pats, **opts):
1831 1831 """show revision history of entire repository or files
1832 1832
1833 1833 Print the revision history of the specified files or the entire project.
1834 1834
1835 1835 By default this command outputs: changeset id and hash, tags,
1836 1836 non-trivial parents, user, date and time, and a summary for each
1837 1837 commit. When the -v/--verbose switch is used, the list of changed
1838 1838 files and full commit message is shown.
1839 1839 """
1840 1840 class dui(object):
1841 1841 # Implement and delegate some ui protocol. Save hunks of
1842 1842 # output for later display in the desired order.
1843 1843 def __init__(self, ui):
1844 1844 self.ui = ui
1845 1845 self.hunk = {}
1846 1846 self.header = {}
1847 1847 def bump(self, rev):
1848 1848 self.rev = rev
1849 1849 self.hunk[rev] = []
1850 1850 self.header[rev] = []
1851 1851 def note(self, *args):
1852 1852 if self.verbose:
1853 1853 self.write(*args)
1854 1854 def status(self, *args):
1855 1855 if not self.quiet:
1856 1856 self.write(*args)
1857 1857 def write(self, *args):
1858 1858 self.hunk[self.rev].append(args)
1859 1859 def write_header(self, *args):
1860 1860 self.header[self.rev].append(args)
1861 1861 def debug(self, *args):
1862 1862 if self.debugflag:
1863 1863 self.write(*args)
1864 1864 def __getattr__(self, key):
1865 1865 return getattr(self.ui, key)
1866 1866
1867 1867 changeiter, getchange, matchfn = walkchangerevs(ui, repo, pats, opts)
1868 1868
1869 1869 if opts['limit']:
1870 1870 try:
1871 1871 limit = int(opts['limit'])
1872 1872 except ValueError:
1873 1873 raise util.Abort(_('limit must be a positive integer'))
1874 1874 if limit <= 0: raise util.Abort(_('limit must be positive'))
1875 1875 else:
1876 1876 limit = sys.maxint
1877 1877 count = 0
1878 1878
1879 1879 displayer = show_changeset(ui, repo, opts)
1880 1880 for st, rev, fns in changeiter:
1881 1881 if st == 'window':
1882 1882 du = dui(ui)
1883 1883 displayer.ui = du
1884 1884 elif st == 'add':
1885 1885 du.bump(rev)
1886 1886 changenode = repo.changelog.node(rev)
1887 1887 parents = [p for p in repo.changelog.parents(changenode)
1888 1888 if p != nullid]
1889 1889 if opts['no_merges'] and len(parents) == 2:
1890 1890 continue
1891 1891 if opts['only_merges'] and len(parents) != 2:
1892 1892 continue
1893 1893
1894 1894 if opts['keyword']:
1895 1895 changes = getchange(rev)
1896 1896 miss = 0
1897 1897 for k in [kw.lower() for kw in opts['keyword']]:
1898 1898 if not (k in changes[1].lower() or
1899 1899 k in changes[4].lower() or
1900 1900 k in " ".join(changes[3][:20]).lower()):
1901 1901 miss = 1
1902 1902 break
1903 1903 if miss:
1904 1904 continue
1905 1905
1906 1906 br = None
1907 1907 if opts['branches']:
1908 1908 br = repo.branchlookup([repo.changelog.node(rev)])
1909 1909
1910 1910 displayer.show(rev, brinfo=br)
1911 1911 if opts['patch']:
1912 1912 prev = (parents and parents[0]) or nullid
1913 1913 dodiff(du, du, repo, prev, changenode, match=matchfn)
1914 1914 du.write("\n\n")
1915 1915 elif st == 'iter':
1916 1916 if count == limit: break
1917 1917 if du.header[rev]:
1918 1918 for args in du.header[rev]:
1919 1919 ui.write_header(*args)
1920 1920 if du.hunk[rev]:
1921 1921 count += 1
1922 1922 for args in du.hunk[rev]:
1923 1923 ui.write(*args)
1924 1924
1925 1925 def manifest(ui, repo, rev=None):
1926 1926 """output the latest or given revision of the project manifest
1927 1927
1928 1928 Print a list of version controlled files for the given revision.
1929 1929
1930 1930 The manifest is the list of files being version controlled. If no revision
1931 1931 is given then the tip is used.
1932 1932 """
1933 1933 if rev:
1934 1934 try:
1935 1935 # assume all revision numbers are for changesets
1936 1936 n = repo.lookup(rev)
1937 1937 change = repo.changelog.read(n)
1938 1938 n = change[0]
1939 1939 except hg.RepoError:
1940 1940 n = repo.manifest.lookup(rev)
1941 1941 else:
1942 1942 n = repo.manifest.tip()
1943 1943 m = repo.manifest.read(n)
1944 1944 mf = repo.manifest.readflags(n)
1945 1945 files = m.keys()
1946 1946 files.sort()
1947 1947
1948 1948 for f in files:
1949 1949 ui.write("%40s %3s %s\n" % (hex(m[f]), mf[f] and "755" or "644", f))
1950 1950
1951 1951 def merge(ui, repo, node=None, **opts):
1952 1952 """Merge working directory with another revision
1953 1953
1954 1954 Merge the contents of the current working directory and the
1955 1955 requested revision. Files that changed between either parent are
1956 1956 marked as changed for the next commit and a commit must be
1957 1957 performed before any further updates are allowed.
1958 1958 """
1959 1959 return update(ui, repo, node=node, merge=True, **opts)
1960 1960
1961 1961 def outgoing(ui, repo, dest="default-push", **opts):
1962 1962 """show changesets not found in destination
1963 1963
1964 1964 Show changesets not found in the specified destination repository or
1965 1965 the default push location. These are the changesets that would be pushed
1966 1966 if a push was requested.
1967 1967
1968 1968 See pull for valid destination format details.
1969 1969 """
1970 1970 dest = ui.expandpath(dest)
1971 1971 if opts['ssh']:
1972 1972 ui.setconfig("ui", "ssh", opts['ssh'])
1973 1973 if opts['remotecmd']:
1974 1974 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
1975 1975
1976 1976 other = hg.repository(ui, dest)
1977 1977 o = repo.findoutgoing(other, force=opts['force'])
1978 1978 if not o:
1979 1979 ui.status(_("no changes found\n"))
1980 1980 return
1981 1981 o = repo.changelog.nodesbetween(o)[0]
1982 1982 if opts['newest_first']:
1983 1983 o.reverse()
1984 1984 displayer = show_changeset(ui, repo, opts)
1985 1985 for n in o:
1986 1986 parents = [p for p in repo.changelog.parents(n) if p != nullid]
1987 1987 if opts['no_merges'] and len(parents) == 2:
1988 1988 continue
1989 1989 displayer.show(changenode=n)
1990 1990 if opts['patch']:
1991 1991 prev = (parents and parents[0]) or nullid
1992 1992 dodiff(ui, ui, repo, prev, n)
1993 1993 ui.write("\n")
1994 1994
1995 1995 def parents(ui, repo, rev=None, branches=None, **opts):
1996 1996 """show the parents of the working dir or revision
1997 1997
1998 1998 Print the working directory's parent revisions.
1999 1999 """
2000 2000 if rev:
2001 2001 p = repo.changelog.parents(repo.lookup(rev))
2002 2002 else:
2003 2003 p = repo.dirstate.parents()
2004 2004
2005 2005 br = None
2006 2006 if branches is not None:
2007 2007 br = repo.branchlookup(p)
2008 2008 displayer = show_changeset(ui, repo, opts)
2009 2009 for n in p:
2010 2010 if n != nullid:
2011 2011 displayer.show(changenode=n, brinfo=br)
2012 2012
2013 2013 def paths(ui, repo, search=None):
2014 2014 """show definition of symbolic path names
2015 2015
2016 2016 Show definition of symbolic path name NAME. If no name is given, show
2017 2017 definition of available names.
2018 2018
2019 2019 Path names are defined in the [paths] section of /etc/mercurial/hgrc
2020 2020 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
2021 2021 """
2022 2022 if search:
2023 2023 for name, path in ui.configitems("paths"):
2024 2024 if name == search:
2025 2025 ui.write("%s\n" % path)
2026 2026 return
2027 2027 ui.warn(_("not found!\n"))
2028 2028 return 1
2029 2029 else:
2030 2030 for name, path in ui.configitems("paths"):
2031 2031 ui.write("%s = %s\n" % (name, path))
2032 2032
2033 2033 def postincoming(ui, repo, modheads, optupdate):
2034 2034 if modheads == 0:
2035 2035 return
2036 2036 if optupdate:
2037 2037 if modheads == 1:
2038 2038 return update(ui, repo)
2039 2039 else:
2040 2040 ui.status(_("not updating, since new heads added\n"))
2041 2041 if modheads > 1:
2042 2042 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2043 2043 else:
2044 2044 ui.status(_("(run 'hg update' to get a working copy)\n"))
2045 2045
2046 2046 def pull(ui, repo, source="default", **opts):
2047 2047 """pull changes from the specified source
2048 2048
2049 2049 Pull changes from a remote repository to a local one.
2050 2050
2051 2051 This finds all changes from the repository at the specified path
2052 2052 or URL and adds them to the local repository. By default, this
2053 2053 does not update the copy of the project in the working directory.
2054 2054
2055 2055 Valid URLs are of the form:
2056 2056
2057 2057 local/filesystem/path
2058 2058 http://[user@]host[:port][/path]
2059 2059 https://[user@]host[:port][/path]
2060 2060 ssh://[user@]host[:port][/path]
2061 2061
2062 2062 Some notes about using SSH with Mercurial:
2063 2063 - SSH requires an accessible shell account on the destination machine
2064 2064 and a copy of hg in the remote path or specified with as remotecmd.
2065 2065 - /path is relative to the remote user's home directory by default.
2066 2066 Use two slashes at the start of a path to specify an absolute path.
2067 2067 - Mercurial doesn't use its own compression via SSH; the right thing
2068 2068 to do is to configure it in your ~/.ssh/ssh_config, e.g.:
2069 2069 Host *.mylocalnetwork.example.com
2070 2070 Compression off
2071 2071 Host *
2072 2072 Compression on
2073 2073 Alternatively specify "ssh -C" as your ssh command in your hgrc or
2074 2074 with the --ssh command line option.
2075 2075 """
2076 2076 source = ui.expandpath(source)
2077 2077 ui.status(_('pulling from %s\n') % (source))
2078 2078
2079 2079 if opts['ssh']:
2080 2080 ui.setconfig("ui", "ssh", opts['ssh'])
2081 2081 if opts['remotecmd']:
2082 2082 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
2083 2083
2084 2084 other = hg.repository(ui, source)
2085 2085 revs = None
2086 2086 if opts['rev'] and not other.local():
2087 2087 raise util.Abort(_("pull -r doesn't work for remote repositories yet"))
2088 2088 elif opts['rev']:
2089 2089 revs = [other.lookup(rev) for rev in opts['rev']]
2090 2090 modheads = repo.pull(other, heads=revs, force=opts['force'])
2091 2091 return postincoming(ui, repo, modheads, opts['update'])
2092 2092
2093 2093 def push(ui, repo, dest="default-push", **opts):
2094 2094 """push changes to the specified destination
2095 2095
2096 2096 Push changes from the local repository to the given destination.
2097 2097
2098 2098 This is the symmetrical operation for pull. It helps to move
2099 2099 changes from the current repository to a different one. If the
2100 2100 destination is local this is identical to a pull in that directory
2101 2101 from the current one.
2102 2102
2103 2103 By default, push will refuse to run if it detects the result would
2104 2104 increase the number of remote heads. This generally indicates the
2105 2105 the client has forgotten to sync and merge before pushing.
2106 2106
2107 2107 Valid URLs are of the form:
2108 2108
2109 2109 local/filesystem/path
2110 2110 ssh://[user@]host[:port][/path]
2111 2111
2112 2112 Look at the help text for the pull command for important details
2113 2113 about ssh:// URLs.
2114 2114 """
2115 2115 dest = ui.expandpath(dest)
2116 2116 ui.status('pushing to %s\n' % (dest))
2117 2117
2118 2118 if opts['ssh']:
2119 2119 ui.setconfig("ui", "ssh", opts['ssh'])
2120 2120 if opts['remotecmd']:
2121 2121 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
2122 2122
2123 2123 other = hg.repository(ui, dest)
2124 2124 revs = None
2125 2125 if opts['rev']:
2126 2126 revs = [repo.lookup(rev) for rev in opts['rev']]
2127 2127 r = repo.push(other, opts['force'], revs=revs)
2128 2128 return r == 0
2129 2129
2130 2130 def rawcommit(ui, repo, *flist, **rc):
2131 2131 """raw commit interface (DEPRECATED)
2132 2132
2133 2133 (DEPRECATED)
2134 2134 Lowlevel commit, for use in helper scripts.
2135 2135
2136 2136 This command is not intended to be used by normal users, as it is
2137 2137 primarily useful for importing from other SCMs.
2138 2138
2139 2139 This command is now deprecated and will be removed in a future
2140 2140 release, please use debugsetparents and commit instead.
2141 2141 """
2142 2142
2143 2143 ui.warn(_("(the rawcommit command is deprecated)\n"))
2144 2144
2145 2145 message = rc['message']
2146 2146 if not message and rc['logfile']:
2147 2147 try:
2148 2148 message = open(rc['logfile']).read()
2149 2149 except IOError:
2150 2150 pass
2151 2151 if not message and not rc['logfile']:
2152 2152 raise util.Abort(_("missing commit message"))
2153 2153
2154 2154 files = relpath(repo, list(flist))
2155 2155 if rc['files']:
2156 2156 files += open(rc['files']).read().splitlines()
2157 2157
2158 2158 rc['parent'] = map(repo.lookup, rc['parent'])
2159 2159
2160 2160 try:
2161 2161 repo.rawcommit(files, message, rc['user'], rc['date'], *rc['parent'])
2162 2162 except ValueError, inst:
2163 2163 raise util.Abort(str(inst))
2164 2164
2165 2165 def recover(ui, repo):
2166 2166 """roll back an interrupted transaction
2167 2167
2168 2168 Recover from an interrupted commit or pull.
2169 2169
2170 2170 This command tries to fix the repository status after an interrupted
2171 2171 operation. It should only be necessary when Mercurial suggests it.
2172 2172 """
2173 2173 if repo.recover():
2174 2174 return repo.verify()
2175 2175 return 1
2176 2176
2177 2177 def remove(ui, repo, *pats, **opts):
2178 2178 """remove the specified files on the next commit
2179 2179
2180 2180 Schedule the indicated files for removal from the repository.
2181 2181
2182 2182 This command schedules the files to be removed at the next commit.
2183 2183 This only removes files from the current branch, not from the
2184 2184 entire project history. If the files still exist in the working
2185 2185 directory, they will be deleted from it. If invoked with --after,
2186 2186 files that have been manually deleted are marked as removed.
2187 2187 """
2188 2188 names = []
2189 2189 if not opts['after'] and not pats:
2190 2190 raise util.Abort(_('no files specified'))
2191 2191 def okaytoremove(abs, rel, exact):
2192 2192 modified, added, removed, deleted, unknown = repo.changes(files=[abs])
2193 2193 reason = None
2194 2194 if not deleted and opts['after']:
2195 2195 reason = _('is still present')
2196 2196 elif modified and not opts['force']:
2197 2197 reason = _('is modified')
2198 2198 elif added:
2199 2199 reason = _('has been marked for add')
2200 2200 elif unknown:
2201 2201 reason = _('is not managed')
2202 2202 elif removed:
2203 2203 return False
2204 2204 if reason:
2205 2205 if exact:
2206 2206 ui.warn(_('not removing %s: file %s\n') % (rel, reason))
2207 2207 else:
2208 2208 return True
2209 2209 for src, abs, rel, exact in walk(repo, pats, opts):
2210 2210 if okaytoremove(abs, rel, exact):
2211 2211 if ui.verbose or not exact:
2212 2212 ui.status(_('removing %s\n') % rel)
2213 2213 names.append(abs)
2214 2214 repo.remove(names, unlink=not opts['after'])
2215 2215
2216 2216 def rename(ui, repo, *pats, **opts):
2217 2217 """rename files; equivalent of copy + remove
2218 2218
2219 2219 Mark dest as copies of sources; mark sources for deletion. If
2220 2220 dest is a directory, copies are put in that directory. If dest is
2221 2221 a file, there can only be one source.
2222 2222
2223 2223 By default, this command copies the contents of files as they
2224 2224 stand in the working directory. If invoked with --after, the
2225 2225 operation is recorded, but no copying is performed.
2226 2226
2227 2227 This command takes effect in the next commit.
2228 2228
2229 2229 NOTE: This command should be treated as experimental. While it
2230 2230 should properly record rename files, this information is not yet
2231 2231 fully used by merge, nor fully reported by log.
2232 2232 """
2233 2233 wlock = repo.wlock(0)
2234 2234 errs, copied = docopy(ui, repo, pats, opts, wlock)
2235 2235 names = []
2236 2236 for abs, rel, exact in copied:
2237 2237 if ui.verbose or not exact:
2238 2238 ui.status(_('removing %s\n') % rel)
2239 2239 names.append(abs)
2240 2240 repo.remove(names, True, wlock)
2241 2241 return errs
2242 2242
2243 2243 def revert(ui, repo, *pats, **opts):
2244 2244 """revert files or dirs to their states as of some revision
2245 2245
2246 2246 With no revision specified, revert the named files or directories
2247 2247 to the contents they had in the parent of the working directory.
2248 2248 This restores the contents of the affected files to an unmodified
2249 2249 state. If the working directory has two parents, you must
2250 2250 explicitly specify the revision to revert to.
2251 2251
2252 2252 Modified files are saved with a .orig suffix before reverting.
2253 2253 To disable these backups, use --no-backup.
2254 2254
2255 2255 Using the -r option, revert the given files or directories to
2256 2256 their contents as of a specific revision. This can be helpful to"roll
2257 2257 back" some or all of a change that should not have been committed.
2258 2258
2259 2259 Revert modifies the working directory. It does not commit any
2260 2260 changes, or change the parent of the working directory. If you
2261 2261 revert to a revision other than the parent of the working
2262 2262 directory, the reverted files will thus appear modified
2263 2263 afterwards.
2264 2264
2265 2265 If a file has been deleted, it is recreated. If the executable
2266 2266 mode of a file was changed, it is reset.
2267 2267
2268 2268 If names are given, all files matching the names are reverted.
2269 2269
2270 2270 If no arguments are given, all files in the repository are reverted.
2271 2271 """
2272 2272 parent, p2 = repo.dirstate.parents()
2273 2273 if opts['rev']:
2274 2274 node = repo.lookup(opts['rev'])
2275 2275 elif p2 != nullid:
2276 2276 raise util.Abort(_('working dir has two parents; '
2277 2277 'you must specify the revision to revert to'))
2278 2278 else:
2279 2279 node = parent
2280 pmf = None
2280 2281 mf = repo.manifest.read(repo.changelog.read(node)[0])
2281 2282
2282 2283 wlock = repo.wlock()
2283 2284
2284 2285 # need all matching names in dirstate and manifest of target rev,
2285 2286 # so have to walk both. do not print errors if files exist in one
2286 2287 # but not other.
2287 2288
2288 2289 names = {}
2289 2290 target_only = {}
2290 2291
2291 2292 # walk dirstate.
2292 2293
2293 2294 for src, abs, rel, exact in walk(repo, pats, opts, badmatch=mf.has_key):
2294 2295 names[abs] = (rel, exact)
2295 2296 if src == 'b':
2296 2297 target_only[abs] = True
2297 2298
2298 2299 # walk target manifest.
2299 2300
2300 2301 for src, abs, rel, exact in walk(repo, pats, opts, node=node,
2301 2302 badmatch=names.has_key):
2302 2303 if abs in names: continue
2303 2304 names[abs] = (rel, exact)
2304 2305 target_only[abs] = True
2305 2306
2306 2307 changes = repo.changes(match=names.has_key, wlock=wlock)
2307 2308 modified, added, removed, deleted, unknown = map(dict.fromkeys, changes)
2308 2309
2309 2310 revert = ([], _('reverting %s\n'))
2310 2311 add = ([], _('adding %s\n'))
2311 2312 remove = ([], _('removing %s\n'))
2312 2313 forget = ([], _('forgetting %s\n'))
2313 2314 undelete = ([], _('undeleting %s\n'))
2314 2315 update = {}
2315 2316
2316 2317 disptable = (
2317 2318 # dispatch table:
2318 2319 # file state
2319 2320 # action if in target manifest
2320 2321 # action if not in target manifest
2321 2322 # make backup if in target manifest
2322 2323 # make backup if not in target manifest
2323 2324 (modified, revert, remove, True, True),
2324 2325 (added, revert, forget, True, False),
2325 2326 (removed, undelete, None, False, False),
2326 2327 (deleted, revert, remove, False, False),
2327 2328 (unknown, add, None, True, False),
2328 2329 (target_only, add, None, False, False),
2329 2330 )
2330 2331
2331 2332 entries = names.items()
2332 2333 entries.sort()
2333 2334
2334 2335 for abs, (rel, exact) in entries:
2335 2336 in_mf = abs in mf
2336 2337 def handle(xlist, dobackup):
2337 2338 xlist[0].append(abs)
2338 2339 if dobackup and not opts['no_backup'] and os.path.exists(rel):
2339 2340 bakname = "%s.orig" % rel
2340 2341 ui.note(_('saving current version of %s as %s\n') %
2341 2342 (rel, bakname))
2342 2343 shutil.copyfile(rel, bakname)
2343 2344 shutil.copymode(rel, bakname)
2344 2345 if ui.verbose or not exact:
2345 2346 ui.status(xlist[1] % rel)
2346 2347 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2347 2348 if abs not in table: continue
2348 2349 # file has changed in dirstate
2349 2350 if in_mf:
2350 2351 handle(hitlist, backuphit)
2351 2352 elif misslist is not None:
2352 2353 handle(misslist, backupmiss)
2353 2354 else:
2354 2355 if exact: ui.warn(_('file not managed: %s\n' % rel))
2355 2356 break
2356 2357 else:
2357 2358 # file has not changed in dirstate
2358 2359 if node == parent:
2359 2360 if exact: ui.warn(_('no changes needed to %s\n' % rel))
2360 2361 continue
2361 2362 if not in_mf:
2362 handle(remove, False)
2363 if pmf is None:
2364 # only need parent manifest in this unlikely case,
2365 # so do not read by default
2366 pmf = repo.manifest.read(repo.changelog.read(parent)[0])
2367 if abs in pmf:
2368 handle(remove, False)
2363 2369 update[abs] = True
2364 2370
2365 2371 repo.dirstate.forget(forget[0])
2366 2372 r = repo.update(node, False, True, update.has_key, False, wlock=wlock,
2367 2373 show_stats=False)
2368 2374 repo.dirstate.update(add[0], 'a')
2369 2375 repo.dirstate.update(undelete[0], 'n')
2370 2376 repo.dirstate.update(remove[0], 'r')
2371 2377 return r
2372 2378
2373 2379 def rollback(ui, repo):
2374 2380 """roll back the last transaction in this repository
2375 2381
2376 2382 Roll back the last transaction in this repository, restoring the
2377 2383 project to its state prior to the transaction.
2378 2384
2379 2385 Transactions are used to encapsulate the effects of all commands
2380 2386 that create new changesets or propagate existing changesets into a
2381 2387 repository. For example, the following commands are transactional,
2382 2388 and their effects can be rolled back:
2383 2389
2384 2390 commit
2385 2391 import
2386 2392 pull
2387 2393 push (with this repository as destination)
2388 2394 unbundle
2389 2395
2390 2396 This command should be used with care. There is only one level of
2391 2397 rollback, and there is no way to undo a rollback.
2392 2398
2393 2399 This command is not intended for use on public repositories. Once
2394 2400 changes are visible for pull by other users, rolling a transaction
2395 2401 back locally is ineffective (someone else may already have pulled
2396 2402 the changes). Furthermore, a race is possible with readers of the
2397 2403 repository; for example an in-progress pull from the repository
2398 2404 may fail if a rollback is performed.
2399 2405 """
2400 2406 repo.undo()
2401 2407
2402 2408 def root(ui, repo):
2403 2409 """print the root (top) of the current working dir
2404 2410
2405 2411 Print the root directory of the current repository.
2406 2412 """
2407 2413 ui.write(repo.root + "\n")
2408 2414
2409 2415 def serve(ui, repo, **opts):
2410 2416 """export the repository via HTTP
2411 2417
2412 2418 Start a local HTTP repository browser and pull server.
2413 2419
2414 2420 By default, the server logs accesses to stdout and errors to
2415 2421 stderr. Use the "-A" and "-E" options to log to files.
2416 2422 """
2417 2423
2418 2424 if opts["stdio"]:
2419 2425 if repo is None:
2420 2426 raise hg.RepoError(_('no repo found'))
2421 2427 fin, fout = sys.stdin, sys.stdout
2422 2428 sys.stdout = sys.stderr
2423 2429
2424 2430 # Prevent insertion/deletion of CRs
2425 2431 util.set_binary(fin)
2426 2432 util.set_binary(fout)
2427 2433
2428 2434 def getarg():
2429 2435 argline = fin.readline()[:-1]
2430 2436 arg, l = argline.split()
2431 2437 val = fin.read(int(l))
2432 2438 return arg, val
2433 2439 def respond(v):
2434 2440 fout.write("%d\n" % len(v))
2435 2441 fout.write(v)
2436 2442 fout.flush()
2437 2443
2438 2444 lock = None
2439 2445
2440 2446 while 1:
2441 2447 cmd = fin.readline()[:-1]
2442 2448 if cmd == '':
2443 2449 return
2444 2450 if cmd == "heads":
2445 2451 h = repo.heads()
2446 2452 respond(" ".join(map(hex, h)) + "\n")
2447 2453 if cmd == "lock":
2448 2454 lock = repo.lock()
2449 2455 respond("")
2450 2456 if cmd == "unlock":
2451 2457 if lock:
2452 2458 lock.release()
2453 2459 lock = None
2454 2460 respond("")
2455 2461 elif cmd == "branches":
2456 2462 arg, nodes = getarg()
2457 2463 nodes = map(bin, nodes.split(" "))
2458 2464 r = []
2459 2465 for b in repo.branches(nodes):
2460 2466 r.append(" ".join(map(hex, b)) + "\n")
2461 2467 respond("".join(r))
2462 2468 elif cmd == "between":
2463 2469 arg, pairs = getarg()
2464 2470 pairs = [map(bin, p.split("-")) for p in pairs.split(" ")]
2465 2471 r = []
2466 2472 for b in repo.between(pairs):
2467 2473 r.append(" ".join(map(hex, b)) + "\n")
2468 2474 respond("".join(r))
2469 2475 elif cmd == "changegroup":
2470 2476 nodes = []
2471 2477 arg, roots = getarg()
2472 2478 nodes = map(bin, roots.split(" "))
2473 2479
2474 2480 cg = repo.changegroup(nodes, 'serve')
2475 2481 while 1:
2476 2482 d = cg.read(4096)
2477 2483 if not d:
2478 2484 break
2479 2485 fout.write(d)
2480 2486
2481 2487 fout.flush()
2482 2488
2483 2489 elif cmd == "addchangegroup":
2484 2490 if not lock:
2485 2491 respond("not locked")
2486 2492 continue
2487 2493 respond("")
2488 2494
2489 2495 r = repo.addchangegroup(fin, 'serve')
2490 2496 respond(str(r))
2491 2497
2492 2498 optlist = ("name templates style address port ipv6"
2493 2499 " accesslog errorlog webdir_conf")
2494 2500 for o in optlist.split():
2495 2501 if opts[o]:
2496 2502 ui.setconfig("web", o, opts[o])
2497 2503
2498 2504 if repo is None and not ui.config("web", "webdir_conf"):
2499 2505 raise hg.RepoError(_('no repo found'))
2500 2506
2501 2507 if opts['daemon'] and not opts['daemon_pipefds']:
2502 2508 rfd, wfd = os.pipe()
2503 2509 args = sys.argv[:]
2504 2510 args.append('--daemon-pipefds=%d,%d' % (rfd, wfd))
2505 2511 pid = os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
2506 2512 args[0], args)
2507 2513 os.close(wfd)
2508 2514 os.read(rfd, 1)
2509 2515 os._exit(0)
2510 2516
2511 2517 try:
2512 2518 httpd = hgweb.create_server(ui, repo)
2513 2519 except socket.error, inst:
2514 2520 raise util.Abort(_('cannot start server: ') + inst.args[1])
2515 2521
2516 2522 if ui.verbose:
2517 2523 addr, port = httpd.socket.getsockname()
2518 2524 if addr == '0.0.0.0':
2519 2525 addr = socket.gethostname()
2520 2526 else:
2521 2527 try:
2522 2528 addr = socket.gethostbyaddr(addr)[0]
2523 2529 except socket.error:
2524 2530 pass
2525 2531 if port != 80:
2526 2532 ui.status(_('listening at http://%s:%d/\n') % (addr, port))
2527 2533 else:
2528 2534 ui.status(_('listening at http://%s/\n') % addr)
2529 2535
2530 2536 if opts['pid_file']:
2531 2537 fp = open(opts['pid_file'], 'w')
2532 2538 fp.write(str(os.getpid()))
2533 2539 fp.close()
2534 2540
2535 2541 if opts['daemon_pipefds']:
2536 2542 rfd, wfd = [int(x) for x in opts['daemon_pipefds'].split(',')]
2537 2543 os.close(rfd)
2538 2544 os.write(wfd, 'y')
2539 2545 os.close(wfd)
2540 2546 sys.stdout.flush()
2541 2547 sys.stderr.flush()
2542 2548 fd = os.open(util.nulldev, os.O_RDWR)
2543 2549 if fd != 0: os.dup2(fd, 0)
2544 2550 if fd != 1: os.dup2(fd, 1)
2545 2551 if fd != 2: os.dup2(fd, 2)
2546 2552 if fd not in (0, 1, 2): os.close(fd)
2547 2553
2548 2554 httpd.serve_forever()
2549 2555
2550 2556 def status(ui, repo, *pats, **opts):
2551 2557 """show changed files in the working directory
2552 2558
2553 2559 Show changed files in the repository. If names are
2554 2560 given, only files that match are shown.
2555 2561
2556 2562 The codes used to show the status of files are:
2557 2563 M = modified
2558 2564 A = added
2559 2565 R = removed
2560 2566 ! = deleted, but still tracked
2561 2567 ? = not tracked
2562 2568 I = ignored (not shown by default)
2563 2569 """
2564 2570
2565 2571 show_ignored = opts['ignored'] and True or False
2566 2572 files, matchfn, anypats = matchpats(repo, pats, opts)
2567 2573 cwd = (pats and repo.getcwd()) or ''
2568 2574 modified, added, removed, deleted, unknown, ignored = [
2569 2575 [util.pathto(cwd, x) for x in n]
2570 2576 for n in repo.changes(files=files, match=matchfn,
2571 2577 show_ignored=show_ignored)]
2572 2578
2573 2579 changetypes = [('modified', 'M', modified),
2574 2580 ('added', 'A', added),
2575 2581 ('removed', 'R', removed),
2576 2582 ('deleted', '!', deleted),
2577 2583 ('unknown', '?', unknown),
2578 2584 ('ignored', 'I', ignored)]
2579 2585
2580 2586 end = opts['print0'] and '\0' or '\n'
2581 2587
2582 2588 for opt, char, changes in ([ct for ct in changetypes if opts[ct[0]]]
2583 2589 or changetypes):
2584 2590 if opts['no_status']:
2585 2591 format = "%%s%s" % end
2586 2592 else:
2587 2593 format = "%s %%s%s" % (char, end)
2588 2594
2589 2595 for f in changes:
2590 2596 ui.write(format % f)
2591 2597
2592 2598 def tag(ui, repo, name, rev_=None, **opts):
2593 2599 """add a tag for the current tip or a given revision
2594 2600
2595 2601 Name a particular revision using <name>.
2596 2602
2597 2603 Tags are used to name particular revisions of the repository and are
2598 2604 very useful to compare different revision, to go back to significant
2599 2605 earlier versions or to mark branch points as releases, etc.
2600 2606
2601 2607 If no revision is given, the tip is used.
2602 2608
2603 2609 To facilitate version control, distribution, and merging of tags,
2604 2610 they are stored as a file named ".hgtags" which is managed
2605 2611 similarly to other project files and can be hand-edited if
2606 2612 necessary. The file '.hg/localtags' is used for local tags (not
2607 2613 shared among repositories).
2608 2614 """
2609 2615 if name == "tip":
2610 2616 raise util.Abort(_("the name 'tip' is reserved"))
2611 2617 if rev_ is not None:
2612 2618 ui.warn(_("use of 'hg tag NAME [REV]' is deprecated, "
2613 2619 "please use 'hg tag [-r REV] NAME' instead\n"))
2614 2620 if opts['rev']:
2615 2621 raise util.Abort(_("use only one form to specify the revision"))
2616 2622 if opts['rev']:
2617 2623 rev_ = opts['rev']
2618 2624 if rev_:
2619 2625 r = hex(repo.lookup(rev_))
2620 2626 else:
2621 2627 r = hex(repo.changelog.tip())
2622 2628
2623 2629 disallowed = (revrangesep, '\r', '\n')
2624 2630 for c in disallowed:
2625 2631 if name.find(c) >= 0:
2626 2632 raise util.Abort(_("%s cannot be used in a tag name") % repr(c))
2627 2633
2628 2634 repo.hook('pretag', throw=True, node=r, tag=name,
2629 2635 local=int(not not opts['local']))
2630 2636
2631 2637 if opts['local']:
2632 2638 repo.opener("localtags", "a").write("%s %s\n" % (r, name))
2633 2639 repo.hook('tag', node=r, tag=name, local=1)
2634 2640 return
2635 2641
2636 2642 for x in repo.changes():
2637 2643 if ".hgtags" in x:
2638 2644 raise util.Abort(_("working copy of .hgtags is changed "
2639 2645 "(please commit .hgtags manually)"))
2640 2646
2641 2647 repo.wfile(".hgtags", "ab").write("%s %s\n" % (r, name))
2642 2648 if repo.dirstate.state(".hgtags") == '?':
2643 2649 repo.add([".hgtags"])
2644 2650
2645 2651 message = (opts['message'] or
2646 2652 _("Added tag %s for changeset %s") % (name, r))
2647 2653 try:
2648 2654 repo.commit([".hgtags"], message, opts['user'], opts['date'])
2649 2655 repo.hook('tag', node=r, tag=name, local=0)
2650 2656 except ValueError, inst:
2651 2657 raise util.Abort(str(inst))
2652 2658
2653 2659 def tags(ui, repo):
2654 2660 """list repository tags
2655 2661
2656 2662 List the repository tags.
2657 2663
2658 2664 This lists both regular and local tags.
2659 2665 """
2660 2666
2661 2667 l = repo.tagslist()
2662 2668 l.reverse()
2663 2669 for t, n in l:
2664 2670 try:
2665 2671 r = "%5d:%s" % (repo.changelog.rev(n), hex(n))
2666 2672 except KeyError:
2667 2673 r = " ?:?"
2668 2674 if ui.quiet:
2669 2675 ui.write("%s\n" % t)
2670 2676 else:
2671 2677 ui.write("%-30s %s\n" % (t, r))
2672 2678
2673 2679 def tip(ui, repo, **opts):
2674 2680 """show the tip revision
2675 2681
2676 2682 Show the tip revision.
2677 2683 """
2678 2684 n = repo.changelog.tip()
2679 2685 br = None
2680 2686 if opts['branches']:
2681 2687 br = repo.branchlookup([n])
2682 2688 show_changeset(ui, repo, opts).show(changenode=n, brinfo=br)
2683 2689 if opts['patch']:
2684 2690 dodiff(ui, ui, repo, repo.changelog.parents(n)[0], n)
2685 2691
2686 2692 def unbundle(ui, repo, fname, **opts):
2687 2693 """apply a changegroup file
2688 2694
2689 2695 Apply a compressed changegroup file generated by the bundle
2690 2696 command.
2691 2697 """
2692 2698 f = urllib.urlopen(fname)
2693 2699
2694 2700 header = f.read(6)
2695 2701 if not header.startswith("HG"):
2696 2702 raise util.Abort(_("%s: not a Mercurial bundle file") % fname)
2697 2703 elif not header.startswith("HG10"):
2698 2704 raise util.Abort(_("%s: unknown bundle version") % fname)
2699 2705 elif header == "HG10BZ":
2700 2706 def generator(f):
2701 2707 zd = bz2.BZ2Decompressor()
2702 2708 zd.decompress("BZ")
2703 2709 for chunk in f:
2704 2710 yield zd.decompress(chunk)
2705 2711 elif header == "HG10UN":
2706 2712 def generator(f):
2707 2713 for chunk in f:
2708 2714 yield chunk
2709 2715 else:
2710 2716 raise util.Abort(_("%s: unknown bundle compression type")
2711 2717 % fname)
2712 2718 gen = generator(util.filechunkiter(f, 4096))
2713 2719 modheads = repo.addchangegroup(util.chunkbuffer(gen), 'unbundle')
2714 2720 return postincoming(ui, repo, modheads, opts['update'])
2715 2721
2716 2722 def undo(ui, repo):
2717 2723 """undo the last commit or pull (DEPRECATED)
2718 2724
2719 2725 (DEPRECATED)
2720 2726 This command is now deprecated and will be removed in a future
2721 2727 release. Please use the rollback command instead. For usage
2722 2728 instructions, see the rollback command.
2723 2729 """
2724 2730 ui.warn(_('(the undo command is deprecated; use rollback instead)\n'))
2725 2731 repo.undo()
2726 2732
2727 2733 def update(ui, repo, node=None, merge=False, clean=False, force=None,
2728 2734 branch=None, **opts):
2729 2735 """update or merge working directory
2730 2736
2731 2737 Update the working directory to the specified revision.
2732 2738
2733 2739 If there are no outstanding changes in the working directory and
2734 2740 there is a linear relationship between the current version and the
2735 2741 requested version, the result is the requested version.
2736 2742
2737 2743 Otherwise the result is a merge between the contents of the
2738 2744 current working directory and the requested version. Files that
2739 2745 changed between either parent are marked as changed for the next
2740 2746 commit and a commit must be performed before any further updates
2741 2747 are allowed.
2742 2748
2743 2749 By default, update will refuse to run if doing so would require
2744 2750 merging or discarding local changes.
2745 2751 """
2746 2752 if branch:
2747 2753 br = repo.branchlookup(branch=branch)
2748 2754 found = []
2749 2755 for x in br:
2750 2756 if branch in br[x]:
2751 2757 found.append(x)
2752 2758 if len(found) > 1:
2753 2759 ui.warn(_("Found multiple heads for %s\n") % branch)
2754 2760 for x in found:
2755 2761 show_changeset(ui, repo, opts).show(changenode=x, brinfo=br)
2756 2762 return 1
2757 2763 if len(found) == 1:
2758 2764 node = found[0]
2759 2765 ui.warn(_("Using head %s for branch %s\n") % (short(node), branch))
2760 2766 else:
2761 2767 ui.warn(_("branch %s not found\n") % (branch))
2762 2768 return 1
2763 2769 else:
2764 2770 node = node and repo.lookup(node) or repo.changelog.tip()
2765 2771 return repo.update(node, allow=merge, force=clean, forcemerge=force)
2766 2772
2767 2773 def verify(ui, repo):
2768 2774 """verify the integrity of the repository
2769 2775
2770 2776 Verify the integrity of the current repository.
2771 2777
2772 2778 This will perform an extensive check of the repository's
2773 2779 integrity, validating the hashes and checksums of each entry in
2774 2780 the changelog, manifest, and tracked files, as well as the
2775 2781 integrity of their crosslinks and indices.
2776 2782 """
2777 2783 return repo.verify()
2778 2784
2779 2785 # Command options and aliases are listed here, alphabetically
2780 2786
2781 2787 table = {
2782 2788 "^add":
2783 2789 (add,
2784 2790 [('I', 'include', [], _('include names matching the given patterns')),
2785 2791 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2786 2792 _('hg add [OPTION]... [FILE]...')),
2787 2793 "debugaddremove|addremove":
2788 2794 (addremove,
2789 2795 [('I', 'include', [], _('include names matching the given patterns')),
2790 2796 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2791 2797 _('hg addremove [OPTION]... [FILE]...')),
2792 2798 "^annotate":
2793 2799 (annotate,
2794 2800 [('r', 'rev', '', _('annotate the specified revision')),
2795 2801 ('a', 'text', None, _('treat all files as text')),
2796 2802 ('u', 'user', None, _('list the author')),
2797 2803 ('d', 'date', None, _('list the date')),
2798 2804 ('n', 'number', None, _('list the revision number (default)')),
2799 2805 ('c', 'changeset', None, _('list the changeset')),
2800 2806 ('I', 'include', [], _('include names matching the given patterns')),
2801 2807 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2802 2808 _('hg annotate [-r REV] [-a] [-u] [-d] [-n] [-c] FILE...')),
2803 2809 "archive":
2804 2810 (archive,
2805 2811 [('', 'no-decode', None, _('do not pass files through decoders')),
2806 2812 ('p', 'prefix', '', _('directory prefix for files in archive')),
2807 2813 ('r', 'rev', '', _('revision to distribute')),
2808 2814 ('t', 'type', '', _('type of distribution to create')),
2809 2815 ('I', 'include', [], _('include names matching the given patterns')),
2810 2816 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2811 2817 _('hg archive [OPTION]... DEST')),
2812 2818 "backout":
2813 2819 (backout,
2814 2820 [('', 'merge', None,
2815 2821 _('merge with old dirstate parent after backout')),
2816 2822 ('m', 'message', '', _('use <text> as commit message')),
2817 2823 ('l', 'logfile', '', _('read commit message from <file>')),
2818 2824 ('d', 'date', '', _('record datecode as commit date')),
2819 2825 ('u', 'user', '', _('record user as committer')),
2820 2826 ('I', 'include', [], _('include names matching the given patterns')),
2821 2827 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2822 2828 _('hg backout [OPTION]... REV')),
2823 2829 "bundle":
2824 2830 (bundle,
2825 2831 [('f', 'force', None,
2826 2832 _('run even when remote repository is unrelated'))],
2827 2833 _('hg bundle FILE DEST')),
2828 2834 "cat":
2829 2835 (cat,
2830 2836 [('o', 'output', '', _('print output to file with formatted name')),
2831 2837 ('r', 'rev', '', _('print the given revision')),
2832 2838 ('I', 'include', [], _('include names matching the given patterns')),
2833 2839 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2834 2840 _('hg cat [OPTION]... FILE...')),
2835 2841 "^clone":
2836 2842 (clone,
2837 2843 [('U', 'noupdate', None, _('do not update the new working directory')),
2838 2844 ('r', 'rev', [],
2839 2845 _('a changeset you would like to have after cloning')),
2840 2846 ('', 'pull', None, _('use pull protocol to copy metadata')),
2841 2847 ('e', 'ssh', '', _('specify ssh command to use')),
2842 2848 ('', 'remotecmd', '',
2843 2849 _('specify hg command to run on the remote side'))],
2844 2850 _('hg clone [OPTION]... SOURCE [DEST]')),
2845 2851 "^commit|ci":
2846 2852 (commit,
2847 2853 [('A', 'addremove', None,
2848 2854 _('mark new/missing files as added/removed before committing')),
2849 2855 ('m', 'message', '', _('use <text> as commit message')),
2850 2856 ('l', 'logfile', '', _('read the commit message from <file>')),
2851 2857 ('d', 'date', '', _('record datecode as commit date')),
2852 2858 ('u', 'user', '', _('record user as commiter')),
2853 2859 ('I', 'include', [], _('include names matching the given patterns')),
2854 2860 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2855 2861 _('hg commit [OPTION]... [FILE]...')),
2856 2862 "copy|cp":
2857 2863 (copy,
2858 2864 [('A', 'after', None, _('record a copy that has already occurred')),
2859 2865 ('f', 'force', None,
2860 2866 _('forcibly copy over an existing managed file')),
2861 2867 ('I', 'include', [], _('include names matching the given patterns')),
2862 2868 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2863 2869 _('hg copy [OPTION]... [SOURCE]... DEST')),
2864 2870 "debugancestor": (debugancestor, [], _('debugancestor INDEX REV1 REV2')),
2865 2871 "debugcomplete":
2866 2872 (debugcomplete,
2867 2873 [('o', 'options', None, _('show the command options'))],
2868 2874 _('debugcomplete [-o] CMD')),
2869 2875 "debugrebuildstate":
2870 2876 (debugrebuildstate,
2871 2877 [('r', 'rev', '', _('revision to rebuild to'))],
2872 2878 _('debugrebuildstate [-r REV] [REV]')),
2873 2879 "debugcheckstate": (debugcheckstate, [], _('debugcheckstate')),
2874 2880 "debugconfig": (debugconfig, [], _('debugconfig')),
2875 2881 "debugsetparents": (debugsetparents, [], _('debugsetparents REV1 [REV2]')),
2876 2882 "debugstate": (debugstate, [], _('debugstate')),
2877 2883 "debugdata": (debugdata, [], _('debugdata FILE REV')),
2878 2884 "debugindex": (debugindex, [], _('debugindex FILE')),
2879 2885 "debugindexdot": (debugindexdot, [], _('debugindexdot FILE')),
2880 2886 "debugrename": (debugrename, [], _('debugrename FILE [REV]')),
2881 2887 "debugwalk":
2882 2888 (debugwalk,
2883 2889 [('I', 'include', [], _('include names matching the given patterns')),
2884 2890 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2885 2891 _('debugwalk [OPTION]... [FILE]...')),
2886 2892 "^diff":
2887 2893 (diff,
2888 2894 [('r', 'rev', [], _('revision')),
2889 2895 ('a', 'text', None, _('treat all files as text')),
2890 2896 ('p', 'show-function', None,
2891 2897 _('show which function each change is in')),
2892 2898 ('w', 'ignore-all-space', None,
2893 2899 _('ignore white space when comparing lines')),
2894 2900 ('I', 'include', [], _('include names matching the given patterns')),
2895 2901 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2896 2902 _('hg diff [-a] [-I] [-X] [-r REV1 [-r REV2]] [FILE]...')),
2897 2903 "^export":
2898 2904 (export,
2899 2905 [('o', 'output', '', _('print output to file with formatted name')),
2900 2906 ('a', 'text', None, _('treat all files as text')),
2901 2907 ('', 'switch-parent', None, _('diff against the second parent'))],
2902 2908 _('hg export [-a] [-o OUTFILESPEC] REV...')),
2903 2909 "debugforget|forget":
2904 2910 (forget,
2905 2911 [('I', 'include', [], _('include names matching the given patterns')),
2906 2912 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2907 2913 _('hg forget [OPTION]... FILE...')),
2908 2914 "grep":
2909 2915 (grep,
2910 2916 [('0', 'print0', None, _('end fields with NUL')),
2911 2917 ('', 'all', None, _('print all revisions that match')),
2912 2918 ('i', 'ignore-case', None, _('ignore case when matching')),
2913 2919 ('l', 'files-with-matches', None,
2914 2920 _('print only filenames and revs that match')),
2915 2921 ('n', 'line-number', None, _('print matching line numbers')),
2916 2922 ('r', 'rev', [], _('search in given revision range')),
2917 2923 ('u', 'user', None, _('print user who committed change')),
2918 2924 ('I', 'include', [], _('include names matching the given patterns')),
2919 2925 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2920 2926 _('hg grep [OPTION]... PATTERN [FILE]...')),
2921 2927 "heads":
2922 2928 (heads,
2923 2929 [('b', 'branches', None, _('show branches')),
2924 2930 ('', 'style', '', _('display using template map file')),
2925 2931 ('r', 'rev', '', _('show only heads which are descendants of rev')),
2926 2932 ('', 'template', '', _('display with template'))],
2927 2933 _('hg heads [-b] [-r <rev>]')),
2928 2934 "help": (help_, [], _('hg help [COMMAND]')),
2929 2935 "identify|id": (identify, [], _('hg identify')),
2930 2936 "import|patch":
2931 2937 (import_,
2932 2938 [('p', 'strip', 1,
2933 2939 _('directory strip option for patch. This has the same\n'
2934 2940 'meaning as the corresponding patch option')),
2935 2941 ('b', 'base', '', _('base path')),
2936 2942 ('f', 'force', None,
2937 2943 _('skip check for outstanding uncommitted changes'))],
2938 2944 _('hg import [-p NUM] [-b BASE] [-f] PATCH...')),
2939 2945 "incoming|in": (incoming,
2940 2946 [('M', 'no-merges', None, _('do not show merges')),
2941 2947 ('f', 'force', None,
2942 2948 _('run even when remote repository is unrelated')),
2943 2949 ('', 'style', '', _('display using template map file')),
2944 2950 ('n', 'newest-first', None, _('show newest record first')),
2945 2951 ('', 'bundle', '', _('file to store the bundles into')),
2946 2952 ('p', 'patch', None, _('show patch')),
2947 2953 ('', 'template', '', _('display with template')),
2948 2954 ('e', 'ssh', '', _('specify ssh command to use')),
2949 2955 ('', 'remotecmd', '',
2950 2956 _('specify hg command to run on the remote side'))],
2951 2957 _('hg incoming [-p] [-n] [-M] [--bundle FILENAME] [SOURCE]')),
2952 2958 "^init": (init, [], _('hg init [DEST]')),
2953 2959 "locate":
2954 2960 (locate,
2955 2961 [('r', 'rev', '', _('search the repository as it stood at rev')),
2956 2962 ('0', 'print0', None,
2957 2963 _('end filenames with NUL, for use with xargs')),
2958 2964 ('f', 'fullpath', None,
2959 2965 _('print complete paths from the filesystem root')),
2960 2966 ('I', 'include', [], _('include names matching the given patterns')),
2961 2967 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2962 2968 _('hg locate [OPTION]... [PATTERN]...')),
2963 2969 "^log|history":
2964 2970 (log,
2965 2971 [('b', 'branches', None, _('show branches')),
2966 2972 ('k', 'keyword', [], _('search for a keyword')),
2967 2973 ('l', 'limit', '', _('limit number of changes displayed')),
2968 2974 ('r', 'rev', [], _('show the specified revision or range')),
2969 2975 ('M', 'no-merges', None, _('do not show merges')),
2970 2976 ('', 'style', '', _('display using template map file')),
2971 2977 ('m', 'only-merges', None, _('show only merges')),
2972 2978 ('p', 'patch', None, _('show patch')),
2973 2979 ('', 'template', '', _('display with template')),
2974 2980 ('I', 'include', [], _('include names matching the given patterns')),
2975 2981 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2976 2982 _('hg log [OPTION]... [FILE]')),
2977 2983 "manifest": (manifest, [], _('hg manifest [REV]')),
2978 2984 "merge":
2979 2985 (merge,
2980 2986 [('b', 'branch', '', _('merge with head of a specific branch')),
2981 2987 ('f', 'force', None, _('force a merge with outstanding changes'))],
2982 2988 _('hg merge [-b TAG] [-f] [REV]')),
2983 2989 "outgoing|out": (outgoing,
2984 2990 [('M', 'no-merges', None, _('do not show merges')),
2985 2991 ('f', 'force', None,
2986 2992 _('run even when remote repository is unrelated')),
2987 2993 ('p', 'patch', None, _('show patch')),
2988 2994 ('', 'style', '', _('display using template map file')),
2989 2995 ('n', 'newest-first', None, _('show newest record first')),
2990 2996 ('', 'template', '', _('display with template')),
2991 2997 ('e', 'ssh', '', _('specify ssh command to use')),
2992 2998 ('', 'remotecmd', '',
2993 2999 _('specify hg command to run on the remote side'))],
2994 3000 _('hg outgoing [-M] [-p] [-n] [DEST]')),
2995 3001 "^parents":
2996 3002 (parents,
2997 3003 [('b', 'branches', None, _('show branches')),
2998 3004 ('', 'style', '', _('display using template map file')),
2999 3005 ('', 'template', '', _('display with template'))],
3000 3006 _('hg parents [-b] [REV]')),
3001 3007 "paths": (paths, [], _('hg paths [NAME]')),
3002 3008 "^pull":
3003 3009 (pull,
3004 3010 [('u', 'update', None,
3005 3011 _('update the working directory to tip after pull')),
3006 3012 ('e', 'ssh', '', _('specify ssh command to use')),
3007 3013 ('f', 'force', None,
3008 3014 _('run even when remote repository is unrelated')),
3009 3015 ('r', 'rev', [], _('a specific revision you would like to pull')),
3010 3016 ('', 'remotecmd', '',
3011 3017 _('specify hg command to run on the remote side'))],
3012 3018 _('hg pull [-u] [-e FILE] [-r REV]... [--remotecmd FILE] [SOURCE]')),
3013 3019 "^push":
3014 3020 (push,
3015 3021 [('f', 'force', None, _('force push')),
3016 3022 ('e', 'ssh', '', _('specify ssh command to use')),
3017 3023 ('r', 'rev', [], _('a specific revision you would like to push')),
3018 3024 ('', 'remotecmd', '',
3019 3025 _('specify hg command to run on the remote side'))],
3020 3026 _('hg push [-f] [-e FILE] [-r REV]... [--remotecmd FILE] [DEST]')),
3021 3027 "debugrawcommit|rawcommit":
3022 3028 (rawcommit,
3023 3029 [('p', 'parent', [], _('parent')),
3024 3030 ('d', 'date', '', _('date code')),
3025 3031 ('u', 'user', '', _('user')),
3026 3032 ('F', 'files', '', _('file list')),
3027 3033 ('m', 'message', '', _('commit message')),
3028 3034 ('l', 'logfile', '', _('commit message file'))],
3029 3035 _('hg debugrawcommit [OPTION]... [FILE]...')),
3030 3036 "recover": (recover, [], _('hg recover')),
3031 3037 "^remove|rm":
3032 3038 (remove,
3033 3039 [('A', 'after', None, _('record remove that has already occurred')),
3034 3040 ('f', 'force', None, _('remove file even if modified')),
3035 3041 ('I', 'include', [], _('include names matching the given patterns')),
3036 3042 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
3037 3043 _('hg remove [OPTION]... FILE...')),
3038 3044 "rename|mv":
3039 3045 (rename,
3040 3046 [('A', 'after', None, _('record a rename that has already occurred')),
3041 3047 ('f', 'force', None,
3042 3048 _('forcibly copy over an existing managed file')),
3043 3049 ('I', 'include', [], _('include names matching the given patterns')),
3044 3050 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
3045 3051 _('hg rename [OPTION]... SOURCE... DEST')),
3046 3052 "^revert":
3047 3053 (revert,
3048 3054 [('r', 'rev', '', _('revision to revert to')),
3049 3055 ('', 'no-backup', None, _('do not save backup copies of files')),
3050 3056 ('I', 'include', [], _('include names matching given patterns')),
3051 3057 ('X', 'exclude', [], _('exclude names matching given patterns'))],
3052 3058 _('hg revert [-r REV] [NAME]...')),
3053 3059 "rollback": (rollback, [], _('hg rollback')),
3054 3060 "root": (root, [], _('hg root')),
3055 3061 "^serve":
3056 3062 (serve,
3057 3063 [('A', 'accesslog', '', _('name of access log file to write to')),
3058 3064 ('d', 'daemon', None, _('run server in background')),
3059 3065 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3060 3066 ('E', 'errorlog', '', _('name of error log file to write to')),
3061 3067 ('p', 'port', 0, _('port to use (default: 8000)')),
3062 3068 ('a', 'address', '', _('address to use')),
3063 3069 ('n', 'name', '',
3064 3070 _('name to show in web pages (default: working dir)')),
3065 3071 ('', 'webdir-conf', '', _('name of the webdir config file'
3066 3072 ' (serve more than one repo)')),
3067 3073 ('', 'pid-file', '', _('name of file to write process ID to')),
3068 3074 ('', 'stdio', None, _('for remote clients')),
3069 3075 ('t', 'templates', '', _('web templates to use')),
3070 3076 ('', 'style', '', _('template style to use')),
3071 3077 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4'))],
3072 3078 _('hg serve [OPTION]...')),
3073 3079 "^status|st":
3074 3080 (status,
3075 3081 [('m', 'modified', None, _('show only modified files')),
3076 3082 ('a', 'added', None, _('show only added files')),
3077 3083 ('r', 'removed', None, _('show only removed files')),
3078 3084 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3079 3085 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3080 3086 ('i', 'ignored', None, _('show ignored files')),
3081 3087 ('n', 'no-status', None, _('hide status prefix')),
3082 3088 ('0', 'print0', None,
3083 3089 _('end filenames with NUL, for use with xargs')),
3084 3090 ('I', 'include', [], _('include names matching the given patterns')),
3085 3091 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
3086 3092 _('hg status [OPTION]... [FILE]...')),
3087 3093 "tag":
3088 3094 (tag,
3089 3095 [('l', 'local', None, _('make the tag local')),
3090 3096 ('m', 'message', '', _('message for tag commit log entry')),
3091 3097 ('d', 'date', '', _('record datecode as commit date')),
3092 3098 ('u', 'user', '', _('record user as commiter')),
3093 3099 ('r', 'rev', '', _('revision to tag'))],
3094 3100 _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME')),
3095 3101 "tags": (tags, [], _('hg tags')),
3096 3102 "tip":
3097 3103 (tip,
3098 3104 [('b', 'branches', None, _('show branches')),
3099 3105 ('', 'style', '', _('display using template map file')),
3100 3106 ('p', 'patch', None, _('show patch')),
3101 3107 ('', 'template', '', _('display with template'))],
3102 3108 _('hg tip [-b] [-p]')),
3103 3109 "unbundle":
3104 3110 (unbundle,
3105 3111 [('u', 'update', None,
3106 3112 _('update the working directory to tip after unbundle'))],
3107 3113 _('hg unbundle [-u] FILE')),
3108 3114 "debugundo|undo": (undo, [], _('hg undo')),
3109 3115 "^update|up|checkout|co":
3110 3116 (update,
3111 3117 [('b', 'branch', '', _('checkout the head of a specific branch')),
3112 3118 ('m', 'merge', None, _('allow merging of branches')),
3113 3119 ('C', 'clean', None, _('overwrite locally modified files')),
3114 3120 ('f', 'force', None, _('force a merge with outstanding changes'))],
3115 3121 _('hg update [-b TAG] [-m] [-C] [-f] [REV]')),
3116 3122 "verify": (verify, [], _('hg verify')),
3117 3123 "version": (show_version, [], _('hg version')),
3118 3124 }
3119 3125
3120 3126 globalopts = [
3121 3127 ('R', 'repository', '',
3122 3128 _('repository root directory or symbolic path name')),
3123 3129 ('', 'cwd', '', _('change working directory')),
3124 3130 ('y', 'noninteractive', None,
3125 3131 _('do not prompt, assume \'yes\' for any required answers')),
3126 3132 ('q', 'quiet', None, _('suppress output')),
3127 3133 ('v', 'verbose', None, _('enable additional output')),
3128 3134 ('', 'debug', None, _('enable debugging output')),
3129 3135 ('', 'debugger', None, _('start debugger')),
3130 3136 ('', 'traceback', None, _('print traceback on exception')),
3131 3137 ('', 'time', None, _('time how long the command takes')),
3132 3138 ('', 'profile', None, _('print command execution profile')),
3133 3139 ('', 'version', None, _('output version information and exit')),
3134 3140 ('h', 'help', None, _('display help and exit')),
3135 3141 ]
3136 3142
3137 3143 norepo = ("clone init version help debugancestor debugcomplete debugdata"
3138 3144 " debugindex debugindexdot")
3139 3145 optionalrepo = ("paths serve debugconfig")
3140 3146
3141 3147 def findpossible(cmd):
3142 3148 """
3143 3149 Return cmd -> (aliases, command table entry)
3144 3150 for each matching command.
3145 3151 Return debug commands (or their aliases) only if no normal command matches.
3146 3152 """
3147 3153 choice = {}
3148 3154 debugchoice = {}
3149 3155 for e in table.keys():
3150 3156 aliases = e.lstrip("^").split("|")
3151 3157 found = None
3152 3158 if cmd in aliases:
3153 3159 found = cmd
3154 3160 else:
3155 3161 for a in aliases:
3156 3162 if a.startswith(cmd):
3157 3163 found = a
3158 3164 break
3159 3165 if found is not None:
3160 3166 if aliases[0].startswith("debug"):
3161 3167 debugchoice[found] = (aliases, table[e])
3162 3168 else:
3163 3169 choice[found] = (aliases, table[e])
3164 3170
3165 3171 if not choice and debugchoice:
3166 3172 choice = debugchoice
3167 3173
3168 3174 return choice
3169 3175
3170 3176 def find(cmd):
3171 3177 """Return (aliases, command table entry) for command string."""
3172 3178 choice = findpossible(cmd)
3173 3179
3174 3180 if choice.has_key(cmd):
3175 3181 return choice[cmd]
3176 3182
3177 3183 if len(choice) > 1:
3178 3184 clist = choice.keys()
3179 3185 clist.sort()
3180 3186 raise AmbiguousCommand(cmd, clist)
3181 3187
3182 3188 if choice:
3183 3189 return choice.values()[0]
3184 3190
3185 3191 raise UnknownCommand(cmd)
3186 3192
3187 3193 def catchterm(*args):
3188 3194 raise util.SignalInterrupt
3189 3195
3190 3196 def run():
3191 3197 sys.exit(dispatch(sys.argv[1:]))
3192 3198
3193 3199 class ParseError(Exception):
3194 3200 """Exception raised on errors in parsing the command line."""
3195 3201
3196 3202 def parse(ui, args):
3197 3203 options = {}
3198 3204 cmdoptions = {}
3199 3205
3200 3206 try:
3201 3207 args = fancyopts.fancyopts(args, globalopts, options)
3202 3208 except fancyopts.getopt.GetoptError, inst:
3203 3209 raise ParseError(None, inst)
3204 3210
3205 3211 if args:
3206 3212 cmd, args = args[0], args[1:]
3207 3213 aliases, i = find(cmd)
3208 3214 cmd = aliases[0]
3209 3215 defaults = ui.config("defaults", cmd)
3210 3216 if defaults:
3211 3217 args = defaults.split() + args
3212 3218 c = list(i[1])
3213 3219 else:
3214 3220 cmd = None
3215 3221 c = []
3216 3222
3217 3223 # combine global options into local
3218 3224 for o in globalopts:
3219 3225 c.append((o[0], o[1], options[o[1]], o[3]))
3220 3226
3221 3227 try:
3222 3228 args = fancyopts.fancyopts(args, c, cmdoptions)
3223 3229 except fancyopts.getopt.GetoptError, inst:
3224 3230 raise ParseError(cmd, inst)
3225 3231
3226 3232 # separate global options back out
3227 3233 for o in globalopts:
3228 3234 n = o[1]
3229 3235 options[n] = cmdoptions[n]
3230 3236 del cmdoptions[n]
3231 3237
3232 3238 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
3233 3239
3234 3240 def dispatch(args):
3235 3241 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
3236 3242 num = getattr(signal, name, None)
3237 3243 if num: signal.signal(num, catchterm)
3238 3244
3239 3245 try:
3240 3246 u = ui.ui(traceback='--traceback' in sys.argv[1:])
3241 3247 except util.Abort, inst:
3242 3248 sys.stderr.write(_("abort: %s\n") % inst)
3243 3249 return -1
3244 3250
3245 3251 external = []
3246 3252 for x in u.extensions():
3247 3253 try:
3248 3254 if x[1]:
3249 3255 mod = imp.load_source(x[0], x[1])
3250 3256 else:
3251 3257 def importh(name):
3252 3258 mod = __import__(name)
3253 3259 components = name.split('.')
3254 3260 for comp in components[1:]:
3255 3261 mod = getattr(mod, comp)
3256 3262 return mod
3257 3263 try:
3258 3264 mod = importh("hgext." + x[0])
3259 3265 except ImportError:
3260 3266 mod = importh(x[0])
3261 3267 external.append(mod)
3262 3268 except Exception, inst:
3263 3269 u.warn(_("*** failed to import extension %s: %s\n") % (x[0], inst))
3264 3270 if u.traceback:
3265 3271 traceback.print_exc()
3266 3272 return 1
3267 3273 continue
3268 3274
3269 3275 for x in external:
3270 3276 cmdtable = getattr(x, 'cmdtable', {})
3271 3277 for t in cmdtable:
3272 3278 if t in table:
3273 3279 u.warn(_("module %s overrides %s\n") % (x.__name__, t))
3274 3280 table.update(cmdtable)
3275 3281
3276 3282 try:
3277 3283 cmd, func, args, options, cmdoptions = parse(u, args)
3278 3284 if options["time"]:
3279 3285 def get_times():
3280 3286 t = os.times()
3281 3287 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
3282 3288 t = (t[0], t[1], t[2], t[3], time.clock())
3283 3289 return t
3284 3290 s = get_times()
3285 3291 def print_time():
3286 3292 t = get_times()
3287 3293 u.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
3288 3294 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
3289 3295 atexit.register(print_time)
3290 3296
3291 3297 u.updateopts(options["verbose"], options["debug"], options["quiet"],
3292 3298 not options["noninteractive"], options["traceback"])
3293 3299
3294 3300 # enter the debugger before command execution
3295 3301 if options['debugger']:
3296 3302 pdb.set_trace()
3297 3303
3298 3304 try:
3299 3305 if options['cwd']:
3300 3306 try:
3301 3307 os.chdir(options['cwd'])
3302 3308 except OSError, inst:
3303 3309 raise util.Abort('%s: %s' %
3304 3310 (options['cwd'], inst.strerror))
3305 3311
3306 3312 path = u.expandpath(options["repository"]) or ""
3307 3313 repo = path and hg.repository(u, path=path) or None
3308 3314
3309 3315 if options['help']:
3310 3316 return help_(u, cmd, options['version'])
3311 3317 elif options['version']:
3312 3318 return show_version(u)
3313 3319 elif not cmd:
3314 3320 return help_(u, 'shortlist')
3315 3321
3316 3322 if cmd not in norepo.split():
3317 3323 try:
3318 3324 if not repo:
3319 3325 repo = hg.repository(u, path=path)
3320 3326 u = repo.ui
3321 3327 for x in external:
3322 3328 if hasattr(x, 'reposetup'):
3323 3329 x.reposetup(u, repo)
3324 3330 except hg.RepoError:
3325 3331 if cmd not in optionalrepo.split():
3326 3332 raise
3327 3333 d = lambda: func(u, repo, *args, **cmdoptions)
3328 3334 else:
3329 3335 d = lambda: func(u, *args, **cmdoptions)
3330 3336
3331 3337 try:
3332 3338 if options['profile']:
3333 3339 import hotshot, hotshot.stats
3334 3340 prof = hotshot.Profile("hg.prof")
3335 3341 try:
3336 3342 try:
3337 3343 return prof.runcall(d)
3338 3344 except:
3339 3345 try:
3340 3346 u.warn(_('exception raised - generating '
3341 3347 'profile anyway\n'))
3342 3348 except:
3343 3349 pass
3344 3350 raise
3345 3351 finally:
3346 3352 prof.close()
3347 3353 stats = hotshot.stats.load("hg.prof")
3348 3354 stats.strip_dirs()
3349 3355 stats.sort_stats('time', 'calls')
3350 3356 stats.print_stats(40)
3351 3357 else:
3352 3358 return d()
3353 3359 finally:
3354 3360 u.flush()
3355 3361 except:
3356 3362 # enter the debugger when we hit an exception
3357 3363 if options['debugger']:
3358 3364 pdb.post_mortem(sys.exc_info()[2])
3359 3365 if u.traceback:
3360 3366 traceback.print_exc()
3361 3367 raise
3362 3368 except ParseError, inst:
3363 3369 if inst.args[0]:
3364 3370 u.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
3365 3371 help_(u, inst.args[0])
3366 3372 else:
3367 3373 u.warn(_("hg: %s\n") % inst.args[1])
3368 3374 help_(u, 'shortlist')
3369 3375 except AmbiguousCommand, inst:
3370 3376 u.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
3371 3377 (inst.args[0], " ".join(inst.args[1])))
3372 3378 except UnknownCommand, inst:
3373 3379 u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
3374 3380 help_(u, 'shortlist')
3375 3381 except hg.RepoError, inst:
3376 3382 u.warn(_("abort: %s!\n") % inst)
3377 3383 except lock.LockHeld, inst:
3378 3384 if inst.errno == errno.ETIMEDOUT:
3379 3385 reason = _('timed out waiting for lock held by %s') % inst.locker
3380 3386 else:
3381 3387 reason = _('lock held by %s') % inst.locker
3382 3388 u.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
3383 3389 except lock.LockUnavailable, inst:
3384 3390 u.warn(_("abort: could not lock %s: %s\n") %
3385 3391 (inst.desc or inst.filename, inst.strerror))
3386 3392 except revlog.RevlogError, inst:
3387 3393 u.warn(_("abort: "), inst, "!\n")
3388 3394 except util.SignalInterrupt:
3389 3395 u.warn(_("killed!\n"))
3390 3396 except KeyboardInterrupt:
3391 3397 try:
3392 3398 u.warn(_("interrupted!\n"))
3393 3399 except IOError, inst:
3394 3400 if inst.errno == errno.EPIPE:
3395 3401 if u.debugflag:
3396 3402 u.warn(_("\nbroken pipe\n"))
3397 3403 else:
3398 3404 raise
3399 3405 except IOError, inst:
3400 3406 if hasattr(inst, "code"):
3401 3407 u.warn(_("abort: %s\n") % inst)
3402 3408 elif hasattr(inst, "reason"):
3403 3409 u.warn(_("abort: error: %s\n") % inst.reason[1])
3404 3410 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
3405 3411 if u.debugflag:
3406 3412 u.warn(_("broken pipe\n"))
3407 3413 elif getattr(inst, "strerror", None):
3408 3414 if getattr(inst, "filename", None):
3409 3415 u.warn(_("abort: %s - %s\n") % (inst.strerror, inst.filename))
3410 3416 else:
3411 3417 u.warn(_("abort: %s\n") % inst.strerror)
3412 3418 else:
3413 3419 raise
3414 3420 except OSError, inst:
3415 3421 if hasattr(inst, "filename"):
3416 3422 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
3417 3423 else:
3418 3424 u.warn(_("abort: %s\n") % inst.strerror)
3419 3425 except util.Abort, inst:
3420 3426 u.warn(_('abort: '), inst.args[0] % inst.args[1:], '\n')
3421 3427 except TypeError, inst:
3422 3428 # was this an argument error?
3423 3429 tb = traceback.extract_tb(sys.exc_info()[2])
3424 3430 if len(tb) > 2: # no
3425 3431 raise
3426 3432 u.debug(inst, "\n")
3427 3433 u.warn(_("%s: invalid arguments\n") % cmd)
3428 3434 help_(u, cmd)
3429 3435 except SystemExit, inst:
3430 3436 # Commands shouldn't sys.exit directly, but give a return code.
3431 3437 # Just in case catch this and and pass exit code to caller.
3432 3438 return inst.code
3433 3439 except:
3434 3440 u.warn(_("** unknown exception encountered, details follow\n"))
3435 3441 u.warn(_("** report bug details to mercurial@selenic.com\n"))
3436 3442 u.warn(_("** Mercurial Distributed SCM (version %s)\n")
3437 3443 % version.get_version())
3438 3444 raise
3439 3445
3440 3446 return -1
@@ -1,71 +1,83 b''
1 1 #!/bin/sh
2 2
3 3 hg init
4 4 echo 123 > a
5 5 echo 123 > c
6 6 echo 123 > e
7 7 hg add a c e
8 8 hg commit -m "first" -d "1000000 0" a c e
9 9 echo 123 > b
10 10 echo %% should show b unknown
11 11 hg status
12 12 echo 12 > c
13 13 echo %% should show b unknown and c modified
14 14 hg status
15 15 hg add b
16 16 echo %% should show b added and c modified
17 17 hg status
18 18 hg rm a
19 19 echo %% should show a removed, b added and c modified
20 20 hg status
21 21 hg revert a
22 22 echo %% should show b added, copy saved, and c modified
23 23 hg status
24 24 hg revert b
25 25 echo %% should show b unknown, and c modified
26 26 hg status
27 27 hg revert --no-backup c
28 28 echo %% should show unknown: b
29 29 hg status
30 30 echo %% should show a b c e
31 31 ls
32 32 echo %% should verbosely save backup to e.orig
33 33 echo z > e
34 34 hg revert -v
35 35 echo %% should say no changes needed
36 36 hg revert a
37 37 echo %% should say file not managed
38 38 echo q > q
39 39 hg revert q
40 40 rm q
41 41 echo %% should say file not found
42 42 hg revert notfound
43 43 hg rm a
44 44 hg commit -m "second" -d "1000000 0"
45 45 echo z > z
46 46 hg add z
47 47 hg st
48 48 echo %% should add a, forget z
49 49 hg revert -r0
50 50 echo %% should forget a
51 51 hg revert -rtip
52 52 rm -f a *.orig
53 53 echo %% should silently add a
54 54 hg revert -r0 a
55 55 hg st a
56 56
57 57 hg update -C
58 58 chmod +x c
59 59 hg revert
60 60 echo %% should print non-executable
61 61 test -x c || echo non-executable
62 62
63 63 chmod +x c
64 64 hg commit -d '1000001 0' -m exe
65 65
66 66 chmod -x c
67 67 hg revert
68 68 echo %% should print executable
69 69 test -x c && echo executable
70 70
71 echo %% issue 241
72 hg init a
73 cd a
74 echo a >> a
75 hg commit -A -d '1 0' -m a
76 echo a >> a
77 hg commit -d '2 0' -m a
78 hg update 0
79 mkdir b
80 echo b > b/b
81 hg revert -rtip
82
71 83 true
@@ -1,53 +1,56 b''
1 1 %% should show b unknown
2 2 ? b
3 3 %% should show b unknown and c modified
4 4 M c
5 5 ? b
6 6 %% should show b added and c modified
7 7 M c
8 8 A b
9 9 %% should show a removed, b added and c modified
10 10 M c
11 11 A b
12 12 R a
13 13 %% should show b added, copy saved, and c modified
14 14 M c
15 15 A b
16 16 %% should show b unknown, and c modified
17 17 M c
18 18 ? b
19 19 %% should show unknown: b
20 20 ? b
21 21 %% should show a b c e
22 22 a
23 23 b
24 24 c
25 25 e
26 26 %% should verbosely save backup to e.orig
27 27 saving current version of e as e.orig
28 28 reverting e
29 29 resolving manifests
30 30 getting e
31 31 %% should say no changes needed
32 32 no changes needed to a
33 33 %% should say file not managed
34 34 file not managed: q
35 35 %% should say file not found
36 36 notfound: No such file in rev 095eacd0c0d7
37 37 A z
38 38 ? b
39 39 ? e.orig
40 40 %% should add a, forget z
41 41 adding a
42 42 forgetting z
43 43 %% should forget a
44 44 forgetting a
45 45 %% should silently add a
46 46 A a
47 47 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
48 48 reverting c
49 49 %% should print non-executable
50 50 non-executable
51 51 reverting c
52 52 %% should print executable
53 53 executable
54 %% issue 241
55 adding a
56 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
General Comments 0
You need to be logged in to leave comments. Login now