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