##// END OF EJS Templates
Borrow patch header detection heuristic from quilt
Matt Mackall -
r1390:ce95599e default
parent child Browse files
Show More
@@ -1,2222 +1,2227 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 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 839 def debugstate(ui, repo):
840 840 """show the contents of the current dirstate"""
841 841 repo.dirstate.read()
842 842 dc = repo.dirstate.map
843 843 keys = dc.keys()
844 844 keys.sort()
845 845 for file_ in keys:
846 846 ui.write("%c %3o %10d %s %s\n"
847 847 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
848 848 time.strftime("%x %X",
849 849 time.localtime(dc[file_][3])), file_))
850 850 for f in repo.dirstate.copies:
851 851 ui.write("copy: %s -> %s\n" % (repo.dirstate.copies[f], f))
852 852
853 853 def debugdata(ui, file_, rev):
854 854 """dump the contents of an data file revision"""
855 855 r = revlog.revlog(file, file_[:-2] + ".i", file_)
856 856 try:
857 857 ui.write(r.revision(r.lookup(rev)))
858 858 except KeyError:
859 859 raise util.Abort('invalid revision identifier %s', rev)
860 860
861 861 def debugindex(ui, file_):
862 862 """dump the contents of an index file"""
863 863 r = revlog.revlog(file, file_, "")
864 864 ui.write(" rev offset length base linkrev" +
865 865 " nodeid p1 p2\n")
866 866 for i in range(r.count()):
867 867 e = r.index[i]
868 868 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
869 869 i, e[0], e[1], e[2], e[3],
870 870 short(e[6]), short(e[4]), short(e[5])))
871 871
872 872 def debugindexdot(ui, file_):
873 873 """dump an index DAG as a .dot file"""
874 874 r = revlog.revlog(file, file_, "")
875 875 ui.write("digraph G {\n")
876 876 for i in range(r.count()):
877 877 e = r.index[i]
878 878 ui.write("\t%d -> %d\n" % (r.rev(e[4]), i))
879 879 if e[5] != nullid:
880 880 ui.write("\t%d -> %d\n" % (r.rev(e[5]), i))
881 881 ui.write("}\n")
882 882
883 883 def debugrename(ui, repo, file, rev=None):
884 884 """dump rename information"""
885 885 r = repo.file(relpath(repo, [file])[0])
886 886 if rev:
887 887 try:
888 888 # assume all revision numbers are for changesets
889 889 n = repo.lookup(rev)
890 890 change = repo.changelog.read(n)
891 891 m = repo.manifest.read(change[0])
892 892 n = m[relpath(repo, [file])[0]]
893 893 except hg.RepoError, KeyError:
894 894 n = r.lookup(rev)
895 895 else:
896 896 n = r.tip()
897 897 m = r.renamed(n)
898 898 if m:
899 899 ui.write("renamed from %s:%s\n" % (m[0], hex(m[1])))
900 900 else:
901 901 ui.write("not renamed\n")
902 902
903 903 def debugwalk(ui, repo, *pats, **opts):
904 904 """show how files match on given patterns"""
905 905 items = list(walk(repo, pats, opts))
906 906 if not items:
907 907 return
908 908 fmt = '%%s %%-%ds %%-%ds %%s' % (
909 909 max([len(abs) for (src, abs, rel, exact) in items]),
910 910 max([len(rel) for (src, abs, rel, exact) in items]))
911 911 for src, abs, rel, exact in items:
912 912 line = fmt % (src, abs, rel, exact and 'exact' or '')
913 913 ui.write("%s\n" % line.rstrip())
914 914
915 915 def diff(ui, repo, *pats, **opts):
916 916 """diff working directory (or selected files)"""
917 917 node1, node2 = None, None
918 918 revs = [repo.lookup(x) for x in opts['rev']]
919 919
920 920 if len(revs) > 0:
921 921 node1 = revs[0]
922 922 if len(revs) > 1:
923 923 node2 = revs[1]
924 924 if len(revs) > 2:
925 925 raise util.Abort("too many revisions to diff")
926 926
927 927 fns, matchfn, anypats = matchpats(repo, repo.getcwd(), pats, opts)
928 928
929 929 dodiff(sys.stdout, ui, repo, node1, node2, fns, match=matchfn,
930 930 text=opts['text'])
931 931
932 932 def doexport(ui, repo, changeset, seqno, total, revwidth, opts):
933 933 node = repo.lookup(changeset)
934 934 prev, other = repo.changelog.parents(node)
935 935 change = repo.changelog.read(node)
936 936
937 937 fp = make_file(repo, repo.changelog, opts['output'],
938 938 node=node, total=total, seqno=seqno,
939 939 revwidth=revwidth)
940 940 if fp != sys.stdout:
941 941 ui.note("%s\n" % fp.name)
942 942
943 943 fp.write("# HG changeset patch\n")
944 944 fp.write("# User %s\n" % change[1])
945 945 fp.write("# Node ID %s\n" % hex(node))
946 946 fp.write("# Parent %s\n" % hex(prev))
947 947 if other != nullid:
948 948 fp.write("# Parent %s\n" % hex(other))
949 949 fp.write(change[4].rstrip())
950 950 fp.write("\n\n")
951 951
952 952 dodiff(fp, ui, repo, prev, node, text=opts['text'])
953 953 if fp != sys.stdout:
954 954 fp.close()
955 955
956 956 def export(ui, repo, *changesets, **opts):
957 957 """dump the header and diffs for one or more changesets"""
958 958 if not changesets:
959 959 raise util.Abort("export requires at least one changeset")
960 960 seqno = 0
961 961 revs = list(revrange(ui, repo, changesets))
962 962 total = len(revs)
963 963 revwidth = max(map(len, revs))
964 964 ui.note(len(revs) > 1 and "Exporting patches:\n" or "Exporting patch:\n")
965 965 for cset in revs:
966 966 seqno += 1
967 967 doexport(ui, repo, cset, seqno, total, revwidth, opts)
968 968
969 969 def forget(ui, repo, *pats, **opts):
970 970 """don't add the specified files on the next commit"""
971 971 forget = []
972 972 for src, abs, rel, exact in walk(repo, pats, opts):
973 973 if repo.dirstate.state(abs) == 'a':
974 974 forget.append(abs)
975 975 if ui.verbose or not exact:
976 976 ui.status('forgetting ', rel, '\n')
977 977 repo.forget(forget)
978 978
979 979 def grep(ui, repo, pattern, *pats, **opts):
980 980 """search for a pattern in specified files and revisions"""
981 981 reflags = 0
982 982 if opts['ignore_case']:
983 983 reflags |= re.I
984 984 regexp = re.compile(pattern, reflags)
985 985 sep, eol = ':', '\n'
986 986 if opts['print0']:
987 987 sep = eol = '\0'
988 988
989 989 fcache = {}
990 990 def getfile(fn):
991 991 if fn not in fcache:
992 992 fcache[fn] = repo.file(fn)
993 993 return fcache[fn]
994 994
995 995 def matchlines(body):
996 996 begin = 0
997 997 linenum = 0
998 998 while True:
999 999 match = regexp.search(body, begin)
1000 1000 if not match:
1001 1001 break
1002 1002 mstart, mend = match.span()
1003 1003 linenum += body.count('\n', begin, mstart) + 1
1004 1004 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1005 1005 lend = body.find('\n', mend)
1006 1006 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1007 1007 begin = lend + 1
1008 1008
1009 1009 class linestate:
1010 1010 def __init__(self, line, linenum, colstart, colend):
1011 1011 self.line = line
1012 1012 self.linenum = linenum
1013 1013 self.colstart = colstart
1014 1014 self.colend = colend
1015 1015 def __eq__(self, other):
1016 1016 return self.line == other.line
1017 1017 def __hash__(self):
1018 1018 return hash(self.line)
1019 1019
1020 1020 matches = {}
1021 1021 def grepbody(fn, rev, body):
1022 1022 matches[rev].setdefault(fn, {})
1023 1023 m = matches[rev][fn]
1024 1024 for lnum, cstart, cend, line in matchlines(body):
1025 1025 s = linestate(line, lnum, cstart, cend)
1026 1026 m[s] = s
1027 1027
1028 1028 prev = {}
1029 1029 ucache = {}
1030 1030 def display(fn, rev, states, prevstates):
1031 1031 diff = list(sets.Set(states).symmetric_difference(sets.Set(prevstates)))
1032 1032 diff.sort(lambda x, y: cmp(x.linenum, y.linenum))
1033 1033 counts = {'-': 0, '+': 0}
1034 1034 filerevmatches = {}
1035 1035 for l in diff:
1036 1036 if incrementing or not opts['all']:
1037 1037 change = ((l in prevstates) and '-') or '+'
1038 1038 r = rev
1039 1039 else:
1040 1040 change = ((l in states) and '-') or '+'
1041 1041 r = prev[fn]
1042 1042 cols = [fn, str(rev)]
1043 1043 if opts['line_number']: cols.append(str(l.linenum))
1044 1044 if opts['all']: cols.append(change)
1045 1045 if opts['user']: cols.append(trimuser(ui, getchange(rev)[1], rev,
1046 1046 ucache))
1047 1047 if opts['files_with_matches']:
1048 1048 c = (fn, rev)
1049 1049 if c in filerevmatches: continue
1050 1050 filerevmatches[c] = 1
1051 1051 else:
1052 1052 cols.append(l.line)
1053 1053 ui.write(sep.join(cols), eol)
1054 1054 counts[change] += 1
1055 1055 return counts['+'], counts['-']
1056 1056
1057 1057 fstate = {}
1058 1058 skip = {}
1059 1059 changeiter, getchange = walkchangerevs(ui, repo, repo.getcwd(), pats, opts)
1060 1060 count = 0
1061 1061 incrementing = False
1062 1062 for st, rev, fns in changeiter:
1063 1063 if st == 'window':
1064 1064 incrementing = rev
1065 1065 matches.clear()
1066 1066 elif st == 'add':
1067 1067 change = repo.changelog.read(repo.lookup(str(rev)))
1068 1068 mf = repo.manifest.read(change[0])
1069 1069 matches[rev] = {}
1070 1070 for fn in fns:
1071 1071 if fn in skip: continue
1072 1072 fstate.setdefault(fn, {})
1073 1073 try:
1074 1074 grepbody(fn, rev, getfile(fn).read(mf[fn]))
1075 1075 except KeyError:
1076 1076 pass
1077 1077 elif st == 'iter':
1078 1078 states = matches[rev].items()
1079 1079 states.sort()
1080 1080 for fn, m in states:
1081 1081 if fn in skip: continue
1082 1082 if incrementing or not opts['all'] or fstate[fn]:
1083 1083 pos, neg = display(fn, rev, m, fstate[fn])
1084 1084 count += pos + neg
1085 1085 if pos and not opts['all']:
1086 1086 skip[fn] = True
1087 1087 fstate[fn] = m
1088 1088 prev[fn] = rev
1089 1089
1090 1090 if not incrementing:
1091 1091 fstate = fstate.items()
1092 1092 fstate.sort()
1093 1093 for fn, state in fstate:
1094 1094 if fn in skip: continue
1095 1095 display(fn, rev, {}, state)
1096 1096 return (count == 0 and 1) or 0
1097 1097
1098 1098 def heads(ui, repo, **opts):
1099 1099 """show current repository heads"""
1100 1100 heads = repo.changelog.heads()
1101 1101 br = None
1102 1102 if opts['branches']:
1103 1103 br = repo.branchlookup(heads)
1104 1104 for n in repo.changelog.heads():
1105 1105 show_changeset(ui, repo, changenode=n, brinfo=br)
1106 1106
1107 1107 def identify(ui, repo):
1108 1108 """print information about the working copy"""
1109 1109 parents = [p for p in repo.dirstate.parents() if p != nullid]
1110 1110 if not parents:
1111 1111 ui.write("unknown\n")
1112 1112 return
1113 1113
1114 1114 hexfunc = ui.verbose and hex or short
1115 1115 (c, a, d, u) = repo.changes()
1116 1116 output = ["%s%s" % ('+'.join([hexfunc(parent) for parent in parents]),
1117 1117 (c or a or d) and "+" or "")]
1118 1118
1119 1119 if not ui.quiet:
1120 1120 # multiple tags for a single parent separated by '/'
1121 1121 parenttags = ['/'.join(tags)
1122 1122 for tags in map(repo.nodetags, parents) if tags]
1123 1123 # tags for multiple parents separated by ' + '
1124 1124 if parenttags:
1125 1125 output.append(' + '.join(parenttags))
1126 1126
1127 1127 ui.write("%s\n" % ' '.join(output))
1128 1128
1129 1129 def import_(ui, repo, patch1, *patches, **opts):
1130 1130 """import an ordered set of patches"""
1131 1131 patches = (patch1,) + patches
1132 1132
1133 1133 if not opts['force']:
1134 1134 (c, a, d, u) = repo.changes()
1135 1135 if c or a or d:
1136 1136 raise util.Abort("outstanding uncommitted changes")
1137 1137
1138 1138 d = opts["base"]
1139 1139 strip = opts["strip"]
1140 1140
1141 1141 mailre = re.compile(r'(?:From |[\w-]+:)')
1142 diffre = re.compile(r'(?:diff -|--- .*\s+\w+ \w+ +\d+ \d+:\d+:\d+ \d+)')
1142
1143 # attempt to detect the start of a patch
1144 # (this heuristic is borrowed from quilt)
1145 diffre = re.compile(r'(?:Index:[ \t]|diff[ \t]|RCS file: |' +
1146 'retrieving revision [0-9]+(\.[0-9]+)*$|' +
1147 '(---|\*\*\*)[ \t])')
1143 1148
1144 1149 for patch in patches:
1145 1150 ui.status("applying %s\n" % patch)
1146 1151 pf = os.path.join(d, patch)
1147 1152
1148 1153 message = []
1149 1154 user = None
1150 1155 hgpatch = False
1151 1156 for line in file(pf):
1152 1157 line = line.rstrip()
1153 1158 if (not message and not hgpatch and
1154 1159 mailre.match(line) and not opts['force']):
1155 1160 if len(line) > 35: line = line[:32] + '...'
1156 1161 raise util.Abort('first line looks like a '
1157 1162 'mail header: ' + line)
1158 1163 if diffre.match(line):
1159 1164 break
1160 1165 elif hgpatch:
1161 1166 # parse values when importing the result of an hg export
1162 1167 if line.startswith("# User "):
1163 1168 user = line[7:]
1164 1169 ui.debug('User: %s\n' % user)
1165 1170 elif not line.startswith("# ") and line:
1166 1171 message.append(line)
1167 1172 hgpatch = False
1168 1173 elif line == '# HG changeset patch':
1169 1174 hgpatch = True
1170 1175 message = [] # We may have collected garbage
1171 1176 else:
1172 1177 message.append(line)
1173 1178
1174 1179 # make sure message isn't empty
1175 1180 if not message:
1176 1181 message = "imported patch %s\n" % patch
1177 1182 else:
1178 1183 message = "%s\n" % '\n'.join(message)
1179 1184 ui.debug('message:\n%s\n' % message)
1180 1185
1181 1186 files = util.patch(strip, pf, ui)
1182 1187
1183 1188 if len(files) > 0:
1184 1189 addremove(ui, repo, *files)
1185 1190 repo.commit(files, message, user)
1186 1191
1187 1192 def incoming(ui, repo, source="default", **opts):
1188 1193 """show new changesets found in source"""
1189 1194 source = ui.expandpath(source)
1190 1195 other = hg.repository(ui, source)
1191 1196 if not other.local():
1192 1197 raise util.Abort("incoming doesn't work for remote repositories yet")
1193 1198 o = repo.findincoming(other)
1194 1199 if not o:
1195 1200 return
1196 1201 o = other.newer(o)
1197 1202 for n in o:
1198 1203 show_changeset(ui, other, changenode=n)
1199 1204 if opts['patch']:
1200 1205 prev = other.changelog.parents(n)[0]
1201 1206 dodiff(ui, ui, other, prev, n)
1202 1207 ui.write("\n")
1203 1208
1204 1209 def init(ui, dest="."):
1205 1210 """create a new repository in the given directory"""
1206 1211 if not os.path.exists(dest):
1207 1212 os.mkdir(dest)
1208 1213 hg.repository(ui, dest, create=1)
1209 1214
1210 1215 def locate(ui, repo, *pats, **opts):
1211 1216 """locate files matching specific patterns"""
1212 1217 end = opts['print0'] and '\0' or '\n'
1213 1218
1214 1219 for src, abs, rel, exact in walk(repo, pats, opts, '(?:.*/|)'):
1215 1220 if repo.dirstate.state(abs) == '?':
1216 1221 continue
1217 1222 if opts['fullpath']:
1218 1223 ui.write(os.path.join(repo.root, abs), end)
1219 1224 else:
1220 1225 ui.write(rel, end)
1221 1226
1222 1227 def log(ui, repo, *pats, **opts):
1223 1228 """show revision history of entire repository or files"""
1224 1229 class dui:
1225 1230 # Implement and delegate some ui protocol. Save hunks of
1226 1231 # output for later display in the desired order.
1227 1232 def __init__(self, ui):
1228 1233 self.ui = ui
1229 1234 self.hunk = {}
1230 1235 def bump(self, rev):
1231 1236 self.rev = rev
1232 1237 self.hunk[rev] = []
1233 1238 def note(self, *args):
1234 1239 if self.verbose:
1235 1240 self.write(*args)
1236 1241 def status(self, *args):
1237 1242 if not self.quiet:
1238 1243 self.write(*args)
1239 1244 def write(self, *args):
1240 1245 self.hunk[self.rev].append(args)
1241 1246 def debug(self, *args):
1242 1247 if self.debugflag:
1243 1248 self.write(*args)
1244 1249 def __getattr__(self, key):
1245 1250 return getattr(self.ui, key)
1246 1251 cwd = repo.getcwd()
1247 1252 if not pats and cwd:
1248 1253 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
1249 1254 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
1250 1255 changeiter, getchange = walkchangerevs(ui, repo, (pats and cwd) or '',
1251 1256 pats, opts)
1252 1257 for st, rev, fns in changeiter:
1253 1258 if st == 'window':
1254 1259 du = dui(ui)
1255 1260 elif st == 'add':
1256 1261 du.bump(rev)
1257 1262 br = None
1258 1263 if opts['branch']:
1259 1264 br = repo.branchlookup([repo.changelog.node(rev)])
1260 1265
1261 1266 if opts['keyword']:
1262 1267 changes = repo.changelog.read(repo.changelog.node(rev))
1263 1268 miss = 0
1264 1269 for k in opts['keyword']:
1265 1270 if not (k in changes[1].lower() or
1266 1271 k in changes[4].lower() or
1267 1272 k in " ".join(changes[3][:20]).lower()):
1268 1273 miss = 1
1269 1274 break
1270 1275 if miss:
1271 1276 continue
1272 1277
1273 1278 show_changeset(du, repo, rev, brinfo=br)
1274 1279 if opts['patch']:
1275 1280 changenode = repo.changelog.node(rev)
1276 1281 prev, other = repo.changelog.parents(changenode)
1277 1282 dodiff(du, du, repo, prev, changenode, fns)
1278 1283 du.write("\n\n")
1279 1284 elif st == 'iter':
1280 1285 for args in du.hunk[rev]:
1281 1286 ui.write(*args)
1282 1287
1283 1288 def manifest(ui, repo, rev=None):
1284 1289 """output the latest or given revision of the project manifest"""
1285 1290 if rev:
1286 1291 try:
1287 1292 # assume all revision numbers are for changesets
1288 1293 n = repo.lookup(rev)
1289 1294 change = repo.changelog.read(n)
1290 1295 n = change[0]
1291 1296 except hg.RepoError:
1292 1297 n = repo.manifest.lookup(rev)
1293 1298 else:
1294 1299 n = repo.manifest.tip()
1295 1300 m = repo.manifest.read(n)
1296 1301 mf = repo.manifest.readflags(n)
1297 1302 files = m.keys()
1298 1303 files.sort()
1299 1304
1300 1305 for f in files:
1301 1306 ui.write("%40s %3s %s\n" % (hex(m[f]), mf[f] and "755" or "644", f))
1302 1307
1303 1308 def outgoing(ui, repo, dest="default-push", **opts):
1304 1309 """show changesets not found in destination"""
1305 1310 dest = ui.expandpath(dest)
1306 1311 other = hg.repository(ui, dest)
1307 1312 o = repo.findoutgoing(other)
1308 1313 o = repo.newer(o)
1309 1314 for n in o:
1310 1315 show_changeset(ui, repo, changenode=n)
1311 1316 if opts['patch']:
1312 1317 prev = repo.changelog.parents(n)[0]
1313 1318 dodiff(ui, ui, repo, prev, n)
1314 1319 ui.write("\n")
1315 1320
1316 1321 def parents(ui, repo, rev=None):
1317 1322 """show the parents of the working dir or revision"""
1318 1323 if rev:
1319 1324 p = repo.changelog.parents(repo.lookup(rev))
1320 1325 else:
1321 1326 p = repo.dirstate.parents()
1322 1327
1323 1328 for n in p:
1324 1329 if n != nullid:
1325 1330 show_changeset(ui, repo, changenode=n)
1326 1331
1327 1332 def paths(ui, search=None):
1328 1333 """show definition of symbolic path names"""
1329 1334 try:
1330 1335 repo = hg.repository(ui=ui)
1331 1336 except hg.RepoError:
1332 1337 pass
1333 1338
1334 1339 if search:
1335 1340 for name, path in ui.configitems("paths"):
1336 1341 if name == search:
1337 1342 ui.write("%s\n" % path)
1338 1343 return
1339 1344 ui.warn("not found!\n")
1340 1345 return 1
1341 1346 else:
1342 1347 for name, path in ui.configitems("paths"):
1343 1348 ui.write("%s = %s\n" % (name, path))
1344 1349
1345 1350 def pull(ui, repo, source="default", **opts):
1346 1351 """pull changes from the specified source"""
1347 1352 source = ui.expandpath(source)
1348 1353 ui.status('pulling from %s\n' % (source))
1349 1354
1350 1355 if opts['ssh']:
1351 1356 ui.setconfig("ui", "ssh", opts['ssh'])
1352 1357 if opts['remotecmd']:
1353 1358 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
1354 1359
1355 1360 other = hg.repository(ui, source)
1356 1361 r = repo.pull(other)
1357 1362 if not r:
1358 1363 if opts['update']:
1359 1364 return update(ui, repo)
1360 1365 else:
1361 1366 ui.status("(run 'hg update' to get a working copy)\n")
1362 1367
1363 1368 return r
1364 1369
1365 1370 def push(ui, repo, dest="default-push", force=False, ssh=None, remotecmd=None):
1366 1371 """push changes to the specified destination"""
1367 1372 dest = ui.expandpath(dest)
1368 1373 ui.status('pushing to %s\n' % (dest))
1369 1374
1370 1375 if ssh:
1371 1376 ui.setconfig("ui", "ssh", ssh)
1372 1377 if remotecmd:
1373 1378 ui.setconfig("ui", "remotecmd", remotecmd)
1374 1379
1375 1380 other = hg.repository(ui, dest)
1376 1381 r = repo.push(other, force)
1377 1382 return r
1378 1383
1379 1384 def rawcommit(ui, repo, *flist, **rc):
1380 1385 "raw commit interface"
1381 1386 if rc['text']:
1382 1387 ui.warn("Warning: -t and --text is deprecated,"
1383 1388 " please use -m or --message instead.\n")
1384 1389 message = rc['message'] or rc['text']
1385 1390 if not message and rc['logfile']:
1386 1391 try:
1387 1392 message = open(rc['logfile']).read()
1388 1393 except IOError:
1389 1394 pass
1390 1395 if not message and not rc['logfile']:
1391 1396 raise util.Abort("missing commit message")
1392 1397
1393 1398 files = relpath(repo, list(flist))
1394 1399 if rc['files']:
1395 1400 files += open(rc['files']).read().splitlines()
1396 1401
1397 1402 rc['parent'] = map(repo.lookup, rc['parent'])
1398 1403
1399 1404 try:
1400 1405 repo.rawcommit(files, message, rc['user'], rc['date'], *rc['parent'])
1401 1406 except ValueError, inst:
1402 1407 raise util.Abort(str(inst))
1403 1408
1404 1409 def recover(ui, repo):
1405 1410 """roll back an interrupted transaction"""
1406 1411 repo.recover()
1407 1412
1408 1413 def remove(ui, repo, pat, *pats, **opts):
1409 1414 """remove the specified files on the next commit"""
1410 1415 names = []
1411 1416 def okaytoremove(abs, rel, exact):
1412 1417 c, a, d, u = repo.changes(files = [abs])
1413 1418 reason = None
1414 1419 if c: reason = 'is modified'
1415 1420 elif a: reason = 'has been marked for add'
1416 1421 elif u: reason = 'is not managed'
1417 1422 if reason:
1418 1423 if exact: ui.warn('not removing %s: file %s\n' % (rel, reason))
1419 1424 else:
1420 1425 return True
1421 1426 for src, abs, rel, exact in walk(repo, (pat,) + pats, opts):
1422 1427 if okaytoremove(abs, rel, exact):
1423 1428 if ui.verbose or not exact: ui.status('removing %s\n' % rel)
1424 1429 names.append(abs)
1425 1430 for name in names:
1426 1431 try:
1427 1432 os.unlink(name)
1428 1433 except OSError, inst:
1429 1434 if inst.errno != errno.ENOENT: raise
1430 1435 repo.remove(names)
1431 1436
1432 1437 def rename(ui, repo, *pats, **opts):
1433 1438 """rename files; equivalent of copy + remove"""
1434 1439 errs, copied = docopy(ui, repo, pats, opts)
1435 1440 names = []
1436 1441 for abs, rel, exact in copied:
1437 1442 if ui.verbose or not exact: ui.status('removing %s\n' % rel)
1438 1443 try:
1439 1444 os.unlink(rel)
1440 1445 except OSError, inst:
1441 1446 if inst.errno != errno.ENOENT: raise
1442 1447 names.append(abs)
1443 1448 repo.remove(names)
1444 1449 return errs
1445 1450
1446 1451 def revert(ui, repo, *names, **opts):
1447 1452 """revert modified files or dirs back to their unmodified states"""
1448 1453 node = opts['rev'] and repo.lookup(opts['rev']) or \
1449 1454 repo.dirstate.parents()[0]
1450 1455 root = os.path.realpath(repo.root)
1451 1456
1452 1457 def trimpath(p):
1453 1458 p = os.path.realpath(p)
1454 1459 if p.startswith(root):
1455 1460 rest = p[len(root):]
1456 1461 if not rest:
1457 1462 return rest
1458 1463 if p.startswith(os.sep):
1459 1464 return rest[1:]
1460 1465 return p
1461 1466
1462 1467 relnames = map(trimpath, names or [os.getcwd()])
1463 1468 chosen = {}
1464 1469
1465 1470 def choose(name):
1466 1471 def body(name):
1467 1472 for r in relnames:
1468 1473 if not name.startswith(r):
1469 1474 continue
1470 1475 rest = name[len(r):]
1471 1476 if not rest:
1472 1477 return r, True
1473 1478 depth = rest.count(os.sep)
1474 1479 if not r:
1475 1480 if depth == 0 or not opts['nonrecursive']:
1476 1481 return r, True
1477 1482 elif rest[0] == os.sep:
1478 1483 if depth == 1 or not opts['nonrecursive']:
1479 1484 return r, True
1480 1485 return None, False
1481 1486 relname, ret = body(name)
1482 1487 if ret:
1483 1488 chosen[relname] = 1
1484 1489 return ret
1485 1490
1486 1491 r = repo.update(node, False, True, choose, False)
1487 1492 for n in relnames:
1488 1493 if n not in chosen:
1489 1494 ui.warn('error: no matches for %s\n' % n)
1490 1495 r = 1
1491 1496 sys.stdout.flush()
1492 1497 return r
1493 1498
1494 1499 def root(ui, repo):
1495 1500 """print the root (top) of the current working dir"""
1496 1501 ui.write(repo.root + "\n")
1497 1502
1498 1503 def serve(ui, repo, **opts):
1499 1504 """export the repository via HTTP"""
1500 1505
1501 1506 if opts["stdio"]:
1502 1507 fin, fout = sys.stdin, sys.stdout
1503 1508 sys.stdout = sys.stderr
1504 1509
1505 1510 def getarg():
1506 1511 argline = fin.readline()[:-1]
1507 1512 arg, l = argline.split()
1508 1513 val = fin.read(int(l))
1509 1514 return arg, val
1510 1515 def respond(v):
1511 1516 fout.write("%d\n" % len(v))
1512 1517 fout.write(v)
1513 1518 fout.flush()
1514 1519
1515 1520 lock = None
1516 1521
1517 1522 while 1:
1518 1523 cmd = fin.readline()[:-1]
1519 1524 if cmd == '':
1520 1525 return
1521 1526 if cmd == "heads":
1522 1527 h = repo.heads()
1523 1528 respond(" ".join(map(hex, h)) + "\n")
1524 1529 if cmd == "lock":
1525 1530 lock = repo.lock()
1526 1531 respond("")
1527 1532 if cmd == "unlock":
1528 1533 if lock:
1529 1534 lock.release()
1530 1535 lock = None
1531 1536 respond("")
1532 1537 elif cmd == "branches":
1533 1538 arg, nodes = getarg()
1534 1539 nodes = map(bin, nodes.split(" "))
1535 1540 r = []
1536 1541 for b in repo.branches(nodes):
1537 1542 r.append(" ".join(map(hex, b)) + "\n")
1538 1543 respond("".join(r))
1539 1544 elif cmd == "between":
1540 1545 arg, pairs = getarg()
1541 1546 pairs = [map(bin, p.split("-")) for p in pairs.split(" ")]
1542 1547 r = []
1543 1548 for b in repo.between(pairs):
1544 1549 r.append(" ".join(map(hex, b)) + "\n")
1545 1550 respond("".join(r))
1546 1551 elif cmd == "changegroup":
1547 1552 nodes = []
1548 1553 arg, roots = getarg()
1549 1554 nodes = map(bin, roots.split(" "))
1550 1555
1551 1556 cg = repo.changegroup(nodes)
1552 1557 while 1:
1553 1558 d = cg.read(4096)
1554 1559 if not d:
1555 1560 break
1556 1561 fout.write(d)
1557 1562
1558 1563 fout.flush()
1559 1564
1560 1565 elif cmd == "addchangegroup":
1561 1566 if not lock:
1562 1567 respond("not locked")
1563 1568 continue
1564 1569 respond("")
1565 1570
1566 1571 r = repo.addchangegroup(fin)
1567 1572 respond("")
1568 1573
1569 1574 optlist = "name templates style address port ipv6 accesslog errorlog"
1570 1575 for o in optlist.split():
1571 1576 if opts[o]:
1572 1577 ui.setconfig("web", o, opts[o])
1573 1578
1574 1579 try:
1575 1580 httpd = hgweb.create_server(repo)
1576 1581 except socket.error, inst:
1577 1582 raise util.Abort('cannot start server: ' + inst.args[1])
1578 1583
1579 1584 if ui.verbose:
1580 1585 addr, port = httpd.socket.getsockname()
1581 1586 if addr == '0.0.0.0':
1582 1587 addr = socket.gethostname()
1583 1588 else:
1584 1589 try:
1585 1590 addr = socket.gethostbyaddr(addr)[0]
1586 1591 except socket.error:
1587 1592 pass
1588 1593 if port != 80:
1589 1594 ui.status('listening at http://%s:%d/\n' % (addr, port))
1590 1595 else:
1591 1596 ui.status('listening at http://%s/\n' % addr)
1592 1597 httpd.serve_forever()
1593 1598
1594 1599 def status(ui, repo, *pats, **opts):
1595 1600 '''show changed files in the working directory
1596 1601
1597 1602 M = modified
1598 1603 A = added
1599 1604 R = removed
1600 1605 ? = not tracked
1601 1606 '''
1602 1607
1603 1608 cwd = repo.getcwd()
1604 1609 files, matchfn, anypats = matchpats(repo, cwd, pats, opts)
1605 1610 (c, a, d, u) = [[util.pathto(cwd, x) for x in n]
1606 1611 for n in repo.changes(files=files, match=matchfn)]
1607 1612
1608 1613 changetypes = [('modified', 'M', c),
1609 1614 ('added', 'A', a),
1610 1615 ('removed', 'R', d),
1611 1616 ('unknown', '?', u)]
1612 1617
1613 1618 end = opts['print0'] and '\0' or '\n'
1614 1619
1615 1620 for opt, char, changes in ([ct for ct in changetypes if opts[ct[0]]]
1616 1621 or changetypes):
1617 1622 if opts['no_status']:
1618 1623 format = "%%s%s" % end
1619 1624 else:
1620 1625 format = "%s %%s%s" % (char, end);
1621 1626
1622 1627 for f in changes:
1623 1628 ui.write(format % f)
1624 1629
1625 1630 def tag(ui, repo, name, rev=None, **opts):
1626 1631 """add a tag for the current tip or a given revision"""
1627 1632 if opts['text']:
1628 1633 ui.warn("Warning: -t and --text is deprecated,"
1629 1634 " please use -m or --message instead.\n")
1630 1635 if name == "tip":
1631 1636 raise util.Abort("the name 'tip' is reserved")
1632 1637 if rev:
1633 1638 r = hex(repo.lookup(rev))
1634 1639 else:
1635 1640 r = hex(repo.changelog.tip())
1636 1641
1637 1642 if name.find(revrangesep) >= 0:
1638 1643 raise util.Abort("'%s' cannot be used in a tag name" % revrangesep)
1639 1644
1640 1645 if opts['local']:
1641 1646 repo.opener("localtags", "a").write("%s %s\n" % (r, name))
1642 1647 return
1643 1648
1644 1649 (c, a, d, u) = repo.changes()
1645 1650 for x in (c, a, d, u):
1646 1651 if ".hgtags" in x:
1647 1652 raise util.Abort("working copy of .hgtags is changed "
1648 1653 "(please commit .hgtags manually)")
1649 1654
1650 1655 repo.wfile(".hgtags", "ab").write("%s %s\n" % (r, name))
1651 1656 if repo.dirstate.state(".hgtags") == '?':
1652 1657 repo.add([".hgtags"])
1653 1658
1654 1659 message = (opts['message'] or opts['text'] or
1655 1660 "Added tag %s for changeset %s" % (name, r))
1656 1661 try:
1657 1662 repo.commit([".hgtags"], message, opts['user'], opts['date'])
1658 1663 except ValueError, inst:
1659 1664 raise util.Abort(str(inst))
1660 1665
1661 1666 def tags(ui, repo):
1662 1667 """list repository tags"""
1663 1668
1664 1669 l = repo.tagslist()
1665 1670 l.reverse()
1666 1671 for t, n in l:
1667 1672 try:
1668 1673 r = "%5d:%s" % (repo.changelog.rev(n), hex(n))
1669 1674 except KeyError:
1670 1675 r = " ?:?"
1671 1676 ui.write("%-30s %s\n" % (t, r))
1672 1677
1673 1678 def tip(ui, repo):
1674 1679 """show the tip revision"""
1675 1680 n = repo.changelog.tip()
1676 1681 show_changeset(ui, repo, changenode=n)
1677 1682
1678 1683 def unbundle(ui, repo, fname):
1679 1684 """apply a changegroup file"""
1680 1685 f = urllib.urlopen(fname)
1681 1686
1682 1687 if f.read(4) != "HG10":
1683 1688 raise util.Abort("%s: not a Mercurial bundle file" % fname)
1684 1689
1685 1690 def bzgenerator(f):
1686 1691 zd = bz2.BZ2Decompressor()
1687 1692 for chunk in f:
1688 1693 yield zd.decompress(chunk)
1689 1694 yield zd.flush()
1690 1695
1691 1696 bzgen = bzgenerator(util.filechunkiter(f, 4096))
1692 1697 repo.addchangegroup(util.chunkbuffer(bzgen))
1693 1698
1694 1699 def undo(ui, repo):
1695 1700 """undo the last commit or pull
1696 1701
1697 1702 Roll back the last pull or commit transaction on the
1698 1703 repository, restoring the project to its earlier state.
1699 1704
1700 1705 This command should be used with care. There is only one level of
1701 1706 undo and there is no redo.
1702 1707
1703 1708 This command is not intended for use on public repositories. Once
1704 1709 a change is visible for pull by other users, undoing it locally is
1705 1710 ineffective.
1706 1711 """
1707 1712 repo.undo()
1708 1713
1709 1714 def update(ui, repo, node=None, merge=False, clean=False, branch=None):
1710 1715 '''update or merge working directory
1711 1716
1712 1717 If there are no outstanding changes in the working directory and
1713 1718 there is a linear relationship between the current version and the
1714 1719 requested version, the result is the requested version.
1715 1720
1716 1721 Otherwise the result is a merge between the contents of the
1717 1722 current working directory and the requested version. Files that
1718 1723 changed between either parent are marked as changed for the next
1719 1724 commit and a commit must be performed before any further updates
1720 1725 are allowed.
1721 1726 '''
1722 1727 if branch:
1723 1728 br = repo.branchlookup(branch=branch)
1724 1729 found = []
1725 1730 for x in br:
1726 1731 if branch in br[x]:
1727 1732 found.append(x)
1728 1733 if len(found) > 1:
1729 1734 ui.warn("Found multiple heads for %s\n" % branch)
1730 1735 for x in found:
1731 1736 show_changeset(ui, repo, changenode=x, brinfo=br)
1732 1737 return 1
1733 1738 if len(found) == 1:
1734 1739 node = found[0]
1735 1740 ui.warn("Using head %s for branch %s\n" % (short(node), branch))
1736 1741 else:
1737 1742 ui.warn("branch %s not found\n" % (branch))
1738 1743 return 1
1739 1744 else:
1740 1745 node = node and repo.lookup(node) or repo.changelog.tip()
1741 1746 return repo.update(node, allow=merge, force=clean)
1742 1747
1743 1748 def verify(ui, repo):
1744 1749 """verify the integrity of the repository"""
1745 1750 return repo.verify()
1746 1751
1747 1752 # Command options and aliases are listed here, alphabetically
1748 1753
1749 1754 table = {
1750 1755 "^add":
1751 1756 (add,
1752 1757 [('I', 'include', [], 'include path in search'),
1753 1758 ('X', 'exclude', [], 'exclude path from search')],
1754 1759 "hg add [OPTION]... [FILE]..."),
1755 1760 "addremove":
1756 1761 (addremove,
1757 1762 [('I', 'include', [], 'include path in search'),
1758 1763 ('X', 'exclude', [], 'exclude path from search')],
1759 1764 "hg addremove [OPTION]... [FILE]..."),
1760 1765 "^annotate":
1761 1766 (annotate,
1762 1767 [('r', 'rev', '', 'revision'),
1763 1768 ('a', 'text', None, 'treat all files as text'),
1764 1769 ('u', 'user', None, 'show user'),
1765 1770 ('n', 'number', None, 'show revision number'),
1766 1771 ('c', 'changeset', None, 'show changeset'),
1767 1772 ('I', 'include', [], 'include path in search'),
1768 1773 ('X', 'exclude', [], 'exclude path from search')],
1769 1774 'hg annotate [OPTION]... FILE...'),
1770 1775 "bundle":
1771 1776 (bundle,
1772 1777 [],
1773 1778 'hg bundle FILE DEST'),
1774 1779 "cat":
1775 1780 (cat,
1776 1781 [('I', 'include', [], 'include path in search'),
1777 1782 ('X', 'exclude', [], 'exclude path from search'),
1778 1783 ('o', 'output', "", 'output to file'),
1779 1784 ('r', 'rev', '', 'revision')],
1780 1785 'hg cat [OPTION]... FILE...'),
1781 1786 "^clone":
1782 1787 (clone,
1783 1788 [('U', 'noupdate', None, 'skip update after cloning'),
1784 1789 ('e', 'ssh', "", 'ssh command'),
1785 1790 ('', 'pull', None, 'use pull protocol to copy metadata'),
1786 1791 ('', 'remotecmd', "", 'remote hg command')],
1787 1792 'hg clone [OPTION]... SOURCE [DEST]'),
1788 1793 "^commit|ci":
1789 1794 (commit,
1790 1795 [('A', 'addremove', None, 'run add/remove during commit'),
1791 1796 ('I', 'include', [], 'include path in search'),
1792 1797 ('X', 'exclude', [], 'exclude path from search'),
1793 1798 ('m', 'message', "", 'commit message'),
1794 1799 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1795 1800 ('l', 'logfile', "", 'commit message file'),
1796 1801 ('d', 'date', "", 'date code'),
1797 1802 ('u', 'user', "", 'user')],
1798 1803 'hg commit [OPTION]... [FILE]...'),
1799 1804 "copy|cp": (copy,
1800 1805 [('I', 'include', [], 'include path in search'),
1801 1806 ('X', 'exclude', [], 'exclude path from search'),
1802 1807 ('A', 'after', None, 'record a copy after it has happened'),
1803 1808 ('f', 'force', None, 'replace destination if it exists'),
1804 1809 ('p', 'parents', None, 'append source path to dest')],
1805 1810 'hg copy [OPTION]... [SOURCE]... DEST'),
1806 1811 "debugancestor": (debugancestor, [], 'debugancestor INDEX REV1 REV2'),
1807 1812 "debugcheckstate": (debugcheckstate, [], 'debugcheckstate'),
1808 1813 "debugconfig": (debugconfig, [], 'debugconfig'),
1809 1814 "debugstate": (debugstate, [], 'debugstate'),
1810 1815 "debugdata": (debugdata, [], 'debugdata FILE REV'),
1811 1816 "debugindex": (debugindex, [], 'debugindex FILE'),
1812 1817 "debugindexdot": (debugindexdot, [], 'debugindexdot FILE'),
1813 1818 "debugrename": (debugrename, [], 'debugrename FILE [REV]'),
1814 1819 "debugwalk":
1815 1820 (debugwalk,
1816 1821 [('I', 'include', [], 'include path in search'),
1817 1822 ('X', 'exclude', [], 'exclude path from search')],
1818 1823 'debugwalk [OPTION]... [FILE]...'),
1819 1824 "^diff":
1820 1825 (diff,
1821 1826 [('r', 'rev', [], 'revision'),
1822 1827 ('a', 'text', None, 'treat all files as text'),
1823 1828 ('I', 'include', [], 'include path in search'),
1824 1829 ('X', 'exclude', [], 'exclude path from search')],
1825 1830 'hg diff [-a] [-I] [-X] [-r REV1 [-r REV2]] [FILE]...'),
1826 1831 "^export":
1827 1832 (export,
1828 1833 [('o', 'output', "", 'output to file'),
1829 1834 ('a', 'text', None, 'treat all files as text')],
1830 1835 "hg export [-a] [-o OUTFILE] REV..."),
1831 1836 "forget":
1832 1837 (forget,
1833 1838 [('I', 'include', [], 'include path in search'),
1834 1839 ('X', 'exclude', [], 'exclude path from search')],
1835 1840 "hg forget [OPTION]... FILE..."),
1836 1841 "grep":
1837 1842 (grep,
1838 1843 [('0', 'print0', None, 'end fields with NUL'),
1839 1844 ('I', 'include', [], 'include path in search'),
1840 1845 ('X', 'exclude', [], 'include path in search'),
1841 1846 ('', 'all', None, 'print all revisions with matches'),
1842 1847 ('i', 'ignore-case', None, 'ignore case when matching'),
1843 1848 ('l', 'files-with-matches', None, 'print names of files and revs with matches'),
1844 1849 ('n', 'line-number', None, 'print line numbers'),
1845 1850 ('r', 'rev', [], 'search in revision rev'),
1846 1851 ('u', 'user', None, 'print user who made change')],
1847 1852 "hg grep [OPTION]... PATTERN [FILE]..."),
1848 1853 "heads":
1849 1854 (heads,
1850 1855 [('b', 'branches', None, 'find branch info')],
1851 1856 'hg heads [-b]'),
1852 1857 "help": (help_, [], 'hg help [COMMAND]'),
1853 1858 "identify|id": (identify, [], 'hg identify'),
1854 1859 "import|patch":
1855 1860 (import_,
1856 1861 [('p', 'strip', 1, 'path strip'),
1857 1862 ('f', 'force', None, 'skip check for outstanding changes'),
1858 1863 ('b', 'base', "", 'base path')],
1859 1864 "hg import [-f] [-p NUM] [-b BASE] PATCH..."),
1860 1865 "incoming|in": (incoming,
1861 1866 [('p', 'patch', None, 'show patch')],
1862 1867 'hg incoming [-p] [SOURCE]'),
1863 1868 "^init": (init, [], 'hg init [DEST]'),
1864 1869 "locate":
1865 1870 (locate,
1866 1871 [('r', 'rev', '', 'revision'),
1867 1872 ('0', 'print0', None, 'end filenames with NUL'),
1868 1873 ('f', 'fullpath', None, 'print complete paths'),
1869 1874 ('I', 'include', [], 'include path in search'),
1870 1875 ('X', 'exclude', [], 'exclude path from search')],
1871 1876 'hg locate [OPTION]... [PATTERN]...'),
1872 1877 "^log|history":
1873 1878 (log,
1874 1879 [('I', 'include', [], 'include path in search'),
1875 1880 ('X', 'exclude', [], 'exclude path from search'),
1876 1881 ('b', 'branch', None, 'show branches'),
1877 1882 ('k', 'keyword', [], 'search for a keyword'),
1878 1883 ('r', 'rev', [], 'revision'),
1879 1884 ('p', 'patch', None, 'show patch')],
1880 1885 'hg log [-I] [-X] [-r REV]... [-p] [FILE]'),
1881 1886 "manifest": (manifest, [], 'hg manifest [REV]'),
1882 1887 "outgoing|out": (outgoing,
1883 1888 [('p', 'patch', None, 'show patch')],
1884 1889 'hg outgoing [-p] [DEST]'),
1885 1890 "parents": (parents, [], 'hg parents [REV]'),
1886 1891 "paths": (paths, [], 'hg paths [NAME]'),
1887 1892 "^pull":
1888 1893 (pull,
1889 1894 [('u', 'update', None, 'update working directory'),
1890 1895 ('e', 'ssh', "", 'ssh command'),
1891 1896 ('', 'remotecmd', "", 'remote hg command')],
1892 1897 'hg pull [-u] [-e FILE] [--remotecmd FILE] [SOURCE]'),
1893 1898 "^push":
1894 1899 (push,
1895 1900 [('f', 'force', None, 'force push'),
1896 1901 ('e', 'ssh', "", 'ssh command'),
1897 1902 ('', 'remotecmd', "", 'remote hg command')],
1898 1903 'hg push [-f] [-e FILE] [--remotecmd FILE] [DEST]'),
1899 1904 "rawcommit":
1900 1905 (rawcommit,
1901 1906 [('p', 'parent', [], 'parent'),
1902 1907 ('d', 'date', "", 'date code'),
1903 1908 ('u', 'user', "", 'user'),
1904 1909 ('F', 'files', "", 'file list'),
1905 1910 ('m', 'message', "", 'commit message'),
1906 1911 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1907 1912 ('l', 'logfile', "", 'commit message file')],
1908 1913 'hg rawcommit [OPTION]... [FILE]...'),
1909 1914 "recover": (recover, [], "hg recover"),
1910 1915 "^remove|rm": (remove,
1911 1916 [('I', 'include', [], 'include path in search'),
1912 1917 ('X', 'exclude', [], 'exclude path from search')],
1913 1918 "hg remove [OPTION]... FILE..."),
1914 1919 "rename|mv": (rename,
1915 1920 [('I', 'include', [], 'include path in search'),
1916 1921 ('X', 'exclude', [], 'exclude path from search'),
1917 1922 ('A', 'after', None, 'record a copy after it has happened'),
1918 1923 ('f', 'force', None, 'replace destination if it exists'),
1919 1924 ('p', 'parents', None, 'append source path to dest')],
1920 1925 'hg rename [OPTION]... [SOURCE]... DEST'),
1921 1926 "^revert":
1922 1927 (revert,
1923 1928 [("n", "nonrecursive", None, "don't recurse into subdirs"),
1924 1929 ("r", "rev", "", "revision")],
1925 1930 "hg revert [-n] [-r REV] [NAME]..."),
1926 1931 "root": (root, [], "hg root"),
1927 1932 "^serve":
1928 1933 (serve,
1929 1934 [('A', 'accesslog', '', 'access log file'),
1930 1935 ('E', 'errorlog', '', 'error log file'),
1931 1936 ('p', 'port', 0, 'listen port'),
1932 1937 ('a', 'address', '', 'interface address'),
1933 1938 ('n', 'name', "", 'repository name'),
1934 1939 ('', 'stdio', None, 'for remote clients'),
1935 1940 ('t', 'templates', "", 'template directory'),
1936 1941 ('', 'style', "", 'template style'),
1937 1942 ('6', 'ipv6', None, 'use IPv6 in addition to IPv4')],
1938 1943 "hg serve [OPTION]..."),
1939 1944 "^status":
1940 1945 (status,
1941 1946 [('m', 'modified', None, 'show only modified files'),
1942 1947 ('a', 'added', None, 'show only added files'),
1943 1948 ('r', 'removed', None, 'show only removed files'),
1944 1949 ('u', 'unknown', None, 'show only unknown (not tracked) files'),
1945 1950 ('n', 'no-status', None, 'hide status prefix'),
1946 1951 ('0', 'print0', None, 'end filenames with NUL'),
1947 1952 ('I', 'include', [], 'include path in search'),
1948 1953 ('X', 'exclude', [], 'exclude path from search')],
1949 1954 "hg status [OPTION]... [FILE]..."),
1950 1955 "tag":
1951 1956 (tag,
1952 1957 [('l', 'local', None, 'make the tag local'),
1953 1958 ('m', 'message', "", 'commit message'),
1954 1959 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1955 1960 ('d', 'date', "", 'date code'),
1956 1961 ('u', 'user', "", 'user')],
1957 1962 'hg tag [OPTION]... NAME [REV]'),
1958 1963 "tags": (tags, [], 'hg tags'),
1959 1964 "tip": (tip, [], 'hg tip'),
1960 1965 "unbundle":
1961 1966 (unbundle,
1962 1967 [],
1963 1968 'hg unbundle FILE'),
1964 1969 "undo": (undo, [], 'hg undo'),
1965 1970 "^update|up|checkout|co":
1966 1971 (update,
1967 1972 [('b', 'branch', "", 'checkout the head of a specific branch'),
1968 1973 ('m', 'merge', None, 'allow merging of conflicts'),
1969 1974 ('C', 'clean', None, 'overwrite locally modified files')],
1970 1975 'hg update [-b TAG] [-m] [-C] [REV]'),
1971 1976 "verify": (verify, [], 'hg verify'),
1972 1977 "version": (show_version, [], 'hg version'),
1973 1978 }
1974 1979
1975 1980 globalopts = [
1976 1981 ('R', 'repository', "", 'repository root directory'),
1977 1982 ('', 'cwd', '', 'change working directory'),
1978 1983 ('y', 'noninteractive', None, 'run non-interactively'),
1979 1984 ('q', 'quiet', None, 'quiet mode'),
1980 1985 ('v', 'verbose', None, 'verbose mode'),
1981 1986 ('', 'debug', None, 'debug mode'),
1982 1987 ('', 'debugger', None, 'start debugger'),
1983 1988 ('', 'traceback', None, 'print traceback on exception'),
1984 1989 ('', 'time', None, 'time how long the command takes'),
1985 1990 ('', 'profile', None, 'profile'),
1986 1991 ('', 'version', None, 'output version information and exit'),
1987 1992 ('h', 'help', None, 'display help and exit'),
1988 1993 ]
1989 1994
1990 1995 norepo = ("clone init version help debugancestor debugconfig debugdata"
1991 1996 " debugindex debugindexdot paths")
1992 1997
1993 1998 def find(cmd):
1994 1999 for e in table.keys():
1995 2000 if re.match("(%s)$" % e, cmd):
1996 2001 return e, table[e]
1997 2002
1998 2003 raise UnknownCommand(cmd)
1999 2004
2000 2005 class SignalInterrupt(Exception):
2001 2006 """Exception raised on SIGTERM and SIGHUP."""
2002 2007
2003 2008 def catchterm(*args):
2004 2009 raise SignalInterrupt
2005 2010
2006 2011 def run():
2007 2012 sys.exit(dispatch(sys.argv[1:]))
2008 2013
2009 2014 class ParseError(Exception):
2010 2015 """Exception raised on errors in parsing the command line."""
2011 2016
2012 2017 def parse(args):
2013 2018 options = {}
2014 2019 cmdoptions = {}
2015 2020
2016 2021 try:
2017 2022 args = fancyopts.fancyopts(args, globalopts, options)
2018 2023 except fancyopts.getopt.GetoptError, inst:
2019 2024 raise ParseError(None, inst)
2020 2025
2021 2026 if args:
2022 2027 cmd, args = args[0], args[1:]
2023 2028 i = find(cmd)[1]
2024 2029 c = list(i[1])
2025 2030 else:
2026 2031 cmd = None
2027 2032 c = []
2028 2033
2029 2034 # combine global options into local
2030 2035 for o in globalopts:
2031 2036 c.append((o[0], o[1], options[o[1]], o[3]))
2032 2037
2033 2038 try:
2034 2039 args = fancyopts.fancyopts(args, c, cmdoptions)
2035 2040 except fancyopts.getopt.GetoptError, inst:
2036 2041 raise ParseError(cmd, inst)
2037 2042
2038 2043 # separate global options back out
2039 2044 for o in globalopts:
2040 2045 n = o[1]
2041 2046 options[n] = cmdoptions[n]
2042 2047 del cmdoptions[n]
2043 2048
2044 2049 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
2045 2050
2046 2051 def dispatch(args):
2047 2052 signal.signal(signal.SIGTERM, catchterm)
2048 2053 try:
2049 2054 signal.signal(signal.SIGHUP, catchterm)
2050 2055 except AttributeError:
2051 2056 pass
2052 2057
2053 2058 u = ui.ui()
2054 2059 external = []
2055 2060 for x in u.extensions():
2056 2061 if x[1]:
2057 2062 try:
2058 2063 mod = imp.load_source(x[0], x[1])
2059 2064 except:
2060 2065 u.warn("*** failed to import extension %s\n" % x[1])
2061 2066 continue
2062 2067 else:
2063 2068 def importh(name):
2064 2069 mod = __import__(name)
2065 2070 components = name.split('.')
2066 2071 for comp in components[1:]:
2067 2072 mod = getattr(mod, comp)
2068 2073 return mod
2069 2074 try:
2070 2075 mod = importh(x[0])
2071 2076 except:
2072 2077 u.warn("failed to import extension %s\n" % x[0])
2073 2078 continue
2074 2079
2075 2080 external.append(mod)
2076 2081 for x in external:
2077 2082 cmdtable = getattr(x, 'cmdtable', {})
2078 2083 for t in cmdtable:
2079 2084 if t in table:
2080 2085 u.warn("module %s overrides %s\n" % (x.__name__, t))
2081 2086 table.update(cmdtable)
2082 2087
2083 2088 try:
2084 2089 cmd, func, args, options, cmdoptions = parse(args)
2085 2090 except ParseError, inst:
2086 2091 if inst.args[0]:
2087 2092 u.warn("hg %s: %s\n" % (inst.args[0], inst.args[1]))
2088 2093 help_(u, inst.args[0])
2089 2094 else:
2090 2095 u.warn("hg: %s\n" % inst.args[1])
2091 2096 help_(u, 'shortlist')
2092 2097 sys.exit(-1)
2093 2098 except UnknownCommand, inst:
2094 2099 u.warn("hg: unknown command '%s'\n" % inst.args[0])
2095 2100 help_(u, 'shortlist')
2096 2101 sys.exit(1)
2097 2102
2098 2103 if options["time"]:
2099 2104 def get_times():
2100 2105 t = os.times()
2101 2106 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
2102 2107 t = (t[0], t[1], t[2], t[3], time.clock())
2103 2108 return t
2104 2109 s = get_times()
2105 2110 def print_time():
2106 2111 t = get_times()
2107 2112 u.warn("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n" %
2108 2113 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
2109 2114 atexit.register(print_time)
2110 2115
2111 2116 u.updateopts(options["verbose"], options["debug"], options["quiet"],
2112 2117 not options["noninteractive"])
2113 2118
2114 2119 # enter the debugger before command execution
2115 2120 if options['debugger']:
2116 2121 pdb.set_trace()
2117 2122
2118 2123 try:
2119 2124 try:
2120 2125 if options['help']:
2121 2126 help_(u, cmd, options['version'])
2122 2127 sys.exit(0)
2123 2128 elif options['version']:
2124 2129 show_version(u)
2125 2130 sys.exit(0)
2126 2131 elif not cmd:
2127 2132 help_(u, 'shortlist')
2128 2133 sys.exit(0)
2129 2134
2130 2135 if options['cwd']:
2131 2136 try:
2132 2137 os.chdir(options['cwd'])
2133 2138 except OSError, inst:
2134 2139 raise util.Abort('%s: %s' %
2135 2140 (options['cwd'], inst.strerror))
2136 2141
2137 2142 if cmd not in norepo.split():
2138 2143 path = options["repository"] or ""
2139 2144 repo = hg.repository(ui=u, path=path)
2140 2145 for x in external:
2141 2146 if hasattr(x, 'reposetup'): x.reposetup(u, repo)
2142 2147 d = lambda: func(u, repo, *args, **cmdoptions)
2143 2148 else:
2144 2149 d = lambda: func(u, *args, **cmdoptions)
2145 2150
2146 2151 if options['profile']:
2147 2152 import hotshot, hotshot.stats
2148 2153 prof = hotshot.Profile("hg.prof")
2149 2154 r = prof.runcall(d)
2150 2155 prof.close()
2151 2156 stats = hotshot.stats.load("hg.prof")
2152 2157 stats.strip_dirs()
2153 2158 stats.sort_stats('time', 'calls')
2154 2159 stats.print_stats(40)
2155 2160 return r
2156 2161 else:
2157 2162 return d()
2158 2163 except:
2159 2164 # enter the debugger when we hit an exception
2160 2165 if options['debugger']:
2161 2166 pdb.post_mortem(sys.exc_info()[2])
2162 2167 if options['traceback']:
2163 2168 traceback.print_exc()
2164 2169 raise
2165 2170 except hg.RepoError, inst:
2166 2171 u.warn("abort: ", inst, "!\n")
2167 2172 except revlog.RevlogError, inst:
2168 2173 u.warn("abort: ", inst, "!\n")
2169 2174 except SignalInterrupt:
2170 2175 u.warn("killed!\n")
2171 2176 except KeyboardInterrupt:
2172 2177 try:
2173 2178 u.warn("interrupted!\n")
2174 2179 except IOError, inst:
2175 2180 if inst.errno == errno.EPIPE:
2176 2181 if u.debugflag:
2177 2182 u.warn("\nbroken pipe\n")
2178 2183 else:
2179 2184 raise
2180 2185 except IOError, inst:
2181 2186 if hasattr(inst, "code"):
2182 2187 u.warn("abort: %s\n" % inst)
2183 2188 elif hasattr(inst, "reason"):
2184 2189 u.warn("abort: error: %s\n" % inst.reason[1])
2185 2190 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
2186 2191 if u.debugflag:
2187 2192 u.warn("broken pipe\n")
2188 2193 elif getattr(inst, "strerror", None):
2189 2194 if getattr(inst, "filename", None):
2190 2195 u.warn("abort: %s - %s\n" % (inst.strerror, inst.filename))
2191 2196 else:
2192 2197 u.warn("abort: %s\n" % inst.strerror)
2193 2198 else:
2194 2199 raise
2195 2200 except OSError, inst:
2196 2201 if hasattr(inst, "filename"):
2197 2202 u.warn("abort: %s: %s\n" % (inst.strerror, inst.filename))
2198 2203 else:
2199 2204 u.warn("abort: %s\n" % inst.strerror)
2200 2205 except util.Abort, inst:
2201 2206 u.warn('abort: ', inst.args[0] % inst.args[1:], '\n')
2202 2207 sys.exit(1)
2203 2208 except TypeError, inst:
2204 2209 # was this an argument error?
2205 2210 tb = traceback.extract_tb(sys.exc_info()[2])
2206 2211 if len(tb) > 2: # no
2207 2212 raise
2208 2213 u.debug(inst, "\n")
2209 2214 u.warn("%s: invalid arguments\n" % cmd)
2210 2215 help_(u, cmd)
2211 2216 except UnknownCommand, inst:
2212 2217 u.warn("hg: unknown command '%s'\n" % inst.args[0])
2213 2218 help_(u, 'shortlist')
2214 2219 except SystemExit:
2215 2220 # don't catch this in the catch-all below
2216 2221 raise
2217 2222 except:
2218 2223 u.warn("** unknown exception encountered, details follow\n")
2219 2224 u.warn("** report bug details to mercurial@selenic.com\n")
2220 2225 raise
2221 2226
2222 2227 sys.exit(-1)
General Comments 0
You need to be logged in to leave comments. Login now