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