##// END OF EJS Templates
Moved --cwd handling to a place where ui and exception handling already exists.
Thomas Arendsen Hein -
r1050:9c09094d default
parent child Browse files
Show More
@@ -1,1714 +1,1713 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 demandload(globals(), "os re sys signal shutil")
10 10 demandload(globals(), "fancyopts ui hg util lock")
11 11 demandload(globals(), "fnmatch hgweb mdiff random signal time traceback")
12 12 demandload(globals(), "errno socket version struct atexit")
13 13
14 14 class UnknownCommand(Exception):
15 15 """Exception raised if command is not in the command table."""
16 16
17 17 def filterfiles(filters, files):
18 18 l = [x for x in files if x in filters]
19 19
20 20 for t in filters:
21 21 if t and t[-1] != "/":
22 22 t += "/"
23 23 l += [x for x in files if x.startswith(t)]
24 24 return l
25 25
26 26 def relpath(repo, args):
27 27 cwd = repo.getcwd()
28 28 if cwd:
29 29 return [util.normpath(os.path.join(cwd, x)) for x in args]
30 30 return args
31 31
32 32 def matchpats(repo, cwd, pats = [], opts = {}, head = ''):
33 33 return util.matcher(repo, cwd, pats or ['.'], opts.get('include'),
34 34 opts.get('exclude'), head)
35 35
36 36 def makewalk(repo, pats, opts, head = ''):
37 37 cwd = repo.getcwd()
38 38 files, matchfn, anypats = matchpats(repo, cwd, pats, opts, head)
39 39 exact = dict(zip(files, files))
40 40 def walk():
41 41 for src, fn in repo.walk(files = files, match = matchfn):
42 42 yield src, fn, util.pathto(cwd, fn), fn in exact
43 43 return files, matchfn, walk()
44 44
45 45 def walk(repo, pats, opts, head = ''):
46 46 files, matchfn, results = makewalk(repo, pats, opts, head)
47 47 for r in results: yield r
48 48
49 49 revrangesep = ':'
50 50
51 51 def revrange(ui, repo, revs, revlog=None):
52 52 if revlog is None:
53 53 revlog = repo.changelog
54 54 revcount = revlog.count()
55 55 def fix(val, defval):
56 56 if not val:
57 57 return defval
58 58 try:
59 59 num = int(val)
60 60 if str(num) != val:
61 61 raise ValueError
62 62 if num < 0:
63 63 num += revcount
64 64 if not (0 <= num < revcount):
65 65 raise ValueError
66 66 except ValueError:
67 67 try:
68 68 num = repo.changelog.rev(repo.lookup(val))
69 69 except KeyError:
70 70 try:
71 71 num = revlog.rev(revlog.lookup(val))
72 72 except KeyError:
73 73 raise util.Abort('invalid revision identifier %s', val)
74 74 return num
75 75 for spec in revs:
76 76 if spec.find(revrangesep) >= 0:
77 77 start, end = spec.split(revrangesep, 1)
78 78 start = fix(start, 0)
79 79 end = fix(end, revcount - 1)
80 80 if end > start:
81 81 end += 1
82 82 step = 1
83 83 else:
84 84 end -= 1
85 85 step = -1
86 86 for rev in xrange(start, end, step):
87 87 yield str(rev)
88 88 else:
89 89 yield str(fix(spec, None))
90 90
91 91 def make_filename(repo, r, pat, node=None,
92 92 total=None, seqno=None, revwidth=None):
93 93 node_expander = {
94 94 'H': lambda: hg.hex(node),
95 95 'R': lambda: str(r.rev(node)),
96 96 'h': lambda: hg.short(node),
97 97 }
98 98 expander = {
99 99 '%': lambda: '%',
100 100 'b': lambda: os.path.basename(repo.root),
101 101 }
102 102
103 103 try:
104 104 if node:
105 105 expander.update(node_expander)
106 106 if node and revwidth is not None:
107 107 expander['r'] = lambda: str(r.rev(node)).zfill(revwidth)
108 108 if total is not None:
109 109 expander['N'] = lambda: str(total)
110 110 if seqno is not None:
111 111 expander['n'] = lambda: str(seqno)
112 112 if total is not None and seqno is not None:
113 113 expander['n'] = lambda:str(seqno).zfill(len(str(total)))
114 114
115 115 newname = []
116 116 patlen = len(pat)
117 117 i = 0
118 118 while i < patlen:
119 119 c = pat[i]
120 120 if c == '%':
121 121 i += 1
122 122 c = pat[i]
123 123 c = expander[c]()
124 124 newname.append(c)
125 125 i += 1
126 126 return ''.join(newname)
127 127 except KeyError, inst:
128 128 raise util.Abort("invalid format spec '%%%s' in output file name",
129 129 inst.args[0])
130 130
131 131 def make_file(repo, r, pat, node=None,
132 132 total=None, seqno=None, revwidth=None, mode='wb'):
133 133 if not pat or pat == '-':
134 134 if 'w' in mode: return sys.stdout
135 135 else: return sys.stdin
136 136 if hasattr(pat, 'write') and 'w' in mode:
137 137 return pat
138 138 if hasattr(pat, 'read') and 'r' in mode:
139 139 return pat
140 140 return open(make_filename(repo, r, pat, node, total, seqno, revwidth),
141 141 mode)
142 142
143 143 def dodiff(fp, ui, repo, node1, node2, files=None, match=util.always,
144 144 changes=None, text=False):
145 145 def date(c):
146 146 return time.asctime(time.gmtime(float(c[2].split(' ')[0])))
147 147
148 148 if not changes:
149 149 (c, a, d, u) = repo.changes(node1, node2, files, match = match)
150 150 else:
151 151 (c, a, d, u) = changes
152 152 if files:
153 153 c, a, d = map(lambda x: filterfiles(files, x), (c, a, d))
154 154
155 155 if not c and not a and not d:
156 156 return
157 157
158 158 if node2:
159 159 change = repo.changelog.read(node2)
160 160 mmap2 = repo.manifest.read(change[0])
161 161 date2 = date(change)
162 162 def read(f):
163 163 return repo.file(f).read(mmap2[f])
164 164 else:
165 165 date2 = time.asctime()
166 166 if not node1:
167 167 node1 = repo.dirstate.parents()[0]
168 168 def read(f):
169 169 return repo.wfile(f).read()
170 170
171 171 if ui.quiet:
172 172 r = None
173 173 else:
174 174 hexfunc = ui.verbose and hg.hex or hg.short
175 175 r = [hexfunc(node) for node in [node1, node2] if node]
176 176
177 177 change = repo.changelog.read(node1)
178 178 mmap = repo.manifest.read(change[0])
179 179 date1 = date(change)
180 180
181 181 for f in c:
182 182 to = None
183 183 if f in mmap:
184 184 to = repo.file(f).read(mmap[f])
185 185 tn = read(f)
186 186 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text))
187 187 for f in a:
188 188 to = None
189 189 tn = read(f)
190 190 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text))
191 191 for f in d:
192 192 to = repo.file(f).read(mmap[f])
193 193 tn = None
194 194 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text))
195 195
196 196 def show_changeset(ui, repo, rev=0, changenode=None, brinfo=None):
197 197 """show a single changeset or file revision"""
198 198 log = repo.changelog
199 199 if changenode is None:
200 200 changenode = log.node(rev)
201 201 elif not rev:
202 202 rev = log.rev(changenode)
203 203
204 204 if ui.quiet:
205 205 ui.write("%d:%s\n" % (rev, hg.short(changenode)))
206 206 return
207 207
208 208 changes = log.read(changenode)
209 209
210 210 t, tz = changes[2].split(' ')
211 211 # a conversion tool was sticking non-integer offsets into repos
212 212 try:
213 213 tz = int(tz)
214 214 except ValueError:
215 215 tz = 0
216 216 date = time.asctime(time.localtime(float(t))) + " %+05d" % (int(tz)/-36)
217 217
218 218 parents = [(log.rev(p), ui.verbose and hg.hex(p) or hg.short(p))
219 219 for p in log.parents(changenode)
220 220 if ui.debugflag or p != hg.nullid]
221 221 if not ui.debugflag and len(parents) == 1 and parents[0][0] == rev-1:
222 222 parents = []
223 223
224 224 if ui.verbose:
225 225 ui.write("changeset: %d:%s\n" % (rev, hg.hex(changenode)))
226 226 else:
227 227 ui.write("changeset: %d:%s\n" % (rev, hg.short(changenode)))
228 228
229 229 for tag in repo.nodetags(changenode):
230 230 ui.status("tag: %s\n" % tag)
231 231 for parent in parents:
232 232 ui.write("parent: %d:%s\n" % parent)
233 233
234 234 if brinfo and changenode in brinfo:
235 235 br = brinfo[changenode]
236 236 ui.write("branch: %s\n" % " ".join(br))
237 237
238 238 ui.debug("manifest: %d:%s\n" % (repo.manifest.rev(changes[0]),
239 239 hg.hex(changes[0])))
240 240 ui.status("user: %s\n" % changes[1])
241 241 ui.status("date: %s\n" % date)
242 242
243 243 if ui.debugflag:
244 244 files = repo.changes(log.parents(changenode)[0], changenode)
245 245 for key, value in zip(["files:", "files+:", "files-:"], files):
246 246 if value:
247 247 ui.note("%-12s %s\n" % (key, " ".join(value)))
248 248 else:
249 249 ui.note("files: %s\n" % " ".join(changes[3]))
250 250
251 251 description = changes[4].strip()
252 252 if description:
253 253 if ui.verbose:
254 254 ui.status("description:\n")
255 255 ui.status(description)
256 256 ui.status("\n\n")
257 257 else:
258 258 ui.status("summary: %s\n" % description.splitlines()[0])
259 259 ui.status("\n")
260 260
261 261 def show_version(ui):
262 262 """output version and copyright information"""
263 263 ui.write("Mercurial Distributed SCM (version %s)\n"
264 264 % version.get_version())
265 265 ui.status(
266 266 "\nCopyright (C) 2005 Matt Mackall <mpm@selenic.com>\n"
267 267 "This is free software; see the source for copying conditions. "
268 268 "There is NO\nwarranty; "
269 269 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
270 270 )
271 271
272 272 def help_(ui, cmd=None, with_version=False):
273 273 """show help for a given command or all commands"""
274 274 if cmd and cmd != 'shortlist':
275 275 if with_version:
276 276 show_version(ui)
277 277 ui.write('\n')
278 278 key, i = find(cmd)
279 279 # synopsis
280 280 ui.write("%s\n\n" % i[2])
281 281
282 282 # description
283 283 doc = i[0].__doc__
284 284 if ui.quiet:
285 285 doc = doc.splitlines(0)[0]
286 286 ui.write("%s\n" % doc.rstrip())
287 287
288 288 # aliases
289 289 if not ui.quiet:
290 290 aliases = ', '.join(key.split('|')[1:])
291 291 if aliases:
292 292 ui.write("\naliases: %s\n" % aliases)
293 293
294 294 # options
295 295 if not ui.quiet and i[1]:
296 296 ui.write("\noptions:\n\n")
297 297 for s, l, d, c in i[1]:
298 298 opt = ' '
299 299 if s:
300 300 opt = opt + '-' + s + ' '
301 301 if l:
302 302 opt = opt + '--' + l + ' '
303 303 if d:
304 304 opt = opt + '(' + str(d) + ')'
305 305 ui.write(opt, "\n")
306 306 if c:
307 307 ui.write(' %s\n' % c)
308 308
309 309 else:
310 310 # program name
311 311 if ui.verbose or with_version:
312 312 show_version(ui)
313 313 else:
314 314 ui.status("Mercurial Distributed SCM\n")
315 315 ui.status('\n')
316 316
317 317 # list of commands
318 318 if cmd == "shortlist":
319 319 ui.status('basic commands (use "hg help" '
320 320 'for the full list or option "-v" for details):\n\n')
321 321 elif ui.verbose:
322 322 ui.status('list of commands:\n\n')
323 323 else:
324 324 ui.status('list of commands (use "hg help -v" '
325 325 'to show aliases and global options):\n\n')
326 326
327 327 h = {}
328 328 cmds = {}
329 329 for c, e in table.items():
330 330 f = c.split("|")[0]
331 331 if cmd == "shortlist" and not f.startswith("^"):
332 332 continue
333 333 f = f.lstrip("^")
334 334 if not ui.debugflag and f.startswith("debug"):
335 335 continue
336 336 d = ""
337 337 if e[0].__doc__:
338 338 d = e[0].__doc__.splitlines(0)[0].rstrip()
339 339 h[f] = d
340 340 cmds[f]=c.lstrip("^")
341 341
342 342 fns = h.keys()
343 343 fns.sort()
344 344 m = max(map(len, fns))
345 345 for f in fns:
346 346 if ui.verbose:
347 347 commands = cmds[f].replace("|",", ")
348 348 ui.write(" %s:\n %s\n"%(commands,h[f]))
349 349 else:
350 350 ui.write(' %-*s %s\n' % (m, f, h[f]))
351 351
352 352 # global options
353 353 if ui.verbose:
354 354 ui.write("\nglobal options:\n\n")
355 355 for s, l, d, c in globalopts:
356 356 opt = ' '
357 357 if s:
358 358 opt = opt + '-' + s + ' '
359 359 if l:
360 360 opt = opt + '--' + l + ' '
361 361 if d:
362 362 opt = opt + '(' + str(d) + ')'
363 363 ui.write(opt, "\n")
364 364 if c:
365 365 ui.write(' %s\n' % c)
366 366
367 367 # Commands start here, listed alphabetically
368 368
369 369 def add(ui, repo, *pats, **opts):
370 370 '''add the specified files on the next commit'''
371 371 names = []
372 372 for src, abs, rel, exact in walk(repo, pats, opts):
373 373 if exact:
374 374 names.append(abs)
375 375 elif repo.dirstate.state(abs) == '?':
376 376 ui.status('adding %s\n' % rel)
377 377 names.append(abs)
378 378 repo.add(names)
379 379
380 380 def addremove(ui, repo, *pats, **opts):
381 381 """add all new files, delete all missing files"""
382 382 add, remove = [], []
383 383 for src, abs, rel, exact in walk(repo, pats, opts):
384 384 if src == 'f' and repo.dirstate.state(abs) == '?':
385 385 add.append(abs)
386 386 if not exact: ui.status('adding ', rel, '\n')
387 387 if repo.dirstate.state(abs) != 'r' and not os.path.exists(rel):
388 388 remove.append(abs)
389 389 if not exact: ui.status('removing ', rel, '\n')
390 390 repo.add(add)
391 391 repo.remove(remove)
392 392
393 393 def annotate(ui, repo, *pats, **opts):
394 394 """show changeset information per file line"""
395 395 def getnode(rev):
396 396 return hg.short(repo.changelog.node(rev))
397 397
398 398 def getname(rev):
399 399 try:
400 400 return bcache[rev]
401 401 except KeyError:
402 402 cl = repo.changelog.read(repo.changelog.node(rev))
403 403 name = cl[1]
404 404 f = name.find('@')
405 405 if f >= 0:
406 406 name = name[:f]
407 407 f = name.find('<')
408 408 if f >= 0:
409 409 name = name[f+1:]
410 410 bcache[rev] = name
411 411 return name
412 412
413 413 if not pats:
414 414 raise util.Abort('at least one file name or pattern required')
415 415
416 416 bcache = {}
417 417 opmap = [['user', getname], ['number', str], ['changeset', getnode]]
418 418 if not opts['user'] and not opts['changeset']:
419 419 opts['number'] = 1
420 420
421 421 if opts['rev']:
422 422 node = repo.changelog.lookup(opts['rev'])
423 423 else:
424 424 node = repo.dirstate.parents()[0]
425 425 change = repo.changelog.read(node)
426 426 mmap = repo.manifest.read(change[0])
427 427
428 428 for src, abs, rel, exact in walk(repo, pats, opts):
429 429 if abs not in mmap:
430 430 ui.warn("warning: %s is not in the repository!\n" % rel)
431 431 continue
432 432
433 433 f = repo.file(abs)
434 434 if not opts['text'] and util.binary(f.read(mmap[abs])):
435 435 ui.write("%s: binary file\n" % rel)
436 436 continue
437 437
438 438 lines = f.annotate(mmap[abs])
439 439 pieces = []
440 440
441 441 for o, f in opmap:
442 442 if opts[o]:
443 443 l = [f(n) for n, dummy in lines]
444 444 if l:
445 445 m = max(map(len, l))
446 446 pieces.append(["%*s" % (m, x) for x in l])
447 447
448 448 if pieces:
449 449 for p, l in zip(zip(*pieces), lines):
450 450 ui.write("%s: %s" % (" ".join(p), l[1]))
451 451
452 452 def cat(ui, repo, file1, rev=None, **opts):
453 453 """output the latest or given revision of a file"""
454 454 r = repo.file(relpath(repo, [file1])[0])
455 455 if rev:
456 456 try:
457 457 # assume all revision numbers are for changesets
458 458 n = repo.lookup(rev)
459 459 change = repo.changelog.read(n)
460 460 m = repo.manifest.read(change[0])
461 461 n = m[relpath(repo, [file1])[0]]
462 462 except hg.RepoError, KeyError:
463 463 n = r.lookup(rev)
464 464 else:
465 465 n = r.tip()
466 466 fp = make_file(repo, r, opts['output'], node=n)
467 467 fp.write(r.read(n))
468 468
469 469 def clone(ui, source, dest=None, **opts):
470 470 """make a copy of an existing repository"""
471 471 if dest is None:
472 472 dest = os.path.basename(os.path.normpath(source))
473 473
474 474 if os.path.exists(dest):
475 475 ui.warn("abort: destination '%s' already exists\n" % dest)
476 476 return 1
477 477
478 478 dest = os.path.realpath(dest)
479 479
480 480 class Dircleanup:
481 481 def __init__(self, dir_):
482 482 self.rmtree = shutil.rmtree
483 483 self.dir_ = dir_
484 484 os.mkdir(dir_)
485 485 def close(self):
486 486 self.dir_ = None
487 487 def __del__(self):
488 488 if self.dir_:
489 489 self.rmtree(self.dir_, True)
490 490
491 491 if opts['ssh']:
492 492 ui.setconfig("ui", "ssh", opts['ssh'])
493 493 if opts['remotecmd']:
494 494 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
495 495
496 496 d = Dircleanup(dest)
497 497 source = ui.expandpath(source)
498 498 abspath = source
499 499 other = hg.repository(ui, source)
500 500
501 501 if other.dev() != -1:
502 502 abspath = os.path.abspath(source)
503 503 copyfile = (os.stat(dest).st_dev == other.dev()
504 504 and getattr(os, 'link', None) or shutil.copy2)
505 505 if copyfile is not shutil.copy2:
506 506 ui.note("cloning by hardlink\n")
507 507 # we use a lock here because because we're not nicely ordered
508 508 l = lock.lock(os.path.join(source, ".hg", "lock"))
509 509
510 510 util.copytree(os.path.join(source, ".hg"), os.path.join(dest, ".hg"),
511 511 copyfile)
512 512 try:
513 513 os.unlink(os.path.join(dest, ".hg", "dirstate"))
514 514 except OSError:
515 515 pass
516 516
517 517 repo = hg.repository(ui, dest)
518 518
519 519 else:
520 520 repo = hg.repository(ui, dest, create=1)
521 521 repo.pull(other)
522 522
523 523 f = repo.opener("hgrc", "w")
524 524 f.write("[paths]\n")
525 525 f.write("default = %s\n" % abspath)
526 526
527 527 if not opts['noupdate']:
528 528 update(ui, repo)
529 529
530 530 d.close()
531 531
532 532 def commit(ui, repo, *pats, **opts):
533 533 """commit the specified files or all outstanding changes"""
534 534 if opts['text']:
535 535 ui.warn("Warning: -t and --text is deprecated,"
536 536 " please use -m or --message instead.\n")
537 537 message = opts['message'] or opts['text']
538 538 logfile = opts['logfile']
539 539 if not message and logfile:
540 540 try:
541 541 if logfile == '-':
542 542 message = sys.stdin.read()
543 543 else:
544 544 message = open(logfile).read()
545 545 except IOError, why:
546 546 ui.warn("Can't read commit message %s: %s\n" % (logfile, why))
547 547
548 548 if opts['addremove']:
549 549 addremove(ui, repo, *pats, **opts)
550 550 cwd = repo.getcwd()
551 551 if not pats and cwd:
552 552 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
553 553 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
554 554 fns, match, anypats = matchpats(repo, (pats and repo.getcwd()) or '',
555 555 pats, opts)
556 556 if pats:
557 557 c, a, d, u = repo.changes(files = fns, match = match)
558 558 files = c + a + [fn for fn in d if repo.dirstate.state(fn) == 'r']
559 559 else:
560 560 files = []
561 561 repo.commit(files, message, opts['user'], opts['date'], match)
562 562
563 563 def copy(ui, repo, source, dest):
564 564 """mark a file as copied or renamed for the next commit"""
565 565 return repo.copy(*relpath(repo, (source, dest)))
566 566
567 567 def debugcheckstate(ui, repo):
568 568 """validate the correctness of the current dirstate"""
569 569 parent1, parent2 = repo.dirstate.parents()
570 570 repo.dirstate.read()
571 571 dc = repo.dirstate.map
572 572 keys = dc.keys()
573 573 keys.sort()
574 574 m1n = repo.changelog.read(parent1)[0]
575 575 m2n = repo.changelog.read(parent2)[0]
576 576 m1 = repo.manifest.read(m1n)
577 577 m2 = repo.manifest.read(m2n)
578 578 errors = 0
579 579 for f in dc:
580 580 state = repo.dirstate.state(f)
581 581 if state in "nr" and f not in m1:
582 582 ui.warn("%s in state %s, but not in manifest1\n" % (f, state))
583 583 errors += 1
584 584 if state in "a" and f in m1:
585 585 ui.warn("%s in state %s, but also in manifest1\n" % (f, state))
586 586 errors += 1
587 587 if state in "m" and f not in m1 and f not in m2:
588 588 ui.warn("%s in state %s, but not in either manifest\n" %
589 589 (f, state))
590 590 errors += 1
591 591 for f in m1:
592 592 state = repo.dirstate.state(f)
593 593 if state not in "nrm":
594 594 ui.warn("%s in manifest1, but listed as state %s" % (f, state))
595 595 errors += 1
596 596 if errors:
597 597 raise util.Abort(".hg/dirstate inconsistent with current parent's manifest")
598 598
599 599 def debugconfig(ui):
600 600 try:
601 601 repo = hg.repository(ui)
602 602 except: pass
603 603 for section, name, value in ui.walkconfig():
604 604 ui.write('%s.%s=%s\n' % (section, name, value))
605 605
606 606 def debugstate(ui, repo):
607 607 """show the contents of the current dirstate"""
608 608 repo.dirstate.read()
609 609 dc = repo.dirstate.map
610 610 keys = dc.keys()
611 611 keys.sort()
612 612 for file_ in keys:
613 613 ui.write("%c %3o %10d %s %s\n"
614 614 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
615 615 time.strftime("%x %X",
616 616 time.localtime(dc[file_][3])), file_))
617 617
618 618 def debugdata(ui, file_, rev):
619 619 """dump the contents of an data file revision"""
620 620 r = hg.revlog(hg.opener(""), file_[:-2] + ".i", file_)
621 621 ui.write(r.revision(r.lookup(rev)))
622 622
623 623 def debugindex(ui, file_):
624 624 """dump the contents of an index file"""
625 625 r = hg.revlog(hg.opener(""), file_, "")
626 626 ui.write(" rev offset length base linkrev" +
627 627 " nodeid p1 p2\n")
628 628 for i in range(r.count()):
629 629 e = r.index[i]
630 630 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
631 631 i, e[0], e[1], e[2], e[3],
632 632 hg.short(e[6]), hg.short(e[4]), hg.short(e[5])))
633 633
634 634 def debugindexdot(ui, file_):
635 635 """dump an index DAG as a .dot file"""
636 636 r = hg.revlog(hg.opener(""), file_, "")
637 637 ui.write("digraph G {\n")
638 638 for i in range(r.count()):
639 639 e = r.index[i]
640 640 ui.write("\t%d -> %d\n" % (r.rev(e[4]), i))
641 641 if e[5] != hg.nullid:
642 642 ui.write("\t%d -> %d\n" % (r.rev(e[5]), i))
643 643 ui.write("}\n")
644 644
645 645 def debugwalk(ui, repo, *pats, **opts):
646 646 items = list(walk(repo, pats, opts))
647 647 if not items: return
648 648 fmt = '%%s %%-%ds %%-%ds %%s' % (
649 649 max([len(abs) for (src, abs, rel, exact) in items]),
650 650 max([len(rel) for (src, abs, rel, exact) in items]))
651 651 exactly = {True: 'exact', False: ''}
652 652 for src, abs, rel, exact in items:
653 653 print fmt % (src, abs, rel, exactly[exact])
654 654
655 655 def diff(ui, repo, *pats, **opts):
656 656 """diff working directory (or selected files)"""
657 657 node1, node2 = None, None
658 658 revs = [repo.lookup(x) for x in opts['rev']]
659 659
660 660 if len(revs) > 0:
661 661 node1 = revs[0]
662 662 if len(revs) > 1:
663 663 node2 = revs[1]
664 664 if len(revs) > 2:
665 665 raise util.Abort("too many revisions to diff")
666 666
667 667 files = []
668 668 match = util.always
669 669 if pats:
670 670 roots, match, results = makewalk(repo, pats, opts)
671 671 for src, abs, rel, exact in results:
672 672 files.append(abs)
673 673
674 674 dodiff(sys.stdout, ui, repo, node1, node2, files, match=match,
675 675 text=opts['text'])
676 676
677 677 def doexport(ui, repo, changeset, seqno, total, revwidth, opts):
678 678 node = repo.lookup(changeset)
679 679 prev, other = repo.changelog.parents(node)
680 680 change = repo.changelog.read(node)
681 681
682 682 fp = make_file(repo, repo.changelog, opts['output'],
683 683 node=node, total=total, seqno=seqno,
684 684 revwidth=revwidth)
685 685 if fp != sys.stdout:
686 686 ui.note("%s\n" % fp.name)
687 687
688 688 fp.write("# HG changeset patch\n")
689 689 fp.write("# User %s\n" % change[1])
690 690 fp.write("# Node ID %s\n" % hg.hex(node))
691 691 fp.write("# Parent %s\n" % hg.hex(prev))
692 692 if other != hg.nullid:
693 693 fp.write("# Parent %s\n" % hg.hex(other))
694 694 fp.write(change[4].rstrip())
695 695 fp.write("\n\n")
696 696
697 697 dodiff(fp, ui, repo, prev, node, text=opts['text'])
698 698 if fp != sys.stdout: fp.close()
699 699
700 700 def export(ui, repo, *changesets, **opts):
701 701 """dump the header and diffs for one or more changesets"""
702 702 if not changesets:
703 703 raise util.Abort("export requires at least one changeset")
704 704 seqno = 0
705 705 revs = list(revrange(ui, repo, changesets))
706 706 total = len(revs)
707 707 revwidth = max(len(revs[0]), len(revs[-1]))
708 708 ui.note(len(revs) > 1 and "Exporting patches:\n" or "Exporting patch:\n")
709 709 for cset in revs:
710 710 seqno += 1
711 711 doexport(ui, repo, cset, seqno, total, revwidth, opts)
712 712
713 713 def forget(ui, repo, *pats, **opts):
714 714 """don't add the specified files on the next commit"""
715 715 forget = []
716 716 for src, abs, rel, exact in walk(repo, pats, opts):
717 717 if repo.dirstate.state(abs) == 'a':
718 718 forget.append(abs)
719 719 if not exact: ui.status('forgetting ', rel, '\n')
720 720 repo.forget(forget)
721 721
722 722 def heads(ui, repo, **opts):
723 723 """show current repository heads"""
724 724 heads = repo.changelog.heads()
725 725 br = None
726 726 if opts['branches']:
727 727 br = repo.branchlookup(heads)
728 728 for n in repo.changelog.heads():
729 729 show_changeset(ui, repo, changenode=n, brinfo=br)
730 730
731 731 def identify(ui, repo):
732 732 """print information about the working copy"""
733 733 parents = [p for p in repo.dirstate.parents() if p != hg.nullid]
734 734 if not parents:
735 735 ui.write("unknown\n")
736 736 return
737 737
738 738 hexfunc = ui.verbose and hg.hex or hg.short
739 739 (c, a, d, u) = repo.changes()
740 740 output = ["%s%s" % ('+'.join([hexfunc(parent) for parent in parents]),
741 741 (c or a or d) and "+" or "")]
742 742
743 743 if not ui.quiet:
744 744 # multiple tags for a single parent separated by '/'
745 745 parenttags = ['/'.join(tags)
746 746 for tags in map(repo.nodetags, parents) if tags]
747 747 # tags for multiple parents separated by ' + '
748 748 if parenttags:
749 749 output.append(' + '.join(parenttags))
750 750
751 751 ui.write("%s\n" % ' '.join(output))
752 752
753 753 def import_(ui, repo, patch1, *patches, **opts):
754 754 """import an ordered set of patches"""
755 755 patches = (patch1,) + patches
756 756
757 757 if not opts['force']:
758 758 (c, a, d, u) = repo.changes()
759 759 if c or a or d:
760 760 ui.warn("abort: outstanding uncommitted changes!\n")
761 761 return 1
762 762
763 763 d = opts["base"]
764 764 strip = opts["strip"]
765 765
766 766 for patch in patches:
767 767 ui.status("applying %s\n" % patch)
768 768 pf = os.path.join(d, patch)
769 769
770 770 message = []
771 771 user = None
772 772 hgpatch = False
773 773 for line in file(pf):
774 774 line = line.rstrip()
775 775 if line.startswith("--- ") or line.startswith("diff -r"):
776 776 break
777 777 elif hgpatch:
778 778 # parse values when importing the result of an hg export
779 779 if line.startswith("# User "):
780 780 user = line[7:]
781 781 ui.debug('User: %s\n' % user)
782 782 elif not line.startswith("# ") and line:
783 783 message.append(line)
784 784 hgpatch = False
785 785 elif line == '# HG changeset patch':
786 786 hgpatch = True
787 787 message = [] # We may have collected garbage
788 788 else:
789 789 message.append(line)
790 790
791 791 # make sure message isn't empty
792 792 if not message:
793 793 message = "imported patch %s\n" % patch
794 794 else:
795 795 message = "%s\n" % '\n'.join(message)
796 796 ui.debug('message:\n%s\n' % message)
797 797
798 798 f = os.popen("patch -p%d < '%s'" % (strip, pf))
799 799 files = []
800 800 for l in f.read().splitlines():
801 801 l.rstrip('\r\n');
802 802 ui.status("%s\n" % l)
803 803 if l.startswith('patching file '):
804 804 pf = l[14:]
805 805 if pf not in files:
806 806 files.append(pf)
807 807 patcherr = f.close()
808 808 if patcherr:
809 809 raise util.Abort("patch failed")
810 810
811 811 if len(files) > 0:
812 812 addremove(ui, repo, *files)
813 813 repo.commit(files, message, user)
814 814
815 815 def incoming(ui, repo, source="default"):
816 816 """show new changesets found in source"""
817 817 source = ui.expandpath(source)
818 818 other = hg.repository(ui, source)
819 819 if not other.local():
820 820 ui.warn("abort: incoming doesn't work for remote"
821 821 + " repositories yet, sorry!\n")
822 822 return 1
823 823 o = repo.findincoming(other)
824 824 if not o:
825 825 return
826 826 o = other.newer(o)
827 827 o.reverse()
828 828 for n in o:
829 829 show_changeset(ui, other, changenode=n)
830 830
831 831 def init(ui, dest="."):
832 832 """create a new repository in the given directory"""
833 833 if not os.path.exists(dest):
834 834 os.mkdir(dest)
835 835 hg.repository(ui, dest, create=1)
836 836
837 837 def locate(ui, repo, *pats, **opts):
838 838 """locate files matching specific patterns"""
839 839 end = '\n'
840 840 if opts['print0']: end = '\0'
841 841
842 842 for src, abs, rel, exact in walk(repo, pats, opts, '(?:.*/|)'):
843 843 if repo.dirstate.state(abs) == '?': continue
844 844 if opts['fullpath']:
845 845 ui.write(os.path.join(repo.root, abs), end)
846 846 else:
847 847 ui.write(rel, end)
848 848
849 849 def log(ui, repo, *pats, **opts):
850 850 """show revision history of entire repository or files"""
851 851 # This code most commonly needs to iterate backwards over the
852 852 # history it is interested in. This has awful (quadratic-looking)
853 853 # performance, so we use iterators that walk forwards through
854 854 # windows of revisions, yielding revisions in reverse order, while
855 855 # walking the windows backwards.
856 856 cwd = repo.getcwd()
857 857 if not pats and cwd:
858 858 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
859 859 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
860 860 files, matchfn, anypats = matchpats(repo, (pats and cwd) or '',
861 861 pats, opts)
862 862 revs = map(int, revrange(ui, repo, opts['rev'] or ['tip:0']))
863 863 wanted = {}
864 864 slowpath = anypats
865 865 window = 300
866 866 if not slowpath and not files:
867 867 # No files, no patterns. Display all revs.
868 868 wanted = dict(zip(revs, revs))
869 869 if not slowpath:
870 870 # Only files, no patterns. Check the history of each file.
871 871 def filerevgen(filelog):
872 872 for i in xrange(filelog.count() - 1, -1, -window):
873 873 revs = []
874 874 for j in xrange(max(0, i - window), i + 1):
875 875 revs.append(filelog.linkrev(filelog.node(j)))
876 876 revs.reverse()
877 877 for rev in revs:
878 878 yield rev
879 879
880 880 minrev, maxrev = min(revs), max(revs)
881 881 for filelog in map(repo.file, files):
882 882 # A zero count may be a directory or deleted file, so
883 883 # try to find matching entries on the slow path.
884 884 if filelog.count() == 0:
885 885 slowpath = True
886 886 break
887 887 for rev in filerevgen(filelog):
888 888 if rev <= maxrev:
889 889 if rev < minrev: break
890 890 wanted[rev] = 1
891 891 if slowpath:
892 892 # The slow path checks files modified in every changeset.
893 893 def mfrevgen():
894 894 for i in xrange(repo.changelog.count() - 1, -1, -window):
895 895 for j in xrange(max(0, i - window), i + 1):
896 896 yield j, repo.changelog.read(repo.lookup(str(j)))[3]
897 897
898 898 for rev, mf in mfrevgen():
899 899 if filter(matchfn, mf):
900 900 wanted[rev] = 1
901 901
902 902 def changerevgen():
903 903 class dui:
904 904 # Implement and delegate some ui protocol. Save hunks of
905 905 # output for later display in the desired order.
906 906 def __init__(self, ui):
907 907 self.ui = ui
908 908 self.hunk = {}
909 909 def bump(self, rev):
910 910 self.rev = rev
911 911 self.hunk[rev] = []
912 912 def note(self, *args):
913 913 if self.verbose: self.write(*args)
914 914 def status(self, *args):
915 915 if not self.quiet: self.write(*args)
916 916 def write(self, *args):
917 917 self.hunk[self.rev].append(args)
918 918 def __getattr__(self, key):
919 919 return getattr(self.ui, key)
920 920 for i in xrange(0, len(revs), window):
921 921 nrevs = [rev for rev in revs[i : min(i + window, len(revs))]
922 922 if rev in wanted]
923 923 srevs = list(nrevs)
924 924 srevs.sort()
925 925 du = dui(ui)
926 926 for rev in srevs:
927 927 du.bump(rev)
928 928 yield rev, du
929 929 for rev in nrevs:
930 930 for args in du.hunk[rev]:
931 931 ui.write(*args)
932 932
933 933 for rev, dui in changerevgen():
934 934 show_changeset(dui, repo, rev)
935 935 if opts['patch']:
936 936 changenode = repo.changelog.node(rev)
937 937 prev, other = repo.changelog.parents(changenode)
938 938 dodiff(dui, dui, repo, prev, changenode, files)
939 939 dui.write("\n\n")
940 940
941 941 def manifest(ui, repo, rev=None):
942 942 """output the latest or given revision of the project manifest"""
943 943 if rev:
944 944 try:
945 945 # assume all revision numbers are for changesets
946 946 n = repo.lookup(rev)
947 947 change = repo.changelog.read(n)
948 948 n = change[0]
949 949 except hg.RepoError:
950 950 n = repo.manifest.lookup(rev)
951 951 else:
952 952 n = repo.manifest.tip()
953 953 m = repo.manifest.read(n)
954 954 mf = repo.manifest.readflags(n)
955 955 files = m.keys()
956 956 files.sort()
957 957
958 958 for f in files:
959 959 ui.write("%40s %3s %s\n" % (hg.hex(m[f]), mf[f] and "755" or "644", f))
960 960
961 961 def outgoing(ui, repo, dest="default-push"):
962 962 """show changesets not found in destination"""
963 963 dest = ui.expandpath(dest)
964 964 other = hg.repository(ui, dest)
965 965 o = repo.findoutgoing(other)
966 966 o = repo.newer(o)
967 967 o.reverse()
968 968 for n in o:
969 969 show_changeset(ui, repo, changenode=n)
970 970
971 971 def parents(ui, repo, rev=None):
972 972 """show the parents of the working dir or revision"""
973 973 if rev:
974 974 p = repo.changelog.parents(repo.lookup(rev))
975 975 else:
976 976 p = repo.dirstate.parents()
977 977
978 978 for n in p:
979 979 if n != hg.nullid:
980 980 show_changeset(ui, repo, changenode=n)
981 981
982 982 def paths(ui, search = None):
983 983 """show definition of symbolic path names"""
984 984 try:
985 985 repo = hg.repository(ui=ui)
986 986 except:
987 987 pass
988 988
989 989 if search:
990 990 for name, path in ui.configitems("paths"):
991 991 if name == search:
992 992 ui.write("%s\n" % path)
993 993 return
994 994 ui.warn("not found!\n")
995 995 return 1
996 996 else:
997 997 for name, path in ui.configitems("paths"):
998 998 ui.write("%s = %s\n" % (name, path))
999 999
1000 1000 def pull(ui, repo, source="default", **opts):
1001 1001 """pull changes from the specified source"""
1002 1002 source = ui.expandpath(source)
1003 1003 ui.status('pulling from %s\n' % (source))
1004 1004
1005 1005 if opts['ssh']:
1006 1006 ui.setconfig("ui", "ssh", opts['ssh'])
1007 1007 if opts['remotecmd']:
1008 1008 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
1009 1009
1010 1010 other = hg.repository(ui, source)
1011 1011 r = repo.pull(other)
1012 1012 if not r:
1013 1013 if opts['update']:
1014 1014 return update(ui, repo)
1015 1015 else:
1016 1016 ui.status("(run 'hg update' to get a working copy)\n")
1017 1017
1018 1018 return r
1019 1019
1020 1020 def push(ui, repo, dest="default-push", force=False, ssh=None, remotecmd=None):
1021 1021 """push changes to the specified destination"""
1022 1022 dest = ui.expandpath(dest)
1023 1023 ui.status('pushing to %s\n' % (dest))
1024 1024
1025 1025 if ssh:
1026 1026 ui.setconfig("ui", "ssh", ssh)
1027 1027 if remotecmd:
1028 1028 ui.setconfig("ui", "remotecmd", remotecmd)
1029 1029
1030 1030 other = hg.repository(ui, dest)
1031 1031 r = repo.push(other, force)
1032 1032 return r
1033 1033
1034 1034 def rawcommit(ui, repo, *flist, **rc):
1035 1035 "raw commit interface"
1036 1036 if rc['text']:
1037 1037 ui.warn("Warning: -t and --text is deprecated,"
1038 1038 " please use -m or --message instead.\n")
1039 1039 message = rc['message'] or rc['text']
1040 1040 if not message and rc['logfile']:
1041 1041 try:
1042 1042 message = open(rc['logfile']).read()
1043 1043 except IOError:
1044 1044 pass
1045 1045 if not message and not rc['logfile']:
1046 1046 ui.warn("abort: missing commit message\n")
1047 1047 return 1
1048 1048
1049 1049 files = relpath(repo, list(flist))
1050 1050 if rc['files']:
1051 1051 files += open(rc['files']).read().splitlines()
1052 1052
1053 1053 rc['parent'] = map(repo.lookup, rc['parent'])
1054 1054
1055 1055 repo.rawcommit(files, message, rc['user'], rc['date'], *rc['parent'])
1056 1056
1057 1057 def recover(ui, repo):
1058 1058 """roll back an interrupted transaction"""
1059 1059 repo.recover()
1060 1060
1061 1061 def remove(ui, repo, file1, *files):
1062 1062 """remove the specified files on the next commit"""
1063 1063 repo.remove(relpath(repo, (file1,) + files))
1064 1064
1065 1065 def revert(ui, repo, *names, **opts):
1066 1066 """revert modified files or dirs back to their unmodified states"""
1067 1067 node = opts['rev'] and repo.lookup(opts['rev']) or \
1068 1068 repo.dirstate.parents()[0]
1069 1069 root = os.path.realpath(repo.root)
1070 1070
1071 1071 def trimpath(p):
1072 1072 p = os.path.realpath(p)
1073 1073 if p.startswith(root):
1074 1074 rest = p[len(root):]
1075 1075 if not rest:
1076 1076 return rest
1077 1077 if p.startswith(os.sep):
1078 1078 return rest[1:]
1079 1079 return p
1080 1080
1081 1081 relnames = map(trimpath, names or [os.getcwd()])
1082 1082 chosen = {}
1083 1083
1084 1084 def choose(name):
1085 1085 def body(name):
1086 1086 for r in relnames:
1087 1087 if not name.startswith(r):
1088 1088 continue
1089 1089 rest = name[len(r):]
1090 1090 if not rest:
1091 1091 return r, True
1092 1092 depth = rest.count(os.sep)
1093 1093 if not r:
1094 1094 if depth == 0 or not opts['nonrecursive']:
1095 1095 return r, True
1096 1096 elif rest[0] == os.sep:
1097 1097 if depth == 1 or not opts['nonrecursive']:
1098 1098 return r, True
1099 1099 return None, False
1100 1100 relname, ret = body(name)
1101 1101 if ret:
1102 1102 chosen[relname] = 1
1103 1103 return ret
1104 1104
1105 1105 r = repo.update(node, False, True, choose, False)
1106 1106 for n in relnames:
1107 1107 if n not in chosen:
1108 1108 ui.warn('error: no matches for %s\n' % n)
1109 1109 r = 1
1110 1110 sys.stdout.flush()
1111 1111 return r
1112 1112
1113 1113 def root(ui, repo):
1114 1114 """print the root (top) of the current working dir"""
1115 1115 ui.write(repo.root + "\n")
1116 1116
1117 1117 def serve(ui, repo, **opts):
1118 1118 """export the repository via HTTP"""
1119 1119
1120 1120 if opts["stdio"]:
1121 1121 fin, fout = sys.stdin, sys.stdout
1122 1122 sys.stdout = sys.stderr
1123 1123
1124 1124 def getarg():
1125 1125 argline = fin.readline()[:-1]
1126 1126 arg, l = argline.split()
1127 1127 val = fin.read(int(l))
1128 1128 return arg, val
1129 1129 def respond(v):
1130 1130 fout.write("%d\n" % len(v))
1131 1131 fout.write(v)
1132 1132 fout.flush()
1133 1133
1134 1134 lock = None
1135 1135
1136 1136 while 1:
1137 1137 cmd = fin.readline()[:-1]
1138 1138 if cmd == '':
1139 1139 return
1140 1140 if cmd == "heads":
1141 1141 h = repo.heads()
1142 1142 respond(" ".join(map(hg.hex, h)) + "\n")
1143 1143 if cmd == "lock":
1144 1144 lock = repo.lock()
1145 1145 respond("")
1146 1146 if cmd == "unlock":
1147 1147 if lock:
1148 1148 lock.release()
1149 1149 lock = None
1150 1150 respond("")
1151 1151 elif cmd == "branches":
1152 1152 arg, nodes = getarg()
1153 1153 nodes = map(hg.bin, nodes.split(" "))
1154 1154 r = []
1155 1155 for b in repo.branches(nodes):
1156 1156 r.append(" ".join(map(hg.hex, b)) + "\n")
1157 1157 respond("".join(r))
1158 1158 elif cmd == "between":
1159 1159 arg, pairs = getarg()
1160 1160 pairs = [map(hg.bin, p.split("-")) for p in pairs.split(" ")]
1161 1161 r = []
1162 1162 for b in repo.between(pairs):
1163 1163 r.append(" ".join(map(hg.hex, b)) + "\n")
1164 1164 respond("".join(r))
1165 1165 elif cmd == "changegroup":
1166 1166 nodes = []
1167 1167 arg, roots = getarg()
1168 1168 nodes = map(hg.bin, roots.split(" "))
1169 1169
1170 1170 cg = repo.changegroup(nodes)
1171 1171 while 1:
1172 1172 d = cg.read(4096)
1173 1173 if not d:
1174 1174 break
1175 1175 fout.write(d)
1176 1176
1177 1177 fout.flush()
1178 1178
1179 1179 elif cmd == "addchangegroup":
1180 1180 if not lock:
1181 1181 respond("not locked")
1182 1182 continue
1183 1183 respond("")
1184 1184
1185 1185 r = repo.addchangegroup(fin)
1186 1186 respond("")
1187 1187
1188 1188 optlist = "name templates style address port ipv6 accesslog errorlog"
1189 1189 for o in optlist.split():
1190 1190 if opts[o]:
1191 1191 ui.setconfig("web", o, opts[o])
1192 1192
1193 1193 httpd = hgweb.create_server(repo)
1194 1194
1195 1195 if ui.verbose:
1196 1196 addr, port = httpd.socket.getsockname()
1197 1197 if addr == '0.0.0.0':
1198 1198 addr = socket.gethostname()
1199 1199 else:
1200 1200 try:
1201 1201 addr = socket.gethostbyaddr(addr)[0]
1202 1202 except socket.error:
1203 1203 pass
1204 1204 if port != 80:
1205 1205 ui.status('listening at http://%s:%d/\n' % (addr, port))
1206 1206 else:
1207 1207 ui.status('listening at http://%s/\n' % addr)
1208 1208 httpd.serve_forever()
1209 1209
1210 1210 def status(ui, repo, *pats, **opts):
1211 1211 '''show changed files in the working directory
1212 1212
1213 1213 M = modified
1214 1214 A = added
1215 1215 R = removed
1216 1216 ? = not tracked
1217 1217 '''
1218 1218
1219 1219 cwd = repo.getcwd()
1220 1220 files, matchfn, anypats = matchpats(repo, cwd, pats, opts)
1221 1221 (c, a, d, u) = [[util.pathto(cwd, x) for x in n]
1222 1222 for n in repo.changes(files=files, match=matchfn)]
1223 1223
1224 1224 changetypes = [('modified', 'M', c),
1225 1225 ('added', 'A', a),
1226 1226 ('removed', 'R', d),
1227 1227 ('unknown', '?', u)]
1228 1228
1229 1229 for opt, char, changes in ([ct for ct in changetypes if opts[ct[0]]]
1230 1230 or changetypes):
1231 1231 for f in changes:
1232 1232 ui.write("%s %s\n" % (char, f))
1233 1233
1234 1234 def tag(ui, repo, name, rev=None, **opts):
1235 1235 """add a tag for the current tip or a given revision"""
1236 1236 if opts['text']:
1237 1237 ui.warn("Warning: -t and --text is deprecated,"
1238 1238 " please use -m or --message instead.\n")
1239 1239 if name == "tip":
1240 1240 ui.warn("abort: 'tip' is a reserved name!\n")
1241 1241 return -1
1242 1242 if rev:
1243 1243 r = hg.hex(repo.lookup(rev))
1244 1244 else:
1245 1245 r = hg.hex(repo.changelog.tip())
1246 1246
1247 1247 if name.find(revrangesep) >= 0:
1248 1248 ui.warn("abort: '%s' cannot be used in a tag name\n" % revrangesep)
1249 1249 return -1
1250 1250
1251 1251 if opts['local']:
1252 1252 repo.opener("localtags", "a").write("%s %s\n" % (r, name))
1253 1253 return
1254 1254
1255 1255 (c, a, d, u) = repo.changes()
1256 1256 for x in (c, a, d, u):
1257 1257 if ".hgtags" in x:
1258 1258 ui.warn("abort: working copy of .hgtags is changed!\n")
1259 1259 ui.status("(please commit .hgtags manually)\n")
1260 1260 return -1
1261 1261
1262 1262 repo.wfile(".hgtags", "ab").write("%s %s\n" % (r, name))
1263 1263 if repo.dirstate.state(".hgtags") == '?':
1264 1264 repo.add([".hgtags"])
1265 1265
1266 1266 message = (opts['message'] or opts['text'] or
1267 1267 "Added tag %s for changeset %s" % (name, r))
1268 1268 repo.commit([".hgtags"], message, opts['user'], opts['date'])
1269 1269
1270 1270 def tags(ui, repo):
1271 1271 """list repository tags"""
1272 1272
1273 1273 l = repo.tagslist()
1274 1274 l.reverse()
1275 1275 for t, n in l:
1276 1276 try:
1277 1277 r = "%5d:%s" % (repo.changelog.rev(n), hg.hex(n))
1278 1278 except KeyError:
1279 1279 r = " ?:?"
1280 1280 ui.write("%-30s %s\n" % (t, r))
1281 1281
1282 1282 def tip(ui, repo):
1283 1283 """show the tip revision"""
1284 1284 n = repo.changelog.tip()
1285 1285 show_changeset(ui, repo, changenode=n)
1286 1286
1287 1287 def undo(ui, repo):
1288 1288 """undo the last commit or pull
1289 1289
1290 1290 Roll back the last pull or commit transaction on the
1291 1291 repository, restoring the project to its earlier state.
1292 1292
1293 1293 This command should be used with care. There is only one level of
1294 1294 undo and there is no redo.
1295 1295
1296 1296 This command is not intended for use on public repositories. Once
1297 1297 a change is visible for pull by other users, undoing it locally is
1298 1298 ineffective.
1299 1299 """
1300 1300 repo.undo()
1301 1301
1302 1302 def update(ui, repo, node=None, merge=False, clean=False, branch=None):
1303 1303 '''update or merge working directory
1304 1304
1305 1305 If there are no outstanding changes in the working directory and
1306 1306 there is a linear relationship between the current version and the
1307 1307 requested version, the result is the requested version.
1308 1308
1309 1309 Otherwise the result is a merge between the contents of the
1310 1310 current working directory and the requested version. Files that
1311 1311 changed between either parent are marked as changed for the next
1312 1312 commit and a commit must be performed before any further updates
1313 1313 are allowed.
1314 1314 '''
1315 1315 if branch:
1316 1316 br = repo.branchlookup(branch=branch)
1317 1317 found = []
1318 1318 for x in br:
1319 1319 if branch in br[x]:
1320 1320 found.append(x)
1321 1321 if len(found) > 1:
1322 1322 ui.warn("Found multiple heads for %s\n" % branch)
1323 1323 for x in found:
1324 1324 show_changeset(ui, repo, changenode=x, brinfo=br)
1325 1325 return 1
1326 1326 if len(found) == 1:
1327 1327 node = found[0]
1328 1328 ui.warn("Using head %s for branch %s\n" % (hg.short(node), branch))
1329 1329 else:
1330 1330 ui.warn("branch %s not found\n" % (branch))
1331 1331 return 1
1332 1332 else:
1333 1333 node = node and repo.lookup(node) or repo.changelog.tip()
1334 1334 return repo.update(node, allow=merge, force=clean)
1335 1335
1336 1336 def verify(ui, repo):
1337 1337 """verify the integrity of the repository"""
1338 1338 return repo.verify()
1339 1339
1340 1340 # Command options and aliases are listed here, alphabetically
1341 1341
1342 1342 table = {
1343 1343 "^add":
1344 1344 (add,
1345 1345 [('I', 'include', [], 'include path in search'),
1346 1346 ('X', 'exclude', [], 'exclude path from search')],
1347 1347 "hg add [OPTION]... [FILE]..."),
1348 1348 "addremove":
1349 1349 (addremove,
1350 1350 [('I', 'include', [], 'include path in search'),
1351 1351 ('X', 'exclude', [], 'exclude path from search')],
1352 1352 "hg addremove [OPTION]... [FILE]..."),
1353 1353 "^annotate":
1354 1354 (annotate,
1355 1355 [('r', 'rev', '', 'revision'),
1356 1356 ('a', 'text', None, 'treat all files as text'),
1357 1357 ('u', 'user', None, 'show user'),
1358 1358 ('n', 'number', None, 'show revision number'),
1359 1359 ('c', 'changeset', None, 'show changeset'),
1360 1360 ('I', 'include', [], 'include path in search'),
1361 1361 ('X', 'exclude', [], 'exclude path from search')],
1362 1362 'hg annotate [OPTION]... FILE...'),
1363 1363 "cat":
1364 1364 (cat,
1365 1365 [('o', 'output', "", 'output to file')],
1366 1366 'hg cat [-o OUTFILE] FILE [REV]'),
1367 1367 "^clone":
1368 1368 (clone,
1369 1369 [('U', 'noupdate', None, 'skip update after cloning'),
1370 1370 ('e', 'ssh', "", 'ssh command'),
1371 1371 ('', 'remotecmd', "", 'remote hg command')],
1372 1372 'hg clone [OPTIONS] SOURCE [DEST]'),
1373 1373 "^commit|ci":
1374 1374 (commit,
1375 1375 [('A', 'addremove', None, 'run add/remove during commit'),
1376 1376 ('I', 'include', [], 'include path in search'),
1377 1377 ('X', 'exclude', [], 'exclude path from search'),
1378 1378 ('m', 'message', "", 'commit message'),
1379 1379 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1380 1380 ('l', 'logfile', "", 'commit message file'),
1381 1381 ('d', 'date', "", 'date code'),
1382 1382 ('u', 'user', "", 'user')],
1383 1383 'hg commit [OPTION]... [FILE]...'),
1384 1384 "copy": (copy, [], 'hg copy SOURCE DEST'),
1385 1385 "debugcheckstate": (debugcheckstate, [], 'debugcheckstate'),
1386 1386 "debugconfig": (debugconfig, [], 'debugconfig'),
1387 1387 "debugstate": (debugstate, [], 'debugstate'),
1388 1388 "debugdata": (debugdata, [], 'debugdata FILE REV'),
1389 1389 "debugindex": (debugindex, [], 'debugindex FILE'),
1390 1390 "debugindexdot": (debugindexdot, [], 'debugindexdot FILE'),
1391 1391 "debugwalk":
1392 1392 (debugwalk,
1393 1393 [('I', 'include', [], 'include path in search'),
1394 1394 ('X', 'exclude', [], 'exclude path from search')],
1395 1395 'debugwalk [OPTION]... [FILE]...'),
1396 1396 "^diff":
1397 1397 (diff,
1398 1398 [('r', 'rev', [], 'revision'),
1399 1399 ('a', 'text', None, 'treat all files as text'),
1400 1400 ('I', 'include', [], 'include path in search'),
1401 1401 ('X', 'exclude', [], 'exclude path from search')],
1402 1402 'hg diff [-I] [-X] [-r REV1 [-r REV2]] [FILE]...'),
1403 1403 "^export":
1404 1404 (export,
1405 1405 [('o', 'output', "", 'output to file'),
1406 1406 ('a', 'text', None, 'treat all files as text')],
1407 1407 "hg export [-o OUTFILE] REV..."),
1408 1408 "forget":
1409 1409 (forget,
1410 1410 [('I', 'include', [], 'include path in search'),
1411 1411 ('X', 'exclude', [], 'exclude path from search')],
1412 1412 "hg forget [OPTION]... FILE..."),
1413 1413 "heads":
1414 1414 (heads,
1415 1415 [('b', 'branches', None, 'find branch info')],
1416 1416 'hg [-b] heads'),
1417 1417 "help": (help_, [], 'hg help [COMMAND]'),
1418 1418 "identify|id": (identify, [], 'hg identify'),
1419 1419 "import|patch":
1420 1420 (import_,
1421 1421 [('p', 'strip', 1, 'path strip'),
1422 1422 ('f', 'force', None, 'skip check for outstanding changes'),
1423 1423 ('b', 'base', "", 'base path')],
1424 1424 "hg import [-p NUM] [-b BASE] PATCH..."),
1425 1425 "incoming|in": (incoming, [], 'hg incoming [SOURCE]'),
1426 1426 "^init": (init, [], 'hg init [DEST]'),
1427 1427 "locate":
1428 1428 (locate,
1429 1429 [('r', 'rev', '', 'revision'),
1430 1430 ('0', 'print0', None, 'end records with NUL'),
1431 1431 ('f', 'fullpath', None, 'print complete paths'),
1432 1432 ('I', 'include', [], 'include path in search'),
1433 1433 ('X', 'exclude', [], 'exclude path from search')],
1434 1434 'hg locate [OPTION]... [PATTERN]...'),
1435 1435 "^log|history":
1436 1436 (log,
1437 1437 [('I', 'include', [], 'include path in search'),
1438 1438 ('X', 'exclude', [], 'exclude path from search'),
1439 1439 ('r', 'rev', [], 'revision'),
1440 1440 ('p', 'patch', None, 'show patch')],
1441 1441 'hg log [-r REV1 [-r REV2]] [-p] [FILE]'),
1442 1442 "manifest": (manifest, [], 'hg manifest [REV]'),
1443 1443 "outgoing|out": (outgoing, [], 'hg outgoing [DEST]'),
1444 1444 "parents": (parents, [], 'hg parents [REV]'),
1445 1445 "paths": (paths, [], 'hg paths [NAME]'),
1446 1446 "^pull":
1447 1447 (pull,
1448 1448 [('u', 'update', None, 'update working directory'),
1449 1449 ('e', 'ssh', "", 'ssh command'),
1450 1450 ('', 'remotecmd', "", 'remote hg command')],
1451 1451 'hg pull [OPTIONS] [SOURCE]'),
1452 1452 "^push":
1453 1453 (push,
1454 1454 [('f', 'force', None, 'force push'),
1455 1455 ('e', 'ssh', "", 'ssh command'),
1456 1456 ('', 'remotecmd', "", 'remote hg command')],
1457 1457 'hg push [-f] [DEST]'),
1458 1458 "rawcommit":
1459 1459 (rawcommit,
1460 1460 [('p', 'parent', [], 'parent'),
1461 1461 ('d', 'date', "", 'date code'),
1462 1462 ('u', 'user', "", 'user'),
1463 1463 ('F', 'files', "", 'file list'),
1464 1464 ('m', 'message', "", 'commit message'),
1465 1465 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1466 1466 ('l', 'logfile', "", 'commit message file')],
1467 1467 'hg rawcommit [OPTION]... [FILE]...'),
1468 1468 "recover": (recover, [], "hg recover"),
1469 1469 "^remove|rm": (remove, [], "hg remove FILE..."),
1470 1470 "^revert":
1471 1471 (revert,
1472 1472 [("n", "nonrecursive", None, "don't recurse into subdirs"),
1473 1473 ("r", "rev", "", "revision")],
1474 1474 "hg revert [-n] [-r REV] [NAME]..."),
1475 1475 "root": (root, [], "hg root"),
1476 1476 "^serve":
1477 1477 (serve,
1478 1478 [('A', 'accesslog', '', 'access log file'),
1479 1479 ('E', 'errorlog', '', 'error log file'),
1480 1480 ('p', 'port', 0, 'listen port'),
1481 1481 ('a', 'address', '', 'interface address'),
1482 1482 ('n', 'name', "", 'repository name'),
1483 1483 ('', 'stdio', None, 'for remote clients'),
1484 1484 ('t', 'templates', "", 'template directory'),
1485 1485 ('', 'style', "", 'template style'),
1486 1486 ('6', 'ipv6', None, 'use IPv6 in addition to IPv4')],
1487 1487 "hg serve [OPTION]..."),
1488 1488 "^status":
1489 1489 (status,
1490 1490 [('m', 'modified', None, 'show only modified files'),
1491 1491 ('a', 'added', None, 'show only added files'),
1492 1492 ('r', 'removed', None, 'show only removed files'),
1493 1493 ('u', 'unknown', None, 'show only unknown (not tracked) files'),
1494 1494 ('I', 'include', [], 'include path in search'),
1495 1495 ('X', 'exclude', [], 'exclude path from search')],
1496 1496 "hg status [OPTION]... [FILE]..."),
1497 1497 "tag":
1498 1498 (tag,
1499 1499 [('l', 'local', None, 'make the tag local'),
1500 1500 ('m', 'message', "", 'commit message'),
1501 1501 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1502 1502 ('d', 'date', "", 'date code'),
1503 1503 ('u', 'user', "", 'user')],
1504 1504 'hg tag [OPTION]... NAME [REV]'),
1505 1505 "tags": (tags, [], 'hg tags'),
1506 1506 "tip": (tip, [], 'hg tip'),
1507 1507 "undo": (undo, [], 'hg undo'),
1508 1508 "^update|up|checkout|co":
1509 1509 (update,
1510 1510 [('b', 'branch', "", 'checkout the head of a specific branch'),
1511 1511 ('m', 'merge', None, 'allow merging of conflicts'),
1512 1512 ('C', 'clean', None, 'overwrite locally modified files')],
1513 1513 'hg update [-b TAG] [-m] [-C] [REV]'),
1514 1514 "verify": (verify, [], 'hg verify'),
1515 1515 "version": (show_version, [], 'hg version'),
1516 1516 }
1517 1517
1518 1518 globalopts = [
1519 1519 ('R', 'repository', "", 'repository root directory'),
1520 1520 ('', 'cwd', '', 'change working directory'),
1521 1521 ('y', 'noninteractive', None, 'run non-interactively'),
1522 1522 ('q', 'quiet', None, 'quiet mode'),
1523 1523 ('v', 'verbose', None, 'verbose mode'),
1524 1524 ('', 'debug', None, 'debug mode'),
1525 1525 ('', 'traceback', None, 'print traceback on exception'),
1526 1526 ('', 'time', None, 'time how long the command takes'),
1527 1527 ('', 'profile', None, 'profile'),
1528 1528 ('', 'version', None, 'output version information and exit'),
1529 1529 ('h', 'help', None, 'display help and exit'),
1530 1530 ]
1531 1531
1532 1532 norepo = "clone init version help debugconfig debugdata" + \
1533 1533 " debugindex debugindexdot paths"
1534 1534
1535 1535 def find(cmd):
1536 1536 for e in table.keys():
1537 1537 if re.match("(%s)$" % e, cmd):
1538 1538 return e, table[e]
1539 1539
1540 1540 raise UnknownCommand(cmd)
1541 1541
1542 1542 class SignalInterrupt(Exception):
1543 1543 """Exception raised on SIGTERM and SIGHUP."""
1544 1544
1545 1545 def catchterm(*args):
1546 1546 raise SignalInterrupt
1547 1547
1548 1548 def run():
1549 1549 sys.exit(dispatch(sys.argv[1:]))
1550 1550
1551 1551 class ParseError(Exception):
1552 1552 """Exception raised on errors in parsing the command line."""
1553 1553
1554 1554 def parse(args):
1555 1555 options = {}
1556 1556 cmdoptions = {}
1557 1557
1558 1558 try:
1559 1559 args = fancyopts.fancyopts(args, globalopts, options)
1560 1560 except fancyopts.getopt.GetoptError, inst:
1561 1561 raise ParseError(None, inst)
1562 1562
1563 1563 if args:
1564 1564 cmd, args = args[0], args[1:]
1565 1565 i = find(cmd)[1]
1566 1566 c = list(i[1])
1567 1567 else:
1568 1568 cmd = None
1569 1569 c = []
1570 1570
1571 1571 # combine global options into local
1572 1572 for o in globalopts:
1573 1573 c.append((o[0], o[1], options[o[1]], o[3]))
1574 1574
1575 1575 try:
1576 1576 args = fancyopts.fancyopts(args, c, cmdoptions)
1577 1577 except fancyopts.getopt.GetoptError, inst:
1578 1578 raise ParseError(cmd, inst)
1579 1579
1580 1580 # separate global options back out
1581 1581 for o in globalopts:
1582 1582 n = o[1]
1583 1583 options[n] = cmdoptions[n]
1584 1584 del cmdoptions[n]
1585 1585
1586 1586 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
1587 1587
1588 1588 def dispatch(args):
1589 1589 signal.signal(signal.SIGTERM, catchterm)
1590 1590 try:
1591 1591 signal.signal(signal.SIGHUP, catchterm)
1592 1592 except AttributeError:
1593 1593 pass
1594 1594
1595 1595 try:
1596 1596 cmd, func, args, options, cmdoptions = parse(args)
1597 1597 except ParseError, inst:
1598 1598 u = ui.ui()
1599 1599 if inst.args[0]:
1600 1600 u.warn("hg %s: %s\n" % (inst.args[0], inst.args[1]))
1601 1601 help_(u, inst.args[0])
1602 1602 else:
1603 1603 u.warn("hg: %s\n" % inst.args[1])
1604 1604 help_(u, 'shortlist')
1605 1605 sys.exit(-1)
1606 1606 except UnknownCommand, inst:
1607 1607 u = ui.ui()
1608 1608 u.warn("hg: unknown command '%s'\n" % inst.args[0])
1609 1609 help_(u, 'shortlist')
1610 1610 sys.exit(1)
1611 1611
1612 if options['cwd']:
1613 try:
1614 os.chdir(options['cwd'])
1615 except OSError, inst:
1616 u = ui.ui()
1617 u.warn('abort: %s: %s\n' % (options['cwd'], inst.strerror))
1618 sys.exit(1)
1619
1620 1612 if options["time"]:
1621 1613 def get_times():
1622 1614 t = os.times()
1623 1615 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
1624 1616 t = (t[0], t[1], t[2], t[3], time.clock())
1625 1617 return t
1626 1618 s = get_times()
1627 1619 def print_time():
1628 1620 t = get_times()
1629 1621 u = ui.ui()
1630 1622 u.warn("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n" %
1631 1623 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
1632 1624 atexit.register(print_time)
1633 1625
1634 1626 u = ui.ui(options["verbose"], options["debug"], options["quiet"],
1635 1627 not options["noninteractive"])
1636 1628
1637 1629 try:
1638 1630 try:
1639 1631 if options['help']:
1640 1632 help_(u, cmd, options['version'])
1641 1633 sys.exit(0)
1642 1634 elif options['version']:
1643 1635 show_version(u)
1644 1636 sys.exit(0)
1645 1637 elif not cmd:
1646 1638 help_(u, 'shortlist')
1647 1639 sys.exit(0)
1648 1640
1641 if options['cwd']:
1642 try:
1643 os.chdir(options['cwd'])
1644 except OSError, inst:
1645 u.warn('abort: %s: %s\n' % (options['cwd'], inst.strerror))
1646 sys.exit(1)
1647
1649 1648 if cmd not in norepo.split():
1650 1649 path = options["repository"] or ""
1651 1650 repo = hg.repository(ui=u, path=path)
1652 1651 d = lambda: func(u, repo, *args, **cmdoptions)
1653 1652 else:
1654 1653 d = lambda: func(u, *args, **cmdoptions)
1655 1654
1656 1655 if options['profile']:
1657 1656 import hotshot, hotshot.stats
1658 1657 prof = hotshot.Profile("hg.prof")
1659 1658 r = prof.runcall(d)
1660 1659 prof.close()
1661 1660 stats = hotshot.stats.load("hg.prof")
1662 1661 stats.strip_dirs()
1663 1662 stats.sort_stats('time', 'calls')
1664 1663 stats.print_stats(40)
1665 1664 return r
1666 1665 else:
1667 1666 return d()
1668 1667 except:
1669 1668 if options['traceback']:
1670 1669 traceback.print_exc()
1671 1670 raise
1672 1671 except hg.RepoError, inst:
1673 1672 u.warn("abort: ", inst, "!\n")
1674 1673 except SignalInterrupt:
1675 1674 u.warn("killed!\n")
1676 1675 except KeyboardInterrupt:
1677 1676 try:
1678 1677 u.warn("interrupted!\n")
1679 1678 except IOError, inst:
1680 1679 if inst.errno == errno.EPIPE:
1681 1680 if u.debugflag:
1682 1681 u.warn("\nbroken pipe\n")
1683 1682 else:
1684 1683 raise
1685 1684 except IOError, inst:
1686 1685 if hasattr(inst, "code"):
1687 1686 u.warn("abort: %s\n" % inst)
1688 1687 elif hasattr(inst, "reason"):
1689 1688 u.warn("abort: error: %s\n" % inst.reason[1])
1690 1689 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
1691 1690 if u.debugflag: u.warn("broken pipe\n")
1692 1691 else:
1693 1692 raise
1694 1693 except OSError, inst:
1695 1694 if hasattr(inst, "filename"):
1696 1695 u.warn("abort: %s: %s\n" % (inst.strerror, inst.filename))
1697 1696 else:
1698 1697 u.warn("abort: %s\n" % inst.strerror)
1699 1698 except util.Abort, inst:
1700 1699 u.warn('abort: ', inst.args[0] % inst.args[1:], '\n')
1701 1700 sys.exit(1)
1702 1701 except TypeError, inst:
1703 1702 # was this an argument error?
1704 1703 tb = traceback.extract_tb(sys.exc_info()[2])
1705 1704 if len(tb) > 2: # no
1706 1705 raise
1707 1706 u.debug(inst, "\n")
1708 1707 u.warn("%s: invalid arguments\n" % cmd)
1709 1708 help_(u, cmd)
1710 1709 except UnknownCommand, inst:
1711 1710 u.warn("hg: unknown command '%s'\n" % inst.args[0])
1712 1711 help_(u, 'shortlist')
1713 1712
1714 1713 sys.exit(-1)
General Comments 0
You need to be logged in to leave comments. Login now