##// END OF EJS Templates
Avoid insertion/deletion of CRs on stdio during hg serve
olivier.maquelin@intel.com -
r1420:b32b3509 default
parent child Browse files
Show More
@@ -1,2233 +1,2237 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 names = []
481 481 for src, abs, rel, exact in walk(repo, pats, opts):
482 482 if exact:
483 483 if ui.verbose: ui.status(_('adding %s\n') % rel)
484 484 names.append(abs)
485 485 elif repo.dirstate.state(abs) == '?':
486 486 ui.status(_('adding %s\n') % rel)
487 487 names.append(abs)
488 488 repo.add(names)
489 489
490 490 def addremove(ui, repo, *pats, **opts):
491 491 """add all new files, delete all missing files"""
492 492 add, remove = [], []
493 493 for src, abs, rel, exact in walk(repo, pats, opts):
494 494 if src == 'f' and repo.dirstate.state(abs) == '?':
495 495 add.append(abs)
496 496 if ui.verbose or not exact:
497 497 ui.status(_('adding %s\n') % rel)
498 498 if repo.dirstate.state(abs) != 'r' and not os.path.exists(rel):
499 499 remove.append(abs)
500 500 if ui.verbose or not exact:
501 501 ui.status(_('removing %s\n') % rel)
502 502 repo.add(add)
503 503 repo.remove(remove)
504 504
505 505 def annotate(ui, repo, *pats, **opts):
506 506 """show changeset information per file line"""
507 507 def getnode(rev):
508 508 return short(repo.changelog.node(rev))
509 509
510 510 ucache = {}
511 511 def getname(rev):
512 512 cl = repo.changelog.read(repo.changelog.node(rev))
513 513 return trimuser(ui, cl[1], rev, ucache)
514 514
515 515 if not pats:
516 516 raise util.Abort(_('at least one file name or pattern required'))
517 517
518 518 opmap = [['user', getname], ['number', str], ['changeset', getnode]]
519 519 if not opts['user'] and not opts['changeset']:
520 520 opts['number'] = 1
521 521
522 522 if opts['rev']:
523 523 node = repo.changelog.lookup(opts['rev'])
524 524 else:
525 525 node = repo.dirstate.parents()[0]
526 526 change = repo.changelog.read(node)
527 527 mmap = repo.manifest.read(change[0])
528 528
529 529 for src, abs, rel, exact in walk(repo, pats, opts):
530 530 if abs not in mmap:
531 531 ui.warn(_("warning: %s is not in the repository!\n") % rel)
532 532 continue
533 533
534 534 f = repo.file(abs)
535 535 if not opts['text'] and util.binary(f.read(mmap[abs])):
536 536 ui.write(_("%s: binary file\n") % rel)
537 537 continue
538 538
539 539 lines = f.annotate(mmap[abs])
540 540 pieces = []
541 541
542 542 for o, f in opmap:
543 543 if opts[o]:
544 544 l = [f(n) for n, dummy in lines]
545 545 if l:
546 546 m = max(map(len, l))
547 547 pieces.append(["%*s" % (m, x) for x in l])
548 548
549 549 if pieces:
550 550 for p, l in zip(zip(*pieces), lines):
551 551 ui.write("%s: %s" % (" ".join(p), l[1]))
552 552
553 553 def bundle(ui, repo, fname, dest="default-push", **opts):
554 554 """create a changegroup file"""
555 555 f = open(fname, "wb")
556 556 dest = ui.expandpath(dest)
557 557 other = hg.repository(ui, dest)
558 558 o = repo.findoutgoing(other)
559 559 cg = repo.changegroup(o)
560 560
561 561 try:
562 562 f.write("HG10")
563 563 z = bz2.BZ2Compressor(9)
564 564 while 1:
565 565 chunk = cg.read(4096)
566 566 if not chunk:
567 567 break
568 568 f.write(z.compress(chunk))
569 569 f.write(z.flush())
570 570 except:
571 571 os.unlink(fname)
572 572 raise
573 573
574 574 def cat(ui, repo, file1, *pats, **opts):
575 575 """output the latest or given revisions of files"""
576 576 mf = {}
577 577 if opts['rev']:
578 578 change = repo.changelog.read(repo.lookup(opts['rev']))
579 579 mf = repo.manifest.read(change[0])
580 580 for src, abs, rel, exact in walk(repo, (file1,) + pats, opts):
581 581 r = repo.file(abs)
582 582 if opts['rev']:
583 583 try:
584 584 n = mf[abs]
585 585 except (hg.RepoError, KeyError):
586 586 try:
587 587 n = r.lookup(rev)
588 588 except KeyError, inst:
589 589 raise util.Abort(_('cannot find file %s in rev %s'), rel, rev)
590 590 else:
591 591 n = r.tip()
592 592 fp = make_file(repo, r, opts['output'], node=n, pathname=abs)
593 593 fp.write(r.read(n))
594 594
595 595 def clone(ui, source, dest=None, **opts):
596 596 """make a copy of an existing repository"""
597 597 if dest is None:
598 598 dest = os.path.basename(os.path.normpath(source))
599 599
600 600 if os.path.exists(dest):
601 601 raise util.Abort(_("destination '%s' already exists"), dest)
602 602
603 603 dest = os.path.realpath(dest)
604 604
605 605 class Dircleanup:
606 606 def __init__(self, dir_):
607 607 self.rmtree = shutil.rmtree
608 608 self.dir_ = dir_
609 609 os.mkdir(dir_)
610 610 def close(self):
611 611 self.dir_ = None
612 612 def __del__(self):
613 613 if self.dir_:
614 614 self.rmtree(self.dir_, True)
615 615
616 616 if opts['ssh']:
617 617 ui.setconfig("ui", "ssh", opts['ssh'])
618 618 if opts['remotecmd']:
619 619 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
620 620
621 621 if not os.path.exists(source):
622 622 source = ui.expandpath(source)
623 623
624 624 d = Dircleanup(dest)
625 625 abspath = source
626 626 other = hg.repository(ui, source)
627 627
628 628 copy = False
629 629 if other.dev() != -1:
630 630 abspath = os.path.abspath(source)
631 631 if not opts['pull']:
632 632 copy = True
633 633
634 634 if copy:
635 635 try:
636 636 # we use a lock here because if we race with commit, we
637 637 # can end up with extra data in the cloned revlogs that's
638 638 # not pointed to by changesets, thus causing verify to
639 639 # fail
640 640 l1 = lock.lock(os.path.join(source, ".hg", "lock"))
641 641 except OSError:
642 642 copy = False
643 643
644 644 if copy:
645 645 # we lock here to avoid premature writing to the target
646 646 os.mkdir(os.path.join(dest, ".hg"))
647 647 l2 = lock.lock(os.path.join(dest, ".hg", "lock"))
648 648
649 649 files = "data 00manifest.d 00manifest.i 00changelog.d 00changelog.i"
650 650 for f in files.split():
651 651 src = os.path.join(source, ".hg", f)
652 652 dst = os.path.join(dest, ".hg", f)
653 653 util.copyfiles(src, dst)
654 654
655 655 repo = hg.repository(ui, dest)
656 656
657 657 else:
658 658 repo = hg.repository(ui, dest, create=1)
659 659 repo.pull(other)
660 660
661 661 f = repo.opener("hgrc", "w", text=True)
662 662 f.write("[paths]\n")
663 663 f.write("default = %s\n" % abspath)
664 664
665 665 if not opts['noupdate']:
666 666 update(ui, repo)
667 667
668 668 d.close()
669 669
670 670 def commit(ui, repo, *pats, **opts):
671 671 """commit the specified files or all outstanding changes"""
672 672 if opts['text']:
673 673 ui.warn(_("Warning: -t and --text is deprecated,"
674 674 " please use -m or --message instead.\n"))
675 675 message = opts['message'] or opts['text']
676 676 logfile = opts['logfile']
677 677
678 678 if message and logfile:
679 679 raise util.Abort(_('options --message and --logfile are mutually '
680 680 'exclusive'))
681 681 if not message and logfile:
682 682 try:
683 683 if logfile == '-':
684 684 message = sys.stdin.read()
685 685 else:
686 686 message = open(logfile).read()
687 687 except IOError, inst:
688 688 raise util.Abort(_("can't read commit message '%s': %s") %
689 689 (logfile, inst.strerror))
690 690
691 691 if opts['addremove']:
692 692 addremove(ui, repo, *pats, **opts)
693 693 cwd = repo.getcwd()
694 694 if not pats and cwd:
695 695 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
696 696 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
697 697 fns, match, anypats = matchpats(repo, (pats and repo.getcwd()) or '',
698 698 pats, opts)
699 699 if pats:
700 700 c, a, d, u = repo.changes(files=fns, match=match)
701 701 files = c + a + [fn for fn in d if repo.dirstate.state(fn) == 'r']
702 702 else:
703 703 files = []
704 704 try:
705 705 repo.commit(files, message, opts['user'], opts['date'], match)
706 706 except ValueError, inst:
707 707 raise util.Abort(str(inst))
708 708
709 709 def docopy(ui, repo, pats, opts):
710 710 if not pats:
711 711 raise util.Abort(_('no source or destination specified'))
712 712 elif len(pats) == 1:
713 713 raise util.Abort(_('no destination specified'))
714 714 pats = list(pats)
715 715 dest = pats.pop()
716 716 sources = []
717 717
718 718 def okaytocopy(abs, rel, exact):
719 719 reasons = {'?': _('is not managed'),
720 720 'a': _('has been marked for add')}
721 721 reason = reasons.get(repo.dirstate.state(abs))
722 722 if reason:
723 723 if exact: ui.warn(_('%s: not copying - file %s\n') % (rel, reason))
724 724 else:
725 725 return True
726 726
727 727 for src, abs, rel, exact in walk(repo, pats, opts):
728 728 if okaytocopy(abs, rel, exact):
729 729 sources.append((abs, rel, exact))
730 730 if not sources:
731 731 raise util.Abort(_('no files to copy'))
732 732
733 733 cwd = repo.getcwd()
734 734 absdest = util.canonpath(repo.root, cwd, dest)
735 735 reldest = util.pathto(cwd, absdest)
736 736 if os.path.exists(reldest):
737 737 destisfile = not os.path.isdir(reldest)
738 738 else:
739 739 destisfile = len(sources) == 1 or repo.dirstate.state(absdest) != '?'
740 740
741 741 if destisfile:
742 742 if opts['parents']:
743 743 raise util.Abort(_('with --parents, destination must be a directory'))
744 744 elif len(sources) > 1:
745 745 raise util.Abort(_('with multiple sources, destination must be a '
746 746 'directory'))
747 747 errs, copied = 0, []
748 748 for abs, rel, exact in sources:
749 749 if opts['parents']:
750 750 mydest = os.path.join(dest, rel)
751 751 elif destisfile:
752 752 mydest = reldest
753 753 else:
754 754 mydest = os.path.join(dest, os.path.basename(rel))
755 755 myabsdest = util.canonpath(repo.root, cwd, mydest)
756 756 myreldest = util.pathto(cwd, myabsdest)
757 757 if not opts['force'] and repo.dirstate.state(myabsdest) not in 'a?':
758 758 ui.warn(_('%s: not overwriting - file already managed\n') % myreldest)
759 759 continue
760 760 mydestdir = os.path.dirname(myreldest) or '.'
761 761 if not opts['after']:
762 762 try:
763 763 if opts['parents']: os.makedirs(mydestdir)
764 764 elif not destisfile: os.mkdir(mydestdir)
765 765 except OSError, inst:
766 766 if inst.errno != errno.EEXIST: raise
767 767 if ui.verbose or not exact:
768 768 ui.status(_('copying %s to %s\n') % (rel, myreldest))
769 769 if not opts['after']:
770 770 try:
771 771 shutil.copyfile(rel, myreldest)
772 772 shutil.copymode(rel, myreldest)
773 773 except shutil.Error, inst:
774 774 raise util.Abort(str(inst))
775 775 except IOError, inst:
776 776 if inst.errno == errno.ENOENT:
777 777 ui.warn(_('%s: deleted in working copy\n') % rel)
778 778 else:
779 779 ui.warn(_('%s: cannot copy - %s\n') % (rel, inst.strerror))
780 780 errs += 1
781 781 continue
782 782 repo.copy(abs, myabsdest)
783 783 copied.append((abs, rel, exact))
784 784 if errs:
785 785 ui.warn(_('(consider using --after)\n'))
786 786 return errs, copied
787 787
788 788 def copy(ui, repo, *pats, **opts):
789 789 """mark files as copied for the next commit"""
790 790 errs, copied = docopy(ui, repo, pats, opts)
791 791 return errs
792 792
793 793 def debugancestor(ui, index, rev1, rev2):
794 794 """find the ancestor revision of two revisions in a given index"""
795 795 r = revlog.revlog(file, index, "")
796 796 a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
797 797 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
798 798
799 799 def debugcheckstate(ui, repo):
800 800 """validate the correctness of the current dirstate"""
801 801 parent1, parent2 = repo.dirstate.parents()
802 802 repo.dirstate.read()
803 803 dc = repo.dirstate.map
804 804 keys = dc.keys()
805 805 keys.sort()
806 806 m1n = repo.changelog.read(parent1)[0]
807 807 m2n = repo.changelog.read(parent2)[0]
808 808 m1 = repo.manifest.read(m1n)
809 809 m2 = repo.manifest.read(m2n)
810 810 errors = 0
811 811 for f in dc:
812 812 state = repo.dirstate.state(f)
813 813 if state in "nr" and f not in m1:
814 814 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
815 815 errors += 1
816 816 if state in "a" and f in m1:
817 817 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
818 818 errors += 1
819 819 if state in "m" and f not in m1 and f not in m2:
820 820 ui.warn(_("%s in state %s, but not in either manifest\n") %
821 821 (f, state))
822 822 errors += 1
823 823 for f in m1:
824 824 state = repo.dirstate.state(f)
825 825 if state not in "nrm":
826 826 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
827 827 errors += 1
828 828 if errors:
829 829 raise util.Abort(_(".hg/dirstate inconsistent with current parent's manifest"))
830 830
831 831 def debugconfig(ui):
832 832 """show combined config settings from all hgrc files"""
833 833 try:
834 834 repo = hg.repository(ui)
835 835 except hg.RepoError:
836 836 pass
837 837 for section, name, value in ui.walkconfig():
838 838 ui.write('%s.%s=%s\n' % (section, name, value))
839 839
840 840 def debugsetparents(ui, repo, rev1, rev2=None):
841 841 """
842 842 manually set the parents of the current working directory
843 843
844 844 This is useful for writing repository conversion tools, but should
845 845 be used with care.
846 846 """
847 847
848 848 if not rev2:
849 849 rev2 = hex(nullid)
850 850
851 851 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
852 852
853 853 def debugstate(ui, repo):
854 854 """show the contents of the current dirstate"""
855 855 repo.dirstate.read()
856 856 dc = repo.dirstate.map
857 857 keys = dc.keys()
858 858 keys.sort()
859 859 for file_ in keys:
860 860 ui.write("%c %3o %10d %s %s\n"
861 861 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
862 862 time.strftime("%x %X",
863 863 time.localtime(dc[file_][3])), file_))
864 864 for f in repo.dirstate.copies:
865 865 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copies[f], f))
866 866
867 867 def debugdata(ui, file_, rev):
868 868 """dump the contents of an data file revision"""
869 869 r = revlog.revlog(file, file_[:-2] + ".i", file_)
870 870 try:
871 871 ui.write(r.revision(r.lookup(rev)))
872 872 except KeyError:
873 873 raise util.Abort(_('invalid revision identifier %s'), rev)
874 874
875 875 def debugindex(ui, file_):
876 876 """dump the contents of an index file"""
877 877 r = revlog.revlog(file, file_, "")
878 878 ui.write(" rev offset length base linkrev" +
879 879 " nodeid p1 p2\n")
880 880 for i in range(r.count()):
881 881 e = r.index[i]
882 882 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
883 883 i, e[0], e[1], e[2], e[3],
884 884 short(e[6]), short(e[4]), short(e[5])))
885 885
886 886 def debugindexdot(ui, file_):
887 887 """dump an index DAG as a .dot file"""
888 888 r = revlog.revlog(file, file_, "")
889 889 ui.write("digraph G {\n")
890 890 for i in range(r.count()):
891 891 e = r.index[i]
892 892 ui.write("\t%d -> %d\n" % (r.rev(e[4]), i))
893 893 if e[5] != nullid:
894 894 ui.write("\t%d -> %d\n" % (r.rev(e[5]), i))
895 895 ui.write("}\n")
896 896
897 897 def debugrename(ui, repo, file, rev=None):
898 898 """dump rename information"""
899 899 r = repo.file(relpath(repo, [file])[0])
900 900 if rev:
901 901 try:
902 902 # assume all revision numbers are for changesets
903 903 n = repo.lookup(rev)
904 904 change = repo.changelog.read(n)
905 905 m = repo.manifest.read(change[0])
906 906 n = m[relpath(repo, [file])[0]]
907 907 except hg.RepoError, KeyError:
908 908 n = r.lookup(rev)
909 909 else:
910 910 n = r.tip()
911 911 m = r.renamed(n)
912 912 if m:
913 913 ui.write(_("renamed from %s:%s\n") % (m[0], hex(m[1])))
914 914 else:
915 915 ui.write(_("not renamed\n"))
916 916
917 917 def debugwalk(ui, repo, *pats, **opts):
918 918 """show how files match on given patterns"""
919 919 items = list(walk(repo, pats, opts))
920 920 if not items:
921 921 return
922 922 fmt = '%%s %%-%ds %%-%ds %%s' % (
923 923 max([len(abs) for (src, abs, rel, exact) in items]),
924 924 max([len(rel) for (src, abs, rel, exact) in items]))
925 925 for src, abs, rel, exact in items:
926 926 line = fmt % (src, abs, rel, exact and 'exact' or '')
927 927 ui.write("%s\n" % line.rstrip())
928 928
929 929 def diff(ui, repo, *pats, **opts):
930 930 """diff working directory (or selected files)"""
931 931 node1, node2 = None, None
932 932 revs = [repo.lookup(x) for x in opts['rev']]
933 933
934 934 if len(revs) > 0:
935 935 node1 = revs[0]
936 936 if len(revs) > 1:
937 937 node2 = revs[1]
938 938 if len(revs) > 2:
939 939 raise util.Abort(_("too many revisions to diff"))
940 940
941 941 fns, matchfn, anypats = matchpats(repo, repo.getcwd(), pats, opts)
942 942
943 943 dodiff(sys.stdout, ui, repo, node1, node2, fns, match=matchfn,
944 944 text=opts['text'])
945 945
946 946 def doexport(ui, repo, changeset, seqno, total, revwidth, opts):
947 947 node = repo.lookup(changeset)
948 948 prev, other = repo.changelog.parents(node)
949 949 change = repo.changelog.read(node)
950 950
951 951 fp = make_file(repo, repo.changelog, opts['output'],
952 952 node=node, total=total, seqno=seqno,
953 953 revwidth=revwidth)
954 954 if fp != sys.stdout:
955 955 ui.note("%s\n" % fp.name)
956 956
957 957 fp.write("# HG changeset patch\n")
958 958 fp.write("# User %s\n" % change[1])
959 959 fp.write("# Node ID %s\n" % hex(node))
960 960 fp.write("# Parent %s\n" % hex(prev))
961 961 if other != nullid:
962 962 fp.write("# Parent %s\n" % hex(other))
963 963 fp.write(change[4].rstrip())
964 964 fp.write("\n\n")
965 965
966 966 dodiff(fp, ui, repo, prev, node, text=opts['text'])
967 967 if fp != sys.stdout:
968 968 fp.close()
969 969
970 970 def export(ui, repo, *changesets, **opts):
971 971 """dump the header and diffs for one or more changesets"""
972 972 if not changesets:
973 973 raise util.Abort(_("export requires at least one changeset"))
974 974 seqno = 0
975 975 revs = list(revrange(ui, repo, changesets))
976 976 total = len(revs)
977 977 revwidth = max(map(len, revs))
978 978 ui.note(len(revs) > 1 and _("Exporting patches:\n") or _("Exporting patch:\n"))
979 979 for cset in revs:
980 980 seqno += 1
981 981 doexport(ui, repo, cset, seqno, total, revwidth, opts)
982 982
983 983 def forget(ui, repo, *pats, **opts):
984 984 """don't add the specified files on the next commit"""
985 985 forget = []
986 986 for src, abs, rel, exact in walk(repo, pats, opts):
987 987 if repo.dirstate.state(abs) == 'a':
988 988 forget.append(abs)
989 989 if ui.verbose or not exact:
990 990 ui.status(_('forgetting %s\n') % rel)
991 991 repo.forget(forget)
992 992
993 993 def grep(ui, repo, pattern, *pats, **opts):
994 994 """search for a pattern in specified files and revisions"""
995 995 reflags = 0
996 996 if opts['ignore_case']:
997 997 reflags |= re.I
998 998 regexp = re.compile(pattern, reflags)
999 999 sep, eol = ':', '\n'
1000 1000 if opts['print0']:
1001 1001 sep = eol = '\0'
1002 1002
1003 1003 fcache = {}
1004 1004 def getfile(fn):
1005 1005 if fn not in fcache:
1006 1006 fcache[fn] = repo.file(fn)
1007 1007 return fcache[fn]
1008 1008
1009 1009 def matchlines(body):
1010 1010 begin = 0
1011 1011 linenum = 0
1012 1012 while True:
1013 1013 match = regexp.search(body, begin)
1014 1014 if not match:
1015 1015 break
1016 1016 mstart, mend = match.span()
1017 1017 linenum += body.count('\n', begin, mstart) + 1
1018 1018 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1019 1019 lend = body.find('\n', mend)
1020 1020 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1021 1021 begin = lend + 1
1022 1022
1023 1023 class linestate:
1024 1024 def __init__(self, line, linenum, colstart, colend):
1025 1025 self.line = line
1026 1026 self.linenum = linenum
1027 1027 self.colstart = colstart
1028 1028 self.colend = colend
1029 1029 def __eq__(self, other):
1030 1030 return self.line == other.line
1031 1031 def __hash__(self):
1032 1032 return hash(self.line)
1033 1033
1034 1034 matches = {}
1035 1035 def grepbody(fn, rev, body):
1036 1036 matches[rev].setdefault(fn, {})
1037 1037 m = matches[rev][fn]
1038 1038 for lnum, cstart, cend, line in matchlines(body):
1039 1039 s = linestate(line, lnum, cstart, cend)
1040 1040 m[s] = s
1041 1041
1042 1042 prev = {}
1043 1043 ucache = {}
1044 1044 def display(fn, rev, states, prevstates):
1045 1045 diff = list(sets.Set(states).symmetric_difference(sets.Set(prevstates)))
1046 1046 diff.sort(lambda x, y: cmp(x.linenum, y.linenum))
1047 1047 counts = {'-': 0, '+': 0}
1048 1048 filerevmatches = {}
1049 1049 for l in diff:
1050 1050 if incrementing or not opts['all']:
1051 1051 change = ((l in prevstates) and '-') or '+'
1052 1052 r = rev
1053 1053 else:
1054 1054 change = ((l in states) and '-') or '+'
1055 1055 r = prev[fn]
1056 1056 cols = [fn, str(rev)]
1057 1057 if opts['line_number']: cols.append(str(l.linenum))
1058 1058 if opts['all']: cols.append(change)
1059 1059 if opts['user']: cols.append(trimuser(ui, getchange(rev)[1], rev,
1060 1060 ucache))
1061 1061 if opts['files_with_matches']:
1062 1062 c = (fn, rev)
1063 1063 if c in filerevmatches: continue
1064 1064 filerevmatches[c] = 1
1065 1065 else:
1066 1066 cols.append(l.line)
1067 1067 ui.write(sep.join(cols), eol)
1068 1068 counts[change] += 1
1069 1069 return counts['+'], counts['-']
1070 1070
1071 1071 fstate = {}
1072 1072 skip = {}
1073 1073 changeiter, getchange = walkchangerevs(ui, repo, repo.getcwd(), pats, opts)
1074 1074 count = 0
1075 1075 incrementing = False
1076 1076 for st, rev, fns in changeiter:
1077 1077 if st == 'window':
1078 1078 incrementing = rev
1079 1079 matches.clear()
1080 1080 elif st == 'add':
1081 1081 change = repo.changelog.read(repo.lookup(str(rev)))
1082 1082 mf = repo.manifest.read(change[0])
1083 1083 matches[rev] = {}
1084 1084 for fn in fns:
1085 1085 if fn in skip: continue
1086 1086 fstate.setdefault(fn, {})
1087 1087 try:
1088 1088 grepbody(fn, rev, getfile(fn).read(mf[fn]))
1089 1089 except KeyError:
1090 1090 pass
1091 1091 elif st == 'iter':
1092 1092 states = matches[rev].items()
1093 1093 states.sort()
1094 1094 for fn, m in states:
1095 1095 if fn in skip: continue
1096 1096 if incrementing or not opts['all'] or fstate[fn]:
1097 1097 pos, neg = display(fn, rev, m, fstate[fn])
1098 1098 count += pos + neg
1099 1099 if pos and not opts['all']:
1100 1100 skip[fn] = True
1101 1101 fstate[fn] = m
1102 1102 prev[fn] = rev
1103 1103
1104 1104 if not incrementing:
1105 1105 fstate = fstate.items()
1106 1106 fstate.sort()
1107 1107 for fn, state in fstate:
1108 1108 if fn in skip: continue
1109 1109 display(fn, rev, {}, state)
1110 1110 return (count == 0 and 1) or 0
1111 1111
1112 1112 def heads(ui, repo, **opts):
1113 1113 """show current repository heads"""
1114 1114 heads = repo.changelog.heads()
1115 1115 br = None
1116 1116 if opts['branches']:
1117 1117 br = repo.branchlookup(heads)
1118 1118 for n in repo.changelog.heads():
1119 1119 show_changeset(ui, repo, changenode=n, brinfo=br)
1120 1120
1121 1121 def identify(ui, repo):
1122 1122 """print information about the working copy"""
1123 1123 parents = [p for p in repo.dirstate.parents() if p != nullid]
1124 1124 if not parents:
1125 1125 ui.write(_("unknown\n"))
1126 1126 return
1127 1127
1128 1128 hexfunc = ui.verbose and hex or short
1129 1129 (c, a, d, u) = repo.changes()
1130 1130 output = ["%s%s" % ('+'.join([hexfunc(parent) for parent in parents]),
1131 1131 (c or a or d) and "+" or "")]
1132 1132
1133 1133 if not ui.quiet:
1134 1134 # multiple tags for a single parent separated by '/'
1135 1135 parenttags = ['/'.join(tags)
1136 1136 for tags in map(repo.nodetags, parents) if tags]
1137 1137 # tags for multiple parents separated by ' + '
1138 1138 if parenttags:
1139 1139 output.append(' + '.join(parenttags))
1140 1140
1141 1141 ui.write("%s\n" % ' '.join(output))
1142 1142
1143 1143 def import_(ui, repo, patch1, *patches, **opts):
1144 1144 """import an ordered set of patches"""
1145 1145 patches = (patch1,) + patches
1146 1146
1147 1147 if not opts['force']:
1148 1148 (c, a, d, u) = repo.changes()
1149 1149 if c or a or d:
1150 1150 raise util.Abort(_("outstanding uncommitted changes"))
1151 1151
1152 1152 d = opts["base"]
1153 1153 strip = opts["strip"]
1154 1154
1155 1155 mailre = re.compile(r'(?:From |[\w-]+:)')
1156 1156
1157 1157 # attempt to detect the start of a patch
1158 1158 # (this heuristic is borrowed from quilt)
1159 1159 diffre = re.compile(r'(?:Index:[ \t]|diff[ \t]|RCS file: |' +
1160 1160 'retrieving revision [0-9]+(\.[0-9]+)*$|' +
1161 1161 '(---|\*\*\*)[ \t])')
1162 1162
1163 1163 for patch in patches:
1164 1164 ui.status(_("applying %s\n") % patch)
1165 1165 pf = os.path.join(d, patch)
1166 1166
1167 1167 message = []
1168 1168 user = None
1169 1169 hgpatch = False
1170 1170 for line in file(pf):
1171 1171 line = line.rstrip()
1172 1172 if (not message and not hgpatch and
1173 1173 mailre.match(line) and not opts['force']):
1174 1174 if len(line) > 35: line = line[:32] + '...'
1175 1175 raise util.Abort(_('first line looks like a '
1176 1176 'mail header: ') + line)
1177 1177 if diffre.match(line):
1178 1178 break
1179 1179 elif hgpatch:
1180 1180 # parse values when importing the result of an hg export
1181 1181 if line.startswith("# User "):
1182 1182 user = line[7:]
1183 1183 ui.debug(_('User: %s\n') % user)
1184 1184 elif not line.startswith("# ") and line:
1185 1185 message.append(line)
1186 1186 hgpatch = False
1187 1187 elif line == '# HG changeset patch':
1188 1188 hgpatch = True
1189 1189 message = [] # We may have collected garbage
1190 1190 else:
1191 1191 message.append(line)
1192 1192
1193 1193 # make sure message isn't empty
1194 1194 if not message:
1195 1195 message = _("imported patch %s\n") % patch
1196 1196 else:
1197 1197 message = "%s\n" % '\n'.join(message)
1198 1198 ui.debug(_('message:\n%s\n') % message)
1199 1199
1200 1200 files = util.patch(strip, pf, ui)
1201 1201
1202 1202 if len(files) > 0:
1203 1203 addremove(ui, repo, *files)
1204 1204 repo.commit(files, message, user)
1205 1205
1206 1206 def incoming(ui, repo, source="default", **opts):
1207 1207 """show new changesets found in source"""
1208 1208 source = ui.expandpath(source)
1209 1209 other = hg.repository(ui, source)
1210 1210 if not other.local():
1211 1211 raise util.Abort(_("incoming doesn't work for remote repositories yet"))
1212 1212 o = repo.findincoming(other)
1213 1213 if not o:
1214 1214 return
1215 1215 o = other.newer(o)
1216 1216 for n in o:
1217 1217 show_changeset(ui, other, changenode=n)
1218 1218 if opts['patch']:
1219 1219 prev = other.changelog.parents(n)[0]
1220 1220 dodiff(ui, ui, other, prev, n)
1221 1221 ui.write("\n")
1222 1222
1223 1223 def init(ui, dest="."):
1224 1224 """create a new repository in the given directory"""
1225 1225 if not os.path.exists(dest):
1226 1226 os.mkdir(dest)
1227 1227 hg.repository(ui, dest, create=1)
1228 1228
1229 1229 def locate(ui, repo, *pats, **opts):
1230 1230 """locate files matching specific patterns"""
1231 1231 end = opts['print0'] and '\0' or '\n'
1232 1232
1233 1233 for src, abs, rel, exact in walk(repo, pats, opts, '(?:.*/|)'):
1234 1234 if repo.dirstate.state(abs) == '?':
1235 1235 continue
1236 1236 if opts['fullpath']:
1237 1237 ui.write(os.path.join(repo.root, abs), end)
1238 1238 else:
1239 1239 ui.write(rel, end)
1240 1240
1241 1241 def log(ui, repo, *pats, **opts):
1242 1242 """show revision history of entire repository or files"""
1243 1243 class dui:
1244 1244 # Implement and delegate some ui protocol. Save hunks of
1245 1245 # output for later display in the desired order.
1246 1246 def __init__(self, ui):
1247 1247 self.ui = ui
1248 1248 self.hunk = {}
1249 1249 def bump(self, rev):
1250 1250 self.rev = rev
1251 1251 self.hunk[rev] = []
1252 1252 def note(self, *args):
1253 1253 if self.verbose:
1254 1254 self.write(*args)
1255 1255 def status(self, *args):
1256 1256 if not self.quiet:
1257 1257 self.write(*args)
1258 1258 def write(self, *args):
1259 1259 self.hunk[self.rev].append(args)
1260 1260 def debug(self, *args):
1261 1261 if self.debugflag:
1262 1262 self.write(*args)
1263 1263 def __getattr__(self, key):
1264 1264 return getattr(self.ui, key)
1265 1265 cwd = repo.getcwd()
1266 1266 if not pats and cwd:
1267 1267 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
1268 1268 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
1269 1269 changeiter, getchange = walkchangerevs(ui, repo, (pats and cwd) or '',
1270 1270 pats, opts)
1271 1271 for st, rev, fns in changeiter:
1272 1272 if st == 'window':
1273 1273 du = dui(ui)
1274 1274 elif st == 'add':
1275 1275 du.bump(rev)
1276 1276 br = None
1277 1277 if opts['keyword']:
1278 1278 changes = repo.changelog.read(repo.changelog.node(rev))
1279 1279 miss = 0
1280 1280 for k in [kw.lower() for kw in opts['keyword']]:
1281 1281 if not (k in changes[1].lower() or
1282 1282 k in changes[4].lower() or
1283 1283 k in " ".join(changes[3][:20]).lower()):
1284 1284 miss = 1
1285 1285 break
1286 1286 if miss:
1287 1287 continue
1288 1288
1289 1289 if opts['branch']:
1290 1290 br = repo.branchlookup([repo.changelog.node(rev)])
1291 1291
1292 1292 show_changeset(du, repo, rev, brinfo=br)
1293 1293 if opts['patch']:
1294 1294 changenode = repo.changelog.node(rev)
1295 1295 prev, other = repo.changelog.parents(changenode)
1296 1296 dodiff(du, du, repo, prev, changenode, fns)
1297 1297 du.write("\n\n")
1298 1298 elif st == 'iter':
1299 1299 for args in du.hunk[rev]:
1300 1300 ui.write(*args)
1301 1301
1302 1302 def manifest(ui, repo, rev=None):
1303 1303 """output the latest or given revision of the project manifest"""
1304 1304 if rev:
1305 1305 try:
1306 1306 # assume all revision numbers are for changesets
1307 1307 n = repo.lookup(rev)
1308 1308 change = repo.changelog.read(n)
1309 1309 n = change[0]
1310 1310 except hg.RepoError:
1311 1311 n = repo.manifest.lookup(rev)
1312 1312 else:
1313 1313 n = repo.manifest.tip()
1314 1314 m = repo.manifest.read(n)
1315 1315 mf = repo.manifest.readflags(n)
1316 1316 files = m.keys()
1317 1317 files.sort()
1318 1318
1319 1319 for f in files:
1320 1320 ui.write("%40s %3s %s\n" % (hex(m[f]), mf[f] and "755" or "644", f))
1321 1321
1322 1322 def outgoing(ui, repo, dest="default-push", **opts):
1323 1323 """show changesets not found in destination"""
1324 1324 dest = ui.expandpath(dest)
1325 1325 other = hg.repository(ui, dest)
1326 1326 o = repo.findoutgoing(other)
1327 1327 o = repo.newer(o)
1328 1328 for n in o:
1329 1329 show_changeset(ui, repo, changenode=n)
1330 1330 if opts['patch']:
1331 1331 prev = repo.changelog.parents(n)[0]
1332 1332 dodiff(ui, ui, repo, prev, n)
1333 1333 ui.write("\n")
1334 1334
1335 1335 def parents(ui, repo, rev=None):
1336 1336 """show the parents of the working dir or revision"""
1337 1337 if rev:
1338 1338 p = repo.changelog.parents(repo.lookup(rev))
1339 1339 else:
1340 1340 p = repo.dirstate.parents()
1341 1341
1342 1342 for n in p:
1343 1343 if n != nullid:
1344 1344 show_changeset(ui, repo, changenode=n)
1345 1345
1346 1346 def paths(ui, search=None):
1347 1347 """show definition of symbolic path names"""
1348 1348 try:
1349 1349 repo = hg.repository(ui=ui)
1350 1350 except hg.RepoError:
1351 1351 pass
1352 1352
1353 1353 if search:
1354 1354 for name, path in ui.configitems("paths"):
1355 1355 if name == search:
1356 1356 ui.write("%s\n" % path)
1357 1357 return
1358 1358 ui.warn(_("not found!\n"))
1359 1359 return 1
1360 1360 else:
1361 1361 for name, path in ui.configitems("paths"):
1362 1362 ui.write("%s = %s\n" % (name, path))
1363 1363
1364 1364 def pull(ui, repo, source="default", **opts):
1365 1365 """pull changes from the specified source"""
1366 1366 source = ui.expandpath(source)
1367 1367 ui.status(_('pulling from %s\n') % (source))
1368 1368
1369 1369 if opts['ssh']:
1370 1370 ui.setconfig("ui", "ssh", opts['ssh'])
1371 1371 if opts['remotecmd']:
1372 1372 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
1373 1373
1374 1374 other = hg.repository(ui, source)
1375 1375 r = repo.pull(other)
1376 1376 if not r:
1377 1377 if opts['update']:
1378 1378 return update(ui, repo)
1379 1379 else:
1380 1380 ui.status(_("(run 'hg update' to get a working copy)\n"))
1381 1381
1382 1382 return r
1383 1383
1384 1384 def push(ui, repo, dest="default-push", force=False, ssh=None, remotecmd=None):
1385 1385 """push changes to the specified destination"""
1386 1386 dest = ui.expandpath(dest)
1387 1387 ui.status('pushing to %s\n' % (dest))
1388 1388
1389 1389 if ssh:
1390 1390 ui.setconfig("ui", "ssh", ssh)
1391 1391 if remotecmd:
1392 1392 ui.setconfig("ui", "remotecmd", remotecmd)
1393 1393
1394 1394 other = hg.repository(ui, dest)
1395 1395 r = repo.push(other, force)
1396 1396 return r
1397 1397
1398 1398 def rawcommit(ui, repo, *flist, **rc):
1399 1399 "raw commit interface"
1400 1400 if rc['text']:
1401 1401 ui.warn(_("Warning: -t and --text is deprecated,"
1402 1402 " please use -m or --message instead.\n"))
1403 1403 message = rc['message'] or rc['text']
1404 1404 if not message and rc['logfile']:
1405 1405 try:
1406 1406 message = open(rc['logfile']).read()
1407 1407 except IOError:
1408 1408 pass
1409 1409 if not message and not rc['logfile']:
1410 1410 raise util.Abort(_("missing commit message"))
1411 1411
1412 1412 files = relpath(repo, list(flist))
1413 1413 if rc['files']:
1414 1414 files += open(rc['files']).read().splitlines()
1415 1415
1416 1416 rc['parent'] = map(repo.lookup, rc['parent'])
1417 1417
1418 1418 try:
1419 1419 repo.rawcommit(files, message, rc['user'], rc['date'], *rc['parent'])
1420 1420 except ValueError, inst:
1421 1421 raise util.Abort(str(inst))
1422 1422
1423 1423 def recover(ui, repo):
1424 1424 """roll back an interrupted transaction"""
1425 1425 repo.recover()
1426 1426
1427 1427 def remove(ui, repo, pat, *pats, **opts):
1428 1428 """remove the specified files on the next commit"""
1429 1429 names = []
1430 1430 def okaytoremove(abs, rel, exact):
1431 1431 c, a, d, u = repo.changes(files = [abs])
1432 1432 reason = None
1433 1433 if c: reason = _('is modified')
1434 1434 elif a: reason = _('has been marked for add')
1435 1435 elif u: reason = _('is not managed')
1436 1436 if reason:
1437 1437 if exact: ui.warn(_('not removing %s: file %s\n') % (rel, reason))
1438 1438 else:
1439 1439 return True
1440 1440 for src, abs, rel, exact in walk(repo, (pat,) + pats, opts):
1441 1441 if okaytoremove(abs, rel, exact):
1442 1442 if ui.verbose or not exact: ui.status(_('removing %s\n') % rel)
1443 1443 names.append(abs)
1444 1444 repo.remove(names, unlink=True)
1445 1445
1446 1446 def rename(ui, repo, *pats, **opts):
1447 1447 """rename files; equivalent of copy + remove"""
1448 1448 errs, copied = docopy(ui, repo, pats, opts)
1449 1449 names = []
1450 1450 for abs, rel, exact in copied:
1451 1451 if ui.verbose or not exact: ui.status(_('removing %s\n') % rel)
1452 1452 names.append(abs)
1453 1453 repo.remove(names, unlink=True)
1454 1454 return errs
1455 1455
1456 1456 def revert(ui, repo, *names, **opts):
1457 1457 """revert modified files or dirs back to their unmodified states"""
1458 1458 node = opts['rev'] and repo.lookup(opts['rev']) or \
1459 1459 repo.dirstate.parents()[0]
1460 1460 root = os.path.realpath(repo.root)
1461 1461
1462 1462 def trimpath(p):
1463 1463 p = os.path.realpath(p)
1464 1464 if p.startswith(root):
1465 1465 rest = p[len(root):]
1466 1466 if not rest:
1467 1467 return rest
1468 1468 if p.startswith(os.sep):
1469 1469 return rest[1:]
1470 1470 return p
1471 1471
1472 1472 relnames = map(trimpath, names or [os.getcwd()])
1473 1473 chosen = {}
1474 1474
1475 1475 def choose(name):
1476 1476 def body(name):
1477 1477 for r in relnames:
1478 1478 if not name.startswith(r):
1479 1479 continue
1480 1480 rest = name[len(r):]
1481 1481 if not rest:
1482 1482 return r, True
1483 1483 depth = rest.count(os.sep)
1484 1484 if not r:
1485 1485 if depth == 0 or not opts['nonrecursive']:
1486 1486 return r, True
1487 1487 elif rest[0] == os.sep:
1488 1488 if depth == 1 or not opts['nonrecursive']:
1489 1489 return r, True
1490 1490 return None, False
1491 1491 relname, ret = body(name)
1492 1492 if ret:
1493 1493 chosen[relname] = 1
1494 1494 return ret
1495 1495
1496 1496 r = repo.update(node, False, True, choose, False)
1497 1497 for n in relnames:
1498 1498 if n not in chosen:
1499 1499 ui.warn(_('error: no matches for %s\n') % n)
1500 1500 r = 1
1501 1501 sys.stdout.flush()
1502 1502 return r
1503 1503
1504 1504 def root(ui, repo):
1505 1505 """print the root (top) of the current working dir"""
1506 1506 ui.write(repo.root + "\n")
1507 1507
1508 1508 def serve(ui, repo, **opts):
1509 1509 """export the repository via HTTP"""
1510 1510
1511 1511 if opts["stdio"]:
1512 1512 fin, fout = sys.stdin, sys.stdout
1513 1513 sys.stdout = sys.stderr
1514 1514
1515 # Prevent insertion/deletion of CRs
1516 util.set_binary(fin)
1517 util.set_binary(fout)
1518
1515 1519 def getarg():
1516 1520 argline = fin.readline()[:-1]
1517 1521 arg, l = argline.split()
1518 1522 val = fin.read(int(l))
1519 1523 return arg, val
1520 1524 def respond(v):
1521 1525 fout.write("%d\n" % len(v))
1522 1526 fout.write(v)
1523 1527 fout.flush()
1524 1528
1525 1529 lock = None
1526 1530
1527 1531 while 1:
1528 1532 cmd = fin.readline()[:-1]
1529 1533 if cmd == '':
1530 1534 return
1531 1535 if cmd == "heads":
1532 1536 h = repo.heads()
1533 1537 respond(" ".join(map(hex, h)) + "\n")
1534 1538 if cmd == "lock":
1535 1539 lock = repo.lock()
1536 1540 respond("")
1537 1541 if cmd == "unlock":
1538 1542 if lock:
1539 1543 lock.release()
1540 1544 lock = None
1541 1545 respond("")
1542 1546 elif cmd == "branches":
1543 1547 arg, nodes = getarg()
1544 1548 nodes = map(bin, nodes.split(" "))
1545 1549 r = []
1546 1550 for b in repo.branches(nodes):
1547 1551 r.append(" ".join(map(hex, b)) + "\n")
1548 1552 respond("".join(r))
1549 1553 elif cmd == "between":
1550 1554 arg, pairs = getarg()
1551 1555 pairs = [map(bin, p.split("-")) for p in pairs.split(" ")]
1552 1556 r = []
1553 1557 for b in repo.between(pairs):
1554 1558 r.append(" ".join(map(hex, b)) + "\n")
1555 1559 respond("".join(r))
1556 1560 elif cmd == "changegroup":
1557 1561 nodes = []
1558 1562 arg, roots = getarg()
1559 1563 nodes = map(bin, roots.split(" "))
1560 1564
1561 1565 cg = repo.changegroup(nodes)
1562 1566 while 1:
1563 1567 d = cg.read(4096)
1564 1568 if not d:
1565 1569 break
1566 1570 fout.write(d)
1567 1571
1568 1572 fout.flush()
1569 1573
1570 1574 elif cmd == "addchangegroup":
1571 1575 if not lock:
1572 1576 respond("not locked")
1573 1577 continue
1574 1578 respond("")
1575 1579
1576 1580 r = repo.addchangegroup(fin)
1577 1581 respond("")
1578 1582
1579 1583 optlist = "name templates style address port ipv6 accesslog errorlog"
1580 1584 for o in optlist.split():
1581 1585 if opts[o]:
1582 1586 ui.setconfig("web", o, opts[o])
1583 1587
1584 1588 try:
1585 1589 httpd = hgweb.create_server(repo)
1586 1590 except socket.error, inst:
1587 1591 raise util.Abort('cannot start server: ' + inst.args[1])
1588 1592
1589 1593 if ui.verbose:
1590 1594 addr, port = httpd.socket.getsockname()
1591 1595 if addr == '0.0.0.0':
1592 1596 addr = socket.gethostname()
1593 1597 else:
1594 1598 try:
1595 1599 addr = socket.gethostbyaddr(addr)[0]
1596 1600 except socket.error:
1597 1601 pass
1598 1602 if port != 80:
1599 1603 ui.status(_('listening at http://%s:%d/\n') % (addr, port))
1600 1604 else:
1601 1605 ui.status(_('listening at http://%s/\n') % addr)
1602 1606 httpd.serve_forever()
1603 1607
1604 1608 def status(ui, repo, *pats, **opts):
1605 1609 '''show changed files in the working directory
1606 1610
1607 1611 M = modified
1608 1612 A = added
1609 1613 R = removed
1610 1614 ? = not tracked
1611 1615 '''
1612 1616
1613 1617 cwd = repo.getcwd()
1614 1618 files, matchfn, anypats = matchpats(repo, cwd, pats, opts)
1615 1619 (c, a, d, u) = [[util.pathto(cwd, x) for x in n]
1616 1620 for n in repo.changes(files=files, match=matchfn)]
1617 1621
1618 1622 changetypes = [(_('modified'), 'M', c),
1619 1623 (_('added'), 'A', a),
1620 1624 (_('removed'), 'R', d),
1621 1625 (_('unknown'), '?', u)]
1622 1626
1623 1627 end = opts['print0'] and '\0' or '\n'
1624 1628
1625 1629 for opt, char, changes in ([ct for ct in changetypes if opts[ct[0]]]
1626 1630 or changetypes):
1627 1631 if opts['no_status']:
1628 1632 format = "%%s%s" % end
1629 1633 else:
1630 1634 format = "%s %%s%s" % (char, end);
1631 1635
1632 1636 for f in changes:
1633 1637 ui.write(format % f)
1634 1638
1635 1639 def tag(ui, repo, name, rev=None, **opts):
1636 1640 """add a tag for the current tip or a given revision"""
1637 1641 if opts['text']:
1638 1642 ui.warn(_("Warning: -t and --text is deprecated,"
1639 1643 " please use -m or --message instead.\n"))
1640 1644 if name == "tip":
1641 1645 raise util.Abort(_("the name 'tip' is reserved"))
1642 1646 if rev:
1643 1647 r = hex(repo.lookup(rev))
1644 1648 else:
1645 1649 r = hex(repo.changelog.tip())
1646 1650
1647 1651 if name.find(revrangesep) >= 0:
1648 1652 raise util.Abort(_("'%s' cannot be used in a tag name") % revrangesep)
1649 1653
1650 1654 if opts['local']:
1651 1655 repo.opener("localtags", "a").write("%s %s\n" % (r, name))
1652 1656 return
1653 1657
1654 1658 (c, a, d, u) = repo.changes()
1655 1659 for x in (c, a, d, u):
1656 1660 if ".hgtags" in x:
1657 1661 raise util.Abort(_("working copy of .hgtags is changed "
1658 1662 "(please commit .hgtags manually)"))
1659 1663
1660 1664 repo.wfile(".hgtags", "ab").write("%s %s\n" % (r, name))
1661 1665 if repo.dirstate.state(".hgtags") == '?':
1662 1666 repo.add([".hgtags"])
1663 1667
1664 1668 message = (opts['message'] or opts['text'] or
1665 1669 _("Added tag %s for changeset %s") % (name, r))
1666 1670 try:
1667 1671 repo.commit([".hgtags"], message, opts['user'], opts['date'])
1668 1672 except ValueError, inst:
1669 1673 raise util.Abort(str(inst))
1670 1674
1671 1675 def tags(ui, repo):
1672 1676 """list repository tags"""
1673 1677
1674 1678 l = repo.tagslist()
1675 1679 l.reverse()
1676 1680 for t, n in l:
1677 1681 try:
1678 1682 r = "%5d:%s" % (repo.changelog.rev(n), hex(n))
1679 1683 except KeyError:
1680 1684 r = " ?:?"
1681 1685 ui.write("%-30s %s\n" % (t, r))
1682 1686
1683 1687 def tip(ui, repo):
1684 1688 """show the tip revision"""
1685 1689 n = repo.changelog.tip()
1686 1690 show_changeset(ui, repo, changenode=n)
1687 1691
1688 1692 def unbundle(ui, repo, fname):
1689 1693 """apply a changegroup file"""
1690 1694 f = urllib.urlopen(fname)
1691 1695
1692 1696 if f.read(4) != "HG10":
1693 1697 raise util.Abort(_("%s: not a Mercurial bundle file") % fname)
1694 1698
1695 1699 def bzgenerator(f):
1696 1700 zd = bz2.BZ2Decompressor()
1697 1701 for chunk in f:
1698 1702 yield zd.decompress(chunk)
1699 1703 yield zd.flush()
1700 1704
1701 1705 bzgen = bzgenerator(util.filechunkiter(f, 4096))
1702 1706 repo.addchangegroup(util.chunkbuffer(bzgen))
1703 1707
1704 1708 def undo(ui, repo):
1705 1709 """undo the last commit or pull
1706 1710
1707 1711 Roll back the last pull or commit transaction on the
1708 1712 repository, restoring the project to its earlier state.
1709 1713
1710 1714 This command should be used with care. There is only one level of
1711 1715 undo and there is no redo.
1712 1716
1713 1717 This command is not intended for use on public repositories. Once
1714 1718 a change is visible for pull by other users, undoing it locally is
1715 1719 ineffective.
1716 1720 """
1717 1721 repo.undo()
1718 1722
1719 1723 def update(ui, repo, node=None, merge=False, clean=False, branch=None):
1720 1724 '''update or merge working directory
1721 1725
1722 1726 If there are no outstanding changes in the working directory and
1723 1727 there is a linear relationship between the current version and the
1724 1728 requested version, the result is the requested version.
1725 1729
1726 1730 Otherwise the result is a merge between the contents of the
1727 1731 current working directory and the requested version. Files that
1728 1732 changed between either parent are marked as changed for the next
1729 1733 commit and a commit must be performed before any further updates
1730 1734 are allowed.
1731 1735 '''
1732 1736 if branch:
1733 1737 br = repo.branchlookup(branch=branch)
1734 1738 found = []
1735 1739 for x in br:
1736 1740 if branch in br[x]:
1737 1741 found.append(x)
1738 1742 if len(found) > 1:
1739 1743 ui.warn(_("Found multiple heads for %s\n") % branch)
1740 1744 for x in found:
1741 1745 show_changeset(ui, repo, changenode=x, brinfo=br)
1742 1746 return 1
1743 1747 if len(found) == 1:
1744 1748 node = found[0]
1745 1749 ui.warn(_("Using head %s for branch %s\n") % (short(node), branch))
1746 1750 else:
1747 1751 ui.warn(_("branch %s not found\n") % (branch))
1748 1752 return 1
1749 1753 else:
1750 1754 node = node and repo.lookup(node) or repo.changelog.tip()
1751 1755 return repo.update(node, allow=merge, force=clean)
1752 1756
1753 1757 def verify(ui, repo):
1754 1758 """verify the integrity of the repository"""
1755 1759 return repo.verify()
1756 1760
1757 1761 # Command options and aliases are listed here, alphabetically
1758 1762
1759 1763 table = {
1760 1764 "^add":
1761 1765 (add,
1762 1766 [('I', 'include', [], _('include path in search')),
1763 1767 ('X', 'exclude', [], _('exclude path from search'))],
1764 1768 "hg add [OPTION]... [FILE]..."),
1765 1769 "addremove":
1766 1770 (addremove,
1767 1771 [('I', 'include', [], _('include path in search')),
1768 1772 ('X', 'exclude', [], _('exclude path from search'))],
1769 1773 _("hg addremove [OPTION]... [FILE]...")),
1770 1774 "^annotate":
1771 1775 (annotate,
1772 1776 [('r', 'rev', '', _('revision')),
1773 1777 ('a', 'text', None, _('treat all files as text')),
1774 1778 ('u', 'user', None, _('show user')),
1775 1779 ('n', 'number', None, _('show revision number')),
1776 1780 ('c', 'changeset', None, _('show changeset')),
1777 1781 ('I', 'include', [], _('include path in search')),
1778 1782 ('X', 'exclude', [], _('exclude path from search'))],
1779 1783 _('hg annotate [OPTION]... FILE...')),
1780 1784 "bundle":
1781 1785 (bundle,
1782 1786 [],
1783 1787 _('hg bundle FILE DEST')),
1784 1788 "cat":
1785 1789 (cat,
1786 1790 [('I', 'include', [], _('include path in search')),
1787 1791 ('X', 'exclude', [], _('exclude path from search')),
1788 1792 ('o', 'output', "", _('output to file')),
1789 1793 ('r', 'rev', '', _('revision'))],
1790 1794 _('hg cat [OPTION]... FILE...')),
1791 1795 "^clone":
1792 1796 (clone,
1793 1797 [('U', 'noupdate', None, _('skip update after cloning')),
1794 1798 ('e', 'ssh', "", _('ssh command')),
1795 1799 ('', 'pull', None, _('use pull protocol to copy metadata')),
1796 1800 ('', 'remotecmd', "", _('remote hg command'))],
1797 1801 _('hg clone [OPTION]... SOURCE [DEST]')),
1798 1802 "^commit|ci":
1799 1803 (commit,
1800 1804 [('A', 'addremove', None, _('run add/remove during commit')),
1801 1805 ('I', 'include', [], _('include path in search')),
1802 1806 ('X', 'exclude', [], _('exclude path from search')),
1803 1807 ('m', 'message', "", _('commit message')),
1804 1808 ('t', 'text', "", _('commit message (deprecated: use -m)')),
1805 1809 ('l', 'logfile', "", _('commit message file')),
1806 1810 ('d', 'date', "", _('date code')),
1807 1811 ('u', 'user', "", _('user'))],
1808 1812 _('hg commit [OPTION]... [FILE]...')),
1809 1813 "copy|cp": (copy,
1810 1814 [('I', 'include', [], _('include path in search')),
1811 1815 ('X', 'exclude', [], _('exclude path from search')),
1812 1816 ('A', 'after', None, _('record a copy after it has happened')),
1813 1817 ('f', 'force', None, _('replace destination if it exists')),
1814 1818 ('p', 'parents', None, _('append source path to dest'))],
1815 1819 _('hg copy [OPTION]... [SOURCE]... DEST')),
1816 1820 "debugancestor": (debugancestor, [], _('debugancestor INDEX REV1 REV2')),
1817 1821 "debugcheckstate": (debugcheckstate, [], _('debugcheckstate')),
1818 1822 "debugconfig": (debugconfig, [], _('debugconfig')),
1819 1823 "debugsetparents": (debugsetparents, [], _('debugsetparents REV1 [REV2]')),
1820 1824 "debugstate": (debugstate, [], _('debugstate')),
1821 1825 "debugdata": (debugdata, [], _('debugdata FILE REV')),
1822 1826 "debugindex": (debugindex, [], _('debugindex FILE')),
1823 1827 "debugindexdot": (debugindexdot, [], _('debugindexdot FILE')),
1824 1828 "debugrename": (debugrename, [], _('debugrename FILE [REV]')),
1825 1829 "debugwalk":
1826 1830 (debugwalk,
1827 1831 [('I', 'include', [], _('include path in search')),
1828 1832 ('X', 'exclude', [], _('exclude path from search'))],
1829 1833 _('debugwalk [OPTION]... [FILE]...')),
1830 1834 "^diff":
1831 1835 (diff,
1832 1836 [('r', 'rev', [], _('revision')),
1833 1837 ('a', 'text', None, _('treat all files as text')),
1834 1838 ('I', 'include', [], _('include path in search')),
1835 1839 ('X', 'exclude', [], _('exclude path from search'))],
1836 1840 _('hg diff [-a] [-I] [-X] [-r REV1 [-r REV2]] [FILE]...')),
1837 1841 "^export":
1838 1842 (export,
1839 1843 [('o', 'output', "", _('output to file')),
1840 1844 ('a', 'text', None, _('treat all files as text'))],
1841 1845 _("hg export [-a] [-o OUTFILE] REV...")),
1842 1846 "forget":
1843 1847 (forget,
1844 1848 [('I', 'include', [], _('include path in search')),
1845 1849 ('X', 'exclude', [], _('exclude path from search'))],
1846 1850 _("hg forget [OPTION]... FILE...")),
1847 1851 "grep":
1848 1852 (grep,
1849 1853 [('0', 'print0', None, _('end fields with NUL')),
1850 1854 ('I', 'include', [], _('include path in search')),
1851 1855 ('X', 'exclude', [], _('include path in search')),
1852 1856 ('', 'all', None, _('print all revisions with matches')),
1853 1857 ('i', 'ignore-case', None, _('ignore case when matching')),
1854 1858 ('l', 'files-with-matches', None, _('print names of files and revs with matches')),
1855 1859 ('n', 'line-number', None, _('print line numbers')),
1856 1860 ('r', 'rev', [], _('search in revision rev')),
1857 1861 ('u', 'user', None, _('print user who made change'))],
1858 1862 _("hg grep [OPTION]... PATTERN [FILE]...")),
1859 1863 "heads":
1860 1864 (heads,
1861 1865 [('b', 'branches', None, _('find branch info'))],
1862 1866 _('hg heads [-b]')),
1863 1867 "help": (help_, [], _('hg help [COMMAND]')),
1864 1868 "identify|id": (identify, [], _('hg identify')),
1865 1869 "import|patch":
1866 1870 (import_,
1867 1871 [('p', 'strip', 1, _('path strip')),
1868 1872 ('f', 'force', None, _('skip check for outstanding changes')),
1869 1873 ('b', 'base', "", _('base path'))],
1870 1874 _("hg import [-f] [-p NUM] [-b BASE] PATCH...")),
1871 1875 "incoming|in": (incoming,
1872 1876 [('p', 'patch', None, _('show patch'))],
1873 1877 _('hg incoming [-p] [SOURCE]')),
1874 1878 "^init": (init, [], _('hg init [DEST]')),
1875 1879 "locate":
1876 1880 (locate,
1877 1881 [('r', 'rev', '', _('revision')),
1878 1882 ('0', 'print0', None, _('end filenames with NUL')),
1879 1883 ('f', 'fullpath', None, _('print complete paths')),
1880 1884 ('I', 'include', [], _('include path in search')),
1881 1885 ('X', 'exclude', [], _('exclude path from search'))],
1882 1886 _('hg locate [OPTION]... [PATTERN]...')),
1883 1887 "^log|history":
1884 1888 (log,
1885 1889 [('I', 'include', [], _('include path in search')),
1886 1890 ('X', 'exclude', [], _('exclude path from search')),
1887 1891 ('b', 'branch', None, _('show branches')),
1888 1892 ('k', 'keyword', [], _('search for a keyword')),
1889 1893 ('r', 'rev', [], _('revision')),
1890 1894 ('p', 'patch', None, _('show patch'))],
1891 1895 _('hg log [-I] [-X] [-r REV]... [-p] [FILE]')),
1892 1896 "manifest": (manifest, [], _('hg manifest [REV]')),
1893 1897 "outgoing|out": (outgoing,
1894 1898 [('p', 'patch', None, _('show patch'))],
1895 1899 _('hg outgoing [-p] [DEST]')),
1896 1900 "parents": (parents, [], _('hg parents [REV]')),
1897 1901 "paths": (paths, [], _('hg paths [NAME]')),
1898 1902 "^pull":
1899 1903 (pull,
1900 1904 [('u', 'update', None, _('update working directory')),
1901 1905 ('e', 'ssh', "", _('ssh command')),
1902 1906 ('', 'remotecmd', "", _('remote hg command'))],
1903 1907 _('hg pull [-u] [-e FILE] [--remotecmd FILE] [SOURCE]')),
1904 1908 "^push":
1905 1909 (push,
1906 1910 [('f', 'force', None, _('force push')),
1907 1911 ('e', 'ssh', "", _('ssh command')),
1908 1912 ('', 'remotecmd', "", _('remote hg command'))],
1909 1913 _('hg push [-f] [-e FILE] [--remotecmd FILE] [DEST]')),
1910 1914 "rawcommit":
1911 1915 (rawcommit,
1912 1916 [('p', 'parent', [], _('parent')),
1913 1917 ('d', 'date', "", _('date code')),
1914 1918 ('u', 'user', "", _('user')),
1915 1919 ('F', 'files', "", _('file list')),
1916 1920 ('m', 'message', "", _('commit message')),
1917 1921 ('t', 'text', "", _('commit message (deprecated: use -m)')),
1918 1922 ('l', 'logfile', "", _('commit message file'))],
1919 1923 _('hg rawcommit [OPTION]... [FILE]...')),
1920 1924 "recover": (recover, [], _("hg recover")),
1921 1925 "^remove|rm": (remove,
1922 1926 [('I', 'include', [], _('include path in search')),
1923 1927 ('X', 'exclude', [], _('exclude path from search'))],
1924 1928 _("hg remove [OPTION]... FILE...")),
1925 1929 "rename|mv": (rename,
1926 1930 [('I', 'include', [], _('include path in search')),
1927 1931 ('X', 'exclude', [], _('exclude path from search')),
1928 1932 ('A', 'after', None, _('record a copy after it has happened')),
1929 1933 ('f', 'force', None, _('replace destination if it exists')),
1930 1934 ('p', 'parents', None, _('append source path to dest'))],
1931 1935 _('hg rename [OPTION]... [SOURCE]... DEST')),
1932 1936 "^revert":
1933 1937 (revert,
1934 1938 [("n", "nonrecursive", None, _("don't recurse into subdirs")),
1935 1939 ("r", "rev", "", _("revision"))],
1936 1940 _("hg revert [-n] [-r REV] [NAME]...")),
1937 1941 "root": (root, [], _("hg root")),
1938 1942 "^serve":
1939 1943 (serve,
1940 1944 [('A', 'accesslog', '', _('access log file')),
1941 1945 ('E', 'errorlog', '', _('error log file')),
1942 1946 ('p', 'port', 0, _('listen port')),
1943 1947 ('a', 'address', '', _('interface address')),
1944 1948 ('n', 'name', "", _('repository name')),
1945 1949 ('', 'stdio', None, _('for remote clients')),
1946 1950 ('t', 'templates', "", _('template directory')),
1947 1951 ('', 'style', "", _('template style')),
1948 1952 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4'))],
1949 1953 _("hg serve [OPTION]...")),
1950 1954 "^status":
1951 1955 (status,
1952 1956 [('m', 'modified', None, _('show only modified files')),
1953 1957 ('a', 'added', None, _('show only added files')),
1954 1958 ('r', 'removed', None, _('show only removed files')),
1955 1959 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
1956 1960 ('n', 'no-status', None, _('hide status prefix')),
1957 1961 ('0', 'print0', None, _('end filenames with NUL')),
1958 1962 ('I', 'include', [], _('include path in search')),
1959 1963 ('X', 'exclude', [], _('exclude path from search'))],
1960 1964 _("hg status [OPTION]... [FILE]...")),
1961 1965 "tag":
1962 1966 (tag,
1963 1967 [('l', 'local', None, _('make the tag local')),
1964 1968 ('m', 'message', "", _('commit message')),
1965 1969 ('t', 'text', "", _('commit message (deprecated: use -m)')),
1966 1970 ('d', 'date', "", _('date code')),
1967 1971 ('u', 'user', "", _('user'))],
1968 1972 _('hg tag [OPTION]... NAME [REV]')),
1969 1973 "tags": (tags, [], _('hg tags')),
1970 1974 "tip": (tip, [], _('hg tip')),
1971 1975 "unbundle":
1972 1976 (unbundle,
1973 1977 [],
1974 1978 _('hg unbundle FILE')),
1975 1979 "undo": (undo, [], _('hg undo')),
1976 1980 "^update|up|checkout|co":
1977 1981 (update,
1978 1982 [('b', 'branch', "", _('checkout the head of a specific branch')),
1979 1983 ('m', 'merge', None, _('allow merging of conflicts')),
1980 1984 ('C', 'clean', None, _('overwrite locally modified files'))],
1981 1985 _('hg update [-b TAG] [-m] [-C] [REV]')),
1982 1986 "verify": (verify, [], _('hg verify')),
1983 1987 "version": (show_version, [], _('hg version')),
1984 1988 }
1985 1989
1986 1990 globalopts = [
1987 1991 ('R', 'repository', "", _('repository root directory')),
1988 1992 ('', 'cwd', '', _('change working directory')),
1989 1993 ('y', 'noninteractive', None, _('run non-interactively')),
1990 1994 ('q', 'quiet', None, _('quiet mode')),
1991 1995 ('v', 'verbose', None, _('verbose mode')),
1992 1996 ('', 'debug', None, _('debug mode')),
1993 1997 ('', 'debugger', None, _('start debugger')),
1994 1998 ('', 'traceback', None, _('print traceback on exception')),
1995 1999 ('', 'time', None, _('time how long the command takes')),
1996 2000 ('', 'profile', None, _('profile')),
1997 2001 ('', 'version', None, _('output version information and exit')),
1998 2002 ('h', 'help', None, _('display help and exit')),
1999 2003 ]
2000 2004
2001 2005 norepo = ("clone init version help debugancestor debugconfig debugdata"
2002 2006 " debugindex debugindexdot paths")
2003 2007
2004 2008 def find(cmd):
2005 2009 for e in table.keys():
2006 2010 if re.match("(%s)$" % e, cmd):
2007 2011 return e, table[e]
2008 2012
2009 2013 raise UnknownCommand(cmd)
2010 2014
2011 2015 class SignalInterrupt(Exception):
2012 2016 """Exception raised on SIGTERM and SIGHUP."""
2013 2017
2014 2018 def catchterm(*args):
2015 2019 raise SignalInterrupt
2016 2020
2017 2021 def run():
2018 2022 sys.exit(dispatch(sys.argv[1:]))
2019 2023
2020 2024 class ParseError(Exception):
2021 2025 """Exception raised on errors in parsing the command line."""
2022 2026
2023 2027 def parse(args):
2024 2028 options = {}
2025 2029 cmdoptions = {}
2026 2030
2027 2031 try:
2028 2032 args = fancyopts.fancyopts(args, globalopts, options)
2029 2033 except fancyopts.getopt.GetoptError, inst:
2030 2034 raise ParseError(None, inst)
2031 2035
2032 2036 if args:
2033 2037 cmd, args = args[0], args[1:]
2034 2038 i = find(cmd)[1]
2035 2039 c = list(i[1])
2036 2040 else:
2037 2041 cmd = None
2038 2042 c = []
2039 2043
2040 2044 # combine global options into local
2041 2045 for o in globalopts:
2042 2046 c.append((o[0], o[1], options[o[1]], o[3]))
2043 2047
2044 2048 try:
2045 2049 args = fancyopts.fancyopts(args, c, cmdoptions)
2046 2050 except fancyopts.getopt.GetoptError, inst:
2047 2051 raise ParseError(cmd, inst)
2048 2052
2049 2053 # separate global options back out
2050 2054 for o in globalopts:
2051 2055 n = o[1]
2052 2056 options[n] = cmdoptions[n]
2053 2057 del cmdoptions[n]
2054 2058
2055 2059 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
2056 2060
2057 2061 def dispatch(args):
2058 2062 signal.signal(signal.SIGTERM, catchterm)
2059 2063 try:
2060 2064 signal.signal(signal.SIGHUP, catchterm)
2061 2065 except AttributeError:
2062 2066 pass
2063 2067
2064 2068 u = ui.ui()
2065 2069 external = []
2066 2070 for x in u.extensions():
2067 2071 if x[1]:
2068 2072 try:
2069 2073 mod = imp.load_source(x[0], x[1])
2070 2074 except:
2071 2075 u.warn(_("*** failed to import extension %s\n") % x[1])
2072 2076 continue
2073 2077 else:
2074 2078 def importh(name):
2075 2079 mod = __import__(name)
2076 2080 components = name.split('.')
2077 2081 for comp in components[1:]:
2078 2082 mod = getattr(mod, comp)
2079 2083 return mod
2080 2084 try:
2081 2085 mod = importh(x[0])
2082 2086 except:
2083 2087 u.warn(_("failed to import extension %s\n") % x[0])
2084 2088 continue
2085 2089
2086 2090 external.append(mod)
2087 2091 for x in external:
2088 2092 cmdtable = getattr(x, 'cmdtable', {})
2089 2093 for t in cmdtable:
2090 2094 if t in table:
2091 2095 u.warn(_("module %s overrides %s\n") % (x.__name__, t))
2092 2096 table.update(cmdtable)
2093 2097
2094 2098 try:
2095 2099 cmd, func, args, options, cmdoptions = parse(args)
2096 2100 except ParseError, inst:
2097 2101 if inst.args[0]:
2098 2102 u.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
2099 2103 help_(u, inst.args[0])
2100 2104 else:
2101 2105 u.warn(_("hg: %s\n") % inst.args[1])
2102 2106 help_(u, 'shortlist')
2103 2107 sys.exit(-1)
2104 2108 except UnknownCommand, inst:
2105 2109 u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
2106 2110 help_(u, 'shortlist')
2107 2111 sys.exit(1)
2108 2112
2109 2113 if options["time"]:
2110 2114 def get_times():
2111 2115 t = os.times()
2112 2116 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
2113 2117 t = (t[0], t[1], t[2], t[3], time.clock())
2114 2118 return t
2115 2119 s = get_times()
2116 2120 def print_time():
2117 2121 t = get_times()
2118 2122 u.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
2119 2123 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
2120 2124 atexit.register(print_time)
2121 2125
2122 2126 u.updateopts(options["verbose"], options["debug"], options["quiet"],
2123 2127 not options["noninteractive"])
2124 2128
2125 2129 # enter the debugger before command execution
2126 2130 if options['debugger']:
2127 2131 pdb.set_trace()
2128 2132
2129 2133 try:
2130 2134 try:
2131 2135 if options['help']:
2132 2136 help_(u, cmd, options['version'])
2133 2137 sys.exit(0)
2134 2138 elif options['version']:
2135 2139 show_version(u)
2136 2140 sys.exit(0)
2137 2141 elif not cmd:
2138 2142 help_(u, 'shortlist')
2139 2143 sys.exit(0)
2140 2144
2141 2145 if options['cwd']:
2142 2146 try:
2143 2147 os.chdir(options['cwd'])
2144 2148 except OSError, inst:
2145 2149 raise util.Abort('%s: %s' %
2146 2150 (options['cwd'], inst.strerror))
2147 2151
2148 2152 if cmd not in norepo.split():
2149 2153 path = options["repository"] or ""
2150 2154 repo = hg.repository(ui=u, path=path)
2151 2155 for x in external:
2152 2156 if hasattr(x, 'reposetup'): x.reposetup(u, repo)
2153 2157 d = lambda: func(u, repo, *args, **cmdoptions)
2154 2158 else:
2155 2159 d = lambda: func(u, *args, **cmdoptions)
2156 2160
2157 2161 if options['profile']:
2158 2162 import hotshot, hotshot.stats
2159 2163 prof = hotshot.Profile("hg.prof")
2160 2164 r = prof.runcall(d)
2161 2165 prof.close()
2162 2166 stats = hotshot.stats.load("hg.prof")
2163 2167 stats.strip_dirs()
2164 2168 stats.sort_stats('time', 'calls')
2165 2169 stats.print_stats(40)
2166 2170 return r
2167 2171 else:
2168 2172 return d()
2169 2173 except:
2170 2174 # enter the debugger when we hit an exception
2171 2175 if options['debugger']:
2172 2176 pdb.post_mortem(sys.exc_info()[2])
2173 2177 if options['traceback']:
2174 2178 traceback.print_exc()
2175 2179 raise
2176 2180 except hg.RepoError, inst:
2177 2181 u.warn(_("abort: "), inst, "!\n")
2178 2182 except revlog.RevlogError, inst:
2179 2183 u.warn(_("abort: "), inst, "!\n")
2180 2184 except SignalInterrupt:
2181 2185 u.warn(_("killed!\n"))
2182 2186 except KeyboardInterrupt:
2183 2187 try:
2184 2188 u.warn(_("interrupted!\n"))
2185 2189 except IOError, inst:
2186 2190 if inst.errno == errno.EPIPE:
2187 2191 if u.debugflag:
2188 2192 u.warn(_("\nbroken pipe\n"))
2189 2193 else:
2190 2194 raise
2191 2195 except IOError, inst:
2192 2196 if hasattr(inst, "code"):
2193 2197 u.warn(_("abort: %s\n") % inst)
2194 2198 elif hasattr(inst, "reason"):
2195 2199 u.warn(_("abort: error: %s\n") % inst.reason[1])
2196 2200 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
2197 2201 if u.debugflag:
2198 2202 u.warn(_("broken pipe\n"))
2199 2203 elif getattr(inst, "strerror", None):
2200 2204 if getattr(inst, "filename", None):
2201 2205 u.warn(_("abort: %s - %s\n") % (inst.strerror, inst.filename))
2202 2206 else:
2203 2207 u.warn(_("abort: %s\n") % inst.strerror)
2204 2208 else:
2205 2209 raise
2206 2210 except OSError, inst:
2207 2211 if hasattr(inst, "filename"):
2208 2212 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
2209 2213 else:
2210 2214 u.warn(_("abort: %s\n") % inst.strerror)
2211 2215 except util.Abort, inst:
2212 2216 u.warn(_('abort: '), inst.args[0] % inst.args[1:], '\n')
2213 2217 sys.exit(1)
2214 2218 except TypeError, inst:
2215 2219 # was this an argument error?
2216 2220 tb = traceback.extract_tb(sys.exc_info()[2])
2217 2221 if len(tb) > 2: # no
2218 2222 raise
2219 2223 u.debug(inst, "\n")
2220 2224 u.warn(_("%s: invalid arguments\n") % cmd)
2221 2225 help_(u, cmd)
2222 2226 except UnknownCommand, inst:
2223 2227 u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
2224 2228 help_(u, 'shortlist')
2225 2229 except SystemExit:
2226 2230 # don't catch this in the catch-all below
2227 2231 raise
2228 2232 except:
2229 2233 u.warn(_("** unknown exception encountered, details follow\n"))
2230 2234 u.warn(_("** report bug details to mercurial@selenic.com\n"))
2231 2235 raise
2232 2236
2233 2237 sys.exit(-1)
@@ -1,596 +1,603 b''
1 1 """
2 2 util.py - Mercurial utility functions and platform specfic implementations
3 3
4 4 Copyright 2005 K. Thananchayan <thananck@yahoo.com>
5 5
6 6 This software may be used and distributed according to the terms
7 7 of the GNU General Public License, incorporated herein by reference.
8 8
9 9 This contains helper routines that are independent of the SCM core and hide
10 10 platform-specific details from the core.
11 11 """
12 12
13 13 import os, errno
14 14 from i18n import gettext as _
15 15 from demandload import *
16 16 demandload(globals(), "re cStringIO shutil popen2 tempfile threading time")
17 17
18 18 def pipefilter(s, cmd):
19 19 '''filter string S through command CMD, returning its output'''
20 20 (pout, pin) = popen2.popen2(cmd, -1, 'b')
21 21 def writer():
22 22 pin.write(s)
23 23 pin.close()
24 24
25 25 # we should use select instead on UNIX, but this will work on most
26 26 # systems, including Windows
27 27 w = threading.Thread(target=writer)
28 28 w.start()
29 29 f = pout.read()
30 30 pout.close()
31 31 w.join()
32 32 return f
33 33
34 34 def tempfilter(s, cmd):
35 35 '''filter string S through a pair of temporary files with CMD.
36 36 CMD is used as a template to create the real command to be run,
37 37 with the strings INFILE and OUTFILE replaced by the real names of
38 38 the temporary files generated.'''
39 39 inname, outname = None, None
40 40 try:
41 41 infd, inname = tempfile.mkstemp(prefix='hgfin')
42 42 fp = os.fdopen(infd, 'wb')
43 43 fp.write(s)
44 44 fp.close()
45 45 outfd, outname = tempfile.mkstemp(prefix='hgfout')
46 46 os.close(outfd)
47 47 cmd = cmd.replace('INFILE', inname)
48 48 cmd = cmd.replace('OUTFILE', outname)
49 49 code = os.system(cmd)
50 50 if code: raise Abort(_("command '%s' failed: %s") %
51 51 (cmd, explain_exit(code)))
52 52 return open(outname, 'rb').read()
53 53 finally:
54 54 try:
55 55 if inname: os.unlink(inname)
56 56 except: pass
57 57 try:
58 58 if outname: os.unlink(outname)
59 59 except: pass
60 60
61 61 filtertable = {
62 62 'tempfile:': tempfilter,
63 63 'pipe:': pipefilter,
64 64 }
65 65
66 66 def filter(s, cmd):
67 67 "filter a string through a command that transforms its input to its output"
68 68 for name, fn in filtertable.iteritems():
69 69 if cmd.startswith(name):
70 70 return fn(s, cmd[len(name):].lstrip())
71 71 return pipefilter(s, cmd)
72 72
73 73 def patch(strip, patchname, ui):
74 74 """apply the patch <patchname> to the working directory.
75 75 a list of patched files is returned"""
76 76 fp = os.popen('patch -p%d < "%s"' % (strip, patchname))
77 77 files = {}
78 78 for line in fp:
79 79 line = line.rstrip()
80 80 ui.status("%s\n" % line)
81 81 if line.startswith('patching file '):
82 82 pf = parse_patch_output(line)
83 83 files.setdefault(pf, 1)
84 84 code = fp.close()
85 85 if code:
86 86 raise Abort(_("patch command failed: %s") % explain_exit(code)[0])
87 87 return files.keys()
88 88
89 89 def binary(s):
90 90 """return true if a string is binary data using diff's heuristic"""
91 91 if s and '\0' in s[:4096]:
92 92 return True
93 93 return False
94 94
95 95 def unique(g):
96 96 """return the uniq elements of iterable g"""
97 97 seen = {}
98 98 for f in g:
99 99 if f not in seen:
100 100 seen[f] = 1
101 101 yield f
102 102
103 103 class Abort(Exception):
104 104 """Raised if a command needs to print an error and exit."""
105 105
106 106 def always(fn): return True
107 107 def never(fn): return False
108 108
109 109 def globre(pat, head='^', tail='$'):
110 110 "convert a glob pattern into a regexp"
111 111 i, n = 0, len(pat)
112 112 res = ''
113 113 group = False
114 114 def peek(): return i < n and pat[i]
115 115 while i < n:
116 116 c = pat[i]
117 117 i = i+1
118 118 if c == '*':
119 119 if peek() == '*':
120 120 i += 1
121 121 res += '.*'
122 122 else:
123 123 res += '[^/]*'
124 124 elif c == '?':
125 125 res += '.'
126 126 elif c == '[':
127 127 j = i
128 128 if j < n and pat[j] in '!]':
129 129 j += 1
130 130 while j < n and pat[j] != ']':
131 131 j += 1
132 132 if j >= n:
133 133 res += '\\['
134 134 else:
135 135 stuff = pat[i:j].replace('\\','\\\\')
136 136 i = j + 1
137 137 if stuff[0] == '!':
138 138 stuff = '^' + stuff[1:]
139 139 elif stuff[0] == '^':
140 140 stuff = '\\' + stuff
141 141 res = '%s[%s]' % (res, stuff)
142 142 elif c == '{':
143 143 group = True
144 144 res += '(?:'
145 145 elif c == '}' and group:
146 146 res += ')'
147 147 group = False
148 148 elif c == ',' and group:
149 149 res += '|'
150 150 else:
151 151 res += re.escape(c)
152 152 return head + res + tail
153 153
154 154 _globchars = {'[': 1, '{': 1, '*': 1, '?': 1}
155 155
156 156 def pathto(n1, n2):
157 157 '''return the relative path from one place to another.
158 158 this returns a path in the form used by the local filesystem, not hg.'''
159 159 if not n1: return localpath(n2)
160 160 a, b = n1.split('/'), n2.split('/')
161 161 a.reverse(), b.reverse()
162 162 while a and b and a[-1] == b[-1]:
163 163 a.pop(), b.pop()
164 164 b.reverse()
165 165 return os.sep.join((['..'] * len(a)) + b)
166 166
167 167 def canonpath(root, cwd, myname):
168 168 """return the canonical path of myname, given cwd and root"""
169 169 rootsep = root + os.sep
170 170 name = myname
171 171 if not name.startswith(os.sep):
172 172 name = os.path.join(root, cwd, name)
173 173 name = os.path.normpath(name)
174 174 if name.startswith(rootsep):
175 175 return pconvert(name[len(rootsep):])
176 176 elif name == root:
177 177 return ''
178 178 else:
179 179 raise Abort('%s not under root' % myname)
180 180
181 181 def matcher(canonroot, cwd='', names=['.'], inc=[], exc=[], head=''):
182 182 return _matcher(canonroot, cwd, names, inc, exc, head, 'glob')
183 183
184 184 def cmdmatcher(canonroot, cwd='', names=['.'], inc=[], exc=[], head=''):
185 185 if os.name == 'nt':
186 186 dflt_pat = 'glob'
187 187 else:
188 188 dflt_pat = 'relpath'
189 189 return _matcher(canonroot, cwd, names, inc, exc, head, dflt_pat)
190 190
191 191 def _matcher(canonroot, cwd, names, inc, exc, head, dflt_pat):
192 192 """build a function to match a set of file patterns
193 193
194 194 arguments:
195 195 canonroot - the canonical root of the tree you're matching against
196 196 cwd - the current working directory, if relevant
197 197 names - patterns to find
198 198 inc - patterns to include
199 199 exc - patterns to exclude
200 200 head - a regex to prepend to patterns to control whether a match is rooted
201 201
202 202 a pattern is one of:
203 203 'glob:<rooted glob>'
204 204 're:<rooted regexp>'
205 205 'path:<rooted path>'
206 206 'relglob:<relative glob>'
207 207 'relpath:<relative path>'
208 208 'relre:<relative regexp>'
209 209 '<rooted path or regexp>'
210 210
211 211 returns:
212 212 a 3-tuple containing
213 213 - list of explicit non-pattern names passed in
214 214 - a bool match(filename) function
215 215 - a bool indicating if any patterns were passed in
216 216
217 217 todo:
218 218 make head regex a rooted bool
219 219 """
220 220
221 221 def patkind(name, dflt_pat='glob'):
222 222 for prefix in 're', 'glob', 'path', 'relglob', 'relpath', 'relre':
223 223 if name.startswith(prefix + ':'): return name.split(':', 1)
224 224 return dflt_pat, name
225 225
226 226 def contains_glob(name):
227 227 for c in name:
228 228 if c in _globchars: return True
229 229 return False
230 230
231 231 def regex(kind, name, tail):
232 232 '''convert a pattern into a regular expression'''
233 233 if kind == 're':
234 234 return name
235 235 elif kind == 'path':
236 236 return '^' + re.escape(name) + '(?:/|$)'
237 237 elif kind == 'relglob':
238 238 return head + globre(name, '(?:|.*/)', tail)
239 239 elif kind == 'relpath':
240 240 return head + re.escape(name) + tail
241 241 elif kind == 'relre':
242 242 if name.startswith('^'):
243 243 return name
244 244 return '.*' + name
245 245 return head + globre(name, '', tail)
246 246
247 247 def matchfn(pats, tail):
248 248 """build a matching function from a set of patterns"""
249 249 if pats:
250 250 pat = '(?:%s)' % '|'.join([regex(k, p, tail) for (k, p) in pats])
251 251 return re.compile(pat).match
252 252
253 253 def globprefix(pat):
254 254 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
255 255 root = []
256 256 for p in pat.split(os.sep):
257 257 if contains_glob(p): break
258 258 root.append(p)
259 259 return '/'.join(root)
260 260
261 261 pats = []
262 262 files = []
263 263 roots = []
264 264 for kind, name in [patkind(p, dflt_pat) for p in names]:
265 265 if kind in ('glob', 'relpath'):
266 266 name = canonpath(canonroot, cwd, name)
267 267 if name == '':
268 268 kind, name = 'glob', '**'
269 269 if kind in ('glob', 'path', 're'):
270 270 pats.append((kind, name))
271 271 if kind == 'glob':
272 272 root = globprefix(name)
273 273 if root: roots.append(root)
274 274 elif kind == 'relpath':
275 275 files.append((kind, name))
276 276 roots.append(name)
277 277
278 278 patmatch = matchfn(pats, '$') or always
279 279 filematch = matchfn(files, '(?:/|$)') or always
280 280 incmatch = always
281 281 if inc:
282 282 incmatch = matchfn(map(patkind, inc), '(?:/|$)')
283 283 excmatch = lambda fn: False
284 284 if exc:
285 285 excmatch = matchfn(map(patkind, exc), '(?:/|$)')
286 286
287 287 return (roots,
288 288 lambda fn: (incmatch(fn) and not excmatch(fn) and
289 289 (fn.endswith('/') or
290 290 (not pats and not files) or
291 291 (pats and patmatch(fn)) or
292 292 (files and filematch(fn)))),
293 293 (inc or exc or (pats and pats != [('glob', '**')])) and True)
294 294
295 295 def system(cmd, errprefix=None):
296 296 """execute a shell command that must succeed"""
297 297 rc = os.system(cmd)
298 298 if rc:
299 299 errmsg = "%s %s" % (os.path.basename(cmd.split(None, 1)[0]),
300 300 explain_exit(rc)[0])
301 301 if errprefix:
302 302 errmsg = "%s: %s" % (errprefix, errmsg)
303 303 raise Abort(errmsg)
304 304
305 305 def rename(src, dst):
306 306 """forcibly rename a file"""
307 307 try:
308 308 os.rename(src, dst)
309 309 except:
310 310 os.unlink(dst)
311 311 os.rename(src, dst)
312 312
313 313 def unlink(f):
314 314 """unlink and remove the directory if it is empty"""
315 315 os.unlink(f)
316 316 # try removing directories that might now be empty
317 317 try: os.removedirs(os.path.dirname(f))
318 318 except: pass
319 319
320 320 def copyfiles(src, dst, hardlink=None):
321 321 """Copy a directory tree using hardlinks if possible"""
322 322
323 323 if hardlink is None:
324 324 hardlink = (os.stat(src).st_dev ==
325 325 os.stat(os.path.dirname(dst)).st_dev)
326 326
327 327 if os.path.isdir(src):
328 328 os.mkdir(dst)
329 329 for name in os.listdir(src):
330 330 srcname = os.path.join(src, name)
331 331 dstname = os.path.join(dst, name)
332 332 copyfiles(srcname, dstname, hardlink)
333 333 else:
334 334 if hardlink:
335 335 try:
336 336 os_link(src, dst)
337 337 except:
338 338 hardlink = False
339 339 shutil.copy2(src, dst)
340 340 else:
341 341 shutil.copy2(src, dst)
342 342
343 343 def opener(base):
344 344 """
345 345 return a function that opens files relative to base
346 346
347 347 this function is used to hide the details of COW semantics and
348 348 remote file access from higher level code.
349 349 """
350 350 p = base
351 351 def o(path, mode="r", text=False):
352 352 f = os.path.join(p, path)
353 353
354 354 if not text:
355 355 mode += "b" # for that other OS
356 356
357 357 if mode[0] != "r":
358 358 try:
359 359 nlink = nlinks(f)
360 360 except OSError:
361 361 d = os.path.dirname(f)
362 362 if not os.path.isdir(d):
363 363 os.makedirs(d)
364 364 else:
365 365 if nlink > 1:
366 366 file(f + ".tmp", "wb").write(file(f, "rb").read())
367 367 rename(f+".tmp", f)
368 368
369 369 return file(f, mode)
370 370
371 371 return o
372 372
373 373 def _makelock_file(info, pathname):
374 374 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
375 375 os.write(ld, info)
376 376 os.close(ld)
377 377
378 378 def _readlock_file(pathname):
379 379 return file(pathname).read()
380 380
381 381 def nlinks(pathname):
382 382 """Return number of hardlinks for the given file."""
383 383 return os.stat(pathname).st_nlink
384 384
385 385 if hasattr(os, 'link'):
386 386 os_link = os.link
387 387 else:
388 388 def os_link(src, dst):
389 389 raise OSError(0, _("Hardlinks not supported"))
390 390
391 391 # Platform specific variants
392 392 if os.name == 'nt':
393 demandload(globals(), "msvcrt")
393 394 nulldev = 'NUL:'
394 395
395 396 try:
396 397 import win32api, win32process
397 398 filename = win32process.GetModuleFileNameEx(win32api.GetCurrentProcess(), 0)
398 399 systemrc = os.path.join(os.path.dirname(filename), 'mercurial.ini')
399 400
400 401 except ImportError:
401 402 systemrc = r'c:\mercurial\mercurial.ini'
402 403 pass
403 404
404 405 rcpath = (systemrc,
405 406 os.path.join(os.path.expanduser('~'), 'mercurial.ini'))
406 407
407 408 def parse_patch_output(output_line):
408 409 """parses the output produced by patch and returns the file name"""
409 410 pf = output_line[14:]
410 411 if pf[0] == '`':
411 412 pf = pf[1:-1] # Remove the quotes
412 413 return pf
413 414
414 415 try: # ActivePython can create hard links using win32file module
415 416 import win32file
416 417
417 418 def os_link(src, dst): # NB will only succeed on NTFS
418 419 win32file.CreateHardLink(dst, src)
419 420
420 421 def nlinks(pathname):
421 422 """Return number of hardlinks for the given file."""
422 423 try:
423 424 fh = win32file.CreateFile(pathname,
424 425 win32file.GENERIC_READ, win32file.FILE_SHARE_READ,
425 426 None, win32file.OPEN_EXISTING, 0, None)
426 427 res = win32file.GetFileInformationByHandle(fh)
427 428 fh.Close()
428 429 return res[7]
429 430 except:
430 431 return os.stat(pathname).st_nlink
431 432
432 433 except ImportError:
433 434 pass
434 435
435 436 def is_exec(f, last):
436 437 return last
437 438
438 439 def set_exec(f, mode):
439 440 pass
440 441
442 def set_binary(fd):
443 msvcrt.setmode(fd.fileno(), os.O_BINARY)
444
441 445 def pconvert(path):
442 446 return path.replace("\\", "/")
443 447
444 448 def localpath(path):
445 449 return path.replace('/', '\\')
446 450
447 451 def normpath(path):
448 452 return pconvert(os.path.normpath(path))
449 453
450 454 makelock = _makelock_file
451 455 readlock = _readlock_file
452 456
453 457 def explain_exit(code):
454 458 return _("exited with status %d") % code, code
455 459
456 460 else:
457 461 nulldev = '/dev/null'
458 462
459 463 hgrcd = '/etc/mercurial/hgrc.d'
460 464 hgrcs = []
461 465 if os.path.isdir(hgrcd):
462 466 hgrcs = [f for f in os.listdir(hgrcd) if f.endswith(".rc")]
463 467 rcpath = map(os.path.normpath, hgrcs +
464 468 ['/etc/mercurial/hgrc', os.path.expanduser('~/.hgrc')])
465 469
466 470 def parse_patch_output(output_line):
467 471 """parses the output produced by patch and returns the file name"""
468 472 return output_line[14:]
469 473
470 474 def is_exec(f, last):
471 475 """check whether a file is executable"""
472 476 return (os.stat(f).st_mode & 0100 != 0)
473 477
474 478 def set_exec(f, mode):
475 479 s = os.stat(f).st_mode
476 480 if (s & 0100 != 0) == mode:
477 481 return
478 482 if mode:
479 483 # Turn on +x for every +r bit when making a file executable
480 484 # and obey umask.
481 485 umask = os.umask(0)
482 486 os.umask(umask)
483 487 os.chmod(f, s | (s & 0444) >> 2 & ~umask)
484 488 else:
485 489 os.chmod(f, s & 0666)
486 490
491 def set_binary(fd):
492 pass
493
487 494 def pconvert(path):
488 495 return path
489 496
490 497 def localpath(path):
491 498 return path
492 499
493 500 normpath = os.path.normpath
494 501
495 502 def makelock(info, pathname):
496 503 try:
497 504 os.symlink(info, pathname)
498 505 except OSError, why:
499 506 if why.errno == errno.EEXIST:
500 507 raise
501 508 else:
502 509 _makelock_file(info, pathname)
503 510
504 511 def readlock(pathname):
505 512 try:
506 513 return os.readlink(pathname)
507 514 except OSError, why:
508 515 if why.errno == errno.EINVAL:
509 516 return _readlock_file(pathname)
510 517 else:
511 518 raise
512 519
513 520 def explain_exit(code):
514 521 """return a 2-tuple (desc, code) describing a process's status"""
515 522 if os.WIFEXITED(code):
516 523 val = os.WEXITSTATUS(code)
517 524 return _("exited with status %d") % val, val
518 525 elif os.WIFSIGNALED(code):
519 526 val = os.WTERMSIG(code)
520 527 return _("killed by signal %d") % val, val
521 528 elif os.WIFSTOPPED(code):
522 529 val = os.WSTOPSIG(code)
523 530 return _("stopped by signal %d") % val, val
524 531 raise ValueError(_("invalid exit code"))
525 532
526 533 class chunkbuffer(object):
527 534 """Allow arbitrary sized chunks of data to be efficiently read from an
528 535 iterator over chunks of arbitrary size."""
529 536
530 537 def __init__(self, in_iter, targetsize = 2**16):
531 538 """in_iter is the iterator that's iterating over the input chunks.
532 539 targetsize is how big a buffer to try to maintain."""
533 540 self.in_iter = iter(in_iter)
534 541 self.buf = ''
535 542 self.targetsize = int(targetsize)
536 543 if self.targetsize <= 0:
537 544 raise ValueError(_("targetsize must be greater than 0, was %d") %
538 545 targetsize)
539 546 self.iterempty = False
540 547
541 548 def fillbuf(self):
542 549 """Ignore target size; read every chunk from iterator until empty."""
543 550 if not self.iterempty:
544 551 collector = cStringIO.StringIO()
545 552 collector.write(self.buf)
546 553 for ch in self.in_iter:
547 554 collector.write(ch)
548 555 self.buf = collector.getvalue()
549 556 self.iterempty = True
550 557
551 558 def read(self, l):
552 559 """Read L bytes of data from the iterator of chunks of data.
553 560 Returns less than L bytes if the iterator runs dry."""
554 561 if l > len(self.buf) and not self.iterempty:
555 562 # Clamp to a multiple of self.targetsize
556 563 targetsize = self.targetsize * ((l // self.targetsize) + 1)
557 564 collector = cStringIO.StringIO()
558 565 collector.write(self.buf)
559 566 collected = len(self.buf)
560 567 for chunk in self.in_iter:
561 568 collector.write(chunk)
562 569 collected += len(chunk)
563 570 if collected >= targetsize:
564 571 break
565 572 if collected < targetsize:
566 573 self.iterempty = True
567 574 self.buf = collector.getvalue()
568 575 s, self.buf = self.buf[:l], buffer(self.buf, l)
569 576 return s
570 577
571 578 def filechunkiter(f, size = 65536):
572 579 """Create a generator that produces all the data in the file size
573 580 (default 65536) bytes at a time. Chunks may be less than size
574 581 bytes if the chunk is the last chunk in the file, or the file is a
575 582 socket or some other type of file that sometimes reads less data
576 583 than is requested."""
577 584 s = f.read(size)
578 585 while len(s) > 0:
579 586 yield s
580 587 s = f.read(size)
581 588
582 589 def makedate():
583 590 t = time.time()
584 591 if time.daylight: tz = time.altzone
585 592 else: tz = time.timezone
586 593 return t, tz
587 594
588 595 def datestr(date=None, format='%c'):
589 596 """represent a (unixtime, offset) tuple as a localized time.
590 597 unixtime is seconds since the epoch, and offset is the time zone's
591 598 number of seconds away from UTC."""
592 599 t, tz = date or makedate()
593 600 return ("%s %+03d%02d" %
594 601 (time.strftime(format, time.gmtime(float(t) - tz)),
595 602 -tz / 3600,
596 603 ((-tz % 3600) / 60)))
General Comments 0
You need to be logged in to leave comments. Login now