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