##// END OF EJS Templates
avoid to copy more than one file to the same destination file
Robin Farine -
r1514:faf46d81 default
parent child Browse files
Show More
@@ -1,2626 +1,2633 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 targets = {}
793 794
794 795 def okaytocopy(abs, rel, exact):
795 796 reasons = {'?': _('is not managed'),
796 797 'a': _('has been marked for add')}
797 798 reason = reasons.get(repo.dirstate.state(abs))
798 799 if reason:
799 800 if exact: ui.warn(_('%s: not copying - file %s\n') % (rel, reason))
800 801 else:
801 802 return True
802 803
803 804 def copy(abssrc, relsrc, target, exact):
804 805 abstarget = util.canonpath(repo.root, cwd, target)
805 806 reltarget = util.pathto(cwd, abstarget)
806 if os.path.exists(reltarget):
807 prevsrc = targets.get(abstarget)
808 if prevsrc is not None:
809 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
810 (reltarget, abssrc, prevsrc))
811 return
812 elif os.path.exists(reltarget):
807 813 if opts['force']:
808 814 os.unlink(reltarget)
809 815 else:
810 816 ui.warn(_('%s: not overwriting - file exists\n') %
811 817 reltarget)
812 818 return
813 819 if ui.verbose or not exact:
814 820 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
815 821 if not opts['after']:
816 822 targetdir = os.path.dirname(reltarget) or '.'
817 823 if not os.path.isdir(targetdir):
818 824 os.makedirs(targetdir)
819 825 try:
820 826 shutil.copyfile(relsrc, reltarget)
821 827 shutil.copymode(relsrc, reltarget)
822 828 except shutil.Error, inst:
823 829 raise util.Abort(str(inst))
824 830 except IOError, inst:
825 831 if inst.errno == errno.ENOENT:
826 832 ui.warn(_('%s: deleted in working copy\n') % relsrc)
827 833 else:
828 834 ui.warn(_('%s: cannot copy - %s\n') %
829 835 (relsrc, inst.strerror))
830 836 errors += 1
831 837 return
838 targets[abstarget] = abssrc
832 839 repo.copy(abssrc, abstarget)
833 840 copied.append((abssrc, relsrc, exact))
834 841
835 842 pats = list(pats)
836 843 if not pats:
837 844 raise util.Abort(_('no source or destination specified'))
838 845 if len(pats) == 1:
839 846 raise util.Abort(_('no destination specified'))
840 847 dest = pats.pop()
841 848 destdirexists = os.path.isdir(dest)
842 849 if (len(pats) > 1 or not os.path.exists(pats[0])) and not destdirexists:
843 850 raise util.Abort(_('with multiple sources, destination must be an '
844 851 'existing directory'))
845 852
846 853 for pat in pats:
847 854 if os.path.isdir(pat):
848 855 if destdirexists:
849 856 striplen = len(os.path.split(pat)[0])
850 857 else:
851 858 striplen = len(pat)
852 859 if striplen:
853 860 striplen += len(os.sep)
854 861 targetpath = lambda p: os.path.join(dest, p[striplen:])
855 862 elif destdirexists:
856 863 targetpath = lambda p: os.path.join(dest, os.path.basename(p))
857 864 else:
858 865 targetpath = lambda p: dest
859 866 for tag, abssrc, relsrc, exact in walk(repo, [pat], opts):
860 867 if okaytocopy(abssrc, relsrc, exact):
861 868 copy(abssrc, relsrc, targetpath(abssrc), exact)
862 869
863 870 if errors:
864 871 ui.warn(_('(consider using --after)\n'))
865 872 if len(copied) == 0:
866 873 raise util.Abort(_('no files to copy'))
867 874 return errors, copied
868 875
869 876 def copy(ui, repo, *pats, **opts):
870 877 """mark files as copied for the next commit
871 878
872 879 Mark dest as having copies of source files. If dest is a
873 880 directory, copies are put in that directory. If dest is a file,
874 881 there can only be one source.
875 882
876 883 By default, this command copies the contents of files as they
877 884 stand in the working directory. If invoked with --after, the
878 885 operation is recorded, but no copying is performed.
879 886
880 887 This command takes effect in the next commit.
881 888
882 889 NOTE: This command should be treated as experimental. While it
883 890 should properly record copied files, this information is not yet
884 891 fully used by merge, nor fully reported by log.
885 892 """
886 893 errs, copied = docopy(ui, repo, pats, opts)
887 894 return errs
888 895
889 896 def debugancestor(ui, index, rev1, rev2):
890 897 """find the ancestor revision of two revisions in a given index"""
891 898 r = revlog.revlog(util.opener(os.getcwd()), index, "")
892 899 a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
893 900 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
894 901
895 902 def debugcheckstate(ui, repo):
896 903 """validate the correctness of the current dirstate"""
897 904 parent1, parent2 = repo.dirstate.parents()
898 905 repo.dirstate.read()
899 906 dc = repo.dirstate.map
900 907 keys = dc.keys()
901 908 keys.sort()
902 909 m1n = repo.changelog.read(parent1)[0]
903 910 m2n = repo.changelog.read(parent2)[0]
904 911 m1 = repo.manifest.read(m1n)
905 912 m2 = repo.manifest.read(m2n)
906 913 errors = 0
907 914 for f in dc:
908 915 state = repo.dirstate.state(f)
909 916 if state in "nr" and f not in m1:
910 917 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
911 918 errors += 1
912 919 if state in "a" and f in m1:
913 920 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
914 921 errors += 1
915 922 if state in "m" and f not in m1 and f not in m2:
916 923 ui.warn(_("%s in state %s, but not in either manifest\n") %
917 924 (f, state))
918 925 errors += 1
919 926 for f in m1:
920 927 state = repo.dirstate.state(f)
921 928 if state not in "nrm":
922 929 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
923 930 errors += 1
924 931 if errors:
925 932 raise util.Abort(_(".hg/dirstate inconsistent with current parent's manifest"))
926 933
927 934 def debugconfig(ui):
928 935 """show combined config settings from all hgrc files"""
929 936 try:
930 937 repo = hg.repository(ui)
931 938 except hg.RepoError:
932 939 pass
933 940 for section, name, value in ui.walkconfig():
934 941 ui.write('%s.%s=%s\n' % (section, name, value))
935 942
936 943 def debugsetparents(ui, repo, rev1, rev2=None):
937 944 """manually set the parents of the current working directory
938 945
939 946 This is useful for writing repository conversion tools, but should
940 947 be used with care.
941 948 """
942 949
943 950 if not rev2:
944 951 rev2 = hex(nullid)
945 952
946 953 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
947 954
948 955 def debugstate(ui, repo):
949 956 """show the contents of the current dirstate"""
950 957 repo.dirstate.read()
951 958 dc = repo.dirstate.map
952 959 keys = dc.keys()
953 960 keys.sort()
954 961 for file_ in keys:
955 962 ui.write("%c %3o %10d %s %s\n"
956 963 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
957 964 time.strftime("%x %X",
958 965 time.localtime(dc[file_][3])), file_))
959 966 for f in repo.dirstate.copies:
960 967 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copies[f], f))
961 968
962 969 def debugdata(ui, file_, rev):
963 970 """dump the contents of an data file revision"""
964 971 r = revlog.revlog(util.opener(os.getcwd()), file_[:-2] + ".i", file_)
965 972 try:
966 973 ui.write(r.revision(r.lookup(rev)))
967 974 except KeyError:
968 975 raise util.Abort(_('invalid revision identifier %s'), rev)
969 976
970 977 def debugindex(ui, file_):
971 978 """dump the contents of an index file"""
972 979 r = revlog.revlog(util.opener(os.getcwd()), file_, "")
973 980 ui.write(" rev offset length base linkrev" +
974 981 " nodeid p1 p2\n")
975 982 for i in range(r.count()):
976 983 e = r.index[i]
977 984 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
978 985 i, e[0], e[1], e[2], e[3],
979 986 short(e[6]), short(e[4]), short(e[5])))
980 987
981 988 def debugindexdot(ui, file_):
982 989 """dump an index DAG as a .dot file"""
983 990 r = revlog.revlog(util.opener(os.getcwd()), file_, "")
984 991 ui.write("digraph G {\n")
985 992 for i in range(r.count()):
986 993 e = r.index[i]
987 994 ui.write("\t%d -> %d\n" % (r.rev(e[4]), i))
988 995 if e[5] != nullid:
989 996 ui.write("\t%d -> %d\n" % (r.rev(e[5]), i))
990 997 ui.write("}\n")
991 998
992 999 def debugrename(ui, repo, file, rev=None):
993 1000 """dump rename information"""
994 1001 r = repo.file(relpath(repo, [file])[0])
995 1002 if rev:
996 1003 try:
997 1004 # assume all revision numbers are for changesets
998 1005 n = repo.lookup(rev)
999 1006 change = repo.changelog.read(n)
1000 1007 m = repo.manifest.read(change[0])
1001 1008 n = m[relpath(repo, [file])[0]]
1002 1009 except hg.RepoError, KeyError:
1003 1010 n = r.lookup(rev)
1004 1011 else:
1005 1012 n = r.tip()
1006 1013 m = r.renamed(n)
1007 1014 if m:
1008 1015 ui.write(_("renamed from %s:%s\n") % (m[0], hex(m[1])))
1009 1016 else:
1010 1017 ui.write(_("not renamed\n"))
1011 1018
1012 1019 def debugwalk(ui, repo, *pats, **opts):
1013 1020 """show how files match on given patterns"""
1014 1021 items = list(walk(repo, pats, opts))
1015 1022 if not items:
1016 1023 return
1017 1024 fmt = '%%s %%-%ds %%-%ds %%s' % (
1018 1025 max([len(abs) for (src, abs, rel, exact) in items]),
1019 1026 max([len(rel) for (src, abs, rel, exact) in items]))
1020 1027 for src, abs, rel, exact in items:
1021 1028 line = fmt % (src, abs, rel, exact and 'exact' or '')
1022 1029 ui.write("%s\n" % line.rstrip())
1023 1030
1024 1031 def diff(ui, repo, *pats, **opts):
1025 1032 """diff working directory (or selected files)
1026 1033
1027 1034 Show differences between revisions for the specified files.
1028 1035
1029 1036 Differences between files are shown using the unified diff format.
1030 1037
1031 1038 When two revision arguments are given, then changes are shown
1032 1039 between those revisions. If only one revision is specified then
1033 1040 that revision is compared to the working directory, and, when no
1034 1041 revisions are specified, the working directory files are compared
1035 1042 to its parent.
1036 1043
1037 1044 Without the -a option, diff will avoid generating diffs of files
1038 1045 it detects as binary. With -a, diff will generate a diff anyway,
1039 1046 probably with undesirable results.
1040 1047 """
1041 1048 node1, node2 = None, None
1042 1049 revs = [repo.lookup(x) for x in opts['rev']]
1043 1050
1044 1051 if len(revs) > 0:
1045 1052 node1 = revs[0]
1046 1053 if len(revs) > 1:
1047 1054 node2 = revs[1]
1048 1055 if len(revs) > 2:
1049 1056 raise util.Abort(_("too many revisions to diff"))
1050 1057
1051 1058 fns, matchfn, anypats = matchpats(repo, repo.getcwd(), pats, opts)
1052 1059
1053 1060 dodiff(sys.stdout, ui, repo, node1, node2, fns, match=matchfn,
1054 1061 text=opts['text'])
1055 1062
1056 1063 def doexport(ui, repo, changeset, seqno, total, revwidth, opts):
1057 1064 node = repo.lookup(changeset)
1058 1065 prev, other = repo.changelog.parents(node)
1059 1066 change = repo.changelog.read(node)
1060 1067
1061 1068 fp = make_file(repo, repo.changelog, opts['output'],
1062 1069 node=node, total=total, seqno=seqno,
1063 1070 revwidth=revwidth)
1064 1071 if fp != sys.stdout:
1065 1072 ui.note("%s\n" % fp.name)
1066 1073
1067 1074 fp.write("# HG changeset patch\n")
1068 1075 fp.write("# User %s\n" % change[1])
1069 1076 fp.write("# Node ID %s\n" % hex(node))
1070 1077 fp.write("# Parent %s\n" % hex(prev))
1071 1078 if other != nullid:
1072 1079 fp.write("# Parent %s\n" % hex(other))
1073 1080 fp.write(change[4].rstrip())
1074 1081 fp.write("\n\n")
1075 1082
1076 1083 dodiff(fp, ui, repo, prev, node, text=opts['text'])
1077 1084 if fp != sys.stdout:
1078 1085 fp.close()
1079 1086
1080 1087 def export(ui, repo, *changesets, **opts):
1081 1088 """dump the header and diffs for one or more changesets
1082 1089
1083 1090 Print the changeset header and diffs for one or more revisions.
1084 1091
1085 1092 The information shown in the changeset header is: author,
1086 1093 changeset hash, parent and commit comment.
1087 1094
1088 1095 Output may be to a file, in which case the name of the file is
1089 1096 given using a format string. The formatting rules are as follows:
1090 1097
1091 1098 %% literal "%" character
1092 1099 %H changeset hash (40 bytes of hexadecimal)
1093 1100 %N number of patches being generated
1094 1101 %R changeset revision number
1095 1102 %b basename of the exporting repository
1096 1103 %h short-form changeset hash (12 bytes of hexadecimal)
1097 1104 %n zero-padded sequence number, starting at 1
1098 1105 %r zero-padded changeset revision number
1099 1106
1100 1107 Without the -a option, export will avoid generating diffs of files
1101 1108 it detects as binary. With -a, export will generate a diff anyway,
1102 1109 probably with undesirable results.
1103 1110 """
1104 1111 if not changesets:
1105 1112 raise util.Abort(_("export requires at least one changeset"))
1106 1113 seqno = 0
1107 1114 revs = list(revrange(ui, repo, changesets))
1108 1115 total = len(revs)
1109 1116 revwidth = max(map(len, revs))
1110 1117 ui.note(len(revs) > 1 and _("Exporting patches:\n") or _("Exporting patch:\n"))
1111 1118 for cset in revs:
1112 1119 seqno += 1
1113 1120 doexport(ui, repo, cset, seqno, total, revwidth, opts)
1114 1121
1115 1122 def forget(ui, repo, *pats, **opts):
1116 1123 """don't add the specified files on the next commit
1117 1124
1118 1125 Undo an 'hg add' scheduled for the next commit.
1119 1126 """
1120 1127 forget = []
1121 1128 for src, abs, rel, exact in walk(repo, pats, opts):
1122 1129 if repo.dirstate.state(abs) == 'a':
1123 1130 forget.append(abs)
1124 1131 if ui.verbose or not exact:
1125 1132 ui.status(_('forgetting %s\n') % rel)
1126 1133 repo.forget(forget)
1127 1134
1128 1135 def grep(ui, repo, pattern, *pats, **opts):
1129 1136 """search for a pattern in specified files and revisions
1130 1137
1131 1138 Search revisions of files for a regular expression.
1132 1139
1133 1140 This command behaves differently than Unix grep. It only accepts
1134 1141 Python/Perl regexps. It searches repository history, not the
1135 1142 working directory. It always prints the revision number in which
1136 1143 a match appears.
1137 1144
1138 1145 By default, grep only prints output for the first revision of a
1139 1146 file in which it finds a match. To get it to print every revision
1140 1147 that contains a change in match status ("-" for a match that
1141 1148 becomes a non-match, or "+" for a non-match that becomes a match),
1142 1149 use the --all flag.
1143 1150 """
1144 1151 reflags = 0
1145 1152 if opts['ignore_case']:
1146 1153 reflags |= re.I
1147 1154 regexp = re.compile(pattern, reflags)
1148 1155 sep, eol = ':', '\n'
1149 1156 if opts['print0']:
1150 1157 sep = eol = '\0'
1151 1158
1152 1159 fcache = {}
1153 1160 def getfile(fn):
1154 1161 if fn not in fcache:
1155 1162 fcache[fn] = repo.file(fn)
1156 1163 return fcache[fn]
1157 1164
1158 1165 def matchlines(body):
1159 1166 begin = 0
1160 1167 linenum = 0
1161 1168 while True:
1162 1169 match = regexp.search(body, begin)
1163 1170 if not match:
1164 1171 break
1165 1172 mstart, mend = match.span()
1166 1173 linenum += body.count('\n', begin, mstart) + 1
1167 1174 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1168 1175 lend = body.find('\n', mend)
1169 1176 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1170 1177 begin = lend + 1
1171 1178
1172 1179 class linestate:
1173 1180 def __init__(self, line, linenum, colstart, colend):
1174 1181 self.line = line
1175 1182 self.linenum = linenum
1176 1183 self.colstart = colstart
1177 1184 self.colend = colend
1178 1185 def __eq__(self, other):
1179 1186 return self.line == other.line
1180 1187 def __hash__(self):
1181 1188 return hash(self.line)
1182 1189
1183 1190 matches = {}
1184 1191 def grepbody(fn, rev, body):
1185 1192 matches[rev].setdefault(fn, {})
1186 1193 m = matches[rev][fn]
1187 1194 for lnum, cstart, cend, line in matchlines(body):
1188 1195 s = linestate(line, lnum, cstart, cend)
1189 1196 m[s] = s
1190 1197
1191 1198 prev = {}
1192 1199 ucache = {}
1193 1200 def display(fn, rev, states, prevstates):
1194 1201 diff = list(sets.Set(states).symmetric_difference(sets.Set(prevstates)))
1195 1202 diff.sort(lambda x, y: cmp(x.linenum, y.linenum))
1196 1203 counts = {'-': 0, '+': 0}
1197 1204 filerevmatches = {}
1198 1205 for l in diff:
1199 1206 if incrementing or not opts['all']:
1200 1207 change = ((l in prevstates) and '-') or '+'
1201 1208 r = rev
1202 1209 else:
1203 1210 change = ((l in states) and '-') or '+'
1204 1211 r = prev[fn]
1205 1212 cols = [fn, str(rev)]
1206 1213 if opts['line_number']: cols.append(str(l.linenum))
1207 1214 if opts['all']: cols.append(change)
1208 1215 if opts['user']: cols.append(trimuser(ui, getchange(rev)[1], rev,
1209 1216 ucache))
1210 1217 if opts['files_with_matches']:
1211 1218 c = (fn, rev)
1212 1219 if c in filerevmatches: continue
1213 1220 filerevmatches[c] = 1
1214 1221 else:
1215 1222 cols.append(l.line)
1216 1223 ui.write(sep.join(cols), eol)
1217 1224 counts[change] += 1
1218 1225 return counts['+'], counts['-']
1219 1226
1220 1227 fstate = {}
1221 1228 skip = {}
1222 1229 changeiter, getchange = walkchangerevs(ui, repo, repo.getcwd(), pats, opts)
1223 1230 count = 0
1224 1231 incrementing = False
1225 1232 for st, rev, fns in changeiter:
1226 1233 if st == 'window':
1227 1234 incrementing = rev
1228 1235 matches.clear()
1229 1236 elif st == 'add':
1230 1237 change = repo.changelog.read(repo.lookup(str(rev)))
1231 1238 mf = repo.manifest.read(change[0])
1232 1239 matches[rev] = {}
1233 1240 for fn in fns:
1234 1241 if fn in skip: continue
1235 1242 fstate.setdefault(fn, {})
1236 1243 try:
1237 1244 grepbody(fn, rev, getfile(fn).read(mf[fn]))
1238 1245 except KeyError:
1239 1246 pass
1240 1247 elif st == 'iter':
1241 1248 states = matches[rev].items()
1242 1249 states.sort()
1243 1250 for fn, m in states:
1244 1251 if fn in skip: continue
1245 1252 if incrementing or not opts['all'] or fstate[fn]:
1246 1253 pos, neg = display(fn, rev, m, fstate[fn])
1247 1254 count += pos + neg
1248 1255 if pos and not opts['all']:
1249 1256 skip[fn] = True
1250 1257 fstate[fn] = m
1251 1258 prev[fn] = rev
1252 1259
1253 1260 if not incrementing:
1254 1261 fstate = fstate.items()
1255 1262 fstate.sort()
1256 1263 for fn, state in fstate:
1257 1264 if fn in skip: continue
1258 1265 display(fn, rev, {}, state)
1259 1266 return (count == 0 and 1) or 0
1260 1267
1261 1268 def heads(ui, repo, **opts):
1262 1269 """show current repository heads
1263 1270
1264 1271 Show all repository head changesets.
1265 1272
1266 1273 Repository "heads" are changesets that don't have children
1267 1274 changesets. They are where development generally takes place and
1268 1275 are the usual targets for update and merge operations.
1269 1276 """
1270 1277 heads = repo.changelog.heads()
1271 1278 br = None
1272 1279 if opts['branches']:
1273 1280 br = repo.branchlookup(heads)
1274 1281 for n in repo.changelog.heads():
1275 1282 show_changeset(ui, repo, changenode=n, brinfo=br)
1276 1283
1277 1284 def identify(ui, repo):
1278 1285 """print information about the working copy
1279 1286
1280 1287 Print a short summary of the current state of the repo.
1281 1288
1282 1289 This summary identifies the repository state using one or two parent
1283 1290 hash identifiers, followed by a "+" if there are uncommitted changes
1284 1291 in the working directory, followed by a list of tags for this revision.
1285 1292 """
1286 1293 parents = [p for p in repo.dirstate.parents() if p != nullid]
1287 1294 if not parents:
1288 1295 ui.write(_("unknown\n"))
1289 1296 return
1290 1297
1291 1298 hexfunc = ui.verbose and hex or short
1292 1299 (c, a, d, u) = repo.changes()
1293 1300 output = ["%s%s" % ('+'.join([hexfunc(parent) for parent in parents]),
1294 1301 (c or a or d) and "+" or "")]
1295 1302
1296 1303 if not ui.quiet:
1297 1304 # multiple tags for a single parent separated by '/'
1298 1305 parenttags = ['/'.join(tags)
1299 1306 for tags in map(repo.nodetags, parents) if tags]
1300 1307 # tags for multiple parents separated by ' + '
1301 1308 if parenttags:
1302 1309 output.append(' + '.join(parenttags))
1303 1310
1304 1311 ui.write("%s\n" % ' '.join(output))
1305 1312
1306 1313 def import_(ui, repo, patch1, *patches, **opts):
1307 1314 """import an ordered set of patches
1308 1315
1309 1316 Import a list of patches and commit them individually.
1310 1317
1311 1318 If there are outstanding changes in the working directory, import
1312 1319 will abort unless given the -f flag.
1313 1320
1314 1321 If a patch looks like a mail message (its first line starts with
1315 1322 "From " or looks like an RFC822 header), it will not be applied
1316 1323 unless the -f option is used. The importer neither parses nor
1317 1324 discards mail headers, so use -f only to override the "mailness"
1318 1325 safety check, not to import a real mail message.
1319 1326 """
1320 1327 patches = (patch1,) + patches
1321 1328
1322 1329 if not opts['force']:
1323 1330 (c, a, d, u) = repo.changes()
1324 1331 if c or a or d:
1325 1332 raise util.Abort(_("outstanding uncommitted changes"))
1326 1333
1327 1334 d = opts["base"]
1328 1335 strip = opts["strip"]
1329 1336
1330 1337 mailre = re.compile(r'(?:From |[\w-]+:)')
1331 1338
1332 1339 # attempt to detect the start of a patch
1333 1340 # (this heuristic is borrowed from quilt)
1334 1341 diffre = re.compile(r'(?:Index:[ \t]|diff[ \t]|RCS file: |' +
1335 1342 'retrieving revision [0-9]+(\.[0-9]+)*$|' +
1336 1343 '(---|\*\*\*)[ \t])')
1337 1344
1338 1345 for patch in patches:
1339 1346 ui.status(_("applying %s\n") % patch)
1340 1347 pf = os.path.join(d, patch)
1341 1348
1342 1349 message = []
1343 1350 user = None
1344 1351 hgpatch = False
1345 1352 for line in file(pf):
1346 1353 line = line.rstrip()
1347 1354 if (not message and not hgpatch and
1348 1355 mailre.match(line) and not opts['force']):
1349 1356 if len(line) > 35: line = line[:32] + '...'
1350 1357 raise util.Abort(_('first line looks like a '
1351 1358 'mail header: ') + line)
1352 1359 if diffre.match(line):
1353 1360 break
1354 1361 elif hgpatch:
1355 1362 # parse values when importing the result of an hg export
1356 1363 if line.startswith("# User "):
1357 1364 user = line[7:]
1358 1365 ui.debug(_('User: %s\n') % user)
1359 1366 elif not line.startswith("# ") and line:
1360 1367 message.append(line)
1361 1368 hgpatch = False
1362 1369 elif line == '# HG changeset patch':
1363 1370 hgpatch = True
1364 1371 message = [] # We may have collected garbage
1365 1372 else:
1366 1373 message.append(line)
1367 1374
1368 1375 # make sure message isn't empty
1369 1376 if not message:
1370 1377 message = _("imported patch %s\n") % patch
1371 1378 else:
1372 1379 message = "%s\n" % '\n'.join(message)
1373 1380 ui.debug(_('message:\n%s\n') % message)
1374 1381
1375 1382 files = util.patch(strip, pf, ui)
1376 1383
1377 1384 if len(files) > 0:
1378 1385 addremove(ui, repo, *files)
1379 1386 repo.commit(files, message, user)
1380 1387
1381 1388 def incoming(ui, repo, source="default", **opts):
1382 1389 """show new changesets found in source
1383 1390
1384 1391 Show new changesets found in the specified repo or the default
1385 1392 pull repo. These are the changesets that would be pulled if a pull
1386 1393 was requested.
1387 1394
1388 1395 Currently only local repositories are supported.
1389 1396 """
1390 1397 source = ui.expandpath(source, repo.root)
1391 1398 other = hg.repository(ui, source)
1392 1399 if not other.local():
1393 1400 raise util.Abort(_("incoming doesn't work for remote repositories yet"))
1394 1401 o = repo.findincoming(other)
1395 1402 if not o:
1396 1403 return
1397 1404 o = other.changelog.nodesbetween(o)[0]
1398 1405 if opts['newest_first']:
1399 1406 o.reverse()
1400 1407 for n in o:
1401 1408 parents = [p for p in other.changelog.parents(n) if p != nullid]
1402 1409 if opts['no_merges'] and len(parents) == 2:
1403 1410 continue
1404 1411 show_changeset(ui, other, changenode=n)
1405 1412 if opts['patch']:
1406 1413 prev = (parents and parents[0]) or nullid
1407 1414 dodiff(ui, ui, other, prev, n)
1408 1415 ui.write("\n")
1409 1416
1410 1417 def init(ui, dest="."):
1411 1418 """create a new repository in the given directory
1412 1419
1413 1420 Initialize a new repository in the given directory. If the given
1414 1421 directory does not exist, it is created.
1415 1422
1416 1423 If no directory is given, the current directory is used.
1417 1424 """
1418 1425 if not os.path.exists(dest):
1419 1426 os.mkdir(dest)
1420 1427 hg.repository(ui, dest, create=1)
1421 1428
1422 1429 def locate(ui, repo, *pats, **opts):
1423 1430 """locate files matching specific patterns
1424 1431
1425 1432 Print all files under Mercurial control whose names match the
1426 1433 given patterns.
1427 1434
1428 1435 This command searches the current directory and its
1429 1436 subdirectories. To search an entire repository, move to the root
1430 1437 of the repository.
1431 1438
1432 1439 If no patterns are given to match, this command prints all file
1433 1440 names.
1434 1441
1435 1442 If you want to feed the output of this command into the "xargs"
1436 1443 command, use the "-0" option to both this command and "xargs".
1437 1444 This will avoid the problem of "xargs" treating single filenames
1438 1445 that contain white space as multiple filenames.
1439 1446 """
1440 1447 end = opts['print0'] and '\0' or '\n'
1441 1448
1442 1449 for src, abs, rel, exact in walk(repo, pats, opts, '(?:.*/|)'):
1443 1450 if repo.dirstate.state(abs) == '?':
1444 1451 continue
1445 1452 if opts['fullpath']:
1446 1453 ui.write(os.path.join(repo.root, abs), end)
1447 1454 else:
1448 1455 ui.write(rel, end)
1449 1456
1450 1457 def log(ui, repo, *pats, **opts):
1451 1458 """show revision history of entire repository or files
1452 1459
1453 1460 Print the revision history of the specified files or the entire project.
1454 1461
1455 1462 By default this command outputs: changeset id and hash, tags,
1456 1463 parents, user, date and time, and a summary for each commit. The
1457 1464 -v switch adds some more detail, such as changed files, manifest
1458 1465 hashes or message signatures.
1459 1466 """
1460 1467 class dui:
1461 1468 # Implement and delegate some ui protocol. Save hunks of
1462 1469 # output for later display in the desired order.
1463 1470 def __init__(self, ui):
1464 1471 self.ui = ui
1465 1472 self.hunk = {}
1466 1473 def bump(self, rev):
1467 1474 self.rev = rev
1468 1475 self.hunk[rev] = []
1469 1476 def note(self, *args):
1470 1477 if self.verbose:
1471 1478 self.write(*args)
1472 1479 def status(self, *args):
1473 1480 if not self.quiet:
1474 1481 self.write(*args)
1475 1482 def write(self, *args):
1476 1483 self.hunk[self.rev].append(args)
1477 1484 def debug(self, *args):
1478 1485 if self.debugflag:
1479 1486 self.write(*args)
1480 1487 def __getattr__(self, key):
1481 1488 return getattr(self.ui, key)
1482 1489 cwd = repo.getcwd()
1483 1490 if not pats and cwd:
1484 1491 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
1485 1492 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
1486 1493 changeiter, getchange = walkchangerevs(ui, repo, (pats and cwd) or '',
1487 1494 pats, opts)
1488 1495 for st, rev, fns in changeiter:
1489 1496 if st == 'window':
1490 1497 du = dui(ui)
1491 1498 elif st == 'add':
1492 1499 du.bump(rev)
1493 1500 changenode = repo.changelog.node(rev)
1494 1501 parents = [p for p in repo.changelog.parents(changenode)
1495 1502 if p != nullid]
1496 1503 if opts['no_merges'] and len(parents) == 2:
1497 1504 continue
1498 1505 if opts['only_merges'] and len(parents) != 2:
1499 1506 continue
1500 1507
1501 1508 br = None
1502 1509 if opts['keyword']:
1503 1510 changes = repo.changelog.read(repo.changelog.node(rev))
1504 1511 miss = 0
1505 1512 for k in [kw.lower() for kw in opts['keyword']]:
1506 1513 if not (k in changes[1].lower() or
1507 1514 k in changes[4].lower() or
1508 1515 k in " ".join(changes[3][:20]).lower()):
1509 1516 miss = 1
1510 1517 break
1511 1518 if miss:
1512 1519 continue
1513 1520
1514 1521 if opts['branch']:
1515 1522 br = repo.branchlookup([repo.changelog.node(rev)])
1516 1523
1517 1524 show_changeset(du, repo, rev, brinfo=br)
1518 1525 if opts['patch']:
1519 1526 prev = (parents and parents[0]) or nullid
1520 1527 dodiff(du, du, repo, prev, changenode, fns)
1521 1528 du.write("\n\n")
1522 1529 elif st == 'iter':
1523 1530 for args in du.hunk[rev]:
1524 1531 ui.write(*args)
1525 1532
1526 1533 def manifest(ui, repo, rev=None):
1527 1534 """output the latest or given revision of the project manifest
1528 1535
1529 1536 Print a list of version controlled files for the given revision.
1530 1537
1531 1538 The manifest is the list of files being version controlled. If no revision
1532 1539 is given then the tip is used.
1533 1540 """
1534 1541 if rev:
1535 1542 try:
1536 1543 # assume all revision numbers are for changesets
1537 1544 n = repo.lookup(rev)
1538 1545 change = repo.changelog.read(n)
1539 1546 n = change[0]
1540 1547 except hg.RepoError:
1541 1548 n = repo.manifest.lookup(rev)
1542 1549 else:
1543 1550 n = repo.manifest.tip()
1544 1551 m = repo.manifest.read(n)
1545 1552 mf = repo.manifest.readflags(n)
1546 1553 files = m.keys()
1547 1554 files.sort()
1548 1555
1549 1556 for f in files:
1550 1557 ui.write("%40s %3s %s\n" % (hex(m[f]), mf[f] and "755" or "644", f))
1551 1558
1552 1559 def outgoing(ui, repo, dest="default-push", **opts):
1553 1560 """show changesets not found in destination
1554 1561
1555 1562 Show changesets not found in the specified destination repo or the
1556 1563 default push repo. These are the changesets that would be pushed
1557 1564 if a push was requested.
1558 1565 """
1559 1566 dest = ui.expandpath(dest, repo.root)
1560 1567 other = hg.repository(ui, dest)
1561 1568 o = repo.findoutgoing(other)
1562 1569 o = repo.changelog.nodesbetween(o)[0]
1563 1570 if opts['newest_first']:
1564 1571 o.reverse()
1565 1572 for n in o:
1566 1573 parents = [p for p in repo.changelog.parents(n) if p != nullid]
1567 1574 if opts['no_merges'] and len(parents) == 2:
1568 1575 continue
1569 1576 show_changeset(ui, repo, changenode=n)
1570 1577 if opts['patch']:
1571 1578 prev = (parents and parents[0]) or nullid
1572 1579 dodiff(ui, ui, repo, prev, n)
1573 1580 ui.write("\n")
1574 1581
1575 1582 def parents(ui, repo, rev=None):
1576 1583 """show the parents of the working dir or revision
1577 1584
1578 1585 Print the working directory's parent revisions.
1579 1586 """
1580 1587 if rev:
1581 1588 p = repo.changelog.parents(repo.lookup(rev))
1582 1589 else:
1583 1590 p = repo.dirstate.parents()
1584 1591
1585 1592 for n in p:
1586 1593 if n != nullid:
1587 1594 show_changeset(ui, repo, changenode=n)
1588 1595
1589 1596 def paths(ui, search=None):
1590 1597 """show definition of symbolic path names
1591 1598
1592 1599 Show definition of symbolic path name NAME. If no name is given, show
1593 1600 definition of available names.
1594 1601
1595 1602 Path names are defined in the [paths] section of /etc/mercurial/hgrc
1596 1603 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
1597 1604 """
1598 1605 try:
1599 1606 repo = hg.repository(ui=ui)
1600 1607 except hg.RepoError:
1601 1608 pass
1602 1609
1603 1610 if search:
1604 1611 for name, path in ui.configitems("paths"):
1605 1612 if name == search:
1606 1613 ui.write("%s\n" % path)
1607 1614 return
1608 1615 ui.warn(_("not found!\n"))
1609 1616 return 1
1610 1617 else:
1611 1618 for name, path in ui.configitems("paths"):
1612 1619 ui.write("%s = %s\n" % (name, path))
1613 1620
1614 1621 def pull(ui, repo, source="default", **opts):
1615 1622 """pull changes from the specified source
1616 1623
1617 1624 Pull changes from a remote repository to a local one.
1618 1625
1619 1626 This finds all changes from the repository at the specified path
1620 1627 or URL and adds them to the local repository. By default, this
1621 1628 does not update the copy of the project in the working directory.
1622 1629
1623 1630 Valid URLs are of the form:
1624 1631
1625 1632 local/filesystem/path
1626 1633 http://[user@]host[:port][/path]
1627 1634 https://[user@]host[:port][/path]
1628 1635 ssh://[user@]host[:port][/path]
1629 1636
1630 1637 SSH requires an accessible shell account on the destination machine
1631 1638 and a copy of hg in the remote path. With SSH, paths are relative
1632 1639 to the remote user's home directory by default; use two slashes at
1633 1640 the start of a path to specify it as relative to the filesystem root.
1634 1641 """
1635 1642 source = ui.expandpath(source, repo.root)
1636 1643 ui.status(_('pulling from %s\n') % (source))
1637 1644
1638 1645 if opts['ssh']:
1639 1646 ui.setconfig("ui", "ssh", opts['ssh'])
1640 1647 if opts['remotecmd']:
1641 1648 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
1642 1649
1643 1650 other = hg.repository(ui, source)
1644 1651 revs = None
1645 1652 if opts['rev'] and not other.local():
1646 1653 raise util.Abort("pull -r doesn't work for remote repositories yet")
1647 1654 elif opts['rev']:
1648 1655 revs = [other.lookup(rev) for rev in opts['rev']]
1649 1656 r = repo.pull(other, heads=revs)
1650 1657 if not r:
1651 1658 if opts['update']:
1652 1659 return update(ui, repo)
1653 1660 else:
1654 1661 ui.status(_("(run 'hg update' to get a working copy)\n"))
1655 1662
1656 1663 return r
1657 1664
1658 1665 def push(ui, repo, dest="default-push", force=False, ssh=None, remotecmd=None):
1659 1666 """push changes to the specified destination
1660 1667
1661 1668 Push changes from the local repository to the given destination.
1662 1669
1663 1670 This is the symmetrical operation for pull. It helps to move
1664 1671 changes from the current repository to a different one. If the
1665 1672 destination is local this is identical to a pull in that directory
1666 1673 from the current one.
1667 1674
1668 1675 By default, push will refuse to run if it detects the result would
1669 1676 increase the number of remote heads. This generally indicates the
1670 1677 the client has forgotten to sync and merge before pushing.
1671 1678
1672 1679 Valid URLs are of the form:
1673 1680
1674 1681 local/filesystem/path
1675 1682 ssh://[user@]host[:port][/path]
1676 1683
1677 1684 SSH requires an accessible shell account on the destination
1678 1685 machine and a copy of hg in the remote path.
1679 1686 """
1680 1687 dest = ui.expandpath(dest, repo.root)
1681 1688 ui.status('pushing to %s\n' % (dest))
1682 1689
1683 1690 if ssh:
1684 1691 ui.setconfig("ui", "ssh", ssh)
1685 1692 if remotecmd:
1686 1693 ui.setconfig("ui", "remotecmd", remotecmd)
1687 1694
1688 1695 other = hg.repository(ui, dest)
1689 1696 r = repo.push(other, force)
1690 1697 return r
1691 1698
1692 1699 def rawcommit(ui, repo, *flist, **rc):
1693 1700 """raw commit interface
1694 1701
1695 1702 Lowlevel commit, for use in helper scripts.
1696 1703
1697 1704 This command is not intended to be used by normal users, as it is
1698 1705 primarily useful for importing from other SCMs.
1699 1706 """
1700 1707 message = rc['message']
1701 1708 if not message and rc['logfile']:
1702 1709 try:
1703 1710 message = open(rc['logfile']).read()
1704 1711 except IOError:
1705 1712 pass
1706 1713 if not message and not rc['logfile']:
1707 1714 raise util.Abort(_("missing commit message"))
1708 1715
1709 1716 files = relpath(repo, list(flist))
1710 1717 if rc['files']:
1711 1718 files += open(rc['files']).read().splitlines()
1712 1719
1713 1720 rc['parent'] = map(repo.lookup, rc['parent'])
1714 1721
1715 1722 try:
1716 1723 repo.rawcommit(files, message, rc['user'], rc['date'], *rc['parent'])
1717 1724 except ValueError, inst:
1718 1725 raise util.Abort(str(inst))
1719 1726
1720 1727 def recover(ui, repo):
1721 1728 """roll back an interrupted transaction
1722 1729
1723 1730 Recover from an interrupted commit or pull.
1724 1731
1725 1732 This command tries to fix the repository status after an interrupted
1726 1733 operation. It should only be necessary when Mercurial suggests it.
1727 1734 """
1728 1735 repo.recover()
1729 1736
1730 1737 def remove(ui, repo, pat, *pats, **opts):
1731 1738 """remove the specified files on the next commit
1732 1739
1733 1740 Schedule the indicated files for removal from the repository.
1734 1741
1735 1742 This command schedules the files to be removed at the next commit.
1736 1743 This only removes files from the current branch, not from the
1737 1744 entire project history. If the files still exist in the working
1738 1745 directory, they will be deleted from it.
1739 1746 """
1740 1747 names = []
1741 1748 def okaytoremove(abs, rel, exact):
1742 1749 c, a, d, u = repo.changes(files = [abs])
1743 1750 reason = None
1744 1751 if c: reason = _('is modified')
1745 1752 elif a: reason = _('has been marked for add')
1746 1753 elif u: reason = _('is not managed')
1747 1754 if reason:
1748 1755 if exact: ui.warn(_('not removing %s: file %s\n') % (rel, reason))
1749 1756 else:
1750 1757 return True
1751 1758 for src, abs, rel, exact in walk(repo, (pat,) + pats, opts):
1752 1759 if okaytoremove(abs, rel, exact):
1753 1760 if ui.verbose or not exact: ui.status(_('removing %s\n') % rel)
1754 1761 names.append(abs)
1755 1762 repo.remove(names, unlink=True)
1756 1763
1757 1764 def rename(ui, repo, *pats, **opts):
1758 1765 """rename files; equivalent of copy + remove
1759 1766
1760 1767 Mark dest as copies of sources; mark sources for deletion. If
1761 1768 dest is a directory, copies are put in that directory. If dest is
1762 1769 a file, there can only be one source.
1763 1770
1764 1771 By default, this command copies the contents of files as they
1765 1772 stand in the working directory. If invoked with --after, the
1766 1773 operation is recorded, but no copying is performed.
1767 1774
1768 1775 This command takes effect in the next commit.
1769 1776
1770 1777 NOTE: This command should be treated as experimental. While it
1771 1778 should properly record rename files, this information is not yet
1772 1779 fully used by merge, nor fully reported by log.
1773 1780 """
1774 1781 errs, copied = docopy(ui, repo, pats, opts)
1775 1782 names = []
1776 1783 for abs, rel, exact in copied:
1777 1784 if ui.verbose or not exact: ui.status(_('removing %s\n') % rel)
1778 1785 names.append(abs)
1779 1786 repo.remove(names, unlink=True)
1780 1787 return errs
1781 1788
1782 1789 def revert(ui, repo, *pats, **opts):
1783 1790 """revert modified files or dirs back to their unmodified states
1784 1791
1785 1792 Revert any uncommitted modifications made to the named files or
1786 1793 directories. This restores the contents of the affected files to
1787 1794 an unmodified state.
1788 1795
1789 1796 If a file has been deleted, it is recreated. If the executable
1790 1797 mode of a file was changed, it is reset.
1791 1798
1792 1799 If names are given, all files matching the names are reverted.
1793 1800
1794 1801 If no names are given, all files in the current directory and
1795 1802 its subdirectories are reverted.
1796 1803 """
1797 1804 node = opts['rev'] and repo.lookup(opts['rev']) or \
1798 1805 repo.dirstate.parents()[0]
1799 1806
1800 1807 files, choose, anypats = matchpats(repo, repo.getcwd(), pats, opts)
1801 1808 (c, a, d, u) = repo.changes(match=choose)
1802 1809 repo.forget(a)
1803 1810 repo.undelete(d)
1804 1811
1805 1812 return repo.update(node, False, True, choose, False)
1806 1813
1807 1814 def root(ui, repo):
1808 1815 """print the root (top) of the current working dir
1809 1816
1810 1817 Print the root directory of the current repository.
1811 1818 """
1812 1819 ui.write(repo.root + "\n")
1813 1820
1814 1821 def serve(ui, repo, **opts):
1815 1822 """export the repository via HTTP
1816 1823
1817 1824 Start a local HTTP repository browser and pull server.
1818 1825
1819 1826 By default, the server logs accesses to stdout and errors to
1820 1827 stderr. Use the "-A" and "-E" options to log to files.
1821 1828 """
1822 1829
1823 1830 if opts["stdio"]:
1824 1831 fin, fout = sys.stdin, sys.stdout
1825 1832 sys.stdout = sys.stderr
1826 1833
1827 1834 # Prevent insertion/deletion of CRs
1828 1835 util.set_binary(fin)
1829 1836 util.set_binary(fout)
1830 1837
1831 1838 def getarg():
1832 1839 argline = fin.readline()[:-1]
1833 1840 arg, l = argline.split()
1834 1841 val = fin.read(int(l))
1835 1842 return arg, val
1836 1843 def respond(v):
1837 1844 fout.write("%d\n" % len(v))
1838 1845 fout.write(v)
1839 1846 fout.flush()
1840 1847
1841 1848 lock = None
1842 1849
1843 1850 while 1:
1844 1851 cmd = fin.readline()[:-1]
1845 1852 if cmd == '':
1846 1853 return
1847 1854 if cmd == "heads":
1848 1855 h = repo.heads()
1849 1856 respond(" ".join(map(hex, h)) + "\n")
1850 1857 if cmd == "lock":
1851 1858 lock = repo.lock()
1852 1859 respond("")
1853 1860 if cmd == "unlock":
1854 1861 if lock:
1855 1862 lock.release()
1856 1863 lock = None
1857 1864 respond("")
1858 1865 elif cmd == "branches":
1859 1866 arg, nodes = getarg()
1860 1867 nodes = map(bin, nodes.split(" "))
1861 1868 r = []
1862 1869 for b in repo.branches(nodes):
1863 1870 r.append(" ".join(map(hex, b)) + "\n")
1864 1871 respond("".join(r))
1865 1872 elif cmd == "between":
1866 1873 arg, pairs = getarg()
1867 1874 pairs = [map(bin, p.split("-")) for p in pairs.split(" ")]
1868 1875 r = []
1869 1876 for b in repo.between(pairs):
1870 1877 r.append(" ".join(map(hex, b)) + "\n")
1871 1878 respond("".join(r))
1872 1879 elif cmd == "changegroup":
1873 1880 nodes = []
1874 1881 arg, roots = getarg()
1875 1882 nodes = map(bin, roots.split(" "))
1876 1883
1877 1884 cg = repo.changegroup(nodes)
1878 1885 while 1:
1879 1886 d = cg.read(4096)
1880 1887 if not d:
1881 1888 break
1882 1889 fout.write(d)
1883 1890
1884 1891 fout.flush()
1885 1892
1886 1893 elif cmd == "addchangegroup":
1887 1894 if not lock:
1888 1895 respond("not locked")
1889 1896 continue
1890 1897 respond("")
1891 1898
1892 1899 r = repo.addchangegroup(fin)
1893 1900 respond("")
1894 1901
1895 1902 optlist = "name templates style address port ipv6 accesslog errorlog"
1896 1903 for o in optlist.split():
1897 1904 if opts[o]:
1898 1905 ui.setconfig("web", o, opts[o])
1899 1906
1900 1907 try:
1901 1908 httpd = hgweb.create_server(repo)
1902 1909 except socket.error, inst:
1903 1910 raise util.Abort('cannot start server: ' + inst.args[1])
1904 1911
1905 1912 if ui.verbose:
1906 1913 addr, port = httpd.socket.getsockname()
1907 1914 if addr == '0.0.0.0':
1908 1915 addr = socket.gethostname()
1909 1916 else:
1910 1917 try:
1911 1918 addr = socket.gethostbyaddr(addr)[0]
1912 1919 except socket.error:
1913 1920 pass
1914 1921 if port != 80:
1915 1922 ui.status(_('listening at http://%s:%d/\n') % (addr, port))
1916 1923 else:
1917 1924 ui.status(_('listening at http://%s/\n') % addr)
1918 1925 httpd.serve_forever()
1919 1926
1920 1927 def status(ui, repo, *pats, **opts):
1921 1928 """show changed files in the working directory
1922 1929
1923 1930 Show changed files in the working directory. If no names are
1924 1931 given, all files are shown. Otherwise, only files matching the
1925 1932 given names are shown.
1926 1933
1927 1934 The codes used to show the status of files are:
1928 1935 M = modified
1929 1936 A = added
1930 1937 R = removed
1931 1938 ? = not tracked
1932 1939 """
1933 1940
1934 1941 cwd = repo.getcwd()
1935 1942 files, matchfn, anypats = matchpats(repo, cwd, pats, opts)
1936 1943 (c, a, d, u) = [[util.pathto(cwd, x) for x in n]
1937 1944 for n in repo.changes(files=files, match=matchfn)]
1938 1945
1939 1946 changetypes = [(_('modified'), 'M', c),
1940 1947 (_('added'), 'A', a),
1941 1948 (_('removed'), 'R', d),
1942 1949 (_('unknown'), '?', u)]
1943 1950
1944 1951 end = opts['print0'] and '\0' or '\n'
1945 1952
1946 1953 for opt, char, changes in ([ct for ct in changetypes if opts[ct[0]]]
1947 1954 or changetypes):
1948 1955 if opts['no_status']:
1949 1956 format = "%%s%s" % end
1950 1957 else:
1951 1958 format = "%s %%s%s" % (char, end);
1952 1959
1953 1960 for f in changes:
1954 1961 ui.write(format % f)
1955 1962
1956 1963 def tag(ui, repo, name, rev=None, **opts):
1957 1964 """add a tag for the current tip or a given revision
1958 1965
1959 1966 Name a particular revision using <name>.
1960 1967
1961 1968 Tags are used to name particular revisions of the repository and are
1962 1969 very useful to compare different revision, to go back to significant
1963 1970 earlier versions or to mark branch points as releases, etc.
1964 1971
1965 1972 If no revision is given, the tip is used.
1966 1973
1967 1974 To facilitate version control, distribution, and merging of tags,
1968 1975 they are stored as a file named ".hgtags" which is managed
1969 1976 similarly to other project files and can be hand-edited if
1970 1977 necessary.
1971 1978 """
1972 1979 if name == "tip":
1973 1980 raise util.Abort(_("the name 'tip' is reserved"))
1974 1981 if 'rev' in opts:
1975 1982 rev = opts['rev']
1976 1983 if rev:
1977 1984 r = hex(repo.lookup(rev))
1978 1985 else:
1979 1986 r = hex(repo.changelog.tip())
1980 1987
1981 1988 if name.find(revrangesep) >= 0:
1982 1989 raise util.Abort(_("'%s' cannot be used in a tag name") % revrangesep)
1983 1990
1984 1991 if opts['local']:
1985 1992 repo.opener("localtags", "a").write("%s %s\n" % (r, name))
1986 1993 return
1987 1994
1988 1995 (c, a, d, u) = repo.changes()
1989 1996 for x in (c, a, d, u):
1990 1997 if ".hgtags" in x:
1991 1998 raise util.Abort(_("working copy of .hgtags is changed "
1992 1999 "(please commit .hgtags manually)"))
1993 2000
1994 2001 repo.wfile(".hgtags", "ab").write("%s %s\n" % (r, name))
1995 2002 if repo.dirstate.state(".hgtags") == '?':
1996 2003 repo.add([".hgtags"])
1997 2004
1998 2005 message = (opts['message'] or
1999 2006 _("Added tag %s for changeset %s") % (name, r))
2000 2007 try:
2001 2008 repo.commit([".hgtags"], message, opts['user'], opts['date'])
2002 2009 except ValueError, inst:
2003 2010 raise util.Abort(str(inst))
2004 2011
2005 2012 def tags(ui, repo):
2006 2013 """list repository tags
2007 2014
2008 2015 List the repository tags.
2009 2016
2010 2017 This lists both regular and local tags.
2011 2018 """
2012 2019
2013 2020 l = repo.tagslist()
2014 2021 l.reverse()
2015 2022 for t, n in l:
2016 2023 try:
2017 2024 r = "%5d:%s" % (repo.changelog.rev(n), hex(n))
2018 2025 except KeyError:
2019 2026 r = " ?:?"
2020 2027 ui.write("%-30s %s\n" % (t, r))
2021 2028
2022 2029 def tip(ui, repo):
2023 2030 """show the tip revision
2024 2031
2025 2032 Show the tip revision.
2026 2033 """
2027 2034 n = repo.changelog.tip()
2028 2035 show_changeset(ui, repo, changenode=n)
2029 2036
2030 2037 def unbundle(ui, repo, fname):
2031 2038 """apply a changegroup file
2032 2039
2033 2040 Apply a compressed changegroup file generated by the bundle
2034 2041 command.
2035 2042 """
2036 2043 f = urllib.urlopen(fname)
2037 2044
2038 2045 if f.read(4) != "HG10":
2039 2046 raise util.Abort(_("%s: not a Mercurial bundle file") % fname)
2040 2047
2041 2048 def bzgenerator(f):
2042 2049 zd = bz2.BZ2Decompressor()
2043 2050 for chunk in f:
2044 2051 yield zd.decompress(chunk)
2045 2052
2046 2053 bzgen = bzgenerator(util.filechunkiter(f, 4096))
2047 2054 repo.addchangegroup(util.chunkbuffer(bzgen))
2048 2055
2049 2056 def undo(ui, repo):
2050 2057 """undo the last commit or pull
2051 2058
2052 2059 Roll back the last pull or commit transaction on the
2053 2060 repository, restoring the project to its earlier state.
2054 2061
2055 2062 This command should be used with care. There is only one level of
2056 2063 undo and there is no redo.
2057 2064
2058 2065 This command is not intended for use on public repositories. Once
2059 2066 a change is visible for pull by other users, undoing it locally is
2060 2067 ineffective.
2061 2068 """
2062 2069 repo.undo()
2063 2070
2064 2071 def update(ui, repo, node=None, merge=False, clean=False, branch=None):
2065 2072 """update or merge working directory
2066 2073
2067 2074 Update the working directory to the specified revision.
2068 2075
2069 2076 If there are no outstanding changes in the working directory and
2070 2077 there is a linear relationship between the current version and the
2071 2078 requested version, the result is the requested version.
2072 2079
2073 2080 Otherwise the result is a merge between the contents of the
2074 2081 current working directory and the requested version. Files that
2075 2082 changed between either parent are marked as changed for the next
2076 2083 commit and a commit must be performed before any further updates
2077 2084 are allowed.
2078 2085
2079 2086 By default, update will refuse to run if doing so would require
2080 2087 merging or discarding local changes.
2081 2088 """
2082 2089 if branch:
2083 2090 br = repo.branchlookup(branch=branch)
2084 2091 found = []
2085 2092 for x in br:
2086 2093 if branch in br[x]:
2087 2094 found.append(x)
2088 2095 if len(found) > 1:
2089 2096 ui.warn(_("Found multiple heads for %s\n") % branch)
2090 2097 for x in found:
2091 2098 show_changeset(ui, repo, changenode=x, brinfo=br)
2092 2099 return 1
2093 2100 if len(found) == 1:
2094 2101 node = found[0]
2095 2102 ui.warn(_("Using head %s for branch %s\n") % (short(node), branch))
2096 2103 else:
2097 2104 ui.warn(_("branch %s not found\n") % (branch))
2098 2105 return 1
2099 2106 else:
2100 2107 node = node and repo.lookup(node) or repo.changelog.tip()
2101 2108 return repo.update(node, allow=merge, force=clean)
2102 2109
2103 2110 def verify(ui, repo):
2104 2111 """verify the integrity of the repository
2105 2112
2106 2113 Verify the integrity of the current repository.
2107 2114
2108 2115 This will perform an extensive check of the repository's
2109 2116 integrity, validating the hashes and checksums of each entry in
2110 2117 the changelog, manifest, and tracked files, as well as the
2111 2118 integrity of their crosslinks and indices.
2112 2119 """
2113 2120 return repo.verify()
2114 2121
2115 2122 # Command options and aliases are listed here, alphabetically
2116 2123
2117 2124 table = {
2118 2125 "^add":
2119 2126 (add,
2120 2127 [('I', 'include', [], _('include names matching the given patterns')),
2121 2128 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2122 2129 "hg add [OPTION]... [FILE]..."),
2123 2130 "addremove":
2124 2131 (addremove,
2125 2132 [('I', 'include', [], _('include names matching the given patterns')),
2126 2133 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2127 2134 "hg addremove [OPTION]... [FILE]..."),
2128 2135 "^annotate":
2129 2136 (annotate,
2130 2137 [('r', 'rev', '', _('annotate the specified revision')),
2131 2138 ('a', 'text', None, _('treat all files as text')),
2132 2139 ('u', 'user', None, _('list the author')),
2133 2140 ('n', 'number', None, _('list the revision number (default)')),
2134 2141 ('c', 'changeset', None, _('list the changeset')),
2135 2142 ('I', 'include', [], _('include names matching the given patterns')),
2136 2143 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2137 2144 _('hg annotate [OPTION]... FILE...')),
2138 2145 "bundle":
2139 2146 (bundle,
2140 2147 [],
2141 2148 _('hg bundle FILE DEST')),
2142 2149 "cat":
2143 2150 (cat,
2144 2151 [('I', 'include', [], _('include names matching the given patterns')),
2145 2152 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2146 2153 ('o', 'output', "", _('print output to file with formatted name')),
2147 2154 ('r', 'rev', '', _('print the given revision'))],
2148 2155 _('hg cat [OPTION]... FILE...')),
2149 2156 "^clone":
2150 2157 (clone,
2151 2158 [('U', 'noupdate', None, _('do not update the new working directory')),
2152 2159 ('e', 'ssh', "", _('specify ssh command to use')),
2153 2160 ('', 'pull', None, _('use pull protocol to copy metadata')),
2154 2161 ('r', 'rev', [], _('a changeset you would like to have after cloning')),
2155 2162 ('', 'remotecmd', "", _('specify hg command to run on the remote side'))],
2156 2163 _('hg clone [OPTION]... SOURCE [DEST]')),
2157 2164 "^commit|ci":
2158 2165 (commit,
2159 2166 [('A', 'addremove', None, _('run addremove during commit')),
2160 2167 ('I', 'include', [], _('include names matching the given patterns')),
2161 2168 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2162 2169 ('m', 'message', "", _('use <text> as commit message')),
2163 2170 ('l', 'logfile', "", _('read the commit message from <file>')),
2164 2171 ('d', 'date', "", _('record datecode as commit date')),
2165 2172 ('u', 'user', "", _('record user as commiter'))],
2166 2173 _('hg commit [OPTION]... [FILE]...')),
2167 2174 "copy|cp": (copy,
2168 2175 [('I', 'include', [], _('include names matching the given patterns')),
2169 2176 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2170 2177 ('A', 'after', None, _('record a copy that has already occurred')),
2171 2178 ('f', 'force', None, _('forcibly copy over an existing managed file'))],
2172 2179 _('hg copy [OPTION]... [SOURCE]... DEST')),
2173 2180 "debugancestor": (debugancestor, [], _('debugancestor INDEX REV1 REV2')),
2174 2181 "debugcheckstate": (debugcheckstate, [], _('debugcheckstate')),
2175 2182 "debugconfig": (debugconfig, [], _('debugconfig')),
2176 2183 "debugsetparents": (debugsetparents, [], _('debugsetparents REV1 [REV2]')),
2177 2184 "debugstate": (debugstate, [], _('debugstate')),
2178 2185 "debugdata": (debugdata, [], _('debugdata FILE REV')),
2179 2186 "debugindex": (debugindex, [], _('debugindex FILE')),
2180 2187 "debugindexdot": (debugindexdot, [], _('debugindexdot FILE')),
2181 2188 "debugrename": (debugrename, [], _('debugrename FILE [REV]')),
2182 2189 "debugwalk":
2183 2190 (debugwalk,
2184 2191 [('I', 'include', [], _('include names matching the given patterns')),
2185 2192 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2186 2193 _('debugwalk [OPTION]... [FILE]...')),
2187 2194 "^diff":
2188 2195 (diff,
2189 2196 [('r', 'rev', [], _('revision')),
2190 2197 ('a', 'text', None, _('treat all files as text')),
2191 2198 ('I', 'include', [], _('include names matching the given patterns')),
2192 2199 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2193 2200 _('hg diff [-a] [-I] [-X] [-r REV1 [-r REV2]] [FILE]...')),
2194 2201 "^export":
2195 2202 (export,
2196 2203 [('o', 'output', "", _('print output to file with formatted name')),
2197 2204 ('a', 'text', None, _('treat all files as text'))],
2198 2205 "hg export [-a] [-o OUTFILE] REV..."),
2199 2206 "forget":
2200 2207 (forget,
2201 2208 [('I', 'include', [], _('include names matching the given patterns')),
2202 2209 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2203 2210 "hg forget [OPTION]... FILE..."),
2204 2211 "grep":
2205 2212 (grep,
2206 2213 [('0', 'print0', None, _('end fields with NUL')),
2207 2214 ('I', 'include', [], _('include names matching the given patterns')),
2208 2215 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2209 2216 ('', 'all', None, _('print all revisions that match')),
2210 2217 ('i', 'ignore-case', None, _('ignore case when matching')),
2211 2218 ('l', 'files-with-matches', None, _('print only filenames and revs that match')),
2212 2219 ('n', 'line-number', None, _('print matching line numbers')),
2213 2220 ('r', 'rev', [], _('search in given revision range')),
2214 2221 ('u', 'user', None, _('print user who committed change'))],
2215 2222 "hg grep [OPTION]... PATTERN [FILE]..."),
2216 2223 "heads":
2217 2224 (heads,
2218 2225 [('b', 'branches', None, _('find branch info'))],
2219 2226 _('hg heads [-b]')),
2220 2227 "help": (help_, [], _('hg help [COMMAND]')),
2221 2228 "identify|id": (identify, [], _('hg identify')),
2222 2229 "import|patch":
2223 2230 (import_,
2224 2231 [('p', 'strip', 1, _('directory strip option for patch. This has the same\n') +
2225 2232 _('meaning as the corresponding patch option')),
2226 2233 ('f', 'force', None, _('skip check for outstanding uncommitted changes')),
2227 2234 ('b', 'base', "", _('base path'))],
2228 2235 "hg import [-f] [-p NUM] [-b BASE] PATCH..."),
2229 2236 "incoming|in": (incoming,
2230 2237 [('M', 'no-merges', None, _("do not show merges")),
2231 2238 ('p', 'patch', None, _('show patch')),
2232 2239 ('n', 'newest-first', None, _('show newest record first'))],
2233 2240 _('hg incoming [-p] [-n] [-M] [SOURCE]')),
2234 2241 "^init": (init, [], _('hg init [DEST]')),
2235 2242 "locate":
2236 2243 (locate,
2237 2244 [('r', 'rev', '', _('search the repository as it stood at rev')),
2238 2245 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
2239 2246 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
2240 2247 ('I', 'include', [], _('include names matching the given patterns')),
2241 2248 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2242 2249 _('hg locate [OPTION]... [PATTERN]...')),
2243 2250 "^log|history":
2244 2251 (log,
2245 2252 [('I', 'include', [], _('include names matching the given patterns')),
2246 2253 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2247 2254 ('b', 'branch', None, _('show branches')),
2248 2255 ('k', 'keyword', [], _('search for a keyword')),
2249 2256 ('r', 'rev', [], _('show the specified revision or range')),
2250 2257 ('M', 'no-merges', None, _("do not show merges")),
2251 2258 ('m', 'only-merges', None, _("show only merges")),
2252 2259 ('p', 'patch', None, _('show patch'))],
2253 2260 _('hg log [-I] [-X] [-r REV]... [-p] [FILE]')),
2254 2261 "manifest": (manifest, [], _('hg manifest [REV]')),
2255 2262 "outgoing|out": (outgoing,
2256 2263 [('M', 'no-merges', None, _("do not show merges")),
2257 2264 ('p', 'patch', None, _('show patch')),
2258 2265 ('n', 'newest-first', None, _('show newest record first'))],
2259 2266 _('hg outgoing [-p] [-n] [-M] [DEST]')),
2260 2267 "^parents": (parents, [], _('hg parents [REV]')),
2261 2268 "paths": (paths, [], _('hg paths [NAME]')),
2262 2269 "^pull":
2263 2270 (pull,
2264 2271 [('u', 'update', None, _('update the working directory to tip after pull')),
2265 2272 ('e', 'ssh', "", _('specify ssh command to use')),
2266 2273 ('r', 'rev', [], _('a specific revision you would like to pull')),
2267 2274 ('', 'remotecmd', "", _('specify hg command to run on the remote side'))],
2268 2275 _('hg pull [-u] [-e FILE] [-r rev] [--remotecmd FILE] [SOURCE]')),
2269 2276 "^push":
2270 2277 (push,
2271 2278 [('f', 'force', None, _('force push')),
2272 2279 ('e', 'ssh', "", _('specify ssh command to use')),
2273 2280 ('', 'remotecmd', "", _('specify hg command to run on the remote side'))],
2274 2281 _('hg push [-f] [-e FILE] [--remotecmd FILE] [DEST]')),
2275 2282 "rawcommit":
2276 2283 (rawcommit,
2277 2284 [('p', 'parent', [], _('parent')),
2278 2285 ('d', 'date', "", _('date code')),
2279 2286 ('u', 'user', "", _('user')),
2280 2287 ('F', 'files', "", _('file list')),
2281 2288 ('m', 'message', "", _('commit message')),
2282 2289 ('l', 'logfile', "", _('commit message file'))],
2283 2290 _('hg rawcommit [OPTION]... [FILE]...')),
2284 2291 "recover": (recover, [], _("hg recover")),
2285 2292 "^remove|rm": (remove,
2286 2293 [('I', 'include', [], _('include names matching the given patterns')),
2287 2294 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2288 2295 _("hg remove [OPTION]... FILE...")),
2289 2296 "rename|mv": (rename,
2290 2297 [('I', 'include', [], _('include names matching the given patterns')),
2291 2298 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2292 2299 ('A', 'after', None, _('record a rename that has already occurred')),
2293 2300 ('f', 'force', None, _('forcibly copy over an existing managed file'))],
2294 2301 _('hg rename [OPTION]... [SOURCE]... DEST')),
2295 2302 "^revert":
2296 2303 (revert,
2297 2304 [('I', 'include', [], _('include names matching the given patterns')),
2298 2305 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2299 2306 ("r", "rev", "", _("revision to revert to"))],
2300 2307 _("hg revert [-n] [-r REV] [NAME]...")),
2301 2308 "root": (root, [], _("hg root")),
2302 2309 "^serve":
2303 2310 (serve,
2304 2311 [('A', 'accesslog', '', _('name of access log file to write to')),
2305 2312 ('E', 'errorlog', '', _('name of error log file to write to')),
2306 2313 ('p', 'port', 0, _('port to use (default: 8000)')),
2307 2314 ('a', 'address', '', _('address to use')),
2308 2315 ('n', 'name', "", _('name to show in web pages (default: working dir)')),
2309 2316 ('', 'stdio', None, _('for remote clients')),
2310 2317 ('t', 'templates', "", _('web templates to use')),
2311 2318 ('', 'style', "", _('template style to use')),
2312 2319 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4'))],
2313 2320 _("hg serve [OPTION]...")),
2314 2321 "^status|st":
2315 2322 (status,
2316 2323 [('m', 'modified', None, _('show only modified files')),
2317 2324 ('a', 'added', None, _('show only added files')),
2318 2325 ('r', 'removed', None, _('show only removed files')),
2319 2326 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
2320 2327 ('n', 'no-status', None, _('hide status prefix')),
2321 2328 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
2322 2329 ('I', 'include', [], _('include names matching the given patterns')),
2323 2330 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2324 2331 _("hg status [OPTION]... [FILE]...")),
2325 2332 "tag":
2326 2333 (tag,
2327 2334 [('l', 'local', None, _('make the tag local')),
2328 2335 ('m', 'message', "", _('message for tag commit log entry')),
2329 2336 ('d', 'date', "", _('record datecode as commit date')),
2330 2337 ('u', 'user', "", _('record user as commiter')),
2331 2338 ('r', 'rev', "", _('revision to tag'))],
2332 2339 _('hg tag [OPTION]... NAME [REV]')),
2333 2340 "tags": (tags, [], _('hg tags')),
2334 2341 "tip": (tip, [], _('hg tip')),
2335 2342 "unbundle":
2336 2343 (unbundle,
2337 2344 [],
2338 2345 _('hg unbundle FILE')),
2339 2346 "undo": (undo, [], _('hg undo')),
2340 2347 "^update|up|checkout|co":
2341 2348 (update,
2342 2349 [('b', 'branch', "", _('checkout the head of a specific branch')),
2343 2350 ('m', 'merge', None, _('allow merging of branches')),
2344 2351 ('C', 'clean', None, _('overwrite locally modified files'))],
2345 2352 _('hg update [-b TAG] [-m] [-C] [REV]')),
2346 2353 "verify": (verify, [], _('hg verify')),
2347 2354 "version": (show_version, [], _('hg version')),
2348 2355 }
2349 2356
2350 2357 globalopts = [
2351 2358 ('R', 'repository', "", _("repository root directory")),
2352 2359 ('', 'cwd', '', _("change working directory")),
2353 2360 ('y', 'noninteractive', None, _("do not prompt, assume 'yes' for any required answers")),
2354 2361 ('q', 'quiet', None, _("suppress output")),
2355 2362 ('v', 'verbose', None, _("enable additional output")),
2356 2363 ('', 'debug', None, _("enable debugging output")),
2357 2364 ('', 'debugger', None, _("start debugger")),
2358 2365 ('', 'traceback', None, _("print traceback on exception")),
2359 2366 ('', 'time', None, _("time how long the command takes")),
2360 2367 ('', 'profile', None, _("print command execution profile")),
2361 2368 ('', 'version', None, _("output version information and exit")),
2362 2369 ('h', 'help', None, _("display help and exit")),
2363 2370 ]
2364 2371
2365 2372 norepo = ("clone init version help debugancestor debugconfig debugdata"
2366 2373 " debugindex debugindexdot paths")
2367 2374
2368 2375 def find(cmd):
2369 2376 choice = []
2370 2377 for e in table.keys():
2371 2378 aliases = e.lstrip("^").split("|")
2372 2379 if cmd in aliases:
2373 2380 return e, table[e]
2374 2381 for a in aliases:
2375 2382 if a.startswith(cmd):
2376 2383 choice.append(e)
2377 2384 if len(choice) == 1:
2378 2385 e = choice[0]
2379 2386 return e, table[e]
2380 2387
2381 2388 raise UnknownCommand(cmd)
2382 2389
2383 2390 class SignalInterrupt(Exception):
2384 2391 """Exception raised on SIGTERM and SIGHUP."""
2385 2392
2386 2393 def catchterm(*args):
2387 2394 raise SignalInterrupt
2388 2395
2389 2396 def run():
2390 2397 sys.exit(dispatch(sys.argv[1:]))
2391 2398
2392 2399 class ParseError(Exception):
2393 2400 """Exception raised on errors in parsing the command line."""
2394 2401
2395 2402 def parse(ui, args):
2396 2403 options = {}
2397 2404 cmdoptions = {}
2398 2405
2399 2406 try:
2400 2407 args = fancyopts.fancyopts(args, globalopts, options)
2401 2408 except fancyopts.getopt.GetoptError, inst:
2402 2409 raise ParseError(None, inst)
2403 2410
2404 2411 if args:
2405 2412 cmd, args = args[0], args[1:]
2406 2413 defaults = ui.config("defaults", cmd)
2407 2414 if defaults:
2408 2415 # reparse with command defaults added
2409 2416 args = [cmd] + defaults.split() + args
2410 2417 try:
2411 2418 args = fancyopts.fancyopts(args, globalopts, options)
2412 2419 except fancyopts.getopt.GetoptError, inst:
2413 2420 raise ParseError(None, inst)
2414 2421
2415 2422 cmd, args = args[0], args[1:]
2416 2423
2417 2424 i = find(cmd)[1]
2418 2425 c = list(i[1])
2419 2426 else:
2420 2427 cmd = None
2421 2428 c = []
2422 2429
2423 2430 # combine global options into local
2424 2431 for o in globalopts:
2425 2432 c.append((o[0], o[1], options[o[1]], o[3]))
2426 2433
2427 2434 try:
2428 2435 args = fancyopts.fancyopts(args, c, cmdoptions)
2429 2436 except fancyopts.getopt.GetoptError, inst:
2430 2437 raise ParseError(cmd, inst)
2431 2438
2432 2439 # separate global options back out
2433 2440 for o in globalopts:
2434 2441 n = o[1]
2435 2442 options[n] = cmdoptions[n]
2436 2443 del cmdoptions[n]
2437 2444
2438 2445 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
2439 2446
2440 2447 def dispatch(args):
2441 2448 signal.signal(signal.SIGTERM, catchterm)
2442 2449 try:
2443 2450 signal.signal(signal.SIGHUP, catchterm)
2444 2451 except AttributeError:
2445 2452 pass
2446 2453
2447 2454 try:
2448 2455 u = ui.ui()
2449 2456 except util.Abort, inst:
2450 2457 sys.stderr.write(_("abort: %s\n") % inst)
2451 2458 sys.exit(1)
2452 2459
2453 2460 external = []
2454 2461 for x in u.extensions():
2455 2462 def on_exception(Exception, inst):
2456 2463 u.warn(_("*** failed to import extension %s\n") % x[1])
2457 2464 u.warn("%s\n" % inst)
2458 2465 if "--traceback" in sys.argv[1:]:
2459 2466 traceback.print_exc()
2460 2467 if x[1]:
2461 2468 try:
2462 2469 mod = imp.load_source(x[0], x[1])
2463 2470 except Exception, inst:
2464 2471 on_exception(Exception, inst)
2465 2472 continue
2466 2473 else:
2467 2474 def importh(name):
2468 2475 mod = __import__(name)
2469 2476 components = name.split('.')
2470 2477 for comp in components[1:]:
2471 2478 mod = getattr(mod, comp)
2472 2479 return mod
2473 2480 try:
2474 2481 mod = importh(x[0])
2475 2482 except Exception, inst:
2476 2483 on_exception(Exception, inst)
2477 2484 continue
2478 2485
2479 2486 external.append(mod)
2480 2487 for x in external:
2481 2488 cmdtable = getattr(x, 'cmdtable', {})
2482 2489 for t in cmdtable:
2483 2490 if t in table:
2484 2491 u.warn(_("module %s overrides %s\n") % (x.__name__, t))
2485 2492 table.update(cmdtable)
2486 2493
2487 2494 try:
2488 2495 cmd, func, args, options, cmdoptions = parse(u, args)
2489 2496 except ParseError, inst:
2490 2497 if inst.args[0]:
2491 2498 u.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
2492 2499 help_(u, inst.args[0])
2493 2500 else:
2494 2501 u.warn(_("hg: %s\n") % inst.args[1])
2495 2502 help_(u, 'shortlist')
2496 2503 sys.exit(-1)
2497 2504 except UnknownCommand, inst:
2498 2505 u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
2499 2506 help_(u, 'shortlist')
2500 2507 sys.exit(1)
2501 2508
2502 2509 if options["time"]:
2503 2510 def get_times():
2504 2511 t = os.times()
2505 2512 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
2506 2513 t = (t[0], t[1], t[2], t[3], time.clock())
2507 2514 return t
2508 2515 s = get_times()
2509 2516 def print_time():
2510 2517 t = get_times()
2511 2518 u.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
2512 2519 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
2513 2520 atexit.register(print_time)
2514 2521
2515 2522 u.updateopts(options["verbose"], options["debug"], options["quiet"],
2516 2523 not options["noninteractive"])
2517 2524
2518 2525 # enter the debugger before command execution
2519 2526 if options['debugger']:
2520 2527 pdb.set_trace()
2521 2528
2522 2529 try:
2523 2530 try:
2524 2531 if options['help']:
2525 2532 help_(u, cmd, options['version'])
2526 2533 sys.exit(0)
2527 2534 elif options['version']:
2528 2535 show_version(u)
2529 2536 sys.exit(0)
2530 2537 elif not cmd:
2531 2538 help_(u, 'shortlist')
2532 2539 sys.exit(0)
2533 2540
2534 2541 if options['cwd']:
2535 2542 try:
2536 2543 os.chdir(options['cwd'])
2537 2544 except OSError, inst:
2538 2545 raise util.Abort('%s: %s' %
2539 2546 (options['cwd'], inst.strerror))
2540 2547
2541 2548 if cmd not in norepo.split():
2542 2549 path = options["repository"] or ""
2543 2550 repo = hg.repository(ui=u, path=path)
2544 2551 for x in external:
2545 2552 if hasattr(x, 'reposetup'): x.reposetup(u, repo)
2546 2553 d = lambda: func(u, repo, *args, **cmdoptions)
2547 2554 else:
2548 2555 d = lambda: func(u, *args, **cmdoptions)
2549 2556
2550 2557 if options['profile']:
2551 2558 import hotshot, hotshot.stats
2552 2559 prof = hotshot.Profile("hg.prof")
2553 2560 r = prof.runcall(d)
2554 2561 prof.close()
2555 2562 stats = hotshot.stats.load("hg.prof")
2556 2563 stats.strip_dirs()
2557 2564 stats.sort_stats('time', 'calls')
2558 2565 stats.print_stats(40)
2559 2566 return r
2560 2567 else:
2561 2568 return d()
2562 2569 except:
2563 2570 # enter the debugger when we hit an exception
2564 2571 if options['debugger']:
2565 2572 pdb.post_mortem(sys.exc_info()[2])
2566 2573 if options['traceback']:
2567 2574 traceback.print_exc()
2568 2575 raise
2569 2576 except hg.RepoError, inst:
2570 2577 u.warn(_("abort: "), inst, "!\n")
2571 2578 except revlog.RevlogError, inst:
2572 2579 u.warn(_("abort: "), inst, "!\n")
2573 2580 except SignalInterrupt:
2574 2581 u.warn(_("killed!\n"))
2575 2582 except KeyboardInterrupt:
2576 2583 try:
2577 2584 u.warn(_("interrupted!\n"))
2578 2585 except IOError, inst:
2579 2586 if inst.errno == errno.EPIPE:
2580 2587 if u.debugflag:
2581 2588 u.warn(_("\nbroken pipe\n"))
2582 2589 else:
2583 2590 raise
2584 2591 except IOError, inst:
2585 2592 if hasattr(inst, "code"):
2586 2593 u.warn(_("abort: %s\n") % inst)
2587 2594 elif hasattr(inst, "reason"):
2588 2595 u.warn(_("abort: error: %s\n") % inst.reason[1])
2589 2596 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
2590 2597 if u.debugflag:
2591 2598 u.warn(_("broken pipe\n"))
2592 2599 elif getattr(inst, "strerror", None):
2593 2600 if getattr(inst, "filename", None):
2594 2601 u.warn(_("abort: %s - %s\n") % (inst.strerror, inst.filename))
2595 2602 else:
2596 2603 u.warn(_("abort: %s\n") % inst.strerror)
2597 2604 else:
2598 2605 raise
2599 2606 except OSError, inst:
2600 2607 if hasattr(inst, "filename"):
2601 2608 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
2602 2609 else:
2603 2610 u.warn(_("abort: %s\n") % inst.strerror)
2604 2611 except util.Abort, inst:
2605 2612 u.warn(_('abort: '), inst.args[0] % inst.args[1:], '\n')
2606 2613 sys.exit(1)
2607 2614 except TypeError, inst:
2608 2615 # was this an argument error?
2609 2616 tb = traceback.extract_tb(sys.exc_info()[2])
2610 2617 if len(tb) > 2: # no
2611 2618 raise
2612 2619 u.debug(inst, "\n")
2613 2620 u.warn(_("%s: invalid arguments\n") % cmd)
2614 2621 help_(u, cmd)
2615 2622 except UnknownCommand, inst:
2616 2623 u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
2617 2624 help_(u, 'shortlist')
2618 2625 except SystemExit:
2619 2626 # don't catch this in the catch-all below
2620 2627 raise
2621 2628 except:
2622 2629 u.warn(_("** unknown exception encountered, details follow\n"))
2623 2630 u.warn(_("** report bug details to mercurial@selenic.com\n"))
2624 2631 raise
2625 2632
2626 2633 sys.exit(-1)
@@ -1,78 +1,84 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 61
62 62 echo "# attempt to overwrite an existing file"
63 63 echo "ca" > d1/ca
64 64 hg rename d1/ba d1/ca
65 65 hg status
66 66 hg update -C
67 67
68 68 echo "# forced overwrite of an existing file"
69 69 echo "ca" > d1/ca
70 70 hg rename --force d1/ba d1/ca
71 71 hg status
72 72 hg update -C
73 73
74 74 echo "# replace a symlink with a file"
75 75 ln -s ba d1/ca
76 76 hg rename --force d1/ba d1/ca
77 77 hg status
78 78 hg update -C
79
80 echo "# do not copy more than one source file to the same destination file"
81 mkdir d3
82 hg rename d1/* d2/* d3
83 hg status
84 hg update -C
@@ -1,103 +1,115 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 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 94 # attempt to overwrite an existing file
95 95 d1/ca: not overwriting - file exists
96 96 abort: no files to copy
97 97 ? d1/ca
98 98 # forced overwrite of an existing file
99 99 A d1/ca
100 100 R d1/ba
101 101 # replace a symlink with a file
102 102 A d1/ca
103 103 R d1/ba
104 # do not copy more than one source file to the same destination file
105 copying d1/d11/a1 to d3/d11/a1
106 d3/b: not overwriting - d2/b collides with d1/b
107 removing d1/d11/a1
108 A d3/a
109 A d3/b
110 A d3/ba
111 A d3/d11/a1
112 R d1/a
113 R d1/b
114 R d1/ba
115 R d1/d11/a1
General Comments 0
You need to be logged in to leave comments. Login now