##// END OF EJS Templates
Add debugsetparents command
Matt Mackall -
r1395:c2eb2049 default
parent child Browse files
Show More
@@ -1,2227 +1,2241
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 demandload(globals(), "os re sys signal shutil imp urllib pdb")
11 11 demandload(globals(), "fancyopts ui hg util lock revlog")
12 12 demandload(globals(), "fnmatch hgweb mdiff random signal time traceback")
13 13 demandload(globals(), "errno socket version struct atexit sets bz2")
14 14
15 15 class UnknownCommand(Exception):
16 16 """Exception raised if command is not in the command table."""
17 17
18 18 def filterfiles(filters, files):
19 19 l = [x for x in files if x in filters]
20 20
21 21 for t in filters:
22 22 if t and t[-1] != "/":
23 23 t += "/"
24 24 l += [x for x in files if x.startswith(t)]
25 25 return l
26 26
27 27 def relpath(repo, args):
28 28 cwd = repo.getcwd()
29 29 if cwd:
30 30 return [util.normpath(os.path.join(cwd, x)) for x in args]
31 31 return args
32 32
33 33 def matchpats(repo, cwd, pats=[], opts={}, head=''):
34 34 return util.matcher(repo.root, cwd, pats or ['.'], opts.get('include'),
35 35 opts.get('exclude'), head)
36 36
37 37 def makewalk(repo, pats, opts, head=''):
38 38 cwd = repo.getcwd()
39 39 files, matchfn, anypats = matchpats(repo, cwd, pats, opts, head)
40 40 exact = dict(zip(files, files))
41 41 def walk():
42 42 for src, fn in repo.walk(files=files, match=matchfn):
43 43 yield src, fn, util.pathto(cwd, fn), fn in exact
44 44 return files, matchfn, walk()
45 45
46 46 def walk(repo, pats, opts, head=''):
47 47 files, matchfn, results = makewalk(repo, pats, opts, head)
48 48 for r in results:
49 49 yield r
50 50
51 51 def walkchangerevs(ui, repo, cwd, pats, opts):
52 52 '''Iterate over files and the revs they changed in.
53 53
54 54 Callers most commonly need to iterate backwards over the history
55 55 it is interested in. Doing so has awful (quadratic-looking)
56 56 performance, so we use iterators in a "windowed" way.
57 57
58 58 We walk a window of revisions in the desired order. Within the
59 59 window, we first walk forwards to gather data, then in the desired
60 60 order (usually backwards) to display it.
61 61
62 62 This function returns an (iterator, getchange) pair. The
63 63 getchange function returns the changelog entry for a numeric
64 64 revision. The iterator yields 3-tuples. They will be of one of
65 65 the following forms:
66 66
67 67 "window", incrementing, lastrev: stepping through a window,
68 68 positive if walking forwards through revs, last rev in the
69 69 sequence iterated over - use to reset state for the current window
70 70
71 71 "add", rev, fns: out-of-order traversal of the given file names
72 72 fns, which changed during revision rev - use to gather data for
73 73 possible display
74 74
75 75 "iter", rev, None: in-order traversal of the revs earlier iterated
76 76 over with "add" - use to display data'''
77 77
78 78 if repo.changelog.count() == 0:
79 79 return [], False
80 80
81 81 cwd = repo.getcwd()
82 82 if not pats and cwd:
83 83 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
84 84 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
85 85 files, matchfn, anypats = matchpats(repo, (pats and cwd) or '',
86 86 pats, opts)
87 87 revs = map(int, revrange(ui, repo, opts['rev'] or ['tip:0']))
88 88 wanted = {}
89 89 slowpath = anypats
90 90 window = 300
91 91 fncache = {}
92 92
93 93 chcache = {}
94 94 def getchange(rev):
95 95 ch = chcache.get(rev)
96 96 if ch is None:
97 97 chcache[rev] = ch = repo.changelog.read(repo.lookup(str(rev)))
98 98 return ch
99 99
100 100 if not slowpath and not files:
101 101 # No files, no patterns. Display all revs.
102 102 wanted = dict(zip(revs, revs))
103 103 if not slowpath:
104 104 # Only files, no patterns. Check the history of each file.
105 105 def filerevgen(filelog):
106 106 for i in xrange(filelog.count() - 1, -1, -window):
107 107 revs = []
108 108 for j in xrange(max(0, i - window), i + 1):
109 109 revs.append(filelog.linkrev(filelog.node(j)))
110 110 revs.reverse()
111 111 for rev in revs:
112 112 yield rev
113 113
114 114 minrev, maxrev = min(revs), max(revs)
115 115 for file in files:
116 116 filelog = repo.file(file)
117 117 # A zero count may be a directory or deleted file, so
118 118 # try to find matching entries on the slow path.
119 119 if filelog.count() == 0:
120 120 slowpath = True
121 121 break
122 122 for rev in filerevgen(filelog):
123 123 if rev <= maxrev:
124 124 if rev < minrev:
125 125 break
126 126 fncache.setdefault(rev, [])
127 127 fncache[rev].append(file)
128 128 wanted[rev] = 1
129 129 if slowpath:
130 130 # The slow path checks files modified in every changeset.
131 131 def changerevgen():
132 132 for i in xrange(repo.changelog.count() - 1, -1, -window):
133 133 for j in xrange(max(0, i - window), i + 1):
134 134 yield j, getchange(j)[3]
135 135
136 136 for rev, changefiles in changerevgen():
137 137 matches = filter(matchfn, changefiles)
138 138 if matches:
139 139 fncache[rev] = matches
140 140 wanted[rev] = 1
141 141
142 142 def iterate():
143 143 for i in xrange(0, len(revs), window):
144 144 yield 'window', revs[0] < revs[-1], revs[-1]
145 145 nrevs = [rev for rev in revs[i:min(i+window, len(revs))]
146 146 if rev in wanted]
147 147 srevs = list(nrevs)
148 148 srevs.sort()
149 149 for rev in srevs:
150 150 fns = fncache.get(rev) or filter(matchfn, getchange(rev)[3])
151 151 yield 'add', rev, fns
152 152 for rev in nrevs:
153 153 yield 'iter', rev, None
154 154 return iterate(), getchange
155 155
156 156 revrangesep = ':'
157 157
158 158 def revrange(ui, repo, revs, revlog=None):
159 159 """Yield revision as strings from a list of revision specifications."""
160 160 if revlog is None:
161 161 revlog = repo.changelog
162 162 revcount = revlog.count()
163 163 def fix(val, defval):
164 164 if not val:
165 165 return defval
166 166 try:
167 167 num = int(val)
168 168 if str(num) != val:
169 169 raise ValueError
170 170 if num < 0: num += revcount
171 171 if num < 0: num = 0
172 172 elif num >= revcount:
173 173 raise ValueError
174 174 except ValueError:
175 175 try:
176 176 num = repo.changelog.rev(repo.lookup(val))
177 177 except KeyError:
178 178 try:
179 179 num = revlog.rev(revlog.lookup(val))
180 180 except KeyError:
181 181 raise util.Abort('invalid revision identifier %s', val)
182 182 return num
183 183 seen = {}
184 184 for spec in revs:
185 185 if spec.find(revrangesep) >= 0:
186 186 start, end = spec.split(revrangesep, 1)
187 187 start = fix(start, 0)
188 188 end = fix(end, revcount - 1)
189 189 step = start > end and -1 or 1
190 190 for rev in xrange(start, end+step, step):
191 191 if rev in seen: continue
192 192 seen[rev] = 1
193 193 yield str(rev)
194 194 else:
195 195 rev = fix(spec, None)
196 196 if rev in seen: continue
197 197 seen[rev] = 1
198 198 yield str(rev)
199 199
200 200 def make_filename(repo, r, pat, node=None,
201 201 total=None, seqno=None, revwidth=None, pathname=None):
202 202 node_expander = {
203 203 'H': lambda: hex(node),
204 204 'R': lambda: str(r.rev(node)),
205 205 'h': lambda: short(node),
206 206 }
207 207 expander = {
208 208 '%': lambda: '%',
209 209 'b': lambda: os.path.basename(repo.root),
210 210 }
211 211
212 212 try:
213 213 if node:
214 214 expander.update(node_expander)
215 215 if node and revwidth is not None:
216 216 expander['r'] = lambda: str(r.rev(node)).zfill(revwidth)
217 217 if total is not None:
218 218 expander['N'] = lambda: str(total)
219 219 if seqno is not None:
220 220 expander['n'] = lambda: str(seqno)
221 221 if total is not None and seqno is not None:
222 222 expander['n'] = lambda:str(seqno).zfill(len(str(total)))
223 223 if pathname is not None:
224 224 expander['s'] = lambda: os.path.basename(pathname)
225 225 expander['d'] = lambda: os.path.dirname(pathname) or '.'
226 226 expander['p'] = lambda: pathname
227 227
228 228 newname = []
229 229 patlen = len(pat)
230 230 i = 0
231 231 while i < patlen:
232 232 c = pat[i]
233 233 if c == '%':
234 234 i += 1
235 235 c = pat[i]
236 236 c = expander[c]()
237 237 newname.append(c)
238 238 i += 1
239 239 return ''.join(newname)
240 240 except KeyError, inst:
241 241 raise util.Abort("invalid format spec '%%%s' in output file name",
242 242 inst.args[0])
243 243
244 244 def make_file(repo, r, pat, node=None,
245 245 total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
246 246 if not pat or pat == '-':
247 247 return 'w' in mode and sys.stdout or sys.stdin
248 248 if hasattr(pat, 'write') and 'w' in mode:
249 249 return pat
250 250 if hasattr(pat, 'read') and 'r' in mode:
251 251 return pat
252 252 return open(make_filename(repo, r, pat, node, total, seqno, revwidth,
253 253 pathname),
254 254 mode)
255 255
256 256 def dodiff(fp, ui, repo, node1, node2, files=None, match=util.always,
257 257 changes=None, text=False):
258 258 if not changes:
259 259 (c, a, d, u) = repo.changes(node1, node2, files, match=match)
260 260 else:
261 261 (c, a, d, u) = changes
262 262 if files:
263 263 c, a, d = map(lambda x: filterfiles(files, x), (c, a, d))
264 264
265 265 if not c and not a and not d:
266 266 return
267 267
268 268 if node2:
269 269 change = repo.changelog.read(node2)
270 270 mmap2 = repo.manifest.read(change[0])
271 271 date2 = util.datestr(change[2])
272 272 def read(f):
273 273 return repo.file(f).read(mmap2[f])
274 274 else:
275 275 date2 = util.datestr()
276 276 if not node1:
277 277 node1 = repo.dirstate.parents()[0]
278 278 def read(f):
279 279 return repo.wfile(f).read()
280 280
281 281 if ui.quiet:
282 282 r = None
283 283 else:
284 284 hexfunc = ui.verbose and hex or short
285 285 r = [hexfunc(node) for node in [node1, node2] if node]
286 286
287 287 change = repo.changelog.read(node1)
288 288 mmap = repo.manifest.read(change[0])
289 289 date1 = util.datestr(change[2])
290 290
291 291 for f in c:
292 292 to = None
293 293 if f in mmap:
294 294 to = repo.file(f).read(mmap[f])
295 295 tn = read(f)
296 296 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text))
297 297 for f in a:
298 298 to = None
299 299 tn = read(f)
300 300 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text))
301 301 for f in d:
302 302 to = repo.file(f).read(mmap[f])
303 303 tn = None
304 304 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text))
305 305
306 306 def trimuser(ui, name, rev, revcache):
307 307 """trim the name of the user who committed a change"""
308 308 user = revcache.get(rev)
309 309 if user is None:
310 310 user = revcache[rev] = ui.shortuser(name)
311 311 return user
312 312
313 313 def show_changeset(ui, repo, rev=0, changenode=None, brinfo=None):
314 314 """show a single changeset or file revision"""
315 315 log = repo.changelog
316 316 if changenode is None:
317 317 changenode = log.node(rev)
318 318 elif not rev:
319 319 rev = log.rev(changenode)
320 320
321 321 if ui.quiet:
322 322 ui.write("%d:%s\n" % (rev, short(changenode)))
323 323 return
324 324
325 325 changes = log.read(changenode)
326 326 date = util.datestr(changes[2])
327 327
328 328 parents = [(log.rev(p), ui.verbose and hex(p) or short(p))
329 329 for p in log.parents(changenode)
330 330 if ui.debugflag or p != nullid]
331 331 if not ui.debugflag and len(parents) == 1 and parents[0][0] == rev-1:
332 332 parents = []
333 333
334 334 if ui.verbose:
335 335 ui.write("changeset: %d:%s\n" % (rev, hex(changenode)))
336 336 else:
337 337 ui.write("changeset: %d:%s\n" % (rev, short(changenode)))
338 338
339 339 for tag in repo.nodetags(changenode):
340 340 ui.status("tag: %s\n" % tag)
341 341 for parent in parents:
342 342 ui.write("parent: %d:%s\n" % parent)
343 343
344 344 if brinfo and changenode in brinfo:
345 345 br = brinfo[changenode]
346 346 ui.write("branch: %s\n" % " ".join(br))
347 347
348 348 ui.debug("manifest: %d:%s\n" % (repo.manifest.rev(changes[0]),
349 349 hex(changes[0])))
350 350 ui.status("user: %s\n" % changes[1])
351 351 ui.status("date: %s\n" % date)
352 352
353 353 if ui.debugflag:
354 354 files = repo.changes(log.parents(changenode)[0], changenode)
355 355 for key, value in zip(["files:", "files+:", "files-:"], files):
356 356 if value:
357 357 ui.note("%-12s %s\n" % (key, " ".join(value)))
358 358 else:
359 359 ui.note("files: %s\n" % " ".join(changes[3]))
360 360
361 361 description = changes[4].strip()
362 362 if description:
363 363 if ui.verbose:
364 364 ui.status("description:\n")
365 365 ui.status(description)
366 366 ui.status("\n\n")
367 367 else:
368 368 ui.status("summary: %s\n" % description.splitlines()[0])
369 369 ui.status("\n")
370 370
371 371 def show_version(ui):
372 372 """output version and copyright information"""
373 373 ui.write("Mercurial Distributed SCM (version %s)\n"
374 374 % version.get_version())
375 375 ui.status(
376 376 "\nCopyright (C) 2005 Matt Mackall <mpm@selenic.com>\n"
377 377 "This is free software; see the source for copying conditions. "
378 378 "There is NO\nwarranty; "
379 379 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
380 380 )
381 381
382 382 def help_(ui, cmd=None, with_version=False):
383 383 """show help for a given command or all commands"""
384 384 option_lists = []
385 385 if cmd and cmd != 'shortlist':
386 386 if with_version:
387 387 show_version(ui)
388 388 ui.write('\n')
389 389 key, i = find(cmd)
390 390 # synopsis
391 391 ui.write("%s\n\n" % i[2])
392 392
393 393 # description
394 394 doc = i[0].__doc__
395 395 if ui.quiet:
396 396 doc = doc.splitlines(0)[0]
397 397 ui.write("%s\n" % doc.rstrip())
398 398
399 399 if not ui.quiet:
400 400 # aliases
401 401 aliases = ', '.join(key.split('|')[1:])
402 402 if aliases:
403 403 ui.write("\naliases: %s\n" % aliases)
404 404
405 405 # options
406 406 if i[1]:
407 407 option_lists.append(("options", i[1]))
408 408
409 409 else:
410 410 # program name
411 411 if ui.verbose or with_version:
412 412 show_version(ui)
413 413 else:
414 414 ui.status("Mercurial Distributed SCM\n")
415 415 ui.status('\n')
416 416
417 417 # list of commands
418 418 if cmd == "shortlist":
419 419 ui.status('basic commands (use "hg help" '
420 420 'for the full list or option "-v" for details):\n\n')
421 421 elif ui.verbose:
422 422 ui.status('list of commands:\n\n')
423 423 else:
424 424 ui.status('list of commands (use "hg help -v" '
425 425 'to show aliases and global options):\n\n')
426 426
427 427 h = {}
428 428 cmds = {}
429 429 for c, e in table.items():
430 430 f = c.split("|")[0]
431 431 if cmd == "shortlist" and not f.startswith("^"):
432 432 continue
433 433 f = f.lstrip("^")
434 434 if not ui.debugflag and f.startswith("debug"):
435 435 continue
436 436 d = ""
437 437 if e[0].__doc__:
438 438 d = e[0].__doc__.splitlines(0)[0].rstrip()
439 439 h[f] = d
440 440 cmds[f]=c.lstrip("^")
441 441
442 442 fns = h.keys()
443 443 fns.sort()
444 444 m = max(map(len, fns))
445 445 for f in fns:
446 446 if ui.verbose:
447 447 commands = cmds[f].replace("|",", ")
448 448 ui.write(" %s:\n %s\n"%(commands,h[f]))
449 449 else:
450 450 ui.write(' %-*s %s\n' % (m, f, h[f]))
451 451
452 452 # global options
453 453 if ui.verbose:
454 454 option_lists.append(("global options", globalopts))
455 455
456 456 # list all option lists
457 457 opt_output = []
458 458 for title, options in option_lists:
459 459 opt_output.append(("\n%s:\n" % title, None))
460 460 for shortopt, longopt, default, desc in options:
461 461 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
462 462 longopt and " --%s" % longopt),
463 463 "%s%s" % (desc,
464 464 default and " (default: %s)" % default
465 465 or "")))
466 466
467 467 if opt_output:
468 468 opts_len = max([len(line[0]) for line in opt_output if line[1]])
469 469 for first, second in opt_output:
470 470 if second:
471 471 ui.write(" %-*s %s\n" % (opts_len, first, second))
472 472 else:
473 473 ui.write("%s\n" % first)
474 474
475 475 # Commands start here, listed alphabetically
476 476
477 477 def add(ui, repo, *pats, **opts):
478 478 '''add the specified files on the next commit'''
479 479 names = []
480 480 for src, abs, rel, exact in walk(repo, pats, opts):
481 481 if exact:
482 482 if ui.verbose: ui.status('adding %s\n' % rel)
483 483 names.append(abs)
484 484 elif repo.dirstate.state(abs) == '?':
485 485 ui.status('adding %s\n' % rel)
486 486 names.append(abs)
487 487 repo.add(names)
488 488
489 489 def addremove(ui, repo, *pats, **opts):
490 490 """add all new files, delete all missing files"""
491 491 add, remove = [], []
492 492 for src, abs, rel, exact in walk(repo, pats, opts):
493 493 if src == 'f' and repo.dirstate.state(abs) == '?':
494 494 add.append(abs)
495 495 if ui.verbose or not exact:
496 496 ui.status('adding ', rel, '\n')
497 497 if repo.dirstate.state(abs) != 'r' and not os.path.exists(rel):
498 498 remove.append(abs)
499 499 if ui.verbose or not exact:
500 500 ui.status('removing ', rel, '\n')
501 501 repo.add(add)
502 502 repo.remove(remove)
503 503
504 504 def annotate(ui, repo, *pats, **opts):
505 505 """show changeset information per file line"""
506 506 def getnode(rev):
507 507 return short(repo.changelog.node(rev))
508 508
509 509 ucache = {}
510 510 def getname(rev):
511 511 cl = repo.changelog.read(repo.changelog.node(rev))
512 512 return trimuser(ui, cl[1], rev, ucache)
513 513
514 514 if not pats:
515 515 raise util.Abort('at least one file name or pattern required')
516 516
517 517 opmap = [['user', getname], ['number', str], ['changeset', getnode]]
518 518 if not opts['user'] and not opts['changeset']:
519 519 opts['number'] = 1
520 520
521 521 if opts['rev']:
522 522 node = repo.changelog.lookup(opts['rev'])
523 523 else:
524 524 node = repo.dirstate.parents()[0]
525 525 change = repo.changelog.read(node)
526 526 mmap = repo.manifest.read(change[0])
527 527
528 528 for src, abs, rel, exact in walk(repo, pats, opts):
529 529 if abs not in mmap:
530 530 ui.warn("warning: %s is not in the repository!\n" % rel)
531 531 continue
532 532
533 533 f = repo.file(abs)
534 534 if not opts['text'] and util.binary(f.read(mmap[abs])):
535 535 ui.write("%s: binary file\n" % rel)
536 536 continue
537 537
538 538 lines = f.annotate(mmap[abs])
539 539 pieces = []
540 540
541 541 for o, f in opmap:
542 542 if opts[o]:
543 543 l = [f(n) for n, dummy in lines]
544 544 if l:
545 545 m = max(map(len, l))
546 546 pieces.append(["%*s" % (m, x) for x in l])
547 547
548 548 if pieces:
549 549 for p, l in zip(zip(*pieces), lines):
550 550 ui.write("%s: %s" % (" ".join(p), l[1]))
551 551
552 552 def bundle(ui, repo, fname, dest="default-push", **opts):
553 553 """create a changegroup file"""
554 554 f = open(fname, "wb")
555 555 dest = ui.expandpath(dest)
556 556 other = hg.repository(ui, dest)
557 557 o = repo.findoutgoing(other)
558 558 cg = repo.changegroup(o)
559 559
560 560 try:
561 561 f.write("HG10")
562 562 z = bz2.BZ2Compressor(9)
563 563 while 1:
564 564 chunk = cg.read(4096)
565 565 if not chunk:
566 566 break
567 567 f.write(z.compress(chunk))
568 568 f.write(z.flush())
569 569 except:
570 570 os.unlink(fname)
571 571 raise
572 572
573 573 def cat(ui, repo, file1, *pats, **opts):
574 574 """output the latest or given revisions of files"""
575 575 mf = {}
576 576 if opts['rev']:
577 577 change = repo.changelog.read(repo.lookup(opts['rev']))
578 578 mf = repo.manifest.read(change[0])
579 579 for src, abs, rel, exact in walk(repo, (file1,) + pats, opts):
580 580 r = repo.file(abs)
581 581 if opts['rev']:
582 582 try:
583 583 n = mf[abs]
584 584 except (hg.RepoError, KeyError):
585 585 try:
586 586 n = r.lookup(rev)
587 587 except KeyError, inst:
588 588 raise util.Abort('cannot find file %s in rev %s', rel, rev)
589 589 else:
590 590 n = r.tip()
591 591 fp = make_file(repo, r, opts['output'], node=n, pathname=abs)
592 592 fp.write(r.read(n))
593 593
594 594 def clone(ui, source, dest=None, **opts):
595 595 """make a copy of an existing repository"""
596 596 if dest is None:
597 597 dest = os.path.basename(os.path.normpath(source))
598 598
599 599 if os.path.exists(dest):
600 600 raise util.Abort("destination '%s' already exists", dest)
601 601
602 602 dest = os.path.realpath(dest)
603 603
604 604 class Dircleanup:
605 605 def __init__(self, dir_):
606 606 self.rmtree = shutil.rmtree
607 607 self.dir_ = dir_
608 608 os.mkdir(dir_)
609 609 def close(self):
610 610 self.dir_ = None
611 611 def __del__(self):
612 612 if self.dir_:
613 613 self.rmtree(self.dir_, True)
614 614
615 615 if opts['ssh']:
616 616 ui.setconfig("ui", "ssh", opts['ssh'])
617 617 if opts['remotecmd']:
618 618 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
619 619
620 620 if not os.path.exists(source):
621 621 source = ui.expandpath(source)
622 622
623 623 d = Dircleanup(dest)
624 624 abspath = source
625 625 other = hg.repository(ui, source)
626 626
627 627 copy = False
628 628 if other.dev() != -1:
629 629 abspath = os.path.abspath(source)
630 630 if not opts['pull']:
631 631 copy = True
632 632
633 633 if copy:
634 634 try:
635 635 # we use a lock here because if we race with commit, we
636 636 # can end up with extra data in the cloned revlogs that's
637 637 # not pointed to by changesets, thus causing verify to
638 638 # fail
639 639 l1 = lock.lock(os.path.join(source, ".hg", "lock"))
640 640 except OSError:
641 641 copy = False
642 642
643 643 if copy:
644 644 # we lock here to avoid premature writing to the target
645 645 os.mkdir(os.path.join(dest, ".hg"))
646 646 l2 = lock.lock(os.path.join(dest, ".hg", "lock"))
647 647
648 648 files = "data 00manifest.d 00manifest.i 00changelog.d 00changelog.i"
649 649 for f in files.split():
650 650 src = os.path.join(source, ".hg", f)
651 651 dst = os.path.join(dest, ".hg", f)
652 652 util.copyfiles(src, dst)
653 653
654 654 repo = hg.repository(ui, dest)
655 655
656 656 else:
657 657 repo = hg.repository(ui, dest, create=1)
658 658 repo.pull(other)
659 659
660 660 f = repo.opener("hgrc", "w", text=True)
661 661 f.write("[paths]\n")
662 662 f.write("default = %s\n" % abspath)
663 663
664 664 if not opts['noupdate']:
665 665 update(ui, repo)
666 666
667 667 d.close()
668 668
669 669 def commit(ui, repo, *pats, **opts):
670 670 """commit the specified files or all outstanding changes"""
671 671 if opts['text']:
672 672 ui.warn("Warning: -t and --text is deprecated,"
673 673 " please use -m or --message instead.\n")
674 674 message = opts['message'] or opts['text']
675 675 logfile = opts['logfile']
676 676
677 677 if message and logfile:
678 678 raise util.Abort('options --message and --logfile are mutually '
679 679 'exclusive')
680 680 if not message and logfile:
681 681 try:
682 682 if logfile == '-':
683 683 message = sys.stdin.read()
684 684 else:
685 685 message = open(logfile).read()
686 686 except IOError, inst:
687 687 raise util.Abort("can't read commit message '%s': %s" %
688 688 (logfile, inst.strerror))
689 689
690 690 if opts['addremove']:
691 691 addremove(ui, repo, *pats, **opts)
692 692 cwd = repo.getcwd()
693 693 if not pats and cwd:
694 694 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
695 695 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
696 696 fns, match, anypats = matchpats(repo, (pats and repo.getcwd()) or '',
697 697 pats, opts)
698 698 if pats:
699 699 c, a, d, u = repo.changes(files=fns, match=match)
700 700 files = c + a + [fn for fn in d if repo.dirstate.state(fn) == 'r']
701 701 else:
702 702 files = []
703 703 try:
704 704 repo.commit(files, message, opts['user'], opts['date'], match)
705 705 except ValueError, inst:
706 706 raise util.Abort(str(inst))
707 707
708 708 def docopy(ui, repo, pats, opts):
709 709 if not pats:
710 710 raise util.Abort('no source or destination specified')
711 711 elif len(pats) == 1:
712 712 raise util.Abort('no destination specified')
713 713 pats = list(pats)
714 714 dest = pats.pop()
715 715 sources = []
716 716
717 717 def okaytocopy(abs, rel, exact):
718 718 reasons = {'?': 'is not managed',
719 719 'a': 'has been marked for add'}
720 720 reason = reasons.get(repo.dirstate.state(abs))
721 721 if reason:
722 722 if exact: ui.warn('%s: not copying - file %s\n' % (rel, reason))
723 723 else:
724 724 return True
725 725
726 726 for src, abs, rel, exact in walk(repo, pats, opts):
727 727 if okaytocopy(abs, rel, exact):
728 728 sources.append((abs, rel, exact))
729 729 if not sources:
730 730 raise util.Abort('no files to copy')
731 731
732 732 cwd = repo.getcwd()
733 733 absdest = util.canonpath(repo.root, cwd, dest)
734 734 reldest = util.pathto(cwd, absdest)
735 735 if os.path.exists(reldest):
736 736 destisfile = not os.path.isdir(reldest)
737 737 else:
738 738 destisfile = len(sources) == 1 or repo.dirstate.state(absdest) != '?'
739 739
740 740 if destisfile:
741 741 if opts['parents']:
742 742 raise util.Abort('with --parents, destination must be a directory')
743 743 elif len(sources) > 1:
744 744 raise util.Abort('with multiple sources, destination must be a '
745 745 'directory')
746 746 errs, copied = 0, []
747 747 for abs, rel, exact in sources:
748 748 if opts['parents']:
749 749 mydest = os.path.join(dest, rel)
750 750 elif destisfile:
751 751 mydest = reldest
752 752 else:
753 753 mydest = os.path.join(dest, os.path.basename(rel))
754 754 myabsdest = util.canonpath(repo.root, cwd, mydest)
755 755 myreldest = util.pathto(cwd, myabsdest)
756 756 if not opts['force'] and repo.dirstate.state(myabsdest) not in 'a?':
757 757 ui.warn('%s: not overwriting - file already managed\n' % myreldest)
758 758 continue
759 759 mydestdir = os.path.dirname(myreldest) or '.'
760 760 if not opts['after']:
761 761 try:
762 762 if opts['parents']: os.makedirs(mydestdir)
763 763 elif not destisfile: os.mkdir(mydestdir)
764 764 except OSError, inst:
765 765 if inst.errno != errno.EEXIST: raise
766 766 if ui.verbose or not exact:
767 767 ui.status('copying %s to %s\n' % (rel, myreldest))
768 768 if not opts['after']:
769 769 try:
770 770 shutil.copyfile(rel, myreldest)
771 771 shutil.copymode(rel, myreldest)
772 772 except shutil.Error, inst:
773 773 raise util.Abort(str(inst))
774 774 except IOError, inst:
775 775 if inst.errno == errno.ENOENT:
776 776 ui.warn('%s: deleted in working copy\n' % rel)
777 777 else:
778 778 ui.warn('%s: cannot copy - %s\n' % (rel, inst.strerror))
779 779 errs += 1
780 780 continue
781 781 repo.copy(abs, myabsdest)
782 782 copied.append((abs, rel, exact))
783 783 if errs:
784 784 ui.warn('(consider using --after)\n')
785 785 return errs, copied
786 786
787 787 def copy(ui, repo, *pats, **opts):
788 788 """mark files as copied for the next commit"""
789 789 errs, copied = docopy(ui, repo, pats, opts)
790 790 return errs
791 791
792 792 def debugancestor(ui, index, rev1, rev2):
793 793 """find the ancestor revision of two revisions in a given index"""
794 794 r = revlog.revlog(file, index, "")
795 795 a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
796 796 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
797 797
798 798 def debugcheckstate(ui, repo):
799 799 """validate the correctness of the current dirstate"""
800 800 parent1, parent2 = repo.dirstate.parents()
801 801 repo.dirstate.read()
802 802 dc = repo.dirstate.map
803 803 keys = dc.keys()
804 804 keys.sort()
805 805 m1n = repo.changelog.read(parent1)[0]
806 806 m2n = repo.changelog.read(parent2)[0]
807 807 m1 = repo.manifest.read(m1n)
808 808 m2 = repo.manifest.read(m2n)
809 809 errors = 0
810 810 for f in dc:
811 811 state = repo.dirstate.state(f)
812 812 if state in "nr" and f not in m1:
813 813 ui.warn("%s in state %s, but not in manifest1\n" % (f, state))
814 814 errors += 1
815 815 if state in "a" and f in m1:
816 816 ui.warn("%s in state %s, but also in manifest1\n" % (f, state))
817 817 errors += 1
818 818 if state in "m" and f not in m1 and f not in m2:
819 819 ui.warn("%s in state %s, but not in either manifest\n" %
820 820 (f, state))
821 821 errors += 1
822 822 for f in m1:
823 823 state = repo.dirstate.state(f)
824 824 if state not in "nrm":
825 825 ui.warn("%s in manifest1, but listed as state %s" % (f, state))
826 826 errors += 1
827 827 if errors:
828 828 raise util.Abort(".hg/dirstate inconsistent with current parent's manifest")
829 829
830 830 def debugconfig(ui):
831 831 """show combined config settings from all hgrc files"""
832 832 try:
833 833 repo = hg.repository(ui)
834 834 except hg.RepoError:
835 835 pass
836 836 for section, name, value in ui.walkconfig():
837 837 ui.write('%s.%s=%s\n' % (section, name, value))
838 838
839 def debugsetparents(ui, repo, rev1, rev2=None):
840 """
841 manually set the parents of the current working directory
842
843 This is useful for writing repository conversion tools, but should
844 be used with care.
845 """
846
847 if not rev2:
848 rev2 = hex(nullid)
849
850 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
851
839 852 def debugstate(ui, repo):
840 853 """show the contents of the current dirstate"""
841 854 repo.dirstate.read()
842 855 dc = repo.dirstate.map
843 856 keys = dc.keys()
844 857 keys.sort()
845 858 for file_ in keys:
846 859 ui.write("%c %3o %10d %s %s\n"
847 860 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
848 861 time.strftime("%x %X",
849 862 time.localtime(dc[file_][3])), file_))
850 863 for f in repo.dirstate.copies:
851 864 ui.write("copy: %s -> %s\n" % (repo.dirstate.copies[f], f))
852 865
853 866 def debugdata(ui, file_, rev):
854 867 """dump the contents of an data file revision"""
855 868 r = revlog.revlog(file, file_[:-2] + ".i", file_)
856 869 try:
857 870 ui.write(r.revision(r.lookup(rev)))
858 871 except KeyError:
859 872 raise util.Abort('invalid revision identifier %s', rev)
860 873
861 874 def debugindex(ui, file_):
862 875 """dump the contents of an index file"""
863 876 r = revlog.revlog(file, file_, "")
864 877 ui.write(" rev offset length base linkrev" +
865 878 " nodeid p1 p2\n")
866 879 for i in range(r.count()):
867 880 e = r.index[i]
868 881 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
869 882 i, e[0], e[1], e[2], e[3],
870 883 short(e[6]), short(e[4]), short(e[5])))
871 884
872 885 def debugindexdot(ui, file_):
873 886 """dump an index DAG as a .dot file"""
874 887 r = revlog.revlog(file, file_, "")
875 888 ui.write("digraph G {\n")
876 889 for i in range(r.count()):
877 890 e = r.index[i]
878 891 ui.write("\t%d -> %d\n" % (r.rev(e[4]), i))
879 892 if e[5] != nullid:
880 893 ui.write("\t%d -> %d\n" % (r.rev(e[5]), i))
881 894 ui.write("}\n")
882 895
883 896 def debugrename(ui, repo, file, rev=None):
884 897 """dump rename information"""
885 898 r = repo.file(relpath(repo, [file])[0])
886 899 if rev:
887 900 try:
888 901 # assume all revision numbers are for changesets
889 902 n = repo.lookup(rev)
890 903 change = repo.changelog.read(n)
891 904 m = repo.manifest.read(change[0])
892 905 n = m[relpath(repo, [file])[0]]
893 906 except hg.RepoError, KeyError:
894 907 n = r.lookup(rev)
895 908 else:
896 909 n = r.tip()
897 910 m = r.renamed(n)
898 911 if m:
899 912 ui.write("renamed from %s:%s\n" % (m[0], hex(m[1])))
900 913 else:
901 914 ui.write("not renamed\n")
902 915
903 916 def debugwalk(ui, repo, *pats, **opts):
904 917 """show how files match on given patterns"""
905 918 items = list(walk(repo, pats, opts))
906 919 if not items:
907 920 return
908 921 fmt = '%%s %%-%ds %%-%ds %%s' % (
909 922 max([len(abs) for (src, abs, rel, exact) in items]),
910 923 max([len(rel) for (src, abs, rel, exact) in items]))
911 924 for src, abs, rel, exact in items:
912 925 line = fmt % (src, abs, rel, exact and 'exact' or '')
913 926 ui.write("%s\n" % line.rstrip())
914 927
915 928 def diff(ui, repo, *pats, **opts):
916 929 """diff working directory (or selected files)"""
917 930 node1, node2 = None, None
918 931 revs = [repo.lookup(x) for x in opts['rev']]
919 932
920 933 if len(revs) > 0:
921 934 node1 = revs[0]
922 935 if len(revs) > 1:
923 936 node2 = revs[1]
924 937 if len(revs) > 2:
925 938 raise util.Abort("too many revisions to diff")
926 939
927 940 fns, matchfn, anypats = matchpats(repo, repo.getcwd(), pats, opts)
928 941
929 942 dodiff(sys.stdout, ui, repo, node1, node2, fns, match=matchfn,
930 943 text=opts['text'])
931 944
932 945 def doexport(ui, repo, changeset, seqno, total, revwidth, opts):
933 946 node = repo.lookup(changeset)
934 947 prev, other = repo.changelog.parents(node)
935 948 change = repo.changelog.read(node)
936 949
937 950 fp = make_file(repo, repo.changelog, opts['output'],
938 951 node=node, total=total, seqno=seqno,
939 952 revwidth=revwidth)
940 953 if fp != sys.stdout:
941 954 ui.note("%s\n" % fp.name)
942 955
943 956 fp.write("# HG changeset patch\n")
944 957 fp.write("# User %s\n" % change[1])
945 958 fp.write("# Node ID %s\n" % hex(node))
946 959 fp.write("# Parent %s\n" % hex(prev))
947 960 if other != nullid:
948 961 fp.write("# Parent %s\n" % hex(other))
949 962 fp.write(change[4].rstrip())
950 963 fp.write("\n\n")
951 964
952 965 dodiff(fp, ui, repo, prev, node, text=opts['text'])
953 966 if fp != sys.stdout:
954 967 fp.close()
955 968
956 969 def export(ui, repo, *changesets, **opts):
957 970 """dump the header and diffs for one or more changesets"""
958 971 if not changesets:
959 972 raise util.Abort("export requires at least one changeset")
960 973 seqno = 0
961 974 revs = list(revrange(ui, repo, changesets))
962 975 total = len(revs)
963 976 revwidth = max(map(len, revs))
964 977 ui.note(len(revs) > 1 and "Exporting patches:\n" or "Exporting patch:\n")
965 978 for cset in revs:
966 979 seqno += 1
967 980 doexport(ui, repo, cset, seqno, total, revwidth, opts)
968 981
969 982 def forget(ui, repo, *pats, **opts):
970 983 """don't add the specified files on the next commit"""
971 984 forget = []
972 985 for src, abs, rel, exact in walk(repo, pats, opts):
973 986 if repo.dirstate.state(abs) == 'a':
974 987 forget.append(abs)
975 988 if ui.verbose or not exact:
976 989 ui.status('forgetting ', rel, '\n')
977 990 repo.forget(forget)
978 991
979 992 def grep(ui, repo, pattern, *pats, **opts):
980 993 """search for a pattern in specified files and revisions"""
981 994 reflags = 0
982 995 if opts['ignore_case']:
983 996 reflags |= re.I
984 997 regexp = re.compile(pattern, reflags)
985 998 sep, eol = ':', '\n'
986 999 if opts['print0']:
987 1000 sep = eol = '\0'
988 1001
989 1002 fcache = {}
990 1003 def getfile(fn):
991 1004 if fn not in fcache:
992 1005 fcache[fn] = repo.file(fn)
993 1006 return fcache[fn]
994 1007
995 1008 def matchlines(body):
996 1009 begin = 0
997 1010 linenum = 0
998 1011 while True:
999 1012 match = regexp.search(body, begin)
1000 1013 if not match:
1001 1014 break
1002 1015 mstart, mend = match.span()
1003 1016 linenum += body.count('\n', begin, mstart) + 1
1004 1017 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1005 1018 lend = body.find('\n', mend)
1006 1019 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1007 1020 begin = lend + 1
1008 1021
1009 1022 class linestate:
1010 1023 def __init__(self, line, linenum, colstart, colend):
1011 1024 self.line = line
1012 1025 self.linenum = linenum
1013 1026 self.colstart = colstart
1014 1027 self.colend = colend
1015 1028 def __eq__(self, other):
1016 1029 return self.line == other.line
1017 1030 def __hash__(self):
1018 1031 return hash(self.line)
1019 1032
1020 1033 matches = {}
1021 1034 def grepbody(fn, rev, body):
1022 1035 matches[rev].setdefault(fn, {})
1023 1036 m = matches[rev][fn]
1024 1037 for lnum, cstart, cend, line in matchlines(body):
1025 1038 s = linestate(line, lnum, cstart, cend)
1026 1039 m[s] = s
1027 1040
1028 1041 prev = {}
1029 1042 ucache = {}
1030 1043 def display(fn, rev, states, prevstates):
1031 1044 diff = list(sets.Set(states).symmetric_difference(sets.Set(prevstates)))
1032 1045 diff.sort(lambda x, y: cmp(x.linenum, y.linenum))
1033 1046 counts = {'-': 0, '+': 0}
1034 1047 filerevmatches = {}
1035 1048 for l in diff:
1036 1049 if incrementing or not opts['all']:
1037 1050 change = ((l in prevstates) and '-') or '+'
1038 1051 r = rev
1039 1052 else:
1040 1053 change = ((l in states) and '-') or '+'
1041 1054 r = prev[fn]
1042 1055 cols = [fn, str(rev)]
1043 1056 if opts['line_number']: cols.append(str(l.linenum))
1044 1057 if opts['all']: cols.append(change)
1045 1058 if opts['user']: cols.append(trimuser(ui, getchange(rev)[1], rev,
1046 1059 ucache))
1047 1060 if opts['files_with_matches']:
1048 1061 c = (fn, rev)
1049 1062 if c in filerevmatches: continue
1050 1063 filerevmatches[c] = 1
1051 1064 else:
1052 1065 cols.append(l.line)
1053 1066 ui.write(sep.join(cols), eol)
1054 1067 counts[change] += 1
1055 1068 return counts['+'], counts['-']
1056 1069
1057 1070 fstate = {}
1058 1071 skip = {}
1059 1072 changeiter, getchange = walkchangerevs(ui, repo, repo.getcwd(), pats, opts)
1060 1073 count = 0
1061 1074 incrementing = False
1062 1075 for st, rev, fns in changeiter:
1063 1076 if st == 'window':
1064 1077 incrementing = rev
1065 1078 matches.clear()
1066 1079 elif st == 'add':
1067 1080 change = repo.changelog.read(repo.lookup(str(rev)))
1068 1081 mf = repo.manifest.read(change[0])
1069 1082 matches[rev] = {}
1070 1083 for fn in fns:
1071 1084 if fn in skip: continue
1072 1085 fstate.setdefault(fn, {})
1073 1086 try:
1074 1087 grepbody(fn, rev, getfile(fn).read(mf[fn]))
1075 1088 except KeyError:
1076 1089 pass
1077 1090 elif st == 'iter':
1078 1091 states = matches[rev].items()
1079 1092 states.sort()
1080 1093 for fn, m in states:
1081 1094 if fn in skip: continue
1082 1095 if incrementing or not opts['all'] or fstate[fn]:
1083 1096 pos, neg = display(fn, rev, m, fstate[fn])
1084 1097 count += pos + neg
1085 1098 if pos and not opts['all']:
1086 1099 skip[fn] = True
1087 1100 fstate[fn] = m
1088 1101 prev[fn] = rev
1089 1102
1090 1103 if not incrementing:
1091 1104 fstate = fstate.items()
1092 1105 fstate.sort()
1093 1106 for fn, state in fstate:
1094 1107 if fn in skip: continue
1095 1108 display(fn, rev, {}, state)
1096 1109 return (count == 0 and 1) or 0
1097 1110
1098 1111 def heads(ui, repo, **opts):
1099 1112 """show current repository heads"""
1100 1113 heads = repo.changelog.heads()
1101 1114 br = None
1102 1115 if opts['branches']:
1103 1116 br = repo.branchlookup(heads)
1104 1117 for n in repo.changelog.heads():
1105 1118 show_changeset(ui, repo, changenode=n, brinfo=br)
1106 1119
1107 1120 def identify(ui, repo):
1108 1121 """print information about the working copy"""
1109 1122 parents = [p for p in repo.dirstate.parents() if p != nullid]
1110 1123 if not parents:
1111 1124 ui.write("unknown\n")
1112 1125 return
1113 1126
1114 1127 hexfunc = ui.verbose and hex or short
1115 1128 (c, a, d, u) = repo.changes()
1116 1129 output = ["%s%s" % ('+'.join([hexfunc(parent) for parent in parents]),
1117 1130 (c or a or d) and "+" or "")]
1118 1131
1119 1132 if not ui.quiet:
1120 1133 # multiple tags for a single parent separated by '/'
1121 1134 parenttags = ['/'.join(tags)
1122 1135 for tags in map(repo.nodetags, parents) if tags]
1123 1136 # tags for multiple parents separated by ' + '
1124 1137 if parenttags:
1125 1138 output.append(' + '.join(parenttags))
1126 1139
1127 1140 ui.write("%s\n" % ' '.join(output))
1128 1141
1129 1142 def import_(ui, repo, patch1, *patches, **opts):
1130 1143 """import an ordered set of patches"""
1131 1144 patches = (patch1,) + patches
1132 1145
1133 1146 if not opts['force']:
1134 1147 (c, a, d, u) = repo.changes()
1135 1148 if c or a or d:
1136 1149 raise util.Abort("outstanding uncommitted changes")
1137 1150
1138 1151 d = opts["base"]
1139 1152 strip = opts["strip"]
1140 1153
1141 1154 mailre = re.compile(r'(?:From |[\w-]+:)')
1142 1155
1143 1156 # attempt to detect the start of a patch
1144 1157 # (this heuristic is borrowed from quilt)
1145 1158 diffre = re.compile(r'(?:Index:[ \t]|diff[ \t]|RCS file: |' +
1146 1159 'retrieving revision [0-9]+(\.[0-9]+)*$|' +
1147 1160 '(---|\*\*\*)[ \t])')
1148 1161
1149 1162 for patch in patches:
1150 1163 ui.status("applying %s\n" % patch)
1151 1164 pf = os.path.join(d, patch)
1152 1165
1153 1166 message = []
1154 1167 user = None
1155 1168 hgpatch = False
1156 1169 for line in file(pf):
1157 1170 line = line.rstrip()
1158 1171 if (not message and not hgpatch and
1159 1172 mailre.match(line) and not opts['force']):
1160 1173 if len(line) > 35: line = line[:32] + '...'
1161 1174 raise util.Abort('first line looks like a '
1162 1175 'mail header: ' + line)
1163 1176 if diffre.match(line):
1164 1177 break
1165 1178 elif hgpatch:
1166 1179 # parse values when importing the result of an hg export
1167 1180 if line.startswith("# User "):
1168 1181 user = line[7:]
1169 1182 ui.debug('User: %s\n' % user)
1170 1183 elif not line.startswith("# ") and line:
1171 1184 message.append(line)
1172 1185 hgpatch = False
1173 1186 elif line == '# HG changeset patch':
1174 1187 hgpatch = True
1175 1188 message = [] # We may have collected garbage
1176 1189 else:
1177 1190 message.append(line)
1178 1191
1179 1192 # make sure message isn't empty
1180 1193 if not message:
1181 1194 message = "imported patch %s\n" % patch
1182 1195 else:
1183 1196 message = "%s\n" % '\n'.join(message)
1184 1197 ui.debug('message:\n%s\n' % message)
1185 1198
1186 1199 files = util.patch(strip, pf, ui)
1187 1200
1188 1201 if len(files) > 0:
1189 1202 addremove(ui, repo, *files)
1190 1203 repo.commit(files, message, user)
1191 1204
1192 1205 def incoming(ui, repo, source="default", **opts):
1193 1206 """show new changesets found in source"""
1194 1207 source = ui.expandpath(source)
1195 1208 other = hg.repository(ui, source)
1196 1209 if not other.local():
1197 1210 raise util.Abort("incoming doesn't work for remote repositories yet")
1198 1211 o = repo.findincoming(other)
1199 1212 if not o:
1200 1213 return
1201 1214 o = other.newer(o)
1202 1215 for n in o:
1203 1216 show_changeset(ui, other, changenode=n)
1204 1217 if opts['patch']:
1205 1218 prev = other.changelog.parents(n)[0]
1206 1219 dodiff(ui, ui, other, prev, n)
1207 1220 ui.write("\n")
1208 1221
1209 1222 def init(ui, dest="."):
1210 1223 """create a new repository in the given directory"""
1211 1224 if not os.path.exists(dest):
1212 1225 os.mkdir(dest)
1213 1226 hg.repository(ui, dest, create=1)
1214 1227
1215 1228 def locate(ui, repo, *pats, **opts):
1216 1229 """locate files matching specific patterns"""
1217 1230 end = opts['print0'] and '\0' or '\n'
1218 1231
1219 1232 for src, abs, rel, exact in walk(repo, pats, opts, '(?:.*/|)'):
1220 1233 if repo.dirstate.state(abs) == '?':
1221 1234 continue
1222 1235 if opts['fullpath']:
1223 1236 ui.write(os.path.join(repo.root, abs), end)
1224 1237 else:
1225 1238 ui.write(rel, end)
1226 1239
1227 1240 def log(ui, repo, *pats, **opts):
1228 1241 """show revision history of entire repository or files"""
1229 1242 class dui:
1230 1243 # Implement and delegate some ui protocol. Save hunks of
1231 1244 # output for later display in the desired order.
1232 1245 def __init__(self, ui):
1233 1246 self.ui = ui
1234 1247 self.hunk = {}
1235 1248 def bump(self, rev):
1236 1249 self.rev = rev
1237 1250 self.hunk[rev] = []
1238 1251 def note(self, *args):
1239 1252 if self.verbose:
1240 1253 self.write(*args)
1241 1254 def status(self, *args):
1242 1255 if not self.quiet:
1243 1256 self.write(*args)
1244 1257 def write(self, *args):
1245 1258 self.hunk[self.rev].append(args)
1246 1259 def debug(self, *args):
1247 1260 if self.debugflag:
1248 1261 self.write(*args)
1249 1262 def __getattr__(self, key):
1250 1263 return getattr(self.ui, key)
1251 1264 cwd = repo.getcwd()
1252 1265 if not pats and cwd:
1253 1266 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
1254 1267 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
1255 1268 changeiter, getchange = walkchangerevs(ui, repo, (pats and cwd) or '',
1256 1269 pats, opts)
1257 1270 for st, rev, fns in changeiter:
1258 1271 if st == 'window':
1259 1272 du = dui(ui)
1260 1273 elif st == 'add':
1261 1274 du.bump(rev)
1262 1275 br = None
1263 1276 if opts['branch']:
1264 1277 br = repo.branchlookup([repo.changelog.node(rev)])
1265 1278
1266 1279 if opts['keyword']:
1267 1280 changes = repo.changelog.read(repo.changelog.node(rev))
1268 1281 miss = 0
1269 1282 for k in opts['keyword']:
1270 1283 if not (k in changes[1].lower() or
1271 1284 k in changes[4].lower() or
1272 1285 k in " ".join(changes[3][:20]).lower()):
1273 1286 miss = 1
1274 1287 break
1275 1288 if miss:
1276 1289 continue
1277 1290
1278 1291 show_changeset(du, repo, rev, brinfo=br)
1279 1292 if opts['patch']:
1280 1293 changenode = repo.changelog.node(rev)
1281 1294 prev, other = repo.changelog.parents(changenode)
1282 1295 dodiff(du, du, repo, prev, changenode, fns)
1283 1296 du.write("\n\n")
1284 1297 elif st == 'iter':
1285 1298 for args in du.hunk[rev]:
1286 1299 ui.write(*args)
1287 1300
1288 1301 def manifest(ui, repo, rev=None):
1289 1302 """output the latest or given revision of the project manifest"""
1290 1303 if rev:
1291 1304 try:
1292 1305 # assume all revision numbers are for changesets
1293 1306 n = repo.lookup(rev)
1294 1307 change = repo.changelog.read(n)
1295 1308 n = change[0]
1296 1309 except hg.RepoError:
1297 1310 n = repo.manifest.lookup(rev)
1298 1311 else:
1299 1312 n = repo.manifest.tip()
1300 1313 m = repo.manifest.read(n)
1301 1314 mf = repo.manifest.readflags(n)
1302 1315 files = m.keys()
1303 1316 files.sort()
1304 1317
1305 1318 for f in files:
1306 1319 ui.write("%40s %3s %s\n" % (hex(m[f]), mf[f] and "755" or "644", f))
1307 1320
1308 1321 def outgoing(ui, repo, dest="default-push", **opts):
1309 1322 """show changesets not found in destination"""
1310 1323 dest = ui.expandpath(dest)
1311 1324 other = hg.repository(ui, dest)
1312 1325 o = repo.findoutgoing(other)
1313 1326 o = repo.newer(o)
1314 1327 for n in o:
1315 1328 show_changeset(ui, repo, changenode=n)
1316 1329 if opts['patch']:
1317 1330 prev = repo.changelog.parents(n)[0]
1318 1331 dodiff(ui, ui, repo, prev, n)
1319 1332 ui.write("\n")
1320 1333
1321 1334 def parents(ui, repo, rev=None):
1322 1335 """show the parents of the working dir or revision"""
1323 1336 if rev:
1324 1337 p = repo.changelog.parents(repo.lookup(rev))
1325 1338 else:
1326 1339 p = repo.dirstate.parents()
1327 1340
1328 1341 for n in p:
1329 1342 if n != nullid:
1330 1343 show_changeset(ui, repo, changenode=n)
1331 1344
1332 1345 def paths(ui, search=None):
1333 1346 """show definition of symbolic path names"""
1334 1347 try:
1335 1348 repo = hg.repository(ui=ui)
1336 1349 except hg.RepoError:
1337 1350 pass
1338 1351
1339 1352 if search:
1340 1353 for name, path in ui.configitems("paths"):
1341 1354 if name == search:
1342 1355 ui.write("%s\n" % path)
1343 1356 return
1344 1357 ui.warn("not found!\n")
1345 1358 return 1
1346 1359 else:
1347 1360 for name, path in ui.configitems("paths"):
1348 1361 ui.write("%s = %s\n" % (name, path))
1349 1362
1350 1363 def pull(ui, repo, source="default", **opts):
1351 1364 """pull changes from the specified source"""
1352 1365 source = ui.expandpath(source)
1353 1366 ui.status('pulling from %s\n' % (source))
1354 1367
1355 1368 if opts['ssh']:
1356 1369 ui.setconfig("ui", "ssh", opts['ssh'])
1357 1370 if opts['remotecmd']:
1358 1371 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
1359 1372
1360 1373 other = hg.repository(ui, source)
1361 1374 r = repo.pull(other)
1362 1375 if not r:
1363 1376 if opts['update']:
1364 1377 return update(ui, repo)
1365 1378 else:
1366 1379 ui.status("(run 'hg update' to get a working copy)\n")
1367 1380
1368 1381 return r
1369 1382
1370 1383 def push(ui, repo, dest="default-push", force=False, ssh=None, remotecmd=None):
1371 1384 """push changes to the specified destination"""
1372 1385 dest = ui.expandpath(dest)
1373 1386 ui.status('pushing to %s\n' % (dest))
1374 1387
1375 1388 if ssh:
1376 1389 ui.setconfig("ui", "ssh", ssh)
1377 1390 if remotecmd:
1378 1391 ui.setconfig("ui", "remotecmd", remotecmd)
1379 1392
1380 1393 other = hg.repository(ui, dest)
1381 1394 r = repo.push(other, force)
1382 1395 return r
1383 1396
1384 1397 def rawcommit(ui, repo, *flist, **rc):
1385 1398 "raw commit interface"
1386 1399 if rc['text']:
1387 1400 ui.warn("Warning: -t and --text is deprecated,"
1388 1401 " please use -m or --message instead.\n")
1389 1402 message = rc['message'] or rc['text']
1390 1403 if not message and rc['logfile']:
1391 1404 try:
1392 1405 message = open(rc['logfile']).read()
1393 1406 except IOError:
1394 1407 pass
1395 1408 if not message and not rc['logfile']:
1396 1409 raise util.Abort("missing commit message")
1397 1410
1398 1411 files = relpath(repo, list(flist))
1399 1412 if rc['files']:
1400 1413 files += open(rc['files']).read().splitlines()
1401 1414
1402 1415 rc['parent'] = map(repo.lookup, rc['parent'])
1403 1416
1404 1417 try:
1405 1418 repo.rawcommit(files, message, rc['user'], rc['date'], *rc['parent'])
1406 1419 except ValueError, inst:
1407 1420 raise util.Abort(str(inst))
1408 1421
1409 1422 def recover(ui, repo):
1410 1423 """roll back an interrupted transaction"""
1411 1424 repo.recover()
1412 1425
1413 1426 def remove(ui, repo, pat, *pats, **opts):
1414 1427 """remove the specified files on the next commit"""
1415 1428 names = []
1416 1429 def okaytoremove(abs, rel, exact):
1417 1430 c, a, d, u = repo.changes(files = [abs])
1418 1431 reason = None
1419 1432 if c: reason = 'is modified'
1420 1433 elif a: reason = 'has been marked for add'
1421 1434 elif u: reason = 'is not managed'
1422 1435 if reason:
1423 1436 if exact: ui.warn('not removing %s: file %s\n' % (rel, reason))
1424 1437 else:
1425 1438 return True
1426 1439 for src, abs, rel, exact in walk(repo, (pat,) + pats, opts):
1427 1440 if okaytoremove(abs, rel, exact):
1428 1441 if ui.verbose or not exact: ui.status('removing %s\n' % rel)
1429 1442 names.append(abs)
1430 1443 for name in names:
1431 1444 try:
1432 1445 os.unlink(name)
1433 1446 except OSError, inst:
1434 1447 if inst.errno != errno.ENOENT: raise
1435 1448 repo.remove(names)
1436 1449
1437 1450 def rename(ui, repo, *pats, **opts):
1438 1451 """rename files; equivalent of copy + remove"""
1439 1452 errs, copied = docopy(ui, repo, pats, opts)
1440 1453 names = []
1441 1454 for abs, rel, exact in copied:
1442 1455 if ui.verbose or not exact: ui.status('removing %s\n' % rel)
1443 1456 try:
1444 1457 os.unlink(rel)
1445 1458 except OSError, inst:
1446 1459 if inst.errno != errno.ENOENT: raise
1447 1460 names.append(abs)
1448 1461 repo.remove(names)
1449 1462 return errs
1450 1463
1451 1464 def revert(ui, repo, *names, **opts):
1452 1465 """revert modified files or dirs back to their unmodified states"""
1453 1466 node = opts['rev'] and repo.lookup(opts['rev']) or \
1454 1467 repo.dirstate.parents()[0]
1455 1468 root = os.path.realpath(repo.root)
1456 1469
1457 1470 def trimpath(p):
1458 1471 p = os.path.realpath(p)
1459 1472 if p.startswith(root):
1460 1473 rest = p[len(root):]
1461 1474 if not rest:
1462 1475 return rest
1463 1476 if p.startswith(os.sep):
1464 1477 return rest[1:]
1465 1478 return p
1466 1479
1467 1480 relnames = map(trimpath, names or [os.getcwd()])
1468 1481 chosen = {}
1469 1482
1470 1483 def choose(name):
1471 1484 def body(name):
1472 1485 for r in relnames:
1473 1486 if not name.startswith(r):
1474 1487 continue
1475 1488 rest = name[len(r):]
1476 1489 if not rest:
1477 1490 return r, True
1478 1491 depth = rest.count(os.sep)
1479 1492 if not r:
1480 1493 if depth == 0 or not opts['nonrecursive']:
1481 1494 return r, True
1482 1495 elif rest[0] == os.sep:
1483 1496 if depth == 1 or not opts['nonrecursive']:
1484 1497 return r, True
1485 1498 return None, False
1486 1499 relname, ret = body(name)
1487 1500 if ret:
1488 1501 chosen[relname] = 1
1489 1502 return ret
1490 1503
1491 1504 r = repo.update(node, False, True, choose, False)
1492 1505 for n in relnames:
1493 1506 if n not in chosen:
1494 1507 ui.warn('error: no matches for %s\n' % n)
1495 1508 r = 1
1496 1509 sys.stdout.flush()
1497 1510 return r
1498 1511
1499 1512 def root(ui, repo):
1500 1513 """print the root (top) of the current working dir"""
1501 1514 ui.write(repo.root + "\n")
1502 1515
1503 1516 def serve(ui, repo, **opts):
1504 1517 """export the repository via HTTP"""
1505 1518
1506 1519 if opts["stdio"]:
1507 1520 fin, fout = sys.stdin, sys.stdout
1508 1521 sys.stdout = sys.stderr
1509 1522
1510 1523 def getarg():
1511 1524 argline = fin.readline()[:-1]
1512 1525 arg, l = argline.split()
1513 1526 val = fin.read(int(l))
1514 1527 return arg, val
1515 1528 def respond(v):
1516 1529 fout.write("%d\n" % len(v))
1517 1530 fout.write(v)
1518 1531 fout.flush()
1519 1532
1520 1533 lock = None
1521 1534
1522 1535 while 1:
1523 1536 cmd = fin.readline()[:-1]
1524 1537 if cmd == '':
1525 1538 return
1526 1539 if cmd == "heads":
1527 1540 h = repo.heads()
1528 1541 respond(" ".join(map(hex, h)) + "\n")
1529 1542 if cmd == "lock":
1530 1543 lock = repo.lock()
1531 1544 respond("")
1532 1545 if cmd == "unlock":
1533 1546 if lock:
1534 1547 lock.release()
1535 1548 lock = None
1536 1549 respond("")
1537 1550 elif cmd == "branches":
1538 1551 arg, nodes = getarg()
1539 1552 nodes = map(bin, nodes.split(" "))
1540 1553 r = []
1541 1554 for b in repo.branches(nodes):
1542 1555 r.append(" ".join(map(hex, b)) + "\n")
1543 1556 respond("".join(r))
1544 1557 elif cmd == "between":
1545 1558 arg, pairs = getarg()
1546 1559 pairs = [map(bin, p.split("-")) for p in pairs.split(" ")]
1547 1560 r = []
1548 1561 for b in repo.between(pairs):
1549 1562 r.append(" ".join(map(hex, b)) + "\n")
1550 1563 respond("".join(r))
1551 1564 elif cmd == "changegroup":
1552 1565 nodes = []
1553 1566 arg, roots = getarg()
1554 1567 nodes = map(bin, roots.split(" "))
1555 1568
1556 1569 cg = repo.changegroup(nodes)
1557 1570 while 1:
1558 1571 d = cg.read(4096)
1559 1572 if not d:
1560 1573 break
1561 1574 fout.write(d)
1562 1575
1563 1576 fout.flush()
1564 1577
1565 1578 elif cmd == "addchangegroup":
1566 1579 if not lock:
1567 1580 respond("not locked")
1568 1581 continue
1569 1582 respond("")
1570 1583
1571 1584 r = repo.addchangegroup(fin)
1572 1585 respond("")
1573 1586
1574 1587 optlist = "name templates style address port ipv6 accesslog errorlog"
1575 1588 for o in optlist.split():
1576 1589 if opts[o]:
1577 1590 ui.setconfig("web", o, opts[o])
1578 1591
1579 1592 try:
1580 1593 httpd = hgweb.create_server(repo)
1581 1594 except socket.error, inst:
1582 1595 raise util.Abort('cannot start server: ' + inst.args[1])
1583 1596
1584 1597 if ui.verbose:
1585 1598 addr, port = httpd.socket.getsockname()
1586 1599 if addr == '0.0.0.0':
1587 1600 addr = socket.gethostname()
1588 1601 else:
1589 1602 try:
1590 1603 addr = socket.gethostbyaddr(addr)[0]
1591 1604 except socket.error:
1592 1605 pass
1593 1606 if port != 80:
1594 1607 ui.status('listening at http://%s:%d/\n' % (addr, port))
1595 1608 else:
1596 1609 ui.status('listening at http://%s/\n' % addr)
1597 1610 httpd.serve_forever()
1598 1611
1599 1612 def status(ui, repo, *pats, **opts):
1600 1613 '''show changed files in the working directory
1601 1614
1602 1615 M = modified
1603 1616 A = added
1604 1617 R = removed
1605 1618 ? = not tracked
1606 1619 '''
1607 1620
1608 1621 cwd = repo.getcwd()
1609 1622 files, matchfn, anypats = matchpats(repo, cwd, pats, opts)
1610 1623 (c, a, d, u) = [[util.pathto(cwd, x) for x in n]
1611 1624 for n in repo.changes(files=files, match=matchfn)]
1612 1625
1613 1626 changetypes = [('modified', 'M', c),
1614 1627 ('added', 'A', a),
1615 1628 ('removed', 'R', d),
1616 1629 ('unknown', '?', u)]
1617 1630
1618 1631 end = opts['print0'] and '\0' or '\n'
1619 1632
1620 1633 for opt, char, changes in ([ct for ct in changetypes if opts[ct[0]]]
1621 1634 or changetypes):
1622 1635 if opts['no_status']:
1623 1636 format = "%%s%s" % end
1624 1637 else:
1625 1638 format = "%s %%s%s" % (char, end);
1626 1639
1627 1640 for f in changes:
1628 1641 ui.write(format % f)
1629 1642
1630 1643 def tag(ui, repo, name, rev=None, **opts):
1631 1644 """add a tag for the current tip or a given revision"""
1632 1645 if opts['text']:
1633 1646 ui.warn("Warning: -t and --text is deprecated,"
1634 1647 " please use -m or --message instead.\n")
1635 1648 if name == "tip":
1636 1649 raise util.Abort("the name 'tip' is reserved")
1637 1650 if rev:
1638 1651 r = hex(repo.lookup(rev))
1639 1652 else:
1640 1653 r = hex(repo.changelog.tip())
1641 1654
1642 1655 if name.find(revrangesep) >= 0:
1643 1656 raise util.Abort("'%s' cannot be used in a tag name" % revrangesep)
1644 1657
1645 1658 if opts['local']:
1646 1659 repo.opener("localtags", "a").write("%s %s\n" % (r, name))
1647 1660 return
1648 1661
1649 1662 (c, a, d, u) = repo.changes()
1650 1663 for x in (c, a, d, u):
1651 1664 if ".hgtags" in x:
1652 1665 raise util.Abort("working copy of .hgtags is changed "
1653 1666 "(please commit .hgtags manually)")
1654 1667
1655 1668 repo.wfile(".hgtags", "ab").write("%s %s\n" % (r, name))
1656 1669 if repo.dirstate.state(".hgtags") == '?':
1657 1670 repo.add([".hgtags"])
1658 1671
1659 1672 message = (opts['message'] or opts['text'] or
1660 1673 "Added tag %s for changeset %s" % (name, r))
1661 1674 try:
1662 1675 repo.commit([".hgtags"], message, opts['user'], opts['date'])
1663 1676 except ValueError, inst:
1664 1677 raise util.Abort(str(inst))
1665 1678
1666 1679 def tags(ui, repo):
1667 1680 """list repository tags"""
1668 1681
1669 1682 l = repo.tagslist()
1670 1683 l.reverse()
1671 1684 for t, n in l:
1672 1685 try:
1673 1686 r = "%5d:%s" % (repo.changelog.rev(n), hex(n))
1674 1687 except KeyError:
1675 1688 r = " ?:?"
1676 1689 ui.write("%-30s %s\n" % (t, r))
1677 1690
1678 1691 def tip(ui, repo):
1679 1692 """show the tip revision"""
1680 1693 n = repo.changelog.tip()
1681 1694 show_changeset(ui, repo, changenode=n)
1682 1695
1683 1696 def unbundle(ui, repo, fname):
1684 1697 """apply a changegroup file"""
1685 1698 f = urllib.urlopen(fname)
1686 1699
1687 1700 if f.read(4) != "HG10":
1688 1701 raise util.Abort("%s: not a Mercurial bundle file" % fname)
1689 1702
1690 1703 def bzgenerator(f):
1691 1704 zd = bz2.BZ2Decompressor()
1692 1705 for chunk in f:
1693 1706 yield zd.decompress(chunk)
1694 1707 yield zd.flush()
1695 1708
1696 1709 bzgen = bzgenerator(util.filechunkiter(f, 4096))
1697 1710 repo.addchangegroup(util.chunkbuffer(bzgen))
1698 1711
1699 1712 def undo(ui, repo):
1700 1713 """undo the last commit or pull
1701 1714
1702 1715 Roll back the last pull or commit transaction on the
1703 1716 repository, restoring the project to its earlier state.
1704 1717
1705 1718 This command should be used with care. There is only one level of
1706 1719 undo and there is no redo.
1707 1720
1708 1721 This command is not intended for use on public repositories. Once
1709 1722 a change is visible for pull by other users, undoing it locally is
1710 1723 ineffective.
1711 1724 """
1712 1725 repo.undo()
1713 1726
1714 1727 def update(ui, repo, node=None, merge=False, clean=False, branch=None):
1715 1728 '''update or merge working directory
1716 1729
1717 1730 If there are no outstanding changes in the working directory and
1718 1731 there is a linear relationship between the current version and the
1719 1732 requested version, the result is the requested version.
1720 1733
1721 1734 Otherwise the result is a merge between the contents of the
1722 1735 current working directory and the requested version. Files that
1723 1736 changed between either parent are marked as changed for the next
1724 1737 commit and a commit must be performed before any further updates
1725 1738 are allowed.
1726 1739 '''
1727 1740 if branch:
1728 1741 br = repo.branchlookup(branch=branch)
1729 1742 found = []
1730 1743 for x in br:
1731 1744 if branch in br[x]:
1732 1745 found.append(x)
1733 1746 if len(found) > 1:
1734 1747 ui.warn("Found multiple heads for %s\n" % branch)
1735 1748 for x in found:
1736 1749 show_changeset(ui, repo, changenode=x, brinfo=br)
1737 1750 return 1
1738 1751 if len(found) == 1:
1739 1752 node = found[0]
1740 1753 ui.warn("Using head %s for branch %s\n" % (short(node), branch))
1741 1754 else:
1742 1755 ui.warn("branch %s not found\n" % (branch))
1743 1756 return 1
1744 1757 else:
1745 1758 node = node and repo.lookup(node) or repo.changelog.tip()
1746 1759 return repo.update(node, allow=merge, force=clean)
1747 1760
1748 1761 def verify(ui, repo):
1749 1762 """verify the integrity of the repository"""
1750 1763 return repo.verify()
1751 1764
1752 1765 # Command options and aliases are listed here, alphabetically
1753 1766
1754 1767 table = {
1755 1768 "^add":
1756 1769 (add,
1757 1770 [('I', 'include', [], 'include path in search'),
1758 1771 ('X', 'exclude', [], 'exclude path from search')],
1759 1772 "hg add [OPTION]... [FILE]..."),
1760 1773 "addremove":
1761 1774 (addremove,
1762 1775 [('I', 'include', [], 'include path in search'),
1763 1776 ('X', 'exclude', [], 'exclude path from search')],
1764 1777 "hg addremove [OPTION]... [FILE]..."),
1765 1778 "^annotate":
1766 1779 (annotate,
1767 1780 [('r', 'rev', '', 'revision'),
1768 1781 ('a', 'text', None, 'treat all files as text'),
1769 1782 ('u', 'user', None, 'show user'),
1770 1783 ('n', 'number', None, 'show revision number'),
1771 1784 ('c', 'changeset', None, 'show changeset'),
1772 1785 ('I', 'include', [], 'include path in search'),
1773 1786 ('X', 'exclude', [], 'exclude path from search')],
1774 1787 'hg annotate [OPTION]... FILE...'),
1775 1788 "bundle":
1776 1789 (bundle,
1777 1790 [],
1778 1791 'hg bundle FILE DEST'),
1779 1792 "cat":
1780 1793 (cat,
1781 1794 [('I', 'include', [], 'include path in search'),
1782 1795 ('X', 'exclude', [], 'exclude path from search'),
1783 1796 ('o', 'output', "", 'output to file'),
1784 1797 ('r', 'rev', '', 'revision')],
1785 1798 'hg cat [OPTION]... FILE...'),
1786 1799 "^clone":
1787 1800 (clone,
1788 1801 [('U', 'noupdate', None, 'skip update after cloning'),
1789 1802 ('e', 'ssh', "", 'ssh command'),
1790 1803 ('', 'pull', None, 'use pull protocol to copy metadata'),
1791 1804 ('', 'remotecmd', "", 'remote hg command')],
1792 1805 'hg clone [OPTION]... SOURCE [DEST]'),
1793 1806 "^commit|ci":
1794 1807 (commit,
1795 1808 [('A', 'addremove', None, 'run add/remove during commit'),
1796 1809 ('I', 'include', [], 'include path in search'),
1797 1810 ('X', 'exclude', [], 'exclude path from search'),
1798 1811 ('m', 'message', "", 'commit message'),
1799 1812 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1800 1813 ('l', 'logfile', "", 'commit message file'),
1801 1814 ('d', 'date', "", 'date code'),
1802 1815 ('u', 'user', "", 'user')],
1803 1816 'hg commit [OPTION]... [FILE]...'),
1804 1817 "copy|cp": (copy,
1805 1818 [('I', 'include', [], 'include path in search'),
1806 1819 ('X', 'exclude', [], 'exclude path from search'),
1807 1820 ('A', 'after', None, 'record a copy after it has happened'),
1808 1821 ('f', 'force', None, 'replace destination if it exists'),
1809 1822 ('p', 'parents', None, 'append source path to dest')],
1810 1823 'hg copy [OPTION]... [SOURCE]... DEST'),
1811 1824 "debugancestor": (debugancestor, [], 'debugancestor INDEX REV1 REV2'),
1812 1825 "debugcheckstate": (debugcheckstate, [], 'debugcheckstate'),
1813 1826 "debugconfig": (debugconfig, [], 'debugconfig'),
1827 "debugsetparents": (debugsetparents, [], 'debugsetparents REV1 [REV2]'),
1814 1828 "debugstate": (debugstate, [], 'debugstate'),
1815 1829 "debugdata": (debugdata, [], 'debugdata FILE REV'),
1816 1830 "debugindex": (debugindex, [], 'debugindex FILE'),
1817 1831 "debugindexdot": (debugindexdot, [], 'debugindexdot FILE'),
1818 1832 "debugrename": (debugrename, [], 'debugrename FILE [REV]'),
1819 1833 "debugwalk":
1820 1834 (debugwalk,
1821 1835 [('I', 'include', [], 'include path in search'),
1822 1836 ('X', 'exclude', [], 'exclude path from search')],
1823 1837 'debugwalk [OPTION]... [FILE]...'),
1824 1838 "^diff":
1825 1839 (diff,
1826 1840 [('r', 'rev', [], 'revision'),
1827 1841 ('a', 'text', None, 'treat all files as text'),
1828 1842 ('I', 'include', [], 'include path in search'),
1829 1843 ('X', 'exclude', [], 'exclude path from search')],
1830 1844 'hg diff [-a] [-I] [-X] [-r REV1 [-r REV2]] [FILE]...'),
1831 1845 "^export":
1832 1846 (export,
1833 1847 [('o', 'output', "", 'output to file'),
1834 1848 ('a', 'text', None, 'treat all files as text')],
1835 1849 "hg export [-a] [-o OUTFILE] REV..."),
1836 1850 "forget":
1837 1851 (forget,
1838 1852 [('I', 'include', [], 'include path in search'),
1839 1853 ('X', 'exclude', [], 'exclude path from search')],
1840 1854 "hg forget [OPTION]... FILE..."),
1841 1855 "grep":
1842 1856 (grep,
1843 1857 [('0', 'print0', None, 'end fields with NUL'),
1844 1858 ('I', 'include', [], 'include path in search'),
1845 1859 ('X', 'exclude', [], 'include path in search'),
1846 1860 ('', 'all', None, 'print all revisions with matches'),
1847 1861 ('i', 'ignore-case', None, 'ignore case when matching'),
1848 1862 ('l', 'files-with-matches', None, 'print names of files and revs with matches'),
1849 1863 ('n', 'line-number', None, 'print line numbers'),
1850 1864 ('r', 'rev', [], 'search in revision rev'),
1851 1865 ('u', 'user', None, 'print user who made change')],
1852 1866 "hg grep [OPTION]... PATTERN [FILE]..."),
1853 1867 "heads":
1854 1868 (heads,
1855 1869 [('b', 'branches', None, 'find branch info')],
1856 1870 'hg heads [-b]'),
1857 1871 "help": (help_, [], 'hg help [COMMAND]'),
1858 1872 "identify|id": (identify, [], 'hg identify'),
1859 1873 "import|patch":
1860 1874 (import_,
1861 1875 [('p', 'strip', 1, 'path strip'),
1862 1876 ('f', 'force', None, 'skip check for outstanding changes'),
1863 1877 ('b', 'base', "", 'base path')],
1864 1878 "hg import [-f] [-p NUM] [-b BASE] PATCH..."),
1865 1879 "incoming|in": (incoming,
1866 1880 [('p', 'patch', None, 'show patch')],
1867 1881 'hg incoming [-p] [SOURCE]'),
1868 1882 "^init": (init, [], 'hg init [DEST]'),
1869 1883 "locate":
1870 1884 (locate,
1871 1885 [('r', 'rev', '', 'revision'),
1872 1886 ('0', 'print0', None, 'end filenames with NUL'),
1873 1887 ('f', 'fullpath', None, 'print complete paths'),
1874 1888 ('I', 'include', [], 'include path in search'),
1875 1889 ('X', 'exclude', [], 'exclude path from search')],
1876 1890 'hg locate [OPTION]... [PATTERN]...'),
1877 1891 "^log|history":
1878 1892 (log,
1879 1893 [('I', 'include', [], 'include path in search'),
1880 1894 ('X', 'exclude', [], 'exclude path from search'),
1881 1895 ('b', 'branch', None, 'show branches'),
1882 1896 ('k', 'keyword', [], 'search for a keyword'),
1883 1897 ('r', 'rev', [], 'revision'),
1884 1898 ('p', 'patch', None, 'show patch')],
1885 1899 'hg log [-I] [-X] [-r REV]... [-p] [FILE]'),
1886 1900 "manifest": (manifest, [], 'hg manifest [REV]'),
1887 1901 "outgoing|out": (outgoing,
1888 1902 [('p', 'patch', None, 'show patch')],
1889 1903 'hg outgoing [-p] [DEST]'),
1890 1904 "parents": (parents, [], 'hg parents [REV]'),
1891 1905 "paths": (paths, [], 'hg paths [NAME]'),
1892 1906 "^pull":
1893 1907 (pull,
1894 1908 [('u', 'update', None, 'update working directory'),
1895 1909 ('e', 'ssh', "", 'ssh command'),
1896 1910 ('', 'remotecmd', "", 'remote hg command')],
1897 1911 'hg pull [-u] [-e FILE] [--remotecmd FILE] [SOURCE]'),
1898 1912 "^push":
1899 1913 (push,
1900 1914 [('f', 'force', None, 'force push'),
1901 1915 ('e', 'ssh', "", 'ssh command'),
1902 1916 ('', 'remotecmd', "", 'remote hg command')],
1903 1917 'hg push [-f] [-e FILE] [--remotecmd FILE] [DEST]'),
1904 1918 "rawcommit":
1905 1919 (rawcommit,
1906 1920 [('p', 'parent', [], 'parent'),
1907 1921 ('d', 'date', "", 'date code'),
1908 1922 ('u', 'user', "", 'user'),
1909 1923 ('F', 'files', "", 'file list'),
1910 1924 ('m', 'message', "", 'commit message'),
1911 1925 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1912 1926 ('l', 'logfile', "", 'commit message file')],
1913 1927 'hg rawcommit [OPTION]... [FILE]...'),
1914 1928 "recover": (recover, [], "hg recover"),
1915 1929 "^remove|rm": (remove,
1916 1930 [('I', 'include', [], 'include path in search'),
1917 1931 ('X', 'exclude', [], 'exclude path from search')],
1918 1932 "hg remove [OPTION]... FILE..."),
1919 1933 "rename|mv": (rename,
1920 1934 [('I', 'include', [], 'include path in search'),
1921 1935 ('X', 'exclude', [], 'exclude path from search'),
1922 1936 ('A', 'after', None, 'record a copy after it has happened'),
1923 1937 ('f', 'force', None, 'replace destination if it exists'),
1924 1938 ('p', 'parents', None, 'append source path to dest')],
1925 1939 'hg rename [OPTION]... [SOURCE]... DEST'),
1926 1940 "^revert":
1927 1941 (revert,
1928 1942 [("n", "nonrecursive", None, "don't recurse into subdirs"),
1929 1943 ("r", "rev", "", "revision")],
1930 1944 "hg revert [-n] [-r REV] [NAME]..."),
1931 1945 "root": (root, [], "hg root"),
1932 1946 "^serve":
1933 1947 (serve,
1934 1948 [('A', 'accesslog', '', 'access log file'),
1935 1949 ('E', 'errorlog', '', 'error log file'),
1936 1950 ('p', 'port', 0, 'listen port'),
1937 1951 ('a', 'address', '', 'interface address'),
1938 1952 ('n', 'name', "", 'repository name'),
1939 1953 ('', 'stdio', None, 'for remote clients'),
1940 1954 ('t', 'templates', "", 'template directory'),
1941 1955 ('', 'style', "", 'template style'),
1942 1956 ('6', 'ipv6', None, 'use IPv6 in addition to IPv4')],
1943 1957 "hg serve [OPTION]..."),
1944 1958 "^status":
1945 1959 (status,
1946 1960 [('m', 'modified', None, 'show only modified files'),
1947 1961 ('a', 'added', None, 'show only added files'),
1948 1962 ('r', 'removed', None, 'show only removed files'),
1949 1963 ('u', 'unknown', None, 'show only unknown (not tracked) files'),
1950 1964 ('n', 'no-status', None, 'hide status prefix'),
1951 1965 ('0', 'print0', None, 'end filenames with NUL'),
1952 1966 ('I', 'include', [], 'include path in search'),
1953 1967 ('X', 'exclude', [], 'exclude path from search')],
1954 1968 "hg status [OPTION]... [FILE]..."),
1955 1969 "tag":
1956 1970 (tag,
1957 1971 [('l', 'local', None, 'make the tag local'),
1958 1972 ('m', 'message', "", 'commit message'),
1959 1973 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1960 1974 ('d', 'date', "", 'date code'),
1961 1975 ('u', 'user', "", 'user')],
1962 1976 'hg tag [OPTION]... NAME [REV]'),
1963 1977 "tags": (tags, [], 'hg tags'),
1964 1978 "tip": (tip, [], 'hg tip'),
1965 1979 "unbundle":
1966 1980 (unbundle,
1967 1981 [],
1968 1982 'hg unbundle FILE'),
1969 1983 "undo": (undo, [], 'hg undo'),
1970 1984 "^update|up|checkout|co":
1971 1985 (update,
1972 1986 [('b', 'branch', "", 'checkout the head of a specific branch'),
1973 1987 ('m', 'merge', None, 'allow merging of conflicts'),
1974 1988 ('C', 'clean', None, 'overwrite locally modified files')],
1975 1989 'hg update [-b TAG] [-m] [-C] [REV]'),
1976 1990 "verify": (verify, [], 'hg verify'),
1977 1991 "version": (show_version, [], 'hg version'),
1978 1992 }
1979 1993
1980 1994 globalopts = [
1981 1995 ('R', 'repository', "", 'repository root directory'),
1982 1996 ('', 'cwd', '', 'change working directory'),
1983 1997 ('y', 'noninteractive', None, 'run non-interactively'),
1984 1998 ('q', 'quiet', None, 'quiet mode'),
1985 1999 ('v', 'verbose', None, 'verbose mode'),
1986 2000 ('', 'debug', None, 'debug mode'),
1987 2001 ('', 'debugger', None, 'start debugger'),
1988 2002 ('', 'traceback', None, 'print traceback on exception'),
1989 2003 ('', 'time', None, 'time how long the command takes'),
1990 2004 ('', 'profile', None, 'profile'),
1991 2005 ('', 'version', None, 'output version information and exit'),
1992 2006 ('h', 'help', None, 'display help and exit'),
1993 2007 ]
1994 2008
1995 2009 norepo = ("clone init version help debugancestor debugconfig debugdata"
1996 2010 " debugindex debugindexdot paths")
1997 2011
1998 2012 def find(cmd):
1999 2013 for e in table.keys():
2000 2014 if re.match("(%s)$" % e, cmd):
2001 2015 return e, table[e]
2002 2016
2003 2017 raise UnknownCommand(cmd)
2004 2018
2005 2019 class SignalInterrupt(Exception):
2006 2020 """Exception raised on SIGTERM and SIGHUP."""
2007 2021
2008 2022 def catchterm(*args):
2009 2023 raise SignalInterrupt
2010 2024
2011 2025 def run():
2012 2026 sys.exit(dispatch(sys.argv[1:]))
2013 2027
2014 2028 class ParseError(Exception):
2015 2029 """Exception raised on errors in parsing the command line."""
2016 2030
2017 2031 def parse(args):
2018 2032 options = {}
2019 2033 cmdoptions = {}
2020 2034
2021 2035 try:
2022 2036 args = fancyopts.fancyopts(args, globalopts, options)
2023 2037 except fancyopts.getopt.GetoptError, inst:
2024 2038 raise ParseError(None, inst)
2025 2039
2026 2040 if args:
2027 2041 cmd, args = args[0], args[1:]
2028 2042 i = find(cmd)[1]
2029 2043 c = list(i[1])
2030 2044 else:
2031 2045 cmd = None
2032 2046 c = []
2033 2047
2034 2048 # combine global options into local
2035 2049 for o in globalopts:
2036 2050 c.append((o[0], o[1], options[o[1]], o[3]))
2037 2051
2038 2052 try:
2039 2053 args = fancyopts.fancyopts(args, c, cmdoptions)
2040 2054 except fancyopts.getopt.GetoptError, inst:
2041 2055 raise ParseError(cmd, inst)
2042 2056
2043 2057 # separate global options back out
2044 2058 for o in globalopts:
2045 2059 n = o[1]
2046 2060 options[n] = cmdoptions[n]
2047 2061 del cmdoptions[n]
2048 2062
2049 2063 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
2050 2064
2051 2065 def dispatch(args):
2052 2066 signal.signal(signal.SIGTERM, catchterm)
2053 2067 try:
2054 2068 signal.signal(signal.SIGHUP, catchterm)
2055 2069 except AttributeError:
2056 2070 pass
2057 2071
2058 2072 u = ui.ui()
2059 2073 external = []
2060 2074 for x in u.extensions():
2061 2075 if x[1]:
2062 2076 try:
2063 2077 mod = imp.load_source(x[0], x[1])
2064 2078 except:
2065 2079 u.warn("*** failed to import extension %s\n" % x[1])
2066 2080 continue
2067 2081 else:
2068 2082 def importh(name):
2069 2083 mod = __import__(name)
2070 2084 components = name.split('.')
2071 2085 for comp in components[1:]:
2072 2086 mod = getattr(mod, comp)
2073 2087 return mod
2074 2088 try:
2075 2089 mod = importh(x[0])
2076 2090 except:
2077 2091 u.warn("failed to import extension %s\n" % x[0])
2078 2092 continue
2079 2093
2080 2094 external.append(mod)
2081 2095 for x in external:
2082 2096 cmdtable = getattr(x, 'cmdtable', {})
2083 2097 for t in cmdtable:
2084 2098 if t in table:
2085 2099 u.warn("module %s overrides %s\n" % (x.__name__, t))
2086 2100 table.update(cmdtable)
2087 2101
2088 2102 try:
2089 2103 cmd, func, args, options, cmdoptions = parse(args)
2090 2104 except ParseError, inst:
2091 2105 if inst.args[0]:
2092 2106 u.warn("hg %s: %s\n" % (inst.args[0], inst.args[1]))
2093 2107 help_(u, inst.args[0])
2094 2108 else:
2095 2109 u.warn("hg: %s\n" % inst.args[1])
2096 2110 help_(u, 'shortlist')
2097 2111 sys.exit(-1)
2098 2112 except UnknownCommand, inst:
2099 2113 u.warn("hg: unknown command '%s'\n" % inst.args[0])
2100 2114 help_(u, 'shortlist')
2101 2115 sys.exit(1)
2102 2116
2103 2117 if options["time"]:
2104 2118 def get_times():
2105 2119 t = os.times()
2106 2120 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
2107 2121 t = (t[0], t[1], t[2], t[3], time.clock())
2108 2122 return t
2109 2123 s = get_times()
2110 2124 def print_time():
2111 2125 t = get_times()
2112 2126 u.warn("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n" %
2113 2127 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
2114 2128 atexit.register(print_time)
2115 2129
2116 2130 u.updateopts(options["verbose"], options["debug"], options["quiet"],
2117 2131 not options["noninteractive"])
2118 2132
2119 2133 # enter the debugger before command execution
2120 2134 if options['debugger']:
2121 2135 pdb.set_trace()
2122 2136
2123 2137 try:
2124 2138 try:
2125 2139 if options['help']:
2126 2140 help_(u, cmd, options['version'])
2127 2141 sys.exit(0)
2128 2142 elif options['version']:
2129 2143 show_version(u)
2130 2144 sys.exit(0)
2131 2145 elif not cmd:
2132 2146 help_(u, 'shortlist')
2133 2147 sys.exit(0)
2134 2148
2135 2149 if options['cwd']:
2136 2150 try:
2137 2151 os.chdir(options['cwd'])
2138 2152 except OSError, inst:
2139 2153 raise util.Abort('%s: %s' %
2140 2154 (options['cwd'], inst.strerror))
2141 2155
2142 2156 if cmd not in norepo.split():
2143 2157 path = options["repository"] or ""
2144 2158 repo = hg.repository(ui=u, path=path)
2145 2159 for x in external:
2146 2160 if hasattr(x, 'reposetup'): x.reposetup(u, repo)
2147 2161 d = lambda: func(u, repo, *args, **cmdoptions)
2148 2162 else:
2149 2163 d = lambda: func(u, *args, **cmdoptions)
2150 2164
2151 2165 if options['profile']:
2152 2166 import hotshot, hotshot.stats
2153 2167 prof = hotshot.Profile("hg.prof")
2154 2168 r = prof.runcall(d)
2155 2169 prof.close()
2156 2170 stats = hotshot.stats.load("hg.prof")
2157 2171 stats.strip_dirs()
2158 2172 stats.sort_stats('time', 'calls')
2159 2173 stats.print_stats(40)
2160 2174 return r
2161 2175 else:
2162 2176 return d()
2163 2177 except:
2164 2178 # enter the debugger when we hit an exception
2165 2179 if options['debugger']:
2166 2180 pdb.post_mortem(sys.exc_info()[2])
2167 2181 if options['traceback']:
2168 2182 traceback.print_exc()
2169 2183 raise
2170 2184 except hg.RepoError, inst:
2171 2185 u.warn("abort: ", inst, "!\n")
2172 2186 except revlog.RevlogError, inst:
2173 2187 u.warn("abort: ", inst, "!\n")
2174 2188 except SignalInterrupt:
2175 2189 u.warn("killed!\n")
2176 2190 except KeyboardInterrupt:
2177 2191 try:
2178 2192 u.warn("interrupted!\n")
2179 2193 except IOError, inst:
2180 2194 if inst.errno == errno.EPIPE:
2181 2195 if u.debugflag:
2182 2196 u.warn("\nbroken pipe\n")
2183 2197 else:
2184 2198 raise
2185 2199 except IOError, inst:
2186 2200 if hasattr(inst, "code"):
2187 2201 u.warn("abort: %s\n" % inst)
2188 2202 elif hasattr(inst, "reason"):
2189 2203 u.warn("abort: error: %s\n" % inst.reason[1])
2190 2204 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
2191 2205 if u.debugflag:
2192 2206 u.warn("broken pipe\n")
2193 2207 elif getattr(inst, "strerror", None):
2194 2208 if getattr(inst, "filename", None):
2195 2209 u.warn("abort: %s - %s\n" % (inst.strerror, inst.filename))
2196 2210 else:
2197 2211 u.warn("abort: %s\n" % inst.strerror)
2198 2212 else:
2199 2213 raise
2200 2214 except OSError, inst:
2201 2215 if hasattr(inst, "filename"):
2202 2216 u.warn("abort: %s: %s\n" % (inst.strerror, inst.filename))
2203 2217 else:
2204 2218 u.warn("abort: %s\n" % inst.strerror)
2205 2219 except util.Abort, inst:
2206 2220 u.warn('abort: ', inst.args[0] % inst.args[1:], '\n')
2207 2221 sys.exit(1)
2208 2222 except TypeError, inst:
2209 2223 # was this an argument error?
2210 2224 tb = traceback.extract_tb(sys.exc_info()[2])
2211 2225 if len(tb) > 2: # no
2212 2226 raise
2213 2227 u.debug(inst, "\n")
2214 2228 u.warn("%s: invalid arguments\n" % cmd)
2215 2229 help_(u, cmd)
2216 2230 except UnknownCommand, inst:
2217 2231 u.warn("hg: unknown command '%s'\n" % inst.args[0])
2218 2232 help_(u, 'shortlist')
2219 2233 except SystemExit:
2220 2234 # don't catch this in the catch-all below
2221 2235 raise
2222 2236 except:
2223 2237 u.warn("** unknown exception encountered, details follow\n")
2224 2238 u.warn("** report bug details to mercurial@selenic.com\n")
2225 2239 raise
2226 2240
2227 2241 sys.exit(-1)
General Comments 0
You need to be logged in to leave comments. Login now