##// END OF EJS Templates
diff: add -b/-B options
Haakon Riiser -
r2580:a20a1bb0 default
parent child Browse files
Show More
@@ -1,3612 +1,3626 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 templater bundlerepo")
13 13 demandload(globals(), "fnmatch mdiff random signal tempfile time")
14 14 demandload(globals(), "traceback errno socket version struct atexit sets bz2")
15 15 demandload(globals(), "archival cStringIO changegroup email.Parser")
16 16 demandload(globals(), "hgweb.server sshserver")
17 17
18 18 class UnknownCommand(Exception):
19 19 """Exception raised if command is not in the command table."""
20 20 class AmbiguousCommand(Exception):
21 21 """Exception raised if command shortcut matches more than one command."""
22 22
23 23 def bail_if_changed(repo):
24 24 modified, added, removed, deleted, unknown = repo.changes()
25 25 if modified or added or removed or deleted:
26 26 raise util.Abort(_("outstanding uncommitted changes"))
27 27
28 28 def filterfiles(filters, files):
29 29 l = [x for x in files if x in filters]
30 30
31 31 for t in filters:
32 32 if t and t[-1] != "/":
33 33 t += "/"
34 34 l += [x for x in files if x.startswith(t)]
35 35 return l
36 36
37 37 def relpath(repo, args):
38 38 cwd = repo.getcwd()
39 39 if cwd:
40 40 return [util.normpath(os.path.join(cwd, x)) for x in args]
41 41 return args
42 42
43 43 def matchpats(repo, pats=[], opts={}, head=''):
44 44 cwd = repo.getcwd()
45 45 if not pats and cwd:
46 46 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
47 47 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
48 48 cwd = ''
49 49 return util.cmdmatcher(repo.root, cwd, pats or ['.'], opts.get('include'),
50 50 opts.get('exclude'), head)
51 51
52 52 def makewalk(repo, pats, opts, node=None, head='', badmatch=None):
53 53 files, matchfn, anypats = matchpats(repo, pats, opts, head)
54 54 exact = dict(zip(files, files))
55 55 def walk():
56 56 for src, fn in repo.walk(node=node, files=files, match=matchfn,
57 57 badmatch=badmatch):
58 58 yield src, fn, util.pathto(repo.getcwd(), fn), fn in exact
59 59 return files, matchfn, walk()
60 60
61 61 def walk(repo, pats, opts, node=None, head='', badmatch=None):
62 62 files, matchfn, results = makewalk(repo, pats, opts, node, head, badmatch)
63 63 for r in results:
64 64 yield r
65 65
66 66 def walkchangerevs(ui, repo, pats, opts):
67 67 '''Iterate over files and the revs they changed in.
68 68
69 69 Callers most commonly need to iterate backwards over the history
70 70 it is interested in. Doing so has awful (quadratic-looking)
71 71 performance, so we use iterators in a "windowed" way.
72 72
73 73 We walk a window of revisions in the desired order. Within the
74 74 window, we first walk forwards to gather data, then in the desired
75 75 order (usually backwards) to display it.
76 76
77 77 This function returns an (iterator, getchange, matchfn) tuple. The
78 78 getchange function returns the changelog entry for a numeric
79 79 revision. The iterator yields 3-tuples. They will be of one of
80 80 the following forms:
81 81
82 82 "window", incrementing, lastrev: stepping through a window,
83 83 positive if walking forwards through revs, last rev in the
84 84 sequence iterated over - use to reset state for the current window
85 85
86 86 "add", rev, fns: out-of-order traversal of the given file names
87 87 fns, which changed during revision rev - use to gather data for
88 88 possible display
89 89
90 90 "iter", rev, None: in-order traversal of the revs earlier iterated
91 91 over with "add" - use to display data'''
92 92
93 93 def increasing_windows(start, end, windowsize=8, sizelimit=512):
94 94 if start < end:
95 95 while start < end:
96 96 yield start, min(windowsize, end-start)
97 97 start += windowsize
98 98 if windowsize < sizelimit:
99 99 windowsize *= 2
100 100 else:
101 101 while start > end:
102 102 yield start, min(windowsize, start-end-1)
103 103 start -= windowsize
104 104 if windowsize < sizelimit:
105 105 windowsize *= 2
106 106
107 107
108 108 files, matchfn, anypats = matchpats(repo, pats, opts)
109 109
110 110 if repo.changelog.count() == 0:
111 111 return [], False, matchfn
112 112
113 113 revs = map(int, revrange(ui, repo, opts['rev'] or ['tip:0']))
114 114 wanted = {}
115 115 slowpath = anypats
116 116 fncache = {}
117 117
118 118 chcache = {}
119 119 def getchange(rev):
120 120 ch = chcache.get(rev)
121 121 if ch is None:
122 122 chcache[rev] = ch = repo.changelog.read(repo.lookup(str(rev)))
123 123 return ch
124 124
125 125 if not slowpath and not files:
126 126 # No files, no patterns. Display all revs.
127 127 wanted = dict(zip(revs, revs))
128 128 if not slowpath:
129 129 # Only files, no patterns. Check the history of each file.
130 130 def filerevgen(filelog):
131 131 for i, window in increasing_windows(filelog.count()-1, -1):
132 132 revs = []
133 133 for j in xrange(i - window, i + 1):
134 134 revs.append(filelog.linkrev(filelog.node(j)))
135 135 revs.reverse()
136 136 for rev in revs:
137 137 yield rev
138 138
139 139 minrev, maxrev = min(revs), max(revs)
140 140 for file_ in files:
141 141 filelog = repo.file(file_)
142 142 # A zero count may be a directory or deleted file, so
143 143 # try to find matching entries on the slow path.
144 144 if filelog.count() == 0:
145 145 slowpath = True
146 146 break
147 147 for rev in filerevgen(filelog):
148 148 if rev <= maxrev:
149 149 if rev < minrev:
150 150 break
151 151 fncache.setdefault(rev, [])
152 152 fncache[rev].append(file_)
153 153 wanted[rev] = 1
154 154 if slowpath:
155 155 # The slow path checks files modified in every changeset.
156 156 def changerevgen():
157 157 for i, window in increasing_windows(repo.changelog.count()-1, -1):
158 158 for j in xrange(i - window, i + 1):
159 159 yield j, getchange(j)[3]
160 160
161 161 for rev, changefiles in changerevgen():
162 162 matches = filter(matchfn, changefiles)
163 163 if matches:
164 164 fncache[rev] = matches
165 165 wanted[rev] = 1
166 166
167 167 def iterate():
168 168 for i, window in increasing_windows(0, len(revs)):
169 169 yield 'window', revs[0] < revs[-1], revs[-1]
170 170 nrevs = [rev for rev in revs[i:i+window]
171 171 if rev in wanted]
172 172 srevs = list(nrevs)
173 173 srevs.sort()
174 174 for rev in srevs:
175 175 fns = fncache.get(rev) or filter(matchfn, getchange(rev)[3])
176 176 yield 'add', rev, fns
177 177 for rev in nrevs:
178 178 yield 'iter', rev, None
179 179 return iterate(), getchange, matchfn
180 180
181 181 revrangesep = ':'
182 182
183 183 def revfix(repo, val, defval):
184 184 '''turn user-level id of changeset into rev number.
185 185 user-level id can be tag, changeset, rev number, or negative rev
186 186 number relative to number of revs (-1 is tip, etc).'''
187 187 if not val:
188 188 return defval
189 189 try:
190 190 num = int(val)
191 191 if str(num) != val:
192 192 raise ValueError
193 193 if num < 0:
194 194 num += repo.changelog.count()
195 195 if num < 0:
196 196 num = 0
197 197 elif num >= repo.changelog.count():
198 198 raise ValueError
199 199 except ValueError:
200 200 try:
201 201 num = repo.changelog.rev(repo.lookup(val))
202 202 except KeyError:
203 203 raise util.Abort(_('invalid revision identifier %s'), val)
204 204 return num
205 205
206 206 def revpair(ui, repo, revs):
207 207 '''return pair of nodes, given list of revisions. second item can
208 208 be None, meaning use working dir.'''
209 209 if not revs:
210 210 return repo.dirstate.parents()[0], None
211 211 end = None
212 212 if len(revs) == 1:
213 213 start = revs[0]
214 214 if revrangesep in start:
215 215 start, end = start.split(revrangesep, 1)
216 216 start = revfix(repo, start, 0)
217 217 end = revfix(repo, end, repo.changelog.count() - 1)
218 218 else:
219 219 start = revfix(repo, start, None)
220 220 elif len(revs) == 2:
221 221 if revrangesep in revs[0] or revrangesep in revs[1]:
222 222 raise util.Abort(_('too many revisions specified'))
223 223 start = revfix(repo, revs[0], None)
224 224 end = revfix(repo, revs[1], None)
225 225 else:
226 226 raise util.Abort(_('too many revisions specified'))
227 227 if end is not None: end = repo.lookup(str(end))
228 228 return repo.lookup(str(start)), end
229 229
230 230 def revrange(ui, repo, revs):
231 231 """Yield revision as strings from a list of revision specifications."""
232 232 seen = {}
233 233 for spec in revs:
234 234 if revrangesep in spec:
235 235 start, end = spec.split(revrangesep, 1)
236 236 start = revfix(repo, start, 0)
237 237 end = revfix(repo, end, repo.changelog.count() - 1)
238 238 step = start > end and -1 or 1
239 239 for rev in xrange(start, end+step, step):
240 240 if rev in seen:
241 241 continue
242 242 seen[rev] = 1
243 243 yield str(rev)
244 244 else:
245 245 rev = revfix(repo, spec, None)
246 246 if rev in seen:
247 247 continue
248 248 seen[rev] = 1
249 249 yield str(rev)
250 250
251 251 def make_filename(repo, pat, node,
252 252 total=None, seqno=None, revwidth=None, pathname=None):
253 253 node_expander = {
254 254 'H': lambda: hex(node),
255 255 'R': lambda: str(repo.changelog.rev(node)),
256 256 'h': lambda: short(node),
257 257 }
258 258 expander = {
259 259 '%': lambda: '%',
260 260 'b': lambda: os.path.basename(repo.root),
261 261 }
262 262
263 263 try:
264 264 if node:
265 265 expander.update(node_expander)
266 266 if node and revwidth is not None:
267 267 expander['r'] = lambda: str(r.rev(node)).zfill(revwidth)
268 268 if total is not None:
269 269 expander['N'] = lambda: str(total)
270 270 if seqno is not None:
271 271 expander['n'] = lambda: str(seqno)
272 272 if total is not None and seqno is not None:
273 273 expander['n'] = lambda:str(seqno).zfill(len(str(total)))
274 274 if pathname is not None:
275 275 expander['s'] = lambda: os.path.basename(pathname)
276 276 expander['d'] = lambda: os.path.dirname(pathname) or '.'
277 277 expander['p'] = lambda: pathname
278 278
279 279 newname = []
280 280 patlen = len(pat)
281 281 i = 0
282 282 while i < patlen:
283 283 c = pat[i]
284 284 if c == '%':
285 285 i += 1
286 286 c = pat[i]
287 287 c = expander[c]()
288 288 newname.append(c)
289 289 i += 1
290 290 return ''.join(newname)
291 291 except KeyError, inst:
292 292 raise util.Abort(_("invalid format spec '%%%s' in output file name"),
293 293 inst.args[0])
294 294
295 295 def make_file(repo, pat, node=None,
296 296 total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
297 297 if not pat or pat == '-':
298 298 return 'w' in mode and sys.stdout or sys.stdin
299 299 if hasattr(pat, 'write') and 'w' in mode:
300 300 return pat
301 301 if hasattr(pat, 'read') and 'r' in mode:
302 302 return pat
303 303 return open(make_filename(repo, pat, node, total, seqno, revwidth,
304 304 pathname),
305 305 mode)
306 306
307 307 def write_bundle(cg, filename=None, compress=True):
308 308 """Write a bundle file and return its filename.
309 309
310 310 Existing files will not be overwritten.
311 311 If no filename is specified, a temporary file is created.
312 312 bz2 compression can be turned off.
313 313 The bundle file will be deleted in case of errors.
314 314 """
315 315 class nocompress(object):
316 316 def compress(self, x):
317 317 return x
318 318 def flush(self):
319 319 return ""
320 320
321 321 fh = None
322 322 cleanup = None
323 323 try:
324 324 if filename:
325 325 if os.path.exists(filename):
326 326 raise util.Abort(_("file '%s' already exists"), filename)
327 327 fh = open(filename, "wb")
328 328 else:
329 329 fd, filename = tempfile.mkstemp(prefix="hg-bundle-", suffix=".hg")
330 330 fh = os.fdopen(fd, "wb")
331 331 cleanup = filename
332 332
333 333 if compress:
334 334 fh.write("HG10")
335 335 z = bz2.BZ2Compressor(9)
336 336 else:
337 337 fh.write("HG10UN")
338 338 z = nocompress()
339 339 # parse the changegroup data, otherwise we will block
340 340 # in case of sshrepo because we don't know the end of the stream
341 341
342 342 # an empty chunkiter is the end of the changegroup
343 343 empty = False
344 344 while not empty:
345 345 empty = True
346 346 for chunk in changegroup.chunkiter(cg):
347 347 empty = False
348 348 fh.write(z.compress(changegroup.genchunk(chunk)))
349 349 fh.write(z.compress(changegroup.closechunk()))
350 350 fh.write(z.flush())
351 351 cleanup = None
352 352 return filename
353 353 finally:
354 354 if fh is not None:
355 355 fh.close()
356 356 if cleanup is not None:
357 357 os.unlink(cleanup)
358 358
359 359 def dodiff(fp, ui, repo, node1, node2, files=None, match=util.always,
360 360 changes=None, text=False, opts={}):
361 361 if not node1:
362 362 node1 = repo.dirstate.parents()[0]
363 363 # reading the data for node1 early allows it to play nicely
364 364 # with repo.changes and the revlog cache.
365 365 change = repo.changelog.read(node1)
366 366 mmap = repo.manifest.read(change[0])
367 367 date1 = util.datestr(change[2])
368 368
369 369 if not changes:
370 370 changes = repo.changes(node1, node2, files, match=match)
371 371 modified, added, removed, deleted, unknown = changes
372 372 if files:
373 373 modified, added, removed = map(lambda x: filterfiles(files, x),
374 374 (modified, added, removed))
375 375
376 376 if not modified and not added and not removed:
377 377 return
378 378
379 379 if node2:
380 380 change = repo.changelog.read(node2)
381 381 mmap2 = repo.manifest.read(change[0])
382 382 _date2 = util.datestr(change[2])
383 383 def date2(f):
384 384 return _date2
385 385 def read(f):
386 386 return repo.file(f).read(mmap2[f])
387 387 else:
388 388 tz = util.makedate()[1]
389 389 _date2 = util.datestr()
390 390 def date2(f):
391 391 try:
392 392 return util.datestr((os.lstat(repo.wjoin(f)).st_mtime, tz))
393 393 except OSError, err:
394 394 if err.errno != errno.ENOENT: raise
395 395 return _date2
396 396 def read(f):
397 397 return repo.wread(f)
398 398
399 399 if ui.quiet:
400 400 r = None
401 401 else:
402 402 hexfunc = ui.verbose and hex or short
403 403 r = [hexfunc(node) for node in [node1, node2] if node]
404 404
405 405 diffopts = ui.diffopts()
406 406 showfunc = opts.get('show_function') or diffopts['showfunc']
407 407 ignorews = opts.get('ignore_all_space') or diffopts['ignorews']
408 ignorewsamount = opts.get('ignore_space_change') or \
409 diffopts['ignorewsamount']
410 ignoreblanklines = opts.get('ignore_blank_lines') or \
411 diffopts['ignoreblanklines']
408 412 for f in modified:
409 413 to = None
410 414 if f in mmap:
411 415 to = repo.file(f).read(mmap[f])
412 416 tn = read(f)
413 417 fp.write(mdiff.unidiff(to, date1, tn, date2(f), f, r, text=text,
414 showfunc=showfunc, ignorews=ignorews))
418 showfunc=showfunc, ignorews=ignorews,
419 ignorewsamount=ignorewsamount,
420 ignoreblanklines=ignoreblanklines))
415 421 for f in added:
416 422 to = None
417 423 tn = read(f)
418 424 fp.write(mdiff.unidiff(to, date1, tn, date2(f), f, r, text=text,
419 showfunc=showfunc, ignorews=ignorews))
425 showfunc=showfunc, ignorews=ignorews,
426 ignorewsamount=ignorewsamount,
427 ignoreblanklines=ignoreblanklines))
420 428 for f in removed:
421 429 to = repo.file(f).read(mmap[f])
422 430 tn = None
423 431 fp.write(mdiff.unidiff(to, date1, tn, date2(f), f, r, text=text,
424 showfunc=showfunc, ignorews=ignorews))
432 showfunc=showfunc, ignorews=ignorews,
433 ignorewsamount=ignorewsamount,
434 ignoreblanklines=ignoreblanklines))
425 435
426 436 def trimuser(ui, name, rev, revcache):
427 437 """trim the name of the user who committed a change"""
428 438 user = revcache.get(rev)
429 439 if user is None:
430 440 user = revcache[rev] = ui.shortuser(name)
431 441 return user
432 442
433 443 class changeset_printer(object):
434 444 '''show changeset information when templating not requested.'''
435 445
436 446 def __init__(self, ui, repo):
437 447 self.ui = ui
438 448 self.repo = repo
439 449
440 450 def show(self, rev=0, changenode=None, brinfo=None):
441 451 '''show a single changeset or file revision'''
442 452 log = self.repo.changelog
443 453 if changenode is None:
444 454 changenode = log.node(rev)
445 455 elif not rev:
446 456 rev = log.rev(changenode)
447 457
448 458 if self.ui.quiet:
449 459 self.ui.write("%d:%s\n" % (rev, short(changenode)))
450 460 return
451 461
452 462 changes = log.read(changenode)
453 463 date = util.datestr(changes[2])
454 464
455 465 parents = [(log.rev(p), self.ui.verbose and hex(p) or short(p))
456 466 for p in log.parents(changenode)
457 467 if self.ui.debugflag or p != nullid]
458 468 if (not self.ui.debugflag and len(parents) == 1 and
459 469 parents[0][0] == rev-1):
460 470 parents = []
461 471
462 472 if self.ui.verbose:
463 473 self.ui.write(_("changeset: %d:%s\n") % (rev, hex(changenode)))
464 474 else:
465 475 self.ui.write(_("changeset: %d:%s\n") % (rev, short(changenode)))
466 476
467 477 for tag in self.repo.nodetags(changenode):
468 478 self.ui.status(_("tag: %s\n") % tag)
469 479 for parent in parents:
470 480 self.ui.write(_("parent: %d:%s\n") % parent)
471 481
472 482 if brinfo and changenode in brinfo:
473 483 br = brinfo[changenode]
474 484 self.ui.write(_("branch: %s\n") % " ".join(br))
475 485
476 486 self.ui.debug(_("manifest: %d:%s\n") %
477 487 (self.repo.manifest.rev(changes[0]), hex(changes[0])))
478 488 self.ui.status(_("user: %s\n") % changes[1])
479 489 self.ui.status(_("date: %s\n") % date)
480 490
481 491 if self.ui.debugflag:
482 492 files = self.repo.changes(log.parents(changenode)[0], changenode)
483 493 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
484 494 files):
485 495 if value:
486 496 self.ui.note("%-12s %s\n" % (key, " ".join(value)))
487 497 else:
488 498 self.ui.note(_("files: %s\n") % " ".join(changes[3]))
489 499
490 500 description = changes[4].strip()
491 501 if description:
492 502 if self.ui.verbose:
493 503 self.ui.status(_("description:\n"))
494 504 self.ui.status(description)
495 505 self.ui.status("\n\n")
496 506 else:
497 507 self.ui.status(_("summary: %s\n") %
498 508 description.splitlines()[0])
499 509 self.ui.status("\n")
500 510
501 511 def show_changeset(ui, repo, opts):
502 512 '''show one changeset. uses template or regular display. caller
503 513 can pass in 'style' and 'template' options in opts.'''
504 514
505 515 tmpl = opts.get('template')
506 516 if tmpl:
507 517 tmpl = templater.parsestring(tmpl, quoted=False)
508 518 else:
509 519 tmpl = ui.config('ui', 'logtemplate')
510 520 if tmpl: tmpl = templater.parsestring(tmpl)
511 521 mapfile = opts.get('style') or ui.config('ui', 'style')
512 522 if tmpl or mapfile:
513 523 if mapfile:
514 524 if not os.path.isfile(mapfile):
515 525 mapname = templater.templatepath('map-cmdline.' + mapfile)
516 526 if not mapname: mapname = templater.templatepath(mapfile)
517 527 if mapname: mapfile = mapname
518 528 try:
519 529 t = templater.changeset_templater(ui, repo, mapfile)
520 530 except SyntaxError, inst:
521 531 raise util.Abort(inst.args[0])
522 532 if tmpl: t.use_template(tmpl)
523 533 return t
524 534 return changeset_printer(ui, repo)
525 535
526 536 def show_version(ui):
527 537 """output version and copyright information"""
528 538 ui.write(_("Mercurial Distributed SCM (version %s)\n")
529 539 % version.get_version())
530 540 ui.status(_(
531 541 "\nCopyright (C) 2005 Matt Mackall <mpm@selenic.com>\n"
532 542 "This is free software; see the source for copying conditions. "
533 543 "There is NO\nwarranty; "
534 544 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
535 545 ))
536 546
537 547 def help_(ui, name=None, with_version=False):
538 548 """show help for a command, extension, or list of commands
539 549
540 550 With no arguments, print a list of commands and short help.
541 551
542 552 Given a command name, print help for that command.
543 553
544 554 Given an extension name, print help for that extension, and the
545 555 commands it provides."""
546 556 option_lists = []
547 557
548 558 def helpcmd(name):
549 559 if with_version:
550 560 show_version(ui)
551 561 ui.write('\n')
552 562 aliases, i = findcmd(name)
553 563 # synopsis
554 564 ui.write("%s\n\n" % i[2])
555 565
556 566 # description
557 567 doc = i[0].__doc__
558 568 if not doc:
559 569 doc = _("(No help text available)")
560 570 if ui.quiet:
561 571 doc = doc.splitlines(0)[0]
562 572 ui.write("%s\n" % doc.rstrip())
563 573
564 574 if not ui.quiet:
565 575 # aliases
566 576 if len(aliases) > 1:
567 577 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
568 578
569 579 # options
570 580 if i[1]:
571 581 option_lists.append(("options", i[1]))
572 582
573 583 def helplist(select=None):
574 584 h = {}
575 585 cmds = {}
576 586 for c, e in table.items():
577 587 f = c.split("|", 1)[0]
578 588 if select and not select(f):
579 589 continue
580 590 if name == "shortlist" and not f.startswith("^"):
581 591 continue
582 592 f = f.lstrip("^")
583 593 if not ui.debugflag and f.startswith("debug"):
584 594 continue
585 595 doc = e[0].__doc__
586 596 if not doc:
587 597 doc = _("(No help text available)")
588 598 h[f] = doc.splitlines(0)[0].rstrip()
589 599 cmds[f] = c.lstrip("^")
590 600
591 601 fns = h.keys()
592 602 fns.sort()
593 603 m = max(map(len, fns))
594 604 for f in fns:
595 605 if ui.verbose:
596 606 commands = cmds[f].replace("|",", ")
597 607 ui.write(" %s:\n %s\n"%(commands, h[f]))
598 608 else:
599 609 ui.write(' %-*s %s\n' % (m, f, h[f]))
600 610
601 611 def helpext(name):
602 612 try:
603 613 mod = findext(name)
604 614 except KeyError:
605 615 raise UnknownCommand(name)
606 616
607 617 doc = (mod.__doc__ or _('No help text available')).splitlines(0)
608 618 ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0]))
609 619 for d in doc[1:]:
610 620 ui.write(d, '\n')
611 621
612 622 ui.status('\n')
613 623 if ui.verbose:
614 624 ui.status(_('list of commands:\n\n'))
615 625 else:
616 626 ui.status(_('list of commands (use "hg help -v %s" '
617 627 'to show aliases and global options):\n\n') % name)
618 628
619 629 modcmds = dict.fromkeys([c.split('|', 1)[0] for c in mod.cmdtable])
620 630 helplist(modcmds.has_key)
621 631
622 632 if name and name != 'shortlist':
623 633 try:
624 634 helpcmd(name)
625 635 except UnknownCommand:
626 636 helpext(name)
627 637
628 638 else:
629 639 # program name
630 640 if ui.verbose or with_version:
631 641 show_version(ui)
632 642 else:
633 643 ui.status(_("Mercurial Distributed SCM\n"))
634 644 ui.status('\n')
635 645
636 646 # list of commands
637 647 if name == "shortlist":
638 648 ui.status(_('basic commands (use "hg help" '
639 649 'for the full list or option "-v" for details):\n\n'))
640 650 elif ui.verbose:
641 651 ui.status(_('list of commands:\n\n'))
642 652 else:
643 653 ui.status(_('list of commands (use "hg help -v" '
644 654 'to show aliases and global options):\n\n'))
645 655
646 656 helplist()
647 657
648 658 # global options
649 659 if ui.verbose:
650 660 option_lists.append(("global options", globalopts))
651 661
652 662 # list all option lists
653 663 opt_output = []
654 664 for title, options in option_lists:
655 665 opt_output.append(("\n%s:\n" % title, None))
656 666 for shortopt, longopt, default, desc in options:
657 667 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
658 668 longopt and " --%s" % longopt),
659 669 "%s%s" % (desc,
660 670 default
661 671 and _(" (default: %s)") % default
662 672 or "")))
663 673
664 674 if opt_output:
665 675 opts_len = max([len(line[0]) for line in opt_output if line[1]])
666 676 for first, second in opt_output:
667 677 if second:
668 678 ui.write(" %-*s %s\n" % (opts_len, first, second))
669 679 else:
670 680 ui.write("%s\n" % first)
671 681
672 682 # Commands start here, listed alphabetically
673 683
674 684 def add(ui, repo, *pats, **opts):
675 685 """add the specified files on the next commit
676 686
677 687 Schedule files to be version controlled and added to the repository.
678 688
679 689 The files will be added to the repository at the next commit.
680 690
681 691 If no names are given, add all files in the repository.
682 692 """
683 693
684 694 names = []
685 695 for src, abs, rel, exact in walk(repo, pats, opts):
686 696 if exact:
687 697 if ui.verbose:
688 698 ui.status(_('adding %s\n') % rel)
689 699 names.append(abs)
690 700 elif repo.dirstate.state(abs) == '?':
691 701 ui.status(_('adding %s\n') % rel)
692 702 names.append(abs)
693 703 if not opts.get('dry_run'):
694 704 repo.add(names)
695 705
696 706 def addremove(ui, repo, *pats, **opts):
697 707 """add all new files, delete all missing files (DEPRECATED)
698 708
699 709 (DEPRECATED)
700 710 Add all new files and remove all missing files from the repository.
701 711
702 712 New files are ignored if they match any of the patterns in .hgignore. As
703 713 with add, these changes take effect at the next commit.
704 714
705 715 This command is now deprecated and will be removed in a future
706 716 release. Please use add and remove --after instead.
707 717 """
708 718 ui.warn(_('(the addremove command is deprecated; use add and remove '
709 719 '--after instead)\n'))
710 720 return addremove_lock(ui, repo, pats, opts)
711 721
712 722 def addremove_lock(ui, repo, pats, opts, wlock=None):
713 723 add, remove = [], []
714 724 for src, abs, rel, exact in walk(repo, pats, opts):
715 725 if src == 'f' and repo.dirstate.state(abs) == '?':
716 726 add.append(abs)
717 727 if ui.verbose or not exact:
718 728 ui.status(_('adding %s\n') % ((pats and rel) or abs))
719 729 if repo.dirstate.state(abs) != 'r' and not os.path.exists(rel):
720 730 remove.append(abs)
721 731 if ui.verbose or not exact:
722 732 ui.status(_('removing %s\n') % ((pats and rel) or abs))
723 733 if not opts.get('dry_run'):
724 734 repo.add(add, wlock=wlock)
725 735 repo.remove(remove, wlock=wlock)
726 736
727 737 def annotate(ui, repo, *pats, **opts):
728 738 """show changeset information per file line
729 739
730 740 List changes in files, showing the revision id responsible for each line
731 741
732 742 This command is useful to discover who did a change or when a change took
733 743 place.
734 744
735 745 Without the -a option, annotate will avoid processing files it
736 746 detects as binary. With -a, annotate will generate an annotation
737 747 anyway, probably with undesirable results.
738 748 """
739 749 def getnode(rev):
740 750 return short(repo.changelog.node(rev))
741 751
742 752 ucache = {}
743 753 def getname(rev):
744 754 try:
745 755 return ucache[rev]
746 756 except:
747 757 u = trimuser(ui, repo.changectx(rev).user(), rev, ucache)
748 758 ucache[rev] = u
749 759 return u
750 760
751 761 dcache = {}
752 762 def getdate(rev):
753 763 datestr = dcache.get(rev)
754 764 if datestr is None:
755 765 datestr = dcache[rev] = util.datestr(repo.changectx(rev).date())
756 766 return datestr
757 767
758 768 if not pats:
759 769 raise util.Abort(_('at least one file name or pattern required'))
760 770
761 771 opmap = [['user', getname], ['number', str], ['changeset', getnode],
762 772 ['date', getdate]]
763 773 if not opts['user'] and not opts['changeset'] and not opts['date']:
764 774 opts['number'] = 1
765 775
766 776 ctx = repo.changectx(opts['rev'] or repo.dirstate.parents()[0])
767 777
768 778 for src, abs, rel, exact in walk(repo, pats, opts, node=ctx.node()):
769 779 fctx = ctx.filectx(abs)
770 780 if not opts['text'] and util.binary(fctx.data()):
771 781 ui.write(_("%s: binary file\n") % ((pats and rel) or abs))
772 782 continue
773 783
774 784 lines = fctx.annotate()
775 785 pieces = []
776 786
777 787 for o, f in opmap:
778 788 if opts[o]:
779 789 l = [f(n) for n, dummy in lines]
780 790 if l:
781 791 m = max(map(len, l))
782 792 pieces.append(["%*s" % (m, x) for x in l])
783 793
784 794 if pieces:
785 795 for p, l in zip(zip(*pieces), lines):
786 796 ui.write("%s: %s" % (" ".join(p), l[1]))
787 797
788 798 def archive(ui, repo, dest, **opts):
789 799 '''create unversioned archive of a repository revision
790 800
791 801 By default, the revision used is the parent of the working
792 802 directory; use "-r" to specify a different revision.
793 803
794 804 To specify the type of archive to create, use "-t". Valid
795 805 types are:
796 806
797 807 "files" (default): a directory full of files
798 808 "tar": tar archive, uncompressed
799 809 "tbz2": tar archive, compressed using bzip2
800 810 "tgz": tar archive, compressed using gzip
801 811 "uzip": zip archive, uncompressed
802 812 "zip": zip archive, compressed using deflate
803 813
804 814 The exact name of the destination archive or directory is given
805 815 using a format string; see "hg help export" for details.
806 816
807 817 Each member added to an archive file has a directory prefix
808 818 prepended. Use "-p" to specify a format string for the prefix.
809 819 The default is the basename of the archive, with suffixes removed.
810 820 '''
811 821
812 822 if opts['rev']:
813 823 node = repo.lookup(opts['rev'])
814 824 else:
815 825 node, p2 = repo.dirstate.parents()
816 826 if p2 != nullid:
817 827 raise util.Abort(_('uncommitted merge - please provide a '
818 828 'specific revision'))
819 829
820 830 dest = make_filename(repo, dest, node)
821 831 if os.path.realpath(dest) == repo.root:
822 832 raise util.Abort(_('repository root cannot be destination'))
823 833 dummy, matchfn, dummy = matchpats(repo, [], opts)
824 834 kind = opts.get('type') or 'files'
825 835 prefix = opts['prefix']
826 836 if dest == '-':
827 837 if kind == 'files':
828 838 raise util.Abort(_('cannot archive plain files to stdout'))
829 839 dest = sys.stdout
830 840 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
831 841 prefix = make_filename(repo, prefix, node)
832 842 archival.archive(repo, dest, node, kind, not opts['no_decode'],
833 843 matchfn, prefix)
834 844
835 845 def backout(ui, repo, rev, **opts):
836 846 '''reverse effect of earlier changeset
837 847
838 848 Commit the backed out changes as a new changeset. The new
839 849 changeset is a child of the backed out changeset.
840 850
841 851 If you back out a changeset other than the tip, a new head is
842 852 created. This head is the parent of the working directory. If
843 853 you back out an old changeset, your working directory will appear
844 854 old after the backout. You should merge the backout changeset
845 855 with another head.
846 856
847 857 The --merge option remembers the parent of the working directory
848 858 before starting the backout, then merges the new head with that
849 859 changeset afterwards. This saves you from doing the merge by
850 860 hand. The result of this merge is not committed, as for a normal
851 861 merge.'''
852 862
853 863 bail_if_changed(repo)
854 864 op1, op2 = repo.dirstate.parents()
855 865 if op2 != nullid:
856 866 raise util.Abort(_('outstanding uncommitted merge'))
857 867 node = repo.lookup(rev)
858 868 parent, p2 = repo.changelog.parents(node)
859 869 if parent == nullid:
860 870 raise util.Abort(_('cannot back out a change with no parents'))
861 871 if p2 != nullid:
862 872 raise util.Abort(_('cannot back out a merge'))
863 873 repo.update(node, force=True, show_stats=False)
864 874 revert_opts = opts.copy()
865 875 revert_opts['rev'] = hex(parent)
866 876 revert(ui, repo, **revert_opts)
867 877 commit_opts = opts.copy()
868 878 commit_opts['addremove'] = False
869 879 if not commit_opts['message'] and not commit_opts['logfile']:
870 880 commit_opts['message'] = _("Backed out changeset %s") % (hex(node))
871 881 commit_opts['force_editor'] = True
872 882 commit(ui, repo, **commit_opts)
873 883 def nice(node):
874 884 return '%d:%s' % (repo.changelog.rev(node), short(node))
875 885 ui.status(_('changeset %s backs out changeset %s\n') %
876 886 (nice(repo.changelog.tip()), nice(node)))
877 887 if op1 != node:
878 888 if opts['merge']:
879 889 ui.status(_('merging with changeset %s\n') % nice(op1))
880 890 doupdate(ui, repo, hex(op1), **opts)
881 891 else:
882 892 ui.status(_('the backout changeset is a new head - '
883 893 'do not forget to merge\n'))
884 894 ui.status(_('(use "backout -m" if you want to auto-merge)\n'))
885 895
886 896 def bundle(ui, repo, fname, dest=None, **opts):
887 897 """create a changegroup file
888 898
889 899 Generate a compressed changegroup file collecting all changesets
890 900 not found in the other repository.
891 901
892 902 This file can then be transferred using conventional means and
893 903 applied to another repository with the unbundle command. This is
894 904 useful when native push and pull are not available or when
895 905 exporting an entire repository is undesirable. The standard file
896 906 extension is ".hg".
897 907
898 908 Unlike import/export, this exactly preserves all changeset
899 909 contents including permissions, rename data, and revision history.
900 910 """
901 911 dest = ui.expandpath(dest or 'default-push', dest or 'default')
902 912 other = hg.repository(ui, dest)
903 913 o = repo.findoutgoing(other, force=opts['force'])
904 914 cg = repo.changegroup(o, 'bundle')
905 915 write_bundle(cg, fname)
906 916
907 917 def cat(ui, repo, file1, *pats, **opts):
908 918 """output the latest or given revisions of files
909 919
910 920 Print the specified files as they were at the given revision.
911 921 If no revision is given then the tip is used.
912 922
913 923 Output may be to a file, in which case the name of the file is
914 924 given using a format string. The formatting rules are the same as
915 925 for the export command, with the following additions:
916 926
917 927 %s basename of file being printed
918 928 %d dirname of file being printed, or '.' if in repo root
919 929 %p root-relative path name of file being printed
920 930 """
921 931 ctx = repo.changectx(opts['rev'] or -1)
922 932 for src, abs, rel, exact in walk(repo, (file1,) + pats, opts, ctx.node()):
923 933 fp = make_file(repo, opts['output'], ctx.node(), pathname=abs)
924 934 fp.write(ctx.filectx(abs).data())
925 935
926 936 def clone(ui, source, dest=None, **opts):
927 937 """make a copy of an existing repository
928 938
929 939 Create a copy of an existing repository in a new directory.
930 940
931 941 If no destination directory name is specified, it defaults to the
932 942 basename of the source.
933 943
934 944 The location of the source is added to the new repository's
935 945 .hg/hgrc file, as the default to be used for future pulls.
936 946
937 947 For efficiency, hardlinks are used for cloning whenever the source
938 948 and destination are on the same filesystem. Some filesystems,
939 949 such as AFS, implement hardlinking incorrectly, but do not report
940 950 errors. In these cases, use the --pull option to avoid
941 951 hardlinking.
942 952
943 953 See pull for valid source format details.
944 954 """
945 955 if dest is None:
946 956 dest = os.path.basename(os.path.normpath(source))
947 957
948 958 if os.path.exists(dest):
949 959 raise util.Abort(_("destination '%s' already exists"), dest)
950 960
951 961 class Dircleanup(object):
952 962 def __init__(self, dir_):
953 963 self.rmtree = shutil.rmtree
954 964 self.dir_ = dir_
955 965 def close(self):
956 966 self.dir_ = None
957 967 def __del__(self):
958 968 if self.dir_:
959 969 self.rmtree(self.dir_, True)
960 970
961 971 if opts['ssh']:
962 972 ui.setconfig("ui", "ssh", opts['ssh'])
963 973 if opts['remotecmd']:
964 974 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
965 975
966 976 source = ui.expandpath(source)
967 977 src_repo = hg.repository(ui, source)
968 978
969 979 dest_repo = None
970 980 try:
971 981 dest_repo = hg.repository(ui, dest)
972 982 raise util.Abort(_("destination '%s' already exists." % dest))
973 983 except hg.RepoError:
974 984 dest_repo = hg.repository(ui, dest, create=1)
975 985
976 986 dest_path = None
977 987 d = None
978 988 if dest_repo.local():
979 989 dest_path = os.path.realpath(dest)
980 990 d = Dircleanup(dest_path)
981 991
982 992 abspath = source
983 993 copy = False
984 994 if src_repo.local() and dest_repo.local():
985 995 abspath = os.path.abspath(source)
986 996 if not opts['pull'] and not opts['rev']:
987 997 copy = True
988 998
989 999 if copy:
990 1000 try:
991 1001 # we use a lock here because if we race with commit, we
992 1002 # can end up with extra data in the cloned revlogs that's
993 1003 # not pointed to by changesets, thus causing verify to
994 1004 # fail
995 1005 l1 = src_repo.lock()
996 1006 except lock.LockException:
997 1007 copy = False
998 1008
999 1009 if copy:
1000 1010 # we lock here to avoid premature writing to the target
1001 1011 l2 = lock.lock(os.path.join(dest_path, ".hg", "lock"))
1002 1012
1003 1013 # we need to remove the (empty) data dir in dest so copyfiles can do it's work
1004 1014 os.rmdir( os.path.join(dest_path, ".hg", "data") )
1005 1015 files = "data 00manifest.d 00manifest.i 00changelog.d 00changelog.i"
1006 1016 for f in files.split():
1007 1017 src = os.path.join(source, ".hg", f)
1008 1018 dst = os.path.join(dest_path, ".hg", f)
1009 1019 try:
1010 1020 util.copyfiles(src, dst)
1011 1021 except OSError, inst:
1012 1022 if inst.errno != errno.ENOENT:
1013 1023 raise
1014 1024
1015 1025 # we need to re-init the repo after manually copying the data into it
1016 1026 dest_repo = hg.repository(ui, dest)
1017 1027
1018 1028 else:
1019 1029 revs = None
1020 1030 if opts['rev']:
1021 1031 if not src_repo.local():
1022 1032 error = _("clone -r not supported yet for remote repositories.")
1023 1033 raise util.Abort(error)
1024 1034 else:
1025 1035 revs = [src_repo.lookup(rev) for rev in opts['rev']]
1026 1036
1027 1037 if dest_repo.local():
1028 1038 dest_repo.pull(src_repo, heads = revs)
1029 1039 elif src_repo.local():
1030 1040 src_repo.push(dest_repo, revs = revs)
1031 1041 else:
1032 1042 error = _("clone from remote to remote not supported.")
1033 1043 raise util.Abort(error)
1034 1044
1035 1045 if dest_repo.local():
1036 1046 f = dest_repo.opener("hgrc", "w", text=True)
1037 1047 f.write("[paths]\n")
1038 1048 f.write("default = %s\n" % abspath)
1039 1049 f.close()
1040 1050
1041 1051 if not opts['noupdate']:
1042 1052 doupdate(dest_repo.ui, dest_repo)
1043 1053
1044 1054 if d:
1045 1055 d.close()
1046 1056
1047 1057 def commit(ui, repo, *pats, **opts):
1048 1058 """commit the specified files or all outstanding changes
1049 1059
1050 1060 Commit changes to the given files into the repository.
1051 1061
1052 1062 If a list of files is omitted, all changes reported by "hg status"
1053 1063 will be committed.
1054 1064
1055 1065 If no commit message is specified, the editor configured in your hgrc
1056 1066 or in the EDITOR environment variable is started to enter a message.
1057 1067 """
1058 1068 message = opts['message']
1059 1069 logfile = opts['logfile']
1060 1070
1061 1071 if message and logfile:
1062 1072 raise util.Abort(_('options --message and --logfile are mutually '
1063 1073 'exclusive'))
1064 1074 if not message and logfile:
1065 1075 try:
1066 1076 if logfile == '-':
1067 1077 message = sys.stdin.read()
1068 1078 else:
1069 1079 message = open(logfile).read()
1070 1080 except IOError, inst:
1071 1081 raise util.Abort(_("can't read commit message '%s': %s") %
1072 1082 (logfile, inst.strerror))
1073 1083
1074 1084 if opts['addremove']:
1075 1085 addremove_lock(ui, repo, pats, opts)
1076 1086 fns, match, anypats = matchpats(repo, pats, opts)
1077 1087 if pats:
1078 1088 modified, added, removed, deleted, unknown = (
1079 1089 repo.changes(files=fns, match=match))
1080 1090 files = modified + added + removed
1081 1091 else:
1082 1092 files = []
1083 1093 try:
1084 1094 repo.commit(files, message, opts['user'], opts['date'], match,
1085 1095 force_editor=opts.get('force_editor'))
1086 1096 except ValueError, inst:
1087 1097 raise util.Abort(str(inst))
1088 1098
1089 1099 def docopy(ui, repo, pats, opts, wlock):
1090 1100 # called with the repo lock held
1091 1101 cwd = repo.getcwd()
1092 1102 errors = 0
1093 1103 copied = []
1094 1104 targets = {}
1095 1105
1096 1106 def okaytocopy(abs, rel, exact):
1097 1107 reasons = {'?': _('is not managed'),
1098 1108 'a': _('has been marked for add'),
1099 1109 'r': _('has been marked for remove')}
1100 1110 state = repo.dirstate.state(abs)
1101 1111 reason = reasons.get(state)
1102 1112 if reason:
1103 1113 if state == 'a':
1104 1114 origsrc = repo.dirstate.copied(abs)
1105 1115 if origsrc is not None:
1106 1116 return origsrc
1107 1117 if exact:
1108 1118 ui.warn(_('%s: not copying - file %s\n') % (rel, reason))
1109 1119 else:
1110 1120 return abs
1111 1121
1112 1122 def copy(origsrc, abssrc, relsrc, target, exact):
1113 1123 abstarget = util.canonpath(repo.root, cwd, target)
1114 1124 reltarget = util.pathto(cwd, abstarget)
1115 1125 prevsrc = targets.get(abstarget)
1116 1126 if prevsrc is not None:
1117 1127 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
1118 1128 (reltarget, abssrc, prevsrc))
1119 1129 return
1120 1130 if (not opts['after'] and os.path.exists(reltarget) or
1121 1131 opts['after'] and repo.dirstate.state(abstarget) not in '?r'):
1122 1132 if not opts['force']:
1123 1133 ui.warn(_('%s: not overwriting - file exists\n') %
1124 1134 reltarget)
1125 1135 return
1126 1136 if not opts['after'] and not opts.get('dry_run'):
1127 1137 os.unlink(reltarget)
1128 1138 if opts['after']:
1129 1139 if not os.path.exists(reltarget):
1130 1140 return
1131 1141 else:
1132 1142 targetdir = os.path.dirname(reltarget) or '.'
1133 1143 if not os.path.isdir(targetdir) and not opts.get('dry_run'):
1134 1144 os.makedirs(targetdir)
1135 1145 try:
1136 1146 restore = repo.dirstate.state(abstarget) == 'r'
1137 1147 if restore and not opts.get('dry_run'):
1138 1148 repo.undelete([abstarget], wlock)
1139 1149 try:
1140 1150 if not opts.get('dry_run'):
1141 1151 shutil.copyfile(relsrc, reltarget)
1142 1152 shutil.copymode(relsrc, reltarget)
1143 1153 restore = False
1144 1154 finally:
1145 1155 if restore:
1146 1156 repo.remove([abstarget], wlock)
1147 1157 except shutil.Error, inst:
1148 1158 raise util.Abort(str(inst))
1149 1159 except IOError, inst:
1150 1160 if inst.errno == errno.ENOENT:
1151 1161 ui.warn(_('%s: deleted in working copy\n') % relsrc)
1152 1162 else:
1153 1163 ui.warn(_('%s: cannot copy - %s\n') %
1154 1164 (relsrc, inst.strerror))
1155 1165 errors += 1
1156 1166 return
1157 1167 if ui.verbose or not exact:
1158 1168 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
1159 1169 targets[abstarget] = abssrc
1160 1170 if abstarget != origsrc and not opts.get('dry_run'):
1161 1171 repo.copy(origsrc, abstarget, wlock)
1162 1172 copied.append((abssrc, relsrc, exact))
1163 1173
1164 1174 def targetpathfn(pat, dest, srcs):
1165 1175 if os.path.isdir(pat):
1166 1176 abspfx = util.canonpath(repo.root, cwd, pat)
1167 1177 if destdirexists:
1168 1178 striplen = len(os.path.split(abspfx)[0])
1169 1179 else:
1170 1180 striplen = len(abspfx)
1171 1181 if striplen:
1172 1182 striplen += len(os.sep)
1173 1183 res = lambda p: os.path.join(dest, p[striplen:])
1174 1184 elif destdirexists:
1175 1185 res = lambda p: os.path.join(dest, os.path.basename(p))
1176 1186 else:
1177 1187 res = lambda p: dest
1178 1188 return res
1179 1189
1180 1190 def targetpathafterfn(pat, dest, srcs):
1181 1191 if util.patkind(pat, None)[0]:
1182 1192 # a mercurial pattern
1183 1193 res = lambda p: os.path.join(dest, os.path.basename(p))
1184 1194 else:
1185 1195 abspfx = util.canonpath(repo.root, cwd, pat)
1186 1196 if len(abspfx) < len(srcs[0][0]):
1187 1197 # A directory. Either the target path contains the last
1188 1198 # component of the source path or it does not.
1189 1199 def evalpath(striplen):
1190 1200 score = 0
1191 1201 for s in srcs:
1192 1202 t = os.path.join(dest, s[0][striplen:])
1193 1203 if os.path.exists(t):
1194 1204 score += 1
1195 1205 return score
1196 1206
1197 1207 striplen = len(abspfx)
1198 1208 if striplen:
1199 1209 striplen += len(os.sep)
1200 1210 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
1201 1211 score = evalpath(striplen)
1202 1212 striplen1 = len(os.path.split(abspfx)[0])
1203 1213 if striplen1:
1204 1214 striplen1 += len(os.sep)
1205 1215 if evalpath(striplen1) > score:
1206 1216 striplen = striplen1
1207 1217 res = lambda p: os.path.join(dest, p[striplen:])
1208 1218 else:
1209 1219 # a file
1210 1220 if destdirexists:
1211 1221 res = lambda p: os.path.join(dest, os.path.basename(p))
1212 1222 else:
1213 1223 res = lambda p: dest
1214 1224 return res
1215 1225
1216 1226
1217 1227 pats = list(pats)
1218 1228 if not pats:
1219 1229 raise util.Abort(_('no source or destination specified'))
1220 1230 if len(pats) == 1:
1221 1231 raise util.Abort(_('no destination specified'))
1222 1232 dest = pats.pop()
1223 1233 destdirexists = os.path.isdir(dest)
1224 1234 if (len(pats) > 1 or util.patkind(pats[0], None)[0]) and not destdirexists:
1225 1235 raise util.Abort(_('with multiple sources, destination must be an '
1226 1236 'existing directory'))
1227 1237 if opts['after']:
1228 1238 tfn = targetpathafterfn
1229 1239 else:
1230 1240 tfn = targetpathfn
1231 1241 copylist = []
1232 1242 for pat in pats:
1233 1243 srcs = []
1234 1244 for tag, abssrc, relsrc, exact in walk(repo, [pat], opts):
1235 1245 origsrc = okaytocopy(abssrc, relsrc, exact)
1236 1246 if origsrc:
1237 1247 srcs.append((origsrc, abssrc, relsrc, exact))
1238 1248 if not srcs:
1239 1249 continue
1240 1250 copylist.append((tfn(pat, dest, srcs), srcs))
1241 1251 if not copylist:
1242 1252 raise util.Abort(_('no files to copy'))
1243 1253
1244 1254 for targetpath, srcs in copylist:
1245 1255 for origsrc, abssrc, relsrc, exact in srcs:
1246 1256 copy(origsrc, abssrc, relsrc, targetpath(abssrc), exact)
1247 1257
1248 1258 if errors:
1249 1259 ui.warn(_('(consider using --after)\n'))
1250 1260 return errors, copied
1251 1261
1252 1262 def copy(ui, repo, *pats, **opts):
1253 1263 """mark files as copied for the next commit
1254 1264
1255 1265 Mark dest as having copies of source files. If dest is a
1256 1266 directory, copies are put in that directory. If dest is a file,
1257 1267 there can only be one source.
1258 1268
1259 1269 By default, this command copies the contents of files as they
1260 1270 stand in the working directory. If invoked with --after, the
1261 1271 operation is recorded, but no copying is performed.
1262 1272
1263 1273 This command takes effect in the next commit.
1264 1274
1265 1275 NOTE: This command should be treated as experimental. While it
1266 1276 should properly record copied files, this information is not yet
1267 1277 fully used by merge, nor fully reported by log.
1268 1278 """
1269 1279 wlock = repo.wlock(0)
1270 1280 errs, copied = docopy(ui, repo, pats, opts, wlock)
1271 1281 return errs
1272 1282
1273 1283 def debugancestor(ui, index, rev1, rev2):
1274 1284 """find the ancestor revision of two revisions in a given index"""
1275 1285 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index, "", 0)
1276 1286 a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
1277 1287 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1278 1288
1279 1289 def debugcomplete(ui, cmd='', **opts):
1280 1290 """returns the completion list associated with the given command"""
1281 1291
1282 1292 if opts['options']:
1283 1293 options = []
1284 1294 otables = [globalopts]
1285 1295 if cmd:
1286 1296 aliases, entry = findcmd(cmd)
1287 1297 otables.append(entry[1])
1288 1298 for t in otables:
1289 1299 for o in t:
1290 1300 if o[0]:
1291 1301 options.append('-%s' % o[0])
1292 1302 options.append('--%s' % o[1])
1293 1303 ui.write("%s\n" % "\n".join(options))
1294 1304 return
1295 1305
1296 1306 clist = findpossible(cmd).keys()
1297 1307 clist.sort()
1298 1308 ui.write("%s\n" % "\n".join(clist))
1299 1309
1300 1310 def debugrebuildstate(ui, repo, rev=None):
1301 1311 """rebuild the dirstate as it would look like for the given revision"""
1302 1312 if not rev:
1303 1313 rev = repo.changelog.tip()
1304 1314 else:
1305 1315 rev = repo.lookup(rev)
1306 1316 change = repo.changelog.read(rev)
1307 1317 n = change[0]
1308 1318 files = repo.manifest.readflags(n)
1309 1319 wlock = repo.wlock()
1310 1320 repo.dirstate.rebuild(rev, files.iteritems())
1311 1321
1312 1322 def debugcheckstate(ui, repo):
1313 1323 """validate the correctness of the current dirstate"""
1314 1324 parent1, parent2 = repo.dirstate.parents()
1315 1325 repo.dirstate.read()
1316 1326 dc = repo.dirstate.map
1317 1327 keys = dc.keys()
1318 1328 keys.sort()
1319 1329 m1n = repo.changelog.read(parent1)[0]
1320 1330 m2n = repo.changelog.read(parent2)[0]
1321 1331 m1 = repo.manifest.read(m1n)
1322 1332 m2 = repo.manifest.read(m2n)
1323 1333 errors = 0
1324 1334 for f in dc:
1325 1335 state = repo.dirstate.state(f)
1326 1336 if state in "nr" and f not in m1:
1327 1337 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1328 1338 errors += 1
1329 1339 if state in "a" and f in m1:
1330 1340 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1331 1341 errors += 1
1332 1342 if state in "m" and f not in m1 and f not in m2:
1333 1343 ui.warn(_("%s in state %s, but not in either manifest\n") %
1334 1344 (f, state))
1335 1345 errors += 1
1336 1346 for f in m1:
1337 1347 state = repo.dirstate.state(f)
1338 1348 if state not in "nrm":
1339 1349 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1340 1350 errors += 1
1341 1351 if errors:
1342 1352 error = _(".hg/dirstate inconsistent with current parent's manifest")
1343 1353 raise util.Abort(error)
1344 1354
1345 1355 def debugconfig(ui, repo, *values):
1346 1356 """show combined config settings from all hgrc files
1347 1357
1348 1358 With no args, print names and values of all config items.
1349 1359
1350 1360 With one arg of the form section.name, print just the value of
1351 1361 that config item.
1352 1362
1353 1363 With multiple args, print names and values of all config items
1354 1364 with matching section names."""
1355 1365
1356 1366 if values:
1357 1367 if len([v for v in values if '.' in v]) > 1:
1358 1368 raise util.Abort(_('only one config item permitted'))
1359 1369 for section, name, value in ui.walkconfig():
1360 1370 sectname = section + '.' + name
1361 1371 if values:
1362 1372 for v in values:
1363 1373 if v == section:
1364 1374 ui.write('%s=%s\n' % (sectname, value))
1365 1375 elif v == sectname:
1366 1376 ui.write(value, '\n')
1367 1377 else:
1368 1378 ui.write('%s=%s\n' % (sectname, value))
1369 1379
1370 1380 def debugsetparents(ui, repo, rev1, rev2=None):
1371 1381 """manually set the parents of the current working directory
1372 1382
1373 1383 This is useful for writing repository conversion tools, but should
1374 1384 be used with care.
1375 1385 """
1376 1386
1377 1387 if not rev2:
1378 1388 rev2 = hex(nullid)
1379 1389
1380 1390 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
1381 1391
1382 1392 def debugstate(ui, repo):
1383 1393 """show the contents of the current dirstate"""
1384 1394 repo.dirstate.read()
1385 1395 dc = repo.dirstate.map
1386 1396 keys = dc.keys()
1387 1397 keys.sort()
1388 1398 for file_ in keys:
1389 1399 ui.write("%c %3o %10d %s %s\n"
1390 1400 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
1391 1401 time.strftime("%x %X",
1392 1402 time.localtime(dc[file_][3])), file_))
1393 1403 for f in repo.dirstate.copies:
1394 1404 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copies[f], f))
1395 1405
1396 1406 def debugdata(ui, file_, rev):
1397 1407 """dump the contents of an data file revision"""
1398 1408 r = revlog.revlog(util.opener(os.getcwd(), audit=False),
1399 1409 file_[:-2] + ".i", file_, 0)
1400 1410 try:
1401 1411 ui.write(r.revision(r.lookup(rev)))
1402 1412 except KeyError:
1403 1413 raise util.Abort(_('invalid revision identifier %s'), rev)
1404 1414
1405 1415 def debugindex(ui, file_):
1406 1416 """dump the contents of an index file"""
1407 1417 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "", 0)
1408 1418 ui.write(" rev offset length base linkrev" +
1409 1419 " nodeid p1 p2\n")
1410 1420 for i in range(r.count()):
1411 1421 node = r.node(i)
1412 1422 pp = r.parents(node)
1413 1423 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1414 1424 i, r.start(i), r.length(i), r.base(i), r.linkrev(node),
1415 1425 short(node), short(pp[0]), short(pp[1])))
1416 1426
1417 1427 def debugindexdot(ui, file_):
1418 1428 """dump an index DAG as a .dot file"""
1419 1429 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "", 0)
1420 1430 ui.write("digraph G {\n")
1421 1431 for i in range(r.count()):
1422 1432 node = r.node(i)
1423 1433 pp = r.parents(node)
1424 1434 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1425 1435 if pp[1] != nullid:
1426 1436 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1427 1437 ui.write("}\n")
1428 1438
1429 1439 def debugrename(ui, repo, file, rev=None):
1430 1440 """dump rename information"""
1431 1441 r = repo.file(relpath(repo, [file])[0])
1432 1442 if rev:
1433 1443 try:
1434 1444 # assume all revision numbers are for changesets
1435 1445 n = repo.lookup(rev)
1436 1446 change = repo.changelog.read(n)
1437 1447 m = repo.manifest.read(change[0])
1438 1448 n = m[relpath(repo, [file])[0]]
1439 1449 except (hg.RepoError, KeyError):
1440 1450 n = r.lookup(rev)
1441 1451 else:
1442 1452 n = r.tip()
1443 1453 m = r.renamed(n)
1444 1454 if m:
1445 1455 ui.write(_("renamed from %s:%s\n") % (m[0], hex(m[1])))
1446 1456 else:
1447 1457 ui.write(_("not renamed\n"))
1448 1458
1449 1459 def debugwalk(ui, repo, *pats, **opts):
1450 1460 """show how files match on given patterns"""
1451 1461 items = list(walk(repo, pats, opts))
1452 1462 if not items:
1453 1463 return
1454 1464 fmt = '%%s %%-%ds %%-%ds %%s' % (
1455 1465 max([len(abs) for (src, abs, rel, exact) in items]),
1456 1466 max([len(rel) for (src, abs, rel, exact) in items]))
1457 1467 for src, abs, rel, exact in items:
1458 1468 line = fmt % (src, abs, rel, exact and 'exact' or '')
1459 1469 ui.write("%s\n" % line.rstrip())
1460 1470
1461 1471 def diff(ui, repo, *pats, **opts):
1462 1472 """diff repository (or selected files)
1463 1473
1464 1474 Show differences between revisions for the specified files.
1465 1475
1466 1476 Differences between files are shown using the unified diff format.
1467 1477
1468 1478 When two revision arguments are given, then changes are shown
1469 1479 between those revisions. If only one revision is specified then
1470 1480 that revision is compared to the working directory, and, when no
1471 1481 revisions are specified, the working directory files are compared
1472 1482 to its parent.
1473 1483
1474 1484 Without the -a option, diff will avoid generating diffs of files
1475 1485 it detects as binary. With -a, diff will generate a diff anyway,
1476 1486 probably with undesirable results.
1477 1487 """
1478 1488 node1, node2 = revpair(ui, repo, opts['rev'])
1479 1489
1480 1490 fns, matchfn, anypats = matchpats(repo, pats, opts)
1481 1491
1482 1492 dodiff(sys.stdout, ui, repo, node1, node2, fns, match=matchfn,
1483 1493 text=opts['text'], opts=opts)
1484 1494
1485 1495 def doexport(ui, repo, changeset, seqno, total, revwidth, opts):
1486 1496 node = repo.lookup(changeset)
1487 1497 parents = [p for p in repo.changelog.parents(node) if p != nullid]
1488 1498 if opts['switch_parent']:
1489 1499 parents.reverse()
1490 1500 prev = (parents and parents[0]) or nullid
1491 1501 change = repo.changelog.read(node)
1492 1502
1493 1503 fp = make_file(repo, opts['output'], node, total=total, seqno=seqno,
1494 1504 revwidth=revwidth)
1495 1505 if fp != sys.stdout:
1496 1506 ui.note("%s\n" % fp.name)
1497 1507
1498 1508 fp.write("# HG changeset patch\n")
1499 1509 fp.write("# User %s\n" % change[1])
1500 1510 fp.write("# Date %d %d\n" % change[2])
1501 1511 fp.write("# Node ID %s\n" % hex(node))
1502 1512 fp.write("# Parent %s\n" % hex(prev))
1503 1513 if len(parents) > 1:
1504 1514 fp.write("# Parent %s\n" % hex(parents[1]))
1505 1515 fp.write(change[4].rstrip())
1506 1516 fp.write("\n\n")
1507 1517
1508 1518 dodiff(fp, ui, repo, prev, node, text=opts['text'])
1509 1519 if fp != sys.stdout:
1510 1520 fp.close()
1511 1521
1512 1522 def export(ui, repo, *changesets, **opts):
1513 1523 """dump the header and diffs for one or more changesets
1514 1524
1515 1525 Print the changeset header and diffs for one or more revisions.
1516 1526
1517 1527 The information shown in the changeset header is: author,
1518 1528 changeset hash, parent and commit comment.
1519 1529
1520 1530 Output may be to a file, in which case the name of the file is
1521 1531 given using a format string. The formatting rules are as follows:
1522 1532
1523 1533 %% literal "%" character
1524 1534 %H changeset hash (40 bytes of hexadecimal)
1525 1535 %N number of patches being generated
1526 1536 %R changeset revision number
1527 1537 %b basename of the exporting repository
1528 1538 %h short-form changeset hash (12 bytes of hexadecimal)
1529 1539 %n zero-padded sequence number, starting at 1
1530 1540 %r zero-padded changeset revision number
1531 1541
1532 1542 Without the -a option, export will avoid generating diffs of files
1533 1543 it detects as binary. With -a, export will generate a diff anyway,
1534 1544 probably with undesirable results.
1535 1545
1536 1546 With the --switch-parent option, the diff will be against the second
1537 1547 parent. It can be useful to review a merge.
1538 1548 """
1539 1549 if not changesets:
1540 1550 raise util.Abort(_("export requires at least one changeset"))
1541 1551 seqno = 0
1542 1552 revs = list(revrange(ui, repo, changesets))
1543 1553 total = len(revs)
1544 1554 revwidth = max(map(len, revs))
1545 1555 msg = len(revs) > 1 and _("Exporting patches:\n") or _("Exporting patch:\n")
1546 1556 ui.note(msg)
1547 1557 for cset in revs:
1548 1558 seqno += 1
1549 1559 doexport(ui, repo, cset, seqno, total, revwidth, opts)
1550 1560
1551 1561 def forget(ui, repo, *pats, **opts):
1552 1562 """don't add the specified files on the next commit (DEPRECATED)
1553 1563
1554 1564 (DEPRECATED)
1555 1565 Undo an 'hg add' scheduled for the next commit.
1556 1566
1557 1567 This command is now deprecated and will be removed in a future
1558 1568 release. Please use revert instead.
1559 1569 """
1560 1570 ui.warn(_("(the forget command is deprecated; use revert instead)\n"))
1561 1571 forget = []
1562 1572 for src, abs, rel, exact in walk(repo, pats, opts):
1563 1573 if repo.dirstate.state(abs) == 'a':
1564 1574 forget.append(abs)
1565 1575 if ui.verbose or not exact:
1566 1576 ui.status(_('forgetting %s\n') % ((pats and rel) or abs))
1567 1577 repo.forget(forget)
1568 1578
1569 1579 def grep(ui, repo, pattern, *pats, **opts):
1570 1580 """search for a pattern in specified files and revisions
1571 1581
1572 1582 Search revisions of files for a regular expression.
1573 1583
1574 1584 This command behaves differently than Unix grep. It only accepts
1575 1585 Python/Perl regexps. It searches repository history, not the
1576 1586 working directory. It always prints the revision number in which
1577 1587 a match appears.
1578 1588
1579 1589 By default, grep only prints output for the first revision of a
1580 1590 file in which it finds a match. To get it to print every revision
1581 1591 that contains a change in match status ("-" for a match that
1582 1592 becomes a non-match, or "+" for a non-match that becomes a match),
1583 1593 use the --all flag.
1584 1594 """
1585 1595 reflags = 0
1586 1596 if opts['ignore_case']:
1587 1597 reflags |= re.I
1588 1598 regexp = re.compile(pattern, reflags)
1589 1599 sep, eol = ':', '\n'
1590 1600 if opts['print0']:
1591 1601 sep = eol = '\0'
1592 1602
1593 1603 fcache = {}
1594 1604 def getfile(fn):
1595 1605 if fn not in fcache:
1596 1606 fcache[fn] = repo.file(fn)
1597 1607 return fcache[fn]
1598 1608
1599 1609 def matchlines(body):
1600 1610 begin = 0
1601 1611 linenum = 0
1602 1612 while True:
1603 1613 match = regexp.search(body, begin)
1604 1614 if not match:
1605 1615 break
1606 1616 mstart, mend = match.span()
1607 1617 linenum += body.count('\n', begin, mstart) + 1
1608 1618 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1609 1619 lend = body.find('\n', mend)
1610 1620 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1611 1621 begin = lend + 1
1612 1622
1613 1623 class linestate(object):
1614 1624 def __init__(self, line, linenum, colstart, colend):
1615 1625 self.line = line
1616 1626 self.linenum = linenum
1617 1627 self.colstart = colstart
1618 1628 self.colend = colend
1619 1629 def __eq__(self, other):
1620 1630 return self.line == other.line
1621 1631 def __hash__(self):
1622 1632 return hash(self.line)
1623 1633
1624 1634 matches = {}
1625 1635 def grepbody(fn, rev, body):
1626 1636 matches[rev].setdefault(fn, {})
1627 1637 m = matches[rev][fn]
1628 1638 for lnum, cstart, cend, line in matchlines(body):
1629 1639 s = linestate(line, lnum, cstart, cend)
1630 1640 m[s] = s
1631 1641
1632 1642 # FIXME: prev isn't used, why ?
1633 1643 prev = {}
1634 1644 ucache = {}
1635 1645 def display(fn, rev, states, prevstates):
1636 1646 diff = list(sets.Set(states).symmetric_difference(sets.Set(prevstates)))
1637 1647 diff.sort(lambda x, y: cmp(x.linenum, y.linenum))
1638 1648 counts = {'-': 0, '+': 0}
1639 1649 filerevmatches = {}
1640 1650 for l in diff:
1641 1651 if incrementing or not opts['all']:
1642 1652 change = ((l in prevstates) and '-') or '+'
1643 1653 r = rev
1644 1654 else:
1645 1655 change = ((l in states) and '-') or '+'
1646 1656 r = prev[fn]
1647 1657 cols = [fn, str(rev)]
1648 1658 if opts['line_number']:
1649 1659 cols.append(str(l.linenum))
1650 1660 if opts['all']:
1651 1661 cols.append(change)
1652 1662 if opts['user']:
1653 1663 cols.append(trimuser(ui, getchange(rev)[1], rev,
1654 1664 ucache))
1655 1665 if opts['files_with_matches']:
1656 1666 c = (fn, rev)
1657 1667 if c in filerevmatches:
1658 1668 continue
1659 1669 filerevmatches[c] = 1
1660 1670 else:
1661 1671 cols.append(l.line)
1662 1672 ui.write(sep.join(cols), eol)
1663 1673 counts[change] += 1
1664 1674 return counts['+'], counts['-']
1665 1675
1666 1676 fstate = {}
1667 1677 skip = {}
1668 1678 changeiter, getchange, matchfn = walkchangerevs(ui, repo, pats, opts)
1669 1679 count = 0
1670 1680 incrementing = False
1671 1681 for st, rev, fns in changeiter:
1672 1682 if st == 'window':
1673 1683 incrementing = rev
1674 1684 matches.clear()
1675 1685 elif st == 'add':
1676 1686 change = repo.changelog.read(repo.lookup(str(rev)))
1677 1687 mf = repo.manifest.read(change[0])
1678 1688 matches[rev] = {}
1679 1689 for fn in fns:
1680 1690 if fn in skip:
1681 1691 continue
1682 1692 fstate.setdefault(fn, {})
1683 1693 try:
1684 1694 grepbody(fn, rev, getfile(fn).read(mf[fn]))
1685 1695 except KeyError:
1686 1696 pass
1687 1697 elif st == 'iter':
1688 1698 states = matches[rev].items()
1689 1699 states.sort()
1690 1700 for fn, m in states:
1691 1701 if fn in skip:
1692 1702 continue
1693 1703 if incrementing or not opts['all'] or fstate[fn]:
1694 1704 pos, neg = display(fn, rev, m, fstate[fn])
1695 1705 count += pos + neg
1696 1706 if pos and not opts['all']:
1697 1707 skip[fn] = True
1698 1708 fstate[fn] = m
1699 1709 prev[fn] = rev
1700 1710
1701 1711 if not incrementing:
1702 1712 fstate = fstate.items()
1703 1713 fstate.sort()
1704 1714 for fn, state in fstate:
1705 1715 if fn in skip:
1706 1716 continue
1707 1717 display(fn, rev, {}, state)
1708 1718 return (count == 0 and 1) or 0
1709 1719
1710 1720 def heads(ui, repo, **opts):
1711 1721 """show current repository heads
1712 1722
1713 1723 Show all repository head changesets.
1714 1724
1715 1725 Repository "heads" are changesets that don't have children
1716 1726 changesets. They are where development generally takes place and
1717 1727 are the usual targets for update and merge operations.
1718 1728 """
1719 1729 if opts['rev']:
1720 1730 heads = repo.heads(repo.lookup(opts['rev']))
1721 1731 else:
1722 1732 heads = repo.heads()
1723 1733 br = None
1724 1734 if opts['branches']:
1725 1735 br = repo.branchlookup(heads)
1726 1736 displayer = show_changeset(ui, repo, opts)
1727 1737 for n in heads:
1728 1738 displayer.show(changenode=n, brinfo=br)
1729 1739
1730 1740 def identify(ui, repo):
1731 1741 """print information about the working copy
1732 1742
1733 1743 Print a short summary of the current state of the repo.
1734 1744
1735 1745 This summary identifies the repository state using one or two parent
1736 1746 hash identifiers, followed by a "+" if there are uncommitted changes
1737 1747 in the working directory, followed by a list of tags for this revision.
1738 1748 """
1739 1749 parents = [p for p in repo.dirstate.parents() if p != nullid]
1740 1750 if not parents:
1741 1751 ui.write(_("unknown\n"))
1742 1752 return
1743 1753
1744 1754 hexfunc = ui.verbose and hex or short
1745 1755 modified, added, removed, deleted, unknown = repo.changes()
1746 1756 output = ["%s%s" %
1747 1757 ('+'.join([hexfunc(parent) for parent in parents]),
1748 1758 (modified or added or removed or deleted) and "+" or "")]
1749 1759
1750 1760 if not ui.quiet:
1751 1761 # multiple tags for a single parent separated by '/'
1752 1762 parenttags = ['/'.join(tags)
1753 1763 for tags in map(repo.nodetags, parents) if tags]
1754 1764 # tags for multiple parents separated by ' + '
1755 1765 if parenttags:
1756 1766 output.append(' + '.join(parenttags))
1757 1767
1758 1768 ui.write("%s\n" % ' '.join(output))
1759 1769
1760 1770 def import_(ui, repo, patch1, *patches, **opts):
1761 1771 """import an ordered set of patches
1762 1772
1763 1773 Import a list of patches and commit them individually.
1764 1774
1765 1775 If there are outstanding changes in the working directory, import
1766 1776 will abort unless given the -f flag.
1767 1777
1768 1778 You can import a patch straight from a mail message. Even patches
1769 1779 as attachments work (body part must be type text/plain or
1770 1780 text/x-patch to be used). From and Subject headers of email
1771 1781 message are used as default committer and commit message. All
1772 1782 text/plain body parts before first diff are added to commit
1773 1783 message.
1774 1784
1775 1785 If imported patch was generated by hg export, user and description
1776 1786 from patch override values from message headers and body. Values
1777 1787 given on command line with -m and -u override these.
1778 1788
1779 1789 To read a patch from standard input, use patch name "-".
1780 1790 """
1781 1791 patches = (patch1,) + patches
1782 1792
1783 1793 if not opts['force']:
1784 1794 bail_if_changed(repo)
1785 1795
1786 1796 d = opts["base"]
1787 1797 strip = opts["strip"]
1788 1798
1789 1799 mailre = re.compile(r'(?:From |[\w-]+:)')
1790 1800
1791 1801 # attempt to detect the start of a patch
1792 1802 # (this heuristic is borrowed from quilt)
1793 1803 diffre = re.compile(r'^(?:Index:[ \t]|diff[ \t]|RCS file: |' +
1794 1804 'retrieving revision [0-9]+(\.[0-9]+)*$|' +
1795 1805 '(---|\*\*\*)[ \t])', re.MULTILINE)
1796 1806
1797 1807 for patch in patches:
1798 1808 pf = os.path.join(d, patch)
1799 1809
1800 1810 message = None
1801 1811 user = None
1802 1812 date = None
1803 1813 hgpatch = False
1804 1814
1805 1815 p = email.Parser.Parser()
1806 1816 if pf == '-':
1807 1817 msg = p.parse(sys.stdin)
1808 1818 ui.status(_("applying patch from stdin\n"))
1809 1819 else:
1810 1820 msg = p.parse(file(pf))
1811 1821 ui.status(_("applying %s\n") % patch)
1812 1822
1813 1823 fd, tmpname = tempfile.mkstemp(prefix='hg-patch-')
1814 1824 tmpfp = os.fdopen(fd, 'w')
1815 1825 try:
1816 1826 message = msg['Subject']
1817 1827 if message:
1818 1828 message = message.replace('\n\t', ' ')
1819 1829 ui.debug('Subject: %s\n' % message)
1820 1830 user = msg['From']
1821 1831 if user:
1822 1832 ui.debug('From: %s\n' % user)
1823 1833 diffs_seen = 0
1824 1834 ok_types = ('text/plain', 'text/x-patch')
1825 1835 for part in msg.walk():
1826 1836 content_type = part.get_content_type()
1827 1837 ui.debug('Content-Type: %s\n' % content_type)
1828 1838 if content_type not in ok_types:
1829 1839 continue
1830 1840 payload = part.get_payload(decode=True)
1831 1841 m = diffre.search(payload)
1832 1842 if m:
1833 1843 ui.debug(_('found patch at byte %d\n') % m.start(0))
1834 1844 diffs_seen += 1
1835 1845 hgpatch = False
1836 1846 fp = cStringIO.StringIO()
1837 1847 if message:
1838 1848 fp.write(message)
1839 1849 fp.write('\n')
1840 1850 for line in payload[:m.start(0)].splitlines():
1841 1851 if line.startswith('# HG changeset patch'):
1842 1852 ui.debug(_('patch generated by hg export\n'))
1843 1853 hgpatch = True
1844 1854 # drop earlier commit message content
1845 1855 fp.seek(0)
1846 1856 fp.truncate()
1847 1857 elif hgpatch:
1848 1858 if line.startswith('# User '):
1849 1859 user = line[7:]
1850 1860 ui.debug('From: %s\n' % user)
1851 1861 elif line.startswith("# Date "):
1852 1862 date = line[7:]
1853 1863 if not line.startswith('# '):
1854 1864 fp.write(line)
1855 1865 fp.write('\n')
1856 1866 message = fp.getvalue()
1857 1867 if tmpfp:
1858 1868 tmpfp.write(payload)
1859 1869 if not payload.endswith('\n'):
1860 1870 tmpfp.write('\n')
1861 1871 elif not diffs_seen and message and content_type == 'text/plain':
1862 1872 message += '\n' + payload
1863 1873
1864 1874 if opts['message']:
1865 1875 # pickup the cmdline msg
1866 1876 message = opts['message']
1867 1877 elif message:
1868 1878 # pickup the patch msg
1869 1879 message = message.strip()
1870 1880 else:
1871 1881 # launch the editor
1872 1882 message = None
1873 1883 ui.debug(_('message:\n%s\n') % message)
1874 1884
1875 1885 tmpfp.close()
1876 1886 if not diffs_seen:
1877 1887 raise util.Abort(_('no diffs found'))
1878 1888
1879 1889 files = util.patch(strip, tmpname, ui)
1880 1890 if len(files) > 0:
1881 1891 addremove_lock(ui, repo, files, {})
1882 1892 repo.commit(files, message, user, date)
1883 1893 finally:
1884 1894 os.unlink(tmpname)
1885 1895
1886 1896 def incoming(ui, repo, source="default", **opts):
1887 1897 """show new changesets found in source
1888 1898
1889 1899 Show new changesets found in the specified path/URL or the default
1890 1900 pull location. These are the changesets that would be pulled if a pull
1891 1901 was requested.
1892 1902
1893 1903 For remote repository, using --bundle avoids downloading the changesets
1894 1904 twice if the incoming is followed by a pull.
1895 1905
1896 1906 See pull for valid source format details.
1897 1907 """
1898 1908 source = ui.expandpath(source)
1899 1909 if opts['ssh']:
1900 1910 ui.setconfig("ui", "ssh", opts['ssh'])
1901 1911 if opts['remotecmd']:
1902 1912 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
1903 1913
1904 1914 other = hg.repository(ui, source)
1905 1915 incoming = repo.findincoming(other, force=opts["force"])
1906 1916 if not incoming:
1907 1917 ui.status(_("no changes found\n"))
1908 1918 return
1909 1919
1910 1920 cleanup = None
1911 1921 try:
1912 1922 fname = opts["bundle"]
1913 1923 if fname or not other.local():
1914 1924 # create a bundle (uncompressed if other repo is not local)
1915 1925 cg = other.changegroup(incoming, "incoming")
1916 1926 fname = cleanup = write_bundle(cg, fname, compress=other.local())
1917 1927 # keep written bundle?
1918 1928 if opts["bundle"]:
1919 1929 cleanup = None
1920 1930 if not other.local():
1921 1931 # use the created uncompressed bundlerepo
1922 1932 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1923 1933
1924 1934 revs = None
1925 1935 if opts['rev']:
1926 1936 revs = [other.lookup(rev) for rev in opts['rev']]
1927 1937 o = other.changelog.nodesbetween(incoming, revs)[0]
1928 1938 if opts['newest_first']:
1929 1939 o.reverse()
1930 1940 displayer = show_changeset(ui, other, opts)
1931 1941 for n in o:
1932 1942 parents = [p for p in other.changelog.parents(n) if p != nullid]
1933 1943 if opts['no_merges'] and len(parents) == 2:
1934 1944 continue
1935 1945 displayer.show(changenode=n)
1936 1946 if opts['patch']:
1937 1947 prev = (parents and parents[0]) or nullid
1938 1948 dodiff(ui, ui, other, prev, n)
1939 1949 ui.write("\n")
1940 1950 finally:
1941 1951 if hasattr(other, 'close'):
1942 1952 other.close()
1943 1953 if cleanup:
1944 1954 os.unlink(cleanup)
1945 1955
1946 1956 def init(ui, dest="."):
1947 1957 """create a new repository in the given directory
1948 1958
1949 1959 Initialize a new repository in the given directory. If the given
1950 1960 directory does not exist, it is created.
1951 1961
1952 1962 If no directory is given, the current directory is used.
1953 1963 """
1954 1964 hg.repository(ui, dest, create=1)
1955 1965
1956 1966 def locate(ui, repo, *pats, **opts):
1957 1967 """locate files matching specific patterns
1958 1968
1959 1969 Print all files under Mercurial control whose names match the
1960 1970 given patterns.
1961 1971
1962 1972 This command searches the current directory and its
1963 1973 subdirectories. To search an entire repository, move to the root
1964 1974 of the repository.
1965 1975
1966 1976 If no patterns are given to match, this command prints all file
1967 1977 names.
1968 1978
1969 1979 If you want to feed the output of this command into the "xargs"
1970 1980 command, use the "-0" option to both this command and "xargs".
1971 1981 This will avoid the problem of "xargs" treating single filenames
1972 1982 that contain white space as multiple filenames.
1973 1983 """
1974 1984 end = opts['print0'] and '\0' or '\n'
1975 1985 rev = opts['rev']
1976 1986 if rev:
1977 1987 node = repo.lookup(rev)
1978 1988 else:
1979 1989 node = None
1980 1990
1981 1991 for src, abs, rel, exact in walk(repo, pats, opts, node=node,
1982 1992 head='(?:.*/|)'):
1983 1993 if not node and repo.dirstate.state(abs) == '?':
1984 1994 continue
1985 1995 if opts['fullpath']:
1986 1996 ui.write(os.path.join(repo.root, abs), end)
1987 1997 else:
1988 1998 ui.write(((pats and rel) or abs), end)
1989 1999
1990 2000 def log(ui, repo, *pats, **opts):
1991 2001 """show revision history of entire repository or files
1992 2002
1993 2003 Print the revision history of the specified files or the entire project.
1994 2004
1995 2005 By default this command outputs: changeset id and hash, tags,
1996 2006 non-trivial parents, user, date and time, and a summary for each
1997 2007 commit. When the -v/--verbose switch is used, the list of changed
1998 2008 files and full commit message is shown.
1999 2009 """
2000 2010 class dui(object):
2001 2011 # Implement and delegate some ui protocol. Save hunks of
2002 2012 # output for later display in the desired order.
2003 2013 def __init__(self, ui):
2004 2014 self.ui = ui
2005 2015 self.hunk = {}
2006 2016 self.header = {}
2007 2017 def bump(self, rev):
2008 2018 self.rev = rev
2009 2019 self.hunk[rev] = []
2010 2020 self.header[rev] = []
2011 2021 def note(self, *args):
2012 2022 if self.verbose:
2013 2023 self.write(*args)
2014 2024 def status(self, *args):
2015 2025 if not self.quiet:
2016 2026 self.write(*args)
2017 2027 def write(self, *args):
2018 2028 self.hunk[self.rev].append(args)
2019 2029 def write_header(self, *args):
2020 2030 self.header[self.rev].append(args)
2021 2031 def debug(self, *args):
2022 2032 if self.debugflag:
2023 2033 self.write(*args)
2024 2034 def __getattr__(self, key):
2025 2035 return getattr(self.ui, key)
2026 2036
2027 2037 changeiter, getchange, matchfn = walkchangerevs(ui, repo, pats, opts)
2028 2038
2029 2039 if opts['limit']:
2030 2040 try:
2031 2041 limit = int(opts['limit'])
2032 2042 except ValueError:
2033 2043 raise util.Abort(_('limit must be a positive integer'))
2034 2044 if limit <= 0: raise util.Abort(_('limit must be positive'))
2035 2045 else:
2036 2046 limit = sys.maxint
2037 2047 count = 0
2038 2048
2039 2049 displayer = show_changeset(ui, repo, opts)
2040 2050 for st, rev, fns in changeiter:
2041 2051 if st == 'window':
2042 2052 du = dui(ui)
2043 2053 displayer.ui = du
2044 2054 elif st == 'add':
2045 2055 du.bump(rev)
2046 2056 changenode = repo.changelog.node(rev)
2047 2057 parents = [p for p in repo.changelog.parents(changenode)
2048 2058 if p != nullid]
2049 2059 if opts['no_merges'] and len(parents) == 2:
2050 2060 continue
2051 2061 if opts['only_merges'] and len(parents) != 2:
2052 2062 continue
2053 2063
2054 2064 if opts['keyword']:
2055 2065 changes = getchange(rev)
2056 2066 miss = 0
2057 2067 for k in [kw.lower() for kw in opts['keyword']]:
2058 2068 if not (k in changes[1].lower() or
2059 2069 k in changes[4].lower() or
2060 2070 k in " ".join(changes[3][:20]).lower()):
2061 2071 miss = 1
2062 2072 break
2063 2073 if miss:
2064 2074 continue
2065 2075
2066 2076 br = None
2067 2077 if opts['branches']:
2068 2078 br = repo.branchlookup([repo.changelog.node(rev)])
2069 2079
2070 2080 displayer.show(rev, brinfo=br)
2071 2081 if opts['patch']:
2072 2082 prev = (parents and parents[0]) or nullid
2073 2083 dodiff(du, du, repo, prev, changenode, match=matchfn)
2074 2084 du.write("\n\n")
2075 2085 elif st == 'iter':
2076 2086 if count == limit: break
2077 2087 if du.header[rev]:
2078 2088 for args in du.header[rev]:
2079 2089 ui.write_header(*args)
2080 2090 if du.hunk[rev]:
2081 2091 count += 1
2082 2092 for args in du.hunk[rev]:
2083 2093 ui.write(*args)
2084 2094
2085 2095 def manifest(ui, repo, rev=None):
2086 2096 """output the latest or given revision of the project manifest
2087 2097
2088 2098 Print a list of version controlled files for the given revision.
2089 2099
2090 2100 The manifest is the list of files being version controlled. If no revision
2091 2101 is given then the tip is used.
2092 2102 """
2093 2103 if rev:
2094 2104 try:
2095 2105 # assume all revision numbers are for changesets
2096 2106 n = repo.lookup(rev)
2097 2107 change = repo.changelog.read(n)
2098 2108 n = change[0]
2099 2109 except hg.RepoError:
2100 2110 n = repo.manifest.lookup(rev)
2101 2111 else:
2102 2112 n = repo.manifest.tip()
2103 2113 m = repo.manifest.read(n)
2104 2114 mf = repo.manifest.readflags(n)
2105 2115 files = m.keys()
2106 2116 files.sort()
2107 2117
2108 2118 for f in files:
2109 2119 ui.write("%40s %3s %s\n" % (hex(m[f]), mf[f] and "755" or "644", f))
2110 2120
2111 2121 def merge(ui, repo, node=None, **opts):
2112 2122 """Merge working directory with another revision
2113 2123
2114 2124 Merge the contents of the current working directory and the
2115 2125 requested revision. Files that changed between either parent are
2116 2126 marked as changed for the next commit and a commit must be
2117 2127 performed before any further updates are allowed.
2118 2128 """
2119 2129 return doupdate(ui, repo, node=node, merge=True, **opts)
2120 2130
2121 2131 def outgoing(ui, repo, dest=None, **opts):
2122 2132 """show changesets not found in destination
2123 2133
2124 2134 Show changesets not found in the specified destination repository or
2125 2135 the default push location. These are the changesets that would be pushed
2126 2136 if a push was requested.
2127 2137
2128 2138 See pull for valid destination format details.
2129 2139 """
2130 2140 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2131 2141 if opts['ssh']:
2132 2142 ui.setconfig("ui", "ssh", opts['ssh'])
2133 2143 if opts['remotecmd']:
2134 2144 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
2135 2145 revs = None
2136 2146 if opts['rev']:
2137 2147 revs = [repo.lookup(rev) for rev in opts['rev']]
2138 2148
2139 2149 other = hg.repository(ui, dest)
2140 2150 o = repo.findoutgoing(other, force=opts['force'])
2141 2151 if not o:
2142 2152 ui.status(_("no changes found\n"))
2143 2153 return
2144 2154 o = repo.changelog.nodesbetween(o, revs)[0]
2145 2155 if opts['newest_first']:
2146 2156 o.reverse()
2147 2157 displayer = show_changeset(ui, repo, opts)
2148 2158 for n in o:
2149 2159 parents = [p for p in repo.changelog.parents(n) if p != nullid]
2150 2160 if opts['no_merges'] and len(parents) == 2:
2151 2161 continue
2152 2162 displayer.show(changenode=n)
2153 2163 if opts['patch']:
2154 2164 prev = (parents and parents[0]) or nullid
2155 2165 dodiff(ui, ui, repo, prev, n)
2156 2166 ui.write("\n")
2157 2167
2158 2168 def parents(ui, repo, rev=None, branches=None, **opts):
2159 2169 """show the parents of the working dir or revision
2160 2170
2161 2171 Print the working directory's parent revisions.
2162 2172 """
2163 2173 if rev:
2164 2174 p = repo.changelog.parents(repo.lookup(rev))
2165 2175 else:
2166 2176 p = repo.dirstate.parents()
2167 2177
2168 2178 br = None
2169 2179 if branches is not None:
2170 2180 br = repo.branchlookup(p)
2171 2181 displayer = show_changeset(ui, repo, opts)
2172 2182 for n in p:
2173 2183 if n != nullid:
2174 2184 displayer.show(changenode=n, brinfo=br)
2175 2185
2176 2186 def paths(ui, repo, search=None):
2177 2187 """show definition of symbolic path names
2178 2188
2179 2189 Show definition of symbolic path name NAME. If no name is given, show
2180 2190 definition of available names.
2181 2191
2182 2192 Path names are defined in the [paths] section of /etc/mercurial/hgrc
2183 2193 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
2184 2194 """
2185 2195 if search:
2186 2196 for name, path in ui.configitems("paths"):
2187 2197 if name == search:
2188 2198 ui.write("%s\n" % path)
2189 2199 return
2190 2200 ui.warn(_("not found!\n"))
2191 2201 return 1
2192 2202 else:
2193 2203 for name, path in ui.configitems("paths"):
2194 2204 ui.write("%s = %s\n" % (name, path))
2195 2205
2196 2206 def postincoming(ui, repo, modheads, optupdate):
2197 2207 if modheads == 0:
2198 2208 return
2199 2209 if optupdate:
2200 2210 if modheads == 1:
2201 2211 return doupdate(ui, repo)
2202 2212 else:
2203 2213 ui.status(_("not updating, since new heads added\n"))
2204 2214 if modheads > 1:
2205 2215 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2206 2216 else:
2207 2217 ui.status(_("(run 'hg update' to get a working copy)\n"))
2208 2218
2209 2219 def pull(ui, repo, source="default", **opts):
2210 2220 """pull changes from the specified source
2211 2221
2212 2222 Pull changes from a remote repository to a local one.
2213 2223
2214 2224 This finds all changes from the repository at the specified path
2215 2225 or URL and adds them to the local repository. By default, this
2216 2226 does not update the copy of the project in the working directory.
2217 2227
2218 2228 Valid URLs are of the form:
2219 2229
2220 2230 local/filesystem/path
2221 2231 http://[user@]host[:port][/path]
2222 2232 https://[user@]host[:port][/path]
2223 2233 ssh://[user@]host[:port][/path]
2224 2234
2225 2235 Some notes about using SSH with Mercurial:
2226 2236 - SSH requires an accessible shell account on the destination machine
2227 2237 and a copy of hg in the remote path or specified with as remotecmd.
2228 2238 - /path is relative to the remote user's home directory by default.
2229 2239 Use two slashes at the start of a path to specify an absolute path.
2230 2240 - Mercurial doesn't use its own compression via SSH; the right thing
2231 2241 to do is to configure it in your ~/.ssh/ssh_config, e.g.:
2232 2242 Host *.mylocalnetwork.example.com
2233 2243 Compression off
2234 2244 Host *
2235 2245 Compression on
2236 2246 Alternatively specify "ssh -C" as your ssh command in your hgrc or
2237 2247 with the --ssh command line option.
2238 2248 """
2239 2249 source = ui.expandpath(source)
2240 2250
2241 2251 if opts['ssh']:
2242 2252 ui.setconfig("ui", "ssh", opts['ssh'])
2243 2253 if opts['remotecmd']:
2244 2254 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
2245 2255
2246 2256 other = hg.repository(ui, source)
2247 2257 ui.status(_('pulling from %s\n') % (source))
2248 2258 revs = None
2249 2259 if opts['rev'] and not other.local():
2250 2260 raise util.Abort(_("pull -r doesn't work for remote repositories yet"))
2251 2261 elif opts['rev']:
2252 2262 revs = [other.lookup(rev) for rev in opts['rev']]
2253 2263 modheads = repo.pull(other, heads=revs, force=opts['force'])
2254 2264 return postincoming(ui, repo, modheads, opts['update'])
2255 2265
2256 2266 def push(ui, repo, dest=None, **opts):
2257 2267 """push changes to the specified destination
2258 2268
2259 2269 Push changes from the local repository to the given destination.
2260 2270
2261 2271 This is the symmetrical operation for pull. It helps to move
2262 2272 changes from the current repository to a different one. If the
2263 2273 destination is local this is identical to a pull in that directory
2264 2274 from the current one.
2265 2275
2266 2276 By default, push will refuse to run if it detects the result would
2267 2277 increase the number of remote heads. This generally indicates the
2268 2278 the client has forgotten to sync and merge before pushing.
2269 2279
2270 2280 Valid URLs are of the form:
2271 2281
2272 2282 local/filesystem/path
2273 2283 ssh://[user@]host[:port][/path]
2274 2284
2275 2285 Look at the help text for the pull command for important details
2276 2286 about ssh:// URLs.
2277 2287 """
2278 2288 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2279 2289
2280 2290 if opts['ssh']:
2281 2291 ui.setconfig("ui", "ssh", opts['ssh'])
2282 2292 if opts['remotecmd']:
2283 2293 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
2284 2294
2285 2295 other = hg.repository(ui, dest)
2286 2296 ui.status('pushing to %s\n' % (dest))
2287 2297 revs = None
2288 2298 if opts['rev']:
2289 2299 revs = [repo.lookup(rev) for rev in opts['rev']]
2290 2300 r = repo.push(other, opts['force'], revs=revs)
2291 2301 return r == 0
2292 2302
2293 2303 def rawcommit(ui, repo, *flist, **rc):
2294 2304 """raw commit interface (DEPRECATED)
2295 2305
2296 2306 (DEPRECATED)
2297 2307 Lowlevel commit, for use in helper scripts.
2298 2308
2299 2309 This command is not intended to be used by normal users, as it is
2300 2310 primarily useful for importing from other SCMs.
2301 2311
2302 2312 This command is now deprecated and will be removed in a future
2303 2313 release, please use debugsetparents and commit instead.
2304 2314 """
2305 2315
2306 2316 ui.warn(_("(the rawcommit command is deprecated)\n"))
2307 2317
2308 2318 message = rc['message']
2309 2319 if not message and rc['logfile']:
2310 2320 try:
2311 2321 message = open(rc['logfile']).read()
2312 2322 except IOError:
2313 2323 pass
2314 2324 if not message and not rc['logfile']:
2315 2325 raise util.Abort(_("missing commit message"))
2316 2326
2317 2327 files = relpath(repo, list(flist))
2318 2328 if rc['files']:
2319 2329 files += open(rc['files']).read().splitlines()
2320 2330
2321 2331 rc['parent'] = map(repo.lookup, rc['parent'])
2322 2332
2323 2333 try:
2324 2334 repo.rawcommit(files, message, rc['user'], rc['date'], *rc['parent'])
2325 2335 except ValueError, inst:
2326 2336 raise util.Abort(str(inst))
2327 2337
2328 2338 def recover(ui, repo):
2329 2339 """roll back an interrupted transaction
2330 2340
2331 2341 Recover from an interrupted commit or pull.
2332 2342
2333 2343 This command tries to fix the repository status after an interrupted
2334 2344 operation. It should only be necessary when Mercurial suggests it.
2335 2345 """
2336 2346 if repo.recover():
2337 2347 return repo.verify()
2338 2348 return 1
2339 2349
2340 2350 def remove(ui, repo, *pats, **opts):
2341 2351 """remove the specified files on the next commit
2342 2352
2343 2353 Schedule the indicated files for removal from the repository.
2344 2354
2345 2355 This command schedules the files to be removed at the next commit.
2346 2356 This only removes files from the current branch, not from the
2347 2357 entire project history. If the files still exist in the working
2348 2358 directory, they will be deleted from it. If invoked with --after,
2349 2359 files that have been manually deleted are marked as removed.
2350 2360
2351 2361 Modified files and added files are not removed by default. To
2352 2362 remove them, use the -f/--force option.
2353 2363 """
2354 2364 names = []
2355 2365 if not opts['after'] and not pats:
2356 2366 raise util.Abort(_('no files specified'))
2357 2367 files, matchfn, anypats = matchpats(repo, pats, opts)
2358 2368 exact = dict.fromkeys(files)
2359 2369 mardu = map(dict.fromkeys, repo.changes(files=files, match=matchfn))
2360 2370 modified, added, removed, deleted, unknown = mardu
2361 2371 remove, forget = [], []
2362 2372 for src, abs, rel, exact in walk(repo, pats, opts):
2363 2373 reason = None
2364 2374 if abs not in deleted and opts['after']:
2365 2375 reason = _('is still present')
2366 2376 elif abs in modified and not opts['force']:
2367 2377 reason = _('is modified (use -f to force removal)')
2368 2378 elif abs in added:
2369 2379 if opts['force']:
2370 2380 forget.append(abs)
2371 2381 continue
2372 2382 reason = _('has been marked for add (use -f to force removal)')
2373 2383 elif abs in unknown:
2374 2384 reason = _('is not managed')
2375 2385 elif abs in removed:
2376 2386 continue
2377 2387 if reason:
2378 2388 if exact:
2379 2389 ui.warn(_('not removing %s: file %s\n') % (rel, reason))
2380 2390 else:
2381 2391 if ui.verbose or not exact:
2382 2392 ui.status(_('removing %s\n') % rel)
2383 2393 remove.append(abs)
2384 2394 repo.forget(forget)
2385 2395 repo.remove(remove, unlink=not opts['after'])
2386 2396
2387 2397 def rename(ui, repo, *pats, **opts):
2388 2398 """rename files; equivalent of copy + remove
2389 2399
2390 2400 Mark dest as copies of sources; mark sources for deletion. If
2391 2401 dest is a directory, copies are put in that directory. If dest is
2392 2402 a file, there can only be one source.
2393 2403
2394 2404 By default, this command copies the contents of files as they
2395 2405 stand in the working directory. If invoked with --after, the
2396 2406 operation is recorded, but no copying is performed.
2397 2407
2398 2408 This command takes effect in the next commit.
2399 2409
2400 2410 NOTE: This command should be treated as experimental. While it
2401 2411 should properly record rename files, this information is not yet
2402 2412 fully used by merge, nor fully reported by log.
2403 2413 """
2404 2414 wlock = repo.wlock(0)
2405 2415 errs, copied = docopy(ui, repo, pats, opts, wlock)
2406 2416 names = []
2407 2417 for abs, rel, exact in copied:
2408 2418 if ui.verbose or not exact:
2409 2419 ui.status(_('removing %s\n') % rel)
2410 2420 names.append(abs)
2411 2421 if not opts.get('dry_run'):
2412 2422 repo.remove(names, True, wlock)
2413 2423 return errs
2414 2424
2415 2425 def revert(ui, repo, *pats, **opts):
2416 2426 """revert files or dirs to their states as of some revision
2417 2427
2418 2428 With no revision specified, revert the named files or directories
2419 2429 to the contents they had in the parent of the working directory.
2420 2430 This restores the contents of the affected files to an unmodified
2421 2431 state. If the working directory has two parents, you must
2422 2432 explicitly specify the revision to revert to.
2423 2433
2424 2434 Modified files are saved with a .orig suffix before reverting.
2425 2435 To disable these backups, use --no-backup.
2426 2436
2427 2437 Using the -r option, revert the given files or directories to
2428 2438 their contents as of a specific revision. This can be helpful to"roll
2429 2439 back" some or all of a change that should not have been committed.
2430 2440
2431 2441 Revert modifies the working directory. It does not commit any
2432 2442 changes, or change the parent of the working directory. If you
2433 2443 revert to a revision other than the parent of the working
2434 2444 directory, the reverted files will thus appear modified
2435 2445 afterwards.
2436 2446
2437 2447 If a file has been deleted, it is recreated. If the executable
2438 2448 mode of a file was changed, it is reset.
2439 2449
2440 2450 If names are given, all files matching the names are reverted.
2441 2451
2442 2452 If no arguments are given, all files in the repository are reverted.
2443 2453 """
2444 2454 parent, p2 = repo.dirstate.parents()
2445 2455 if opts['rev']:
2446 2456 node = repo.lookup(opts['rev'])
2447 2457 elif p2 != nullid:
2448 2458 raise util.Abort(_('working dir has two parents; '
2449 2459 'you must specify the revision to revert to'))
2450 2460 else:
2451 2461 node = parent
2452 2462 mf = repo.manifest.read(repo.changelog.read(node)[0])
2453 2463 if node == parent:
2454 2464 pmf = mf
2455 2465 else:
2456 2466 pmf = None
2457 2467
2458 2468 wlock = repo.wlock()
2459 2469
2460 2470 # need all matching names in dirstate and manifest of target rev,
2461 2471 # so have to walk both. do not print errors if files exist in one
2462 2472 # but not other.
2463 2473
2464 2474 names = {}
2465 2475 target_only = {}
2466 2476
2467 2477 # walk dirstate.
2468 2478
2469 2479 for src, abs, rel, exact in walk(repo, pats, opts, badmatch=mf.has_key):
2470 2480 names[abs] = (rel, exact)
2471 2481 if src == 'b':
2472 2482 target_only[abs] = True
2473 2483
2474 2484 # walk target manifest.
2475 2485
2476 2486 for src, abs, rel, exact in walk(repo, pats, opts, node=node,
2477 2487 badmatch=names.has_key):
2478 2488 if abs in names: continue
2479 2489 names[abs] = (rel, exact)
2480 2490 target_only[abs] = True
2481 2491
2482 2492 changes = repo.changes(match=names.has_key, wlock=wlock)
2483 2493 modified, added, removed, deleted, unknown = map(dict.fromkeys, changes)
2484 2494
2485 2495 revert = ([], _('reverting %s\n'))
2486 2496 add = ([], _('adding %s\n'))
2487 2497 remove = ([], _('removing %s\n'))
2488 2498 forget = ([], _('forgetting %s\n'))
2489 2499 undelete = ([], _('undeleting %s\n'))
2490 2500 update = {}
2491 2501
2492 2502 disptable = (
2493 2503 # dispatch table:
2494 2504 # file state
2495 2505 # action if in target manifest
2496 2506 # action if not in target manifest
2497 2507 # make backup if in target manifest
2498 2508 # make backup if not in target manifest
2499 2509 (modified, revert, remove, True, True),
2500 2510 (added, revert, forget, True, False),
2501 2511 (removed, undelete, None, False, False),
2502 2512 (deleted, revert, remove, False, False),
2503 2513 (unknown, add, None, True, False),
2504 2514 (target_only, add, None, False, False),
2505 2515 )
2506 2516
2507 2517 entries = names.items()
2508 2518 entries.sort()
2509 2519
2510 2520 for abs, (rel, exact) in entries:
2511 2521 mfentry = mf.get(abs)
2512 2522 def handle(xlist, dobackup):
2513 2523 xlist[0].append(abs)
2514 2524 update[abs] = 1
2515 2525 if dobackup and not opts['no_backup'] and os.path.exists(rel):
2516 2526 bakname = "%s.orig" % rel
2517 2527 ui.note(_('saving current version of %s as %s\n') %
2518 2528 (rel, bakname))
2519 2529 if not opts.get('dry_run'):
2520 2530 shutil.copyfile(rel, bakname)
2521 2531 shutil.copymode(rel, bakname)
2522 2532 if ui.verbose or not exact:
2523 2533 ui.status(xlist[1] % rel)
2524 2534 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2525 2535 if abs not in table: continue
2526 2536 # file has changed in dirstate
2527 2537 if mfentry:
2528 2538 handle(hitlist, backuphit)
2529 2539 elif misslist is not None:
2530 2540 handle(misslist, backupmiss)
2531 2541 else:
2532 2542 if exact: ui.warn(_('file not managed: %s\n' % rel))
2533 2543 break
2534 2544 else:
2535 2545 # file has not changed in dirstate
2536 2546 if node == parent:
2537 2547 if exact: ui.warn(_('no changes needed to %s\n' % rel))
2538 2548 continue
2539 2549 if pmf is None:
2540 2550 # only need parent manifest in this unlikely case,
2541 2551 # so do not read by default
2542 2552 pmf = repo.manifest.read(repo.changelog.read(parent)[0])
2543 2553 if abs in pmf:
2544 2554 if mfentry:
2545 2555 # if version of file is same in parent and target
2546 2556 # manifests, do nothing
2547 2557 if pmf[abs] != mfentry:
2548 2558 handle(revert, False)
2549 2559 else:
2550 2560 handle(remove, False)
2551 2561
2552 2562 if not opts.get('dry_run'):
2553 2563 repo.dirstate.forget(forget[0])
2554 2564 r = repo.update(node, False, True, update.has_key, False, wlock=wlock,
2555 2565 show_stats=False)
2556 2566 repo.dirstate.update(add[0], 'a')
2557 2567 repo.dirstate.update(undelete[0], 'n')
2558 2568 repo.dirstate.update(remove[0], 'r')
2559 2569 return r
2560 2570
2561 2571 def rollback(ui, repo):
2562 2572 """roll back the last transaction in this repository
2563 2573
2564 2574 Roll back the last transaction in this repository, restoring the
2565 2575 project to its state prior to the transaction.
2566 2576
2567 2577 Transactions are used to encapsulate the effects of all commands
2568 2578 that create new changesets or propagate existing changesets into a
2569 2579 repository. For example, the following commands are transactional,
2570 2580 and their effects can be rolled back:
2571 2581
2572 2582 commit
2573 2583 import
2574 2584 pull
2575 2585 push (with this repository as destination)
2576 2586 unbundle
2577 2587
2578 2588 This command should be used with care. There is only one level of
2579 2589 rollback, and there is no way to undo a rollback.
2580 2590
2581 2591 This command is not intended for use on public repositories. Once
2582 2592 changes are visible for pull by other users, rolling a transaction
2583 2593 back locally is ineffective (someone else may already have pulled
2584 2594 the changes). Furthermore, a race is possible with readers of the
2585 2595 repository; for example an in-progress pull from the repository
2586 2596 may fail if a rollback is performed.
2587 2597 """
2588 2598 repo.rollback()
2589 2599
2590 2600 def root(ui, repo):
2591 2601 """print the root (top) of the current working dir
2592 2602
2593 2603 Print the root directory of the current repository.
2594 2604 """
2595 2605 ui.write(repo.root + "\n")
2596 2606
2597 2607 def serve(ui, repo, **opts):
2598 2608 """export the repository via HTTP
2599 2609
2600 2610 Start a local HTTP repository browser and pull server.
2601 2611
2602 2612 By default, the server logs accesses to stdout and errors to
2603 2613 stderr. Use the "-A" and "-E" options to log to files.
2604 2614 """
2605 2615
2606 2616 if opts["stdio"]:
2607 2617 if repo is None:
2608 2618 raise hg.RepoError(_('no repo found'))
2609 2619 s = sshserver.sshserver(ui, repo)
2610 2620 s.serve_forever()
2611 2621
2612 2622 optlist = ("name templates style address port ipv6"
2613 2623 " accesslog errorlog webdir_conf")
2614 2624 for o in optlist.split():
2615 2625 if opts[o]:
2616 2626 ui.setconfig("web", o, opts[o])
2617 2627
2618 2628 if repo is None and not ui.config("web", "webdir_conf"):
2619 2629 raise hg.RepoError(_('no repo found'))
2620 2630
2621 2631 if opts['daemon'] and not opts['daemon_pipefds']:
2622 2632 rfd, wfd = os.pipe()
2623 2633 args = sys.argv[:]
2624 2634 args.append('--daemon-pipefds=%d,%d' % (rfd, wfd))
2625 2635 pid = os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
2626 2636 args[0], args)
2627 2637 os.close(wfd)
2628 2638 os.read(rfd, 1)
2629 2639 os._exit(0)
2630 2640
2631 2641 try:
2632 2642 httpd = hgweb.server.create_server(ui, repo)
2633 2643 except socket.error, inst:
2634 2644 raise util.Abort(_('cannot start server: ') + inst.args[1])
2635 2645
2636 2646 if ui.verbose:
2637 2647 addr, port = httpd.socket.getsockname()
2638 2648 if addr == '0.0.0.0':
2639 2649 addr = socket.gethostname()
2640 2650 else:
2641 2651 try:
2642 2652 addr = socket.gethostbyaddr(addr)[0]
2643 2653 except socket.error:
2644 2654 pass
2645 2655 if port != 80:
2646 2656 ui.status(_('listening at http://%s:%d/\n') % (addr, port))
2647 2657 else:
2648 2658 ui.status(_('listening at http://%s/\n') % addr)
2649 2659
2650 2660 if opts['pid_file']:
2651 2661 fp = open(opts['pid_file'], 'w')
2652 2662 fp.write(str(os.getpid()) + '\n')
2653 2663 fp.close()
2654 2664
2655 2665 if opts['daemon_pipefds']:
2656 2666 rfd, wfd = [int(x) for x in opts['daemon_pipefds'].split(',')]
2657 2667 os.close(rfd)
2658 2668 os.write(wfd, 'y')
2659 2669 os.close(wfd)
2660 2670 sys.stdout.flush()
2661 2671 sys.stderr.flush()
2662 2672 fd = os.open(util.nulldev, os.O_RDWR)
2663 2673 if fd != 0: os.dup2(fd, 0)
2664 2674 if fd != 1: os.dup2(fd, 1)
2665 2675 if fd != 2: os.dup2(fd, 2)
2666 2676 if fd not in (0, 1, 2): os.close(fd)
2667 2677
2668 2678 httpd.serve_forever()
2669 2679
2670 2680 def status(ui, repo, *pats, **opts):
2671 2681 """show changed files in the working directory
2672 2682
2673 2683 Show changed files in the repository. If names are
2674 2684 given, only files that match are shown.
2675 2685
2676 2686 The codes used to show the status of files are:
2677 2687 M = modified
2678 2688 A = added
2679 2689 R = removed
2680 2690 ! = deleted, but still tracked
2681 2691 ? = not tracked
2682 2692 I = ignored (not shown by default)
2683 2693 """
2684 2694
2685 2695 show_ignored = opts['ignored'] and True or False
2686 2696 files, matchfn, anypats = matchpats(repo, pats, opts)
2687 2697 cwd = (pats and repo.getcwd()) or ''
2688 2698 modified, added, removed, deleted, unknown, ignored = [
2689 2699 [util.pathto(cwd, x) for x in n]
2690 2700 for n in repo.changes(files=files, match=matchfn,
2691 2701 show_ignored=show_ignored)]
2692 2702
2693 2703 changetypes = [('modified', 'M', modified),
2694 2704 ('added', 'A', added),
2695 2705 ('removed', 'R', removed),
2696 2706 ('deleted', '!', deleted),
2697 2707 ('unknown', '?', unknown),
2698 2708 ('ignored', 'I', ignored)]
2699 2709
2700 2710 end = opts['print0'] and '\0' or '\n'
2701 2711
2702 2712 for opt, char, changes in ([ct for ct in changetypes if opts[ct[0]]]
2703 2713 or changetypes):
2704 2714 if opts['no_status']:
2705 2715 format = "%%s%s" % end
2706 2716 else:
2707 2717 format = "%s %%s%s" % (char, end)
2708 2718
2709 2719 for f in changes:
2710 2720 ui.write(format % f)
2711 2721
2712 2722 def tag(ui, repo, name, rev_=None, **opts):
2713 2723 """add a tag for the current tip or a given revision
2714 2724
2715 2725 Name a particular revision using <name>.
2716 2726
2717 2727 Tags are used to name particular revisions of the repository and are
2718 2728 very useful to compare different revision, to go back to significant
2719 2729 earlier versions or to mark branch points as releases, etc.
2720 2730
2721 2731 If no revision is given, the tip is used.
2722 2732
2723 2733 To facilitate version control, distribution, and merging of tags,
2724 2734 they are stored as a file named ".hgtags" which is managed
2725 2735 similarly to other project files and can be hand-edited if
2726 2736 necessary. The file '.hg/localtags' is used for local tags (not
2727 2737 shared among repositories).
2728 2738 """
2729 2739 if name == "tip":
2730 2740 raise util.Abort(_("the name 'tip' is reserved"))
2731 2741 if rev_ is not None:
2732 2742 ui.warn(_("use of 'hg tag NAME [REV]' is deprecated, "
2733 2743 "please use 'hg tag [-r REV] NAME' instead\n"))
2734 2744 if opts['rev']:
2735 2745 raise util.Abort(_("use only one form to specify the revision"))
2736 2746 if opts['rev']:
2737 2747 rev_ = opts['rev']
2738 2748 if rev_:
2739 2749 r = hex(repo.lookup(rev_))
2740 2750 else:
2741 2751 r = hex(repo.changelog.tip())
2742 2752
2743 2753 disallowed = (revrangesep, '\r', '\n')
2744 2754 for c in disallowed:
2745 2755 if c in name:
2746 2756 raise util.Abort(_("%s cannot be used in a tag name") % repr(c))
2747 2757
2748 2758 repo.hook('pretag', throw=True, node=r, tag=name,
2749 2759 local=int(not not opts['local']))
2750 2760
2751 2761 if opts['local']:
2752 2762 repo.opener("localtags", "a").write("%s %s\n" % (r, name))
2753 2763 repo.hook('tag', node=r, tag=name, local=1)
2754 2764 return
2755 2765
2756 2766 for x in repo.changes():
2757 2767 if ".hgtags" in x:
2758 2768 raise util.Abort(_("working copy of .hgtags is changed "
2759 2769 "(please commit .hgtags manually)"))
2760 2770
2761 2771 repo.wfile(".hgtags", "ab").write("%s %s\n" % (r, name))
2762 2772 if repo.dirstate.state(".hgtags") == '?':
2763 2773 repo.add([".hgtags"])
2764 2774
2765 2775 message = (opts['message'] or
2766 2776 _("Added tag %s for changeset %s") % (name, r))
2767 2777 try:
2768 2778 repo.commit([".hgtags"], message, opts['user'], opts['date'])
2769 2779 repo.hook('tag', node=r, tag=name, local=0)
2770 2780 except ValueError, inst:
2771 2781 raise util.Abort(str(inst))
2772 2782
2773 2783 def tags(ui, repo):
2774 2784 """list repository tags
2775 2785
2776 2786 List the repository tags.
2777 2787
2778 2788 This lists both regular and local tags.
2779 2789 """
2780 2790
2781 2791 l = repo.tagslist()
2782 2792 l.reverse()
2783 2793 for t, n in l:
2784 2794 try:
2785 2795 r = "%5d:%s" % (repo.changelog.rev(n), hex(n))
2786 2796 except KeyError:
2787 2797 r = " ?:?"
2788 2798 if ui.quiet:
2789 2799 ui.write("%s\n" % t)
2790 2800 else:
2791 2801 ui.write("%-30s %s\n" % (t, r))
2792 2802
2793 2803 def tip(ui, repo, **opts):
2794 2804 """show the tip revision
2795 2805
2796 2806 Show the tip revision.
2797 2807 """
2798 2808 n = repo.changelog.tip()
2799 2809 br = None
2800 2810 if opts['branches']:
2801 2811 br = repo.branchlookup([n])
2802 2812 show_changeset(ui, repo, opts).show(changenode=n, brinfo=br)
2803 2813 if opts['patch']:
2804 2814 dodiff(ui, ui, repo, repo.changelog.parents(n)[0], n)
2805 2815
2806 2816 def unbundle(ui, repo, fname, **opts):
2807 2817 """apply a changegroup file
2808 2818
2809 2819 Apply a compressed changegroup file generated by the bundle
2810 2820 command.
2811 2821 """
2812 2822 f = urllib.urlopen(fname)
2813 2823
2814 2824 header = f.read(6)
2815 2825 if not header.startswith("HG"):
2816 2826 raise util.Abort(_("%s: not a Mercurial bundle file") % fname)
2817 2827 elif not header.startswith("HG10"):
2818 2828 raise util.Abort(_("%s: unknown bundle version") % fname)
2819 2829 elif header == "HG10BZ":
2820 2830 def generator(f):
2821 2831 zd = bz2.BZ2Decompressor()
2822 2832 zd.decompress("BZ")
2823 2833 for chunk in f:
2824 2834 yield zd.decompress(chunk)
2825 2835 elif header == "HG10UN":
2826 2836 def generator(f):
2827 2837 for chunk in f:
2828 2838 yield chunk
2829 2839 else:
2830 2840 raise util.Abort(_("%s: unknown bundle compression type")
2831 2841 % fname)
2832 2842 gen = generator(util.filechunkiter(f, 4096))
2833 2843 modheads = repo.addchangegroup(util.chunkbuffer(gen), 'unbundle')
2834 2844 return postincoming(ui, repo, modheads, opts['update'])
2835 2845
2836 2846 def undo(ui, repo):
2837 2847 """undo the last commit or pull (DEPRECATED)
2838 2848
2839 2849 (DEPRECATED)
2840 2850 This command is now deprecated and will be removed in a future
2841 2851 release. Please use the rollback command instead. For usage
2842 2852 instructions, see the rollback command.
2843 2853 """
2844 2854 ui.warn(_('(the undo command is deprecated; use rollback instead)\n'))
2845 2855 repo.rollback()
2846 2856
2847 2857 def update(ui, repo, node=None, merge=False, clean=False, force=None,
2848 2858 branch=None, **opts):
2849 2859 """update or merge working directory
2850 2860
2851 2861 Update the working directory to the specified revision.
2852 2862
2853 2863 If there are no outstanding changes in the working directory and
2854 2864 there is a linear relationship between the current version and the
2855 2865 requested version, the result is the requested version.
2856 2866
2857 2867 To merge the working directory with another revision, use the
2858 2868 merge command.
2859 2869
2860 2870 By default, update will refuse to run if doing so would require
2861 2871 merging or discarding local changes.
2862 2872 """
2863 2873 if merge:
2864 2874 ui.warn(_('(the -m/--merge option is deprecated; '
2865 2875 'use the merge command instead)\n'))
2866 2876 return doupdate(ui, repo, node, merge, clean, force, branch, **opts)
2867 2877
2868 2878 def doupdate(ui, repo, node=None, merge=False, clean=False, force=None,
2869 2879 branch=None, **opts):
2870 2880 if branch:
2871 2881 br = repo.branchlookup(branch=branch)
2872 2882 found = []
2873 2883 for x in br:
2874 2884 if branch in br[x]:
2875 2885 found.append(x)
2876 2886 if len(found) > 1:
2877 2887 ui.warn(_("Found multiple heads for %s\n") % branch)
2878 2888 for x in found:
2879 2889 show_changeset(ui, repo, opts).show(changenode=x, brinfo=br)
2880 2890 return 1
2881 2891 if len(found) == 1:
2882 2892 node = found[0]
2883 2893 ui.warn(_("Using head %s for branch %s\n") % (short(node), branch))
2884 2894 else:
2885 2895 ui.warn(_("branch %s not found\n") % (branch))
2886 2896 return 1
2887 2897 else:
2888 2898 node = node and repo.lookup(node) or repo.changelog.tip()
2889 2899 return repo.update(node, allow=merge, force=clean, forcemerge=force)
2890 2900
2891 2901 def verify(ui, repo):
2892 2902 """verify the integrity of the repository
2893 2903
2894 2904 Verify the integrity of the current repository.
2895 2905
2896 2906 This will perform an extensive check of the repository's
2897 2907 integrity, validating the hashes and checksums of each entry in
2898 2908 the changelog, manifest, and tracked files, as well as the
2899 2909 integrity of their crosslinks and indices.
2900 2910 """
2901 2911 return repo.verify()
2902 2912
2903 2913 # Command options and aliases are listed here, alphabetically
2904 2914
2905 2915 table = {
2906 2916 "^add":
2907 2917 (add,
2908 2918 [('I', 'include', [], _('include names matching the given patterns')),
2909 2919 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2910 2920 ('n', 'dry-run', None, _('do not perform actions, just print output'))],
2911 2921 _('hg add [OPTION]... [FILE]...')),
2912 2922 "debugaddremove|addremove":
2913 2923 (addremove,
2914 2924 [('I', 'include', [], _('include names matching the given patterns')),
2915 2925 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2916 2926 ('n', 'dry-run', None, _('do not perform actions, just print output'))],
2917 2927 _('hg addremove [OPTION]... [FILE]...')),
2918 2928 "^annotate":
2919 2929 (annotate,
2920 2930 [('r', 'rev', '', _('annotate the specified revision')),
2921 2931 ('a', 'text', None, _('treat all files as text')),
2922 2932 ('u', 'user', None, _('list the author')),
2923 2933 ('d', 'date', None, _('list the date')),
2924 2934 ('n', 'number', None, _('list the revision number (default)')),
2925 2935 ('c', 'changeset', None, _('list the changeset')),
2926 2936 ('I', 'include', [], _('include names matching the given patterns')),
2927 2937 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2928 2938 _('hg annotate [-r REV] [-a] [-u] [-d] [-n] [-c] FILE...')),
2929 2939 "archive":
2930 2940 (archive,
2931 2941 [('', 'no-decode', None, _('do not pass files through decoders')),
2932 2942 ('p', 'prefix', '', _('directory prefix for files in archive')),
2933 2943 ('r', 'rev', '', _('revision to distribute')),
2934 2944 ('t', 'type', '', _('type of distribution to create')),
2935 2945 ('I', 'include', [], _('include names matching the given patterns')),
2936 2946 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2937 2947 _('hg archive [OPTION]... DEST')),
2938 2948 "backout":
2939 2949 (backout,
2940 2950 [('', 'merge', None,
2941 2951 _('merge with old dirstate parent after backout')),
2942 2952 ('m', 'message', '', _('use <text> as commit message')),
2943 2953 ('l', 'logfile', '', _('read commit message from <file>')),
2944 2954 ('d', 'date', '', _('record datecode as commit date')),
2945 2955 ('u', 'user', '', _('record user as committer')),
2946 2956 ('I', 'include', [], _('include names matching the given patterns')),
2947 2957 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2948 2958 _('hg backout [OPTION]... REV')),
2949 2959 "bundle":
2950 2960 (bundle,
2951 2961 [('f', 'force', None,
2952 2962 _('run even when remote repository is unrelated'))],
2953 2963 _('hg bundle FILE DEST')),
2954 2964 "cat":
2955 2965 (cat,
2956 2966 [('o', 'output', '', _('print output to file with formatted name')),
2957 2967 ('r', 'rev', '', _('print the given revision')),
2958 2968 ('I', 'include', [], _('include names matching the given patterns')),
2959 2969 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2960 2970 _('hg cat [OPTION]... FILE...')),
2961 2971 "^clone":
2962 2972 (clone,
2963 2973 [('U', 'noupdate', None, _('do not update the new working directory')),
2964 2974 ('r', 'rev', [],
2965 2975 _('a changeset you would like to have after cloning')),
2966 2976 ('', 'pull', None, _('use pull protocol to copy metadata')),
2967 2977 ('e', 'ssh', '', _('specify ssh command to use')),
2968 2978 ('', 'remotecmd', '',
2969 2979 _('specify hg command to run on the remote side'))],
2970 2980 _('hg clone [OPTION]... SOURCE [DEST]')),
2971 2981 "^commit|ci":
2972 2982 (commit,
2973 2983 [('A', 'addremove', None,
2974 2984 _('mark new/missing files as added/removed before committing')),
2975 2985 ('m', 'message', '', _('use <text> as commit message')),
2976 2986 ('l', 'logfile', '', _('read the commit message from <file>')),
2977 2987 ('d', 'date', '', _('record datecode as commit date')),
2978 2988 ('u', 'user', '', _('record user as commiter')),
2979 2989 ('I', 'include', [], _('include names matching the given patterns')),
2980 2990 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2981 2991 _('hg commit [OPTION]... [FILE]...')),
2982 2992 "copy|cp":
2983 2993 (copy,
2984 2994 [('A', 'after', None, _('record a copy that has already occurred')),
2985 2995 ('f', 'force', None,
2986 2996 _('forcibly copy over an existing managed file')),
2987 2997 ('I', 'include', [], _('include names matching the given patterns')),
2988 2998 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2989 2999 ('n', 'dry-run', None, _('do not perform actions, just print output'))],
2990 3000 _('hg copy [OPTION]... [SOURCE]... DEST')),
2991 3001 "debugancestor": (debugancestor, [], _('debugancestor INDEX REV1 REV2')),
2992 3002 "debugcomplete":
2993 3003 (debugcomplete,
2994 3004 [('o', 'options', None, _('show the command options'))],
2995 3005 _('debugcomplete [-o] CMD')),
2996 3006 "debugrebuildstate":
2997 3007 (debugrebuildstate,
2998 3008 [('r', 'rev', '', _('revision to rebuild to'))],
2999 3009 _('debugrebuildstate [-r REV] [REV]')),
3000 3010 "debugcheckstate": (debugcheckstate, [], _('debugcheckstate')),
3001 3011 "debugconfig": (debugconfig, [], _('debugconfig [NAME]...')),
3002 3012 "debugsetparents": (debugsetparents, [], _('debugsetparents REV1 [REV2]')),
3003 3013 "debugstate": (debugstate, [], _('debugstate')),
3004 3014 "debugdata": (debugdata, [], _('debugdata FILE REV')),
3005 3015 "debugindex": (debugindex, [], _('debugindex FILE')),
3006 3016 "debugindexdot": (debugindexdot, [], _('debugindexdot FILE')),
3007 3017 "debugrename": (debugrename, [], _('debugrename FILE [REV]')),
3008 3018 "debugwalk":
3009 3019 (debugwalk,
3010 3020 [('I', 'include', [], _('include names matching the given patterns')),
3011 3021 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
3012 3022 _('debugwalk [OPTION]... [FILE]...')),
3013 3023 "^diff":
3014 3024 (diff,
3015 3025 [('r', 'rev', [], _('revision')),
3016 3026 ('a', 'text', None, _('treat all files as text')),
3017 3027 ('p', 'show-function', None,
3018 3028 _('show which function each change is in')),
3019 3029 ('w', 'ignore-all-space', None,
3020 3030 _('ignore white space when comparing lines')),
3031 ('b', 'ignore-space-change', None,
3032 _('ignore changes in the amount of white space')),
3033 ('B', 'ignore-blank-lines', None,
3034 _('ignore changes whose lines are all blank')),
3021 3035 ('I', 'include', [], _('include names matching the given patterns')),
3022 3036 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
3023 3037 _('hg diff [-a] [-I] [-X] [-r REV1 [-r REV2]] [FILE]...')),
3024 3038 "^export":
3025 3039 (export,
3026 3040 [('o', 'output', '', _('print output to file with formatted name')),
3027 3041 ('a', 'text', None, _('treat all files as text')),
3028 3042 ('', 'switch-parent', None, _('diff against the second parent'))],
3029 3043 _('hg export [-a] [-o OUTFILESPEC] REV...')),
3030 3044 "debugforget|forget":
3031 3045 (forget,
3032 3046 [('I', 'include', [], _('include names matching the given patterns')),
3033 3047 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
3034 3048 _('hg forget [OPTION]... FILE...')),
3035 3049 "grep":
3036 3050 (grep,
3037 3051 [('0', 'print0', None, _('end fields with NUL')),
3038 3052 ('', 'all', None, _('print all revisions that match')),
3039 3053 ('i', 'ignore-case', None, _('ignore case when matching')),
3040 3054 ('l', 'files-with-matches', None,
3041 3055 _('print only filenames and revs that match')),
3042 3056 ('n', 'line-number', None, _('print matching line numbers')),
3043 3057 ('r', 'rev', [], _('search in given revision range')),
3044 3058 ('u', 'user', None, _('print user who committed change')),
3045 3059 ('I', 'include', [], _('include names matching the given patterns')),
3046 3060 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
3047 3061 _('hg grep [OPTION]... PATTERN [FILE]...')),
3048 3062 "heads":
3049 3063 (heads,
3050 3064 [('b', 'branches', None, _('show branches')),
3051 3065 ('', 'style', '', _('display using template map file')),
3052 3066 ('r', 'rev', '', _('show only heads which are descendants of rev')),
3053 3067 ('', 'template', '', _('display with template'))],
3054 3068 _('hg heads [-b] [-r <rev>]')),
3055 3069 "help": (help_, [], _('hg help [COMMAND]')),
3056 3070 "identify|id": (identify, [], _('hg identify')),
3057 3071 "import|patch":
3058 3072 (import_,
3059 3073 [('p', 'strip', 1,
3060 3074 _('directory strip option for patch. This has the same\n'
3061 3075 'meaning as the corresponding patch option')),
3062 3076 ('m', 'message', '', _('use <text> as commit message')),
3063 3077 ('b', 'base', '', _('base path')),
3064 3078 ('f', 'force', None,
3065 3079 _('skip check for outstanding uncommitted changes'))],
3066 3080 _('hg import [-p NUM] [-b BASE] [-m MESSAGE] [-f] PATCH...')),
3067 3081 "incoming|in": (incoming,
3068 3082 [('M', 'no-merges', None, _('do not show merges')),
3069 3083 ('f', 'force', None,
3070 3084 _('run even when remote repository is unrelated')),
3071 3085 ('', 'style', '', _('display using template map file')),
3072 3086 ('n', 'newest-first', None, _('show newest record first')),
3073 3087 ('', 'bundle', '', _('file to store the bundles into')),
3074 3088 ('p', 'patch', None, _('show patch')),
3075 3089 ('r', 'rev', [], _('a specific revision you would like to pull')),
3076 3090 ('', 'template', '', _('display with template')),
3077 3091 ('e', 'ssh', '', _('specify ssh command to use')),
3078 3092 ('', 'remotecmd', '',
3079 3093 _('specify hg command to run on the remote side'))],
3080 3094 _('hg incoming [-p] [-n] [-M] [-r REV]...'
3081 3095 ' [--bundle FILENAME] [SOURCE]')),
3082 3096 "^init": (init, [], _('hg init [DEST]')),
3083 3097 "locate":
3084 3098 (locate,
3085 3099 [('r', 'rev', '', _('search the repository as it stood at rev')),
3086 3100 ('0', 'print0', None,
3087 3101 _('end filenames with NUL, for use with xargs')),
3088 3102 ('f', 'fullpath', None,
3089 3103 _('print complete paths from the filesystem root')),
3090 3104 ('I', 'include', [], _('include names matching the given patterns')),
3091 3105 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
3092 3106 _('hg locate [OPTION]... [PATTERN]...')),
3093 3107 "^log|history":
3094 3108 (log,
3095 3109 [('b', 'branches', None, _('show branches')),
3096 3110 ('k', 'keyword', [], _('search for a keyword')),
3097 3111 ('l', 'limit', '', _('limit number of changes displayed')),
3098 3112 ('r', 'rev', [], _('show the specified revision or range')),
3099 3113 ('M', 'no-merges', None, _('do not show merges')),
3100 3114 ('', 'style', '', _('display using template map file')),
3101 3115 ('m', 'only-merges', None, _('show only merges')),
3102 3116 ('p', 'patch', None, _('show patch')),
3103 3117 ('', 'template', '', _('display with template')),
3104 3118 ('I', 'include', [], _('include names matching the given patterns')),
3105 3119 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
3106 3120 _('hg log [OPTION]... [FILE]')),
3107 3121 "manifest": (manifest, [], _('hg manifest [REV]')),
3108 3122 "merge":
3109 3123 (merge,
3110 3124 [('b', 'branch', '', _('merge with head of a specific branch')),
3111 3125 ('f', 'force', None, _('force a merge with outstanding changes'))],
3112 3126 _('hg merge [-b TAG] [-f] [REV]')),
3113 3127 "outgoing|out": (outgoing,
3114 3128 [('M', 'no-merges', None, _('do not show merges')),
3115 3129 ('f', 'force', None,
3116 3130 _('run even when remote repository is unrelated')),
3117 3131 ('p', 'patch', None, _('show patch')),
3118 3132 ('', 'style', '', _('display using template map file')),
3119 3133 ('r', 'rev', [], _('a specific revision you would like to push')),
3120 3134 ('n', 'newest-first', None, _('show newest record first')),
3121 3135 ('', 'template', '', _('display with template')),
3122 3136 ('e', 'ssh', '', _('specify ssh command to use')),
3123 3137 ('', 'remotecmd', '',
3124 3138 _('specify hg command to run on the remote side'))],
3125 3139 _('hg outgoing [-M] [-p] [-n] [-r REV]... [DEST]')),
3126 3140 "^parents":
3127 3141 (parents,
3128 3142 [('b', 'branches', None, _('show branches')),
3129 3143 ('', 'style', '', _('display using template map file')),
3130 3144 ('', 'template', '', _('display with template'))],
3131 3145 _('hg parents [-b] [REV]')),
3132 3146 "paths": (paths, [], _('hg paths [NAME]')),
3133 3147 "^pull":
3134 3148 (pull,
3135 3149 [('u', 'update', None,
3136 3150 _('update the working directory to tip after pull')),
3137 3151 ('e', 'ssh', '', _('specify ssh command to use')),
3138 3152 ('f', 'force', None,
3139 3153 _('run even when remote repository is unrelated')),
3140 3154 ('r', 'rev', [], _('a specific revision you would like to pull')),
3141 3155 ('', 'remotecmd', '',
3142 3156 _('specify hg command to run on the remote side'))],
3143 3157 _('hg pull [-u] [-e FILE] [-r REV]... [--remotecmd FILE] [SOURCE]')),
3144 3158 "^push":
3145 3159 (push,
3146 3160 [('f', 'force', None, _('force push')),
3147 3161 ('e', 'ssh', '', _('specify ssh command to use')),
3148 3162 ('r', 'rev', [], _('a specific revision you would like to push')),
3149 3163 ('', 'remotecmd', '',
3150 3164 _('specify hg command to run on the remote side'))],
3151 3165 _('hg push [-f] [-e FILE] [-r REV]... [--remotecmd FILE] [DEST]')),
3152 3166 "debugrawcommit|rawcommit":
3153 3167 (rawcommit,
3154 3168 [('p', 'parent', [], _('parent')),
3155 3169 ('d', 'date', '', _('date code')),
3156 3170 ('u', 'user', '', _('user')),
3157 3171 ('F', 'files', '', _('file list')),
3158 3172 ('m', 'message', '', _('commit message')),
3159 3173 ('l', 'logfile', '', _('commit message file'))],
3160 3174 _('hg debugrawcommit [OPTION]... [FILE]...')),
3161 3175 "recover": (recover, [], _('hg recover')),
3162 3176 "^remove|rm":
3163 3177 (remove,
3164 3178 [('A', 'after', None, _('record remove that has already occurred')),
3165 3179 ('f', 'force', None, _('remove file even if modified')),
3166 3180 ('I', 'include', [], _('include names matching the given patterns')),
3167 3181 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
3168 3182 _('hg remove [OPTION]... FILE...')),
3169 3183 "rename|mv":
3170 3184 (rename,
3171 3185 [('A', 'after', None, _('record a rename that has already occurred')),
3172 3186 ('f', 'force', None,
3173 3187 _('forcibly copy over an existing managed file')),
3174 3188 ('I', 'include', [], _('include names matching the given patterns')),
3175 3189 ('X', 'exclude', [], _('exclude names matching the given patterns')),
3176 3190 ('n', 'dry-run', None, _('do not perform actions, just print output'))],
3177 3191 _('hg rename [OPTION]... SOURCE... DEST')),
3178 3192 "^revert":
3179 3193 (revert,
3180 3194 [('r', 'rev', '', _('revision to revert to')),
3181 3195 ('', 'no-backup', None, _('do not save backup copies of files')),
3182 3196 ('I', 'include', [], _('include names matching given patterns')),
3183 3197 ('X', 'exclude', [], _('exclude names matching given patterns')),
3184 3198 ('n', 'dry-run', None, _('do not perform actions, just print output'))],
3185 3199 _('hg revert [-r REV] [NAME]...')),
3186 3200 "rollback": (rollback, [], _('hg rollback')),
3187 3201 "root": (root, [], _('hg root')),
3188 3202 "^serve":
3189 3203 (serve,
3190 3204 [('A', 'accesslog', '', _('name of access log file to write to')),
3191 3205 ('d', 'daemon', None, _('run server in background')),
3192 3206 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3193 3207 ('E', 'errorlog', '', _('name of error log file to write to')),
3194 3208 ('p', 'port', 0, _('port to use (default: 8000)')),
3195 3209 ('a', 'address', '', _('address to use')),
3196 3210 ('n', 'name', '',
3197 3211 _('name to show in web pages (default: working dir)')),
3198 3212 ('', 'webdir-conf', '', _('name of the webdir config file'
3199 3213 ' (serve more than one repo)')),
3200 3214 ('', 'pid-file', '', _('name of file to write process ID to')),
3201 3215 ('', 'stdio', None, _('for remote clients')),
3202 3216 ('t', 'templates', '', _('web templates to use')),
3203 3217 ('', 'style', '', _('template style to use')),
3204 3218 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4'))],
3205 3219 _('hg serve [OPTION]...')),
3206 3220 "^status|st":
3207 3221 (status,
3208 3222 [('m', 'modified', None, _('show only modified files')),
3209 3223 ('a', 'added', None, _('show only added files')),
3210 3224 ('r', 'removed', None, _('show only removed files')),
3211 3225 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3212 3226 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3213 3227 ('i', 'ignored', None, _('show ignored files')),
3214 3228 ('n', 'no-status', None, _('hide status prefix')),
3215 3229 ('0', 'print0', None,
3216 3230 _('end filenames with NUL, for use with xargs')),
3217 3231 ('I', 'include', [], _('include names matching the given patterns')),
3218 3232 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
3219 3233 _('hg status [OPTION]... [FILE]...')),
3220 3234 "tag":
3221 3235 (tag,
3222 3236 [('l', 'local', None, _('make the tag local')),
3223 3237 ('m', 'message', '', _('message for tag commit log entry')),
3224 3238 ('d', 'date', '', _('record datecode as commit date')),
3225 3239 ('u', 'user', '', _('record user as commiter')),
3226 3240 ('r', 'rev', '', _('revision to tag'))],
3227 3241 _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME')),
3228 3242 "tags": (tags, [], _('hg tags')),
3229 3243 "tip":
3230 3244 (tip,
3231 3245 [('b', 'branches', None, _('show branches')),
3232 3246 ('', 'style', '', _('display using template map file')),
3233 3247 ('p', 'patch', None, _('show patch')),
3234 3248 ('', 'template', '', _('display with template'))],
3235 3249 _('hg tip [-b] [-p]')),
3236 3250 "unbundle":
3237 3251 (unbundle,
3238 3252 [('u', 'update', None,
3239 3253 _('update the working directory to tip after unbundle'))],
3240 3254 _('hg unbundle [-u] FILE')),
3241 3255 "debugundo|undo": (undo, [], _('hg undo')),
3242 3256 "^update|up|checkout|co":
3243 3257 (update,
3244 3258 [('b', 'branch', '', _('checkout the head of a specific branch')),
3245 3259 ('m', 'merge', None, _('allow merging of branches (DEPRECATED)')),
3246 3260 ('C', 'clean', None, _('overwrite locally modified files')),
3247 3261 ('f', 'force', None, _('force a merge with outstanding changes'))],
3248 3262 _('hg update [-b TAG] [-m] [-C] [-f] [REV]')),
3249 3263 "verify": (verify, [], _('hg verify')),
3250 3264 "version": (show_version, [], _('hg version')),
3251 3265 }
3252 3266
3253 3267 globalopts = [
3254 3268 ('R', 'repository', '',
3255 3269 _('repository root directory or symbolic path name')),
3256 3270 ('', 'cwd', '', _('change working directory')),
3257 3271 ('y', 'noninteractive', None,
3258 3272 _('do not prompt, assume \'yes\' for any required answers')),
3259 3273 ('q', 'quiet', None, _('suppress output')),
3260 3274 ('v', 'verbose', None, _('enable additional output')),
3261 3275 ('', 'config', [], _('set/override config option')),
3262 3276 ('', 'debug', None, _('enable debugging output')),
3263 3277 ('', 'debugger', None, _('start debugger')),
3264 3278 ('', 'lsprof', None, _('print improved command execution profile')),
3265 3279 ('', 'traceback', None, _('print traceback on exception')),
3266 3280 ('', 'time', None, _('time how long the command takes')),
3267 3281 ('', 'profile', None, _('print command execution profile')),
3268 3282 ('', 'version', None, _('output version information and exit')),
3269 3283 ('h', 'help', None, _('display help and exit')),
3270 3284 ]
3271 3285
3272 3286 norepo = ("clone init version help debugancestor debugcomplete debugdata"
3273 3287 " debugindex debugindexdot")
3274 3288 optionalrepo = ("paths serve debugconfig")
3275 3289
3276 3290 def findpossible(cmd):
3277 3291 """
3278 3292 Return cmd -> (aliases, command table entry)
3279 3293 for each matching command.
3280 3294 Return debug commands (or their aliases) only if no normal command matches.
3281 3295 """
3282 3296 choice = {}
3283 3297 debugchoice = {}
3284 3298 for e in table.keys():
3285 3299 aliases = e.lstrip("^").split("|")
3286 3300 found = None
3287 3301 if cmd in aliases:
3288 3302 found = cmd
3289 3303 else:
3290 3304 for a in aliases:
3291 3305 if a.startswith(cmd):
3292 3306 found = a
3293 3307 break
3294 3308 if found is not None:
3295 3309 if aliases[0].startswith("debug"):
3296 3310 debugchoice[found] = (aliases, table[e])
3297 3311 else:
3298 3312 choice[found] = (aliases, table[e])
3299 3313
3300 3314 if not choice and debugchoice:
3301 3315 choice = debugchoice
3302 3316
3303 3317 return choice
3304 3318
3305 3319 def findcmd(cmd):
3306 3320 """Return (aliases, command table entry) for command string."""
3307 3321 choice = findpossible(cmd)
3308 3322
3309 3323 if choice.has_key(cmd):
3310 3324 return choice[cmd]
3311 3325
3312 3326 if len(choice) > 1:
3313 3327 clist = choice.keys()
3314 3328 clist.sort()
3315 3329 raise AmbiguousCommand(cmd, clist)
3316 3330
3317 3331 if choice:
3318 3332 return choice.values()[0]
3319 3333
3320 3334 raise UnknownCommand(cmd)
3321 3335
3322 3336 def catchterm(*args):
3323 3337 raise util.SignalInterrupt
3324 3338
3325 3339 def run():
3326 3340 sys.exit(dispatch(sys.argv[1:]))
3327 3341
3328 3342 class ParseError(Exception):
3329 3343 """Exception raised on errors in parsing the command line."""
3330 3344
3331 3345 def parse(ui, args):
3332 3346 options = {}
3333 3347 cmdoptions = {}
3334 3348
3335 3349 try:
3336 3350 args = fancyopts.fancyopts(args, globalopts, options)
3337 3351 except fancyopts.getopt.GetoptError, inst:
3338 3352 raise ParseError(None, inst)
3339 3353
3340 3354 if args:
3341 3355 cmd, args = args[0], args[1:]
3342 3356 aliases, i = findcmd(cmd)
3343 3357 cmd = aliases[0]
3344 3358 defaults = ui.config("defaults", cmd)
3345 3359 if defaults:
3346 3360 args = defaults.split() + args
3347 3361 c = list(i[1])
3348 3362 else:
3349 3363 cmd = None
3350 3364 c = []
3351 3365
3352 3366 # combine global options into local
3353 3367 for o in globalopts:
3354 3368 c.append((o[0], o[1], options[o[1]], o[3]))
3355 3369
3356 3370 try:
3357 3371 args = fancyopts.fancyopts(args, c, cmdoptions)
3358 3372 except fancyopts.getopt.GetoptError, inst:
3359 3373 raise ParseError(cmd, inst)
3360 3374
3361 3375 # separate global options back out
3362 3376 for o in globalopts:
3363 3377 n = o[1]
3364 3378 options[n] = cmdoptions[n]
3365 3379 del cmdoptions[n]
3366 3380
3367 3381 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
3368 3382
3369 3383 external = {}
3370 3384
3371 3385 def findext(name):
3372 3386 '''return module with given extension name'''
3373 3387 try:
3374 3388 return sys.modules[external[name]]
3375 3389 except KeyError:
3376 3390 dotname = '.' + name
3377 3391 for k, v in external.iteritems():
3378 3392 if k.endswith('.' + name) or v == name:
3379 3393 return sys.modules[v]
3380 3394 raise KeyError(name)
3381 3395
3382 3396 def dispatch(args):
3383 3397 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
3384 3398 num = getattr(signal, name, None)
3385 3399 if num: signal.signal(num, catchterm)
3386 3400
3387 3401 try:
3388 3402 u = ui.ui(traceback='--traceback' in sys.argv[1:])
3389 3403 except util.Abort, inst:
3390 3404 sys.stderr.write(_("abort: %s\n") % inst)
3391 3405 return -1
3392 3406
3393 3407 for ext_name, load_from_name in u.extensions():
3394 3408 try:
3395 3409 if load_from_name:
3396 3410 # the module will be loaded in sys.modules
3397 3411 # choose an unique name so that it doesn't
3398 3412 # conflicts with other modules
3399 3413 module_name = "hgext_%s" % ext_name.replace('.', '_')
3400 3414 mod = imp.load_source(module_name, load_from_name)
3401 3415 else:
3402 3416 def importh(name):
3403 3417 mod = __import__(name)
3404 3418 components = name.split('.')
3405 3419 for comp in components[1:]:
3406 3420 mod = getattr(mod, comp)
3407 3421 return mod
3408 3422 try:
3409 3423 mod = importh("hgext.%s" % ext_name)
3410 3424 except ImportError:
3411 3425 mod = importh(ext_name)
3412 3426 external[ext_name] = mod.__name__
3413 3427 except (util.SignalInterrupt, KeyboardInterrupt):
3414 3428 raise
3415 3429 except Exception, inst:
3416 3430 u.warn(_("*** failed to import extension %s: %s\n") % (x[0], inst))
3417 3431 if u.print_exc():
3418 3432 return 1
3419 3433
3420 3434 for name in external.itervalues():
3421 3435 mod = sys.modules[name]
3422 3436 uisetup = getattr(mod, 'uisetup', None)
3423 3437 if uisetup:
3424 3438 uisetup(u)
3425 3439 cmdtable = getattr(mod, 'cmdtable', {})
3426 3440 for t in cmdtable:
3427 3441 if t in table:
3428 3442 u.warn(_("module %s overrides %s\n") % (name, t))
3429 3443 table.update(cmdtable)
3430 3444
3431 3445 try:
3432 3446 cmd, func, args, options, cmdoptions = parse(u, args)
3433 3447 if options["time"]:
3434 3448 def get_times():
3435 3449 t = os.times()
3436 3450 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
3437 3451 t = (t[0], t[1], t[2], t[3], time.clock())
3438 3452 return t
3439 3453 s = get_times()
3440 3454 def print_time():
3441 3455 t = get_times()
3442 3456 u.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
3443 3457 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
3444 3458 atexit.register(print_time)
3445 3459
3446 3460 u.updateopts(options["verbose"], options["debug"], options["quiet"],
3447 3461 not options["noninteractive"], options["traceback"],
3448 3462 options["config"])
3449 3463
3450 3464 # enter the debugger before command execution
3451 3465 if options['debugger']:
3452 3466 pdb.set_trace()
3453 3467
3454 3468 try:
3455 3469 if options['cwd']:
3456 3470 try:
3457 3471 os.chdir(options['cwd'])
3458 3472 except OSError, inst:
3459 3473 raise util.Abort('%s: %s' %
3460 3474 (options['cwd'], inst.strerror))
3461 3475
3462 3476 path = u.expandpath(options["repository"]) or ""
3463 3477 repo = path and hg.repository(u, path=path) or None
3464 3478
3465 3479 if options['help']:
3466 3480 return help_(u, cmd, options['version'])
3467 3481 elif options['version']:
3468 3482 return show_version(u)
3469 3483 elif not cmd:
3470 3484 return help_(u, 'shortlist')
3471 3485
3472 3486 if cmd not in norepo.split():
3473 3487 try:
3474 3488 if not repo:
3475 3489 repo = hg.repository(u, path=path)
3476 3490 u = repo.ui
3477 3491 for name in external.itervalues():
3478 3492 mod = sys.modules[name]
3479 3493 if hasattr(mod, 'reposetup'):
3480 3494 mod.reposetup(u, repo)
3481 3495 except hg.RepoError:
3482 3496 if cmd not in optionalrepo.split():
3483 3497 raise
3484 3498 d = lambda: func(u, repo, *args, **cmdoptions)
3485 3499 else:
3486 3500 d = lambda: func(u, *args, **cmdoptions)
3487 3501
3488 3502 try:
3489 3503 if options['profile']:
3490 3504 import hotshot, hotshot.stats
3491 3505 prof = hotshot.Profile("hg.prof")
3492 3506 try:
3493 3507 try:
3494 3508 return prof.runcall(d)
3495 3509 except:
3496 3510 try:
3497 3511 u.warn(_('exception raised - generating '
3498 3512 'profile anyway\n'))
3499 3513 except:
3500 3514 pass
3501 3515 raise
3502 3516 finally:
3503 3517 prof.close()
3504 3518 stats = hotshot.stats.load("hg.prof")
3505 3519 stats.strip_dirs()
3506 3520 stats.sort_stats('time', 'calls')
3507 3521 stats.print_stats(40)
3508 3522 elif options['lsprof']:
3509 3523 try:
3510 3524 from mercurial import lsprof
3511 3525 except ImportError:
3512 3526 raise util.Abort(_(
3513 3527 'lsprof not available - install from '
3514 3528 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
3515 3529 p = lsprof.Profiler()
3516 3530 p.enable(subcalls=True)
3517 3531 try:
3518 3532 return d()
3519 3533 finally:
3520 3534 p.disable()
3521 3535 stats = lsprof.Stats(p.getstats())
3522 3536 stats.sort()
3523 3537 stats.pprint(top=10, file=sys.stderr, climit=5)
3524 3538 else:
3525 3539 return d()
3526 3540 finally:
3527 3541 u.flush()
3528 3542 except:
3529 3543 # enter the debugger when we hit an exception
3530 3544 if options['debugger']:
3531 3545 pdb.post_mortem(sys.exc_info()[2])
3532 3546 u.print_exc()
3533 3547 raise
3534 3548 except ParseError, inst:
3535 3549 if inst.args[0]:
3536 3550 u.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
3537 3551 help_(u, inst.args[0])
3538 3552 else:
3539 3553 u.warn(_("hg: %s\n") % inst.args[1])
3540 3554 help_(u, 'shortlist')
3541 3555 except AmbiguousCommand, inst:
3542 3556 u.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
3543 3557 (inst.args[0], " ".join(inst.args[1])))
3544 3558 except UnknownCommand, inst:
3545 3559 u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
3546 3560 help_(u, 'shortlist')
3547 3561 except hg.RepoError, inst:
3548 3562 u.warn(_("abort: %s!\n") % inst)
3549 3563 except lock.LockHeld, inst:
3550 3564 if inst.errno == errno.ETIMEDOUT:
3551 3565 reason = _('timed out waiting for lock held by %s') % inst.locker
3552 3566 else:
3553 3567 reason = _('lock held by %s') % inst.locker
3554 3568 u.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
3555 3569 except lock.LockUnavailable, inst:
3556 3570 u.warn(_("abort: could not lock %s: %s\n") %
3557 3571 (inst.desc or inst.filename, inst.strerror))
3558 3572 except revlog.RevlogError, inst:
3559 3573 u.warn(_("abort: "), inst, "!\n")
3560 3574 except util.SignalInterrupt:
3561 3575 u.warn(_("killed!\n"))
3562 3576 except KeyboardInterrupt:
3563 3577 try:
3564 3578 u.warn(_("interrupted!\n"))
3565 3579 except IOError, inst:
3566 3580 if inst.errno == errno.EPIPE:
3567 3581 if u.debugflag:
3568 3582 u.warn(_("\nbroken pipe\n"))
3569 3583 else:
3570 3584 raise
3571 3585 except IOError, inst:
3572 3586 if hasattr(inst, "code"):
3573 3587 u.warn(_("abort: %s\n") % inst)
3574 3588 elif hasattr(inst, "reason"):
3575 3589 u.warn(_("abort: error: %s\n") % inst.reason[1])
3576 3590 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
3577 3591 if u.debugflag:
3578 3592 u.warn(_("broken pipe\n"))
3579 3593 elif getattr(inst, "strerror", None):
3580 3594 if getattr(inst, "filename", None):
3581 3595 u.warn(_("abort: %s - %s\n") % (inst.strerror, inst.filename))
3582 3596 else:
3583 3597 u.warn(_("abort: %s\n") % inst.strerror)
3584 3598 else:
3585 3599 raise
3586 3600 except OSError, inst:
3587 3601 if hasattr(inst, "filename"):
3588 3602 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
3589 3603 else:
3590 3604 u.warn(_("abort: %s\n") % inst.strerror)
3591 3605 except util.Abort, inst:
3592 3606 u.warn(_('abort: '), inst.args[0] % inst.args[1:], '\n')
3593 3607 except TypeError, inst:
3594 3608 # was this an argument error?
3595 3609 tb = traceback.extract_tb(sys.exc_info()[2])
3596 3610 if len(tb) > 2: # no
3597 3611 raise
3598 3612 u.debug(inst, "\n")
3599 3613 u.warn(_("%s: invalid arguments\n") % cmd)
3600 3614 help_(u, cmd)
3601 3615 except SystemExit, inst:
3602 3616 # Commands shouldn't sys.exit directly, but give a return code.
3603 3617 # Just in case catch this and and pass exit code to caller.
3604 3618 return inst.code
3605 3619 except:
3606 3620 u.warn(_("** unknown exception encountered, details follow\n"))
3607 3621 u.warn(_("** report bug details to mercurial@selenic.com\n"))
3608 3622 u.warn(_("** Mercurial Distributed SCM (version %s)\n")
3609 3623 % version.get_version())
3610 3624 raise
3611 3625
3612 3626 return -1
@@ -1,944 +1,952 b''
1 1 # hgweb/hgweb_mod.py - Web interface for a repository.
2 2 #
3 3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
4 4 # Copyright 2005 Matt Mackall <mpm@selenic.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 import os
10 10 import os.path
11 11 import mimetypes
12 12 from mercurial.demandload import demandload
13 13 demandload(globals(), "re zlib ConfigParser mimetools cStringIO sys tempfile")
14 14 demandload(globals(), "mercurial:mdiff,ui,hg,util,archival,templater")
15 15 demandload(globals(), "mercurial.hgweb.common:get_mtime,staticfile")
16 16 from mercurial.node import *
17 17 from mercurial.i18n import gettext as _
18 18
19 19 def _up(p):
20 20 if p[0] != "/":
21 21 p = "/" + p
22 22 if p[-1] == "/":
23 23 p = p[:-1]
24 24 up = os.path.dirname(p)
25 25 if up == "/":
26 26 return "/"
27 27 return up + "/"
28 28
29 29 class hgweb(object):
30 30 def __init__(self, repo, name=None):
31 31 if type(repo) == type(""):
32 32 self.repo = hg.repository(ui.ui(), repo)
33 33 else:
34 34 self.repo = repo
35 35
36 36 self.mtime = -1
37 37 self.reponame = name
38 38 self.archives = 'zip', 'gz', 'bz2'
39 39 self.templatepath = self.repo.ui.config("web", "templates",
40 40 templater.templatepath())
41 41
42 42 def refresh(self):
43 43 mtime = get_mtime(self.repo.root)
44 44 if mtime != self.mtime:
45 45 self.mtime = mtime
46 46 self.repo = hg.repository(self.repo.ui, self.repo.root)
47 47 self.maxchanges = int(self.repo.ui.config("web", "maxchanges", 10))
48 48 self.maxfiles = int(self.repo.ui.config("web", "maxfiles", 10))
49 49 self.allowpull = self.repo.ui.configbool("web", "allowpull", True)
50 50
51 51 def archivelist(self, nodeid):
52 52 allowed = self.repo.ui.configlist("web", "allow_archive")
53 53 for i in self.archives:
54 54 if i in allowed or self.repo.ui.configbool("web", "allow" + i):
55 55 yield {"type" : i, "node" : nodeid, "url": ""}
56 56
57 57 def listfiles(self, files, mf):
58 58 for f in files[:self.maxfiles]:
59 59 yield self.t("filenodelink", node=hex(mf[f]), file=f)
60 60 if len(files) > self.maxfiles:
61 61 yield self.t("fileellipses")
62 62
63 63 def listfilediffs(self, files, changeset):
64 64 for f in files[:self.maxfiles]:
65 65 yield self.t("filedifflink", node=hex(changeset), file=f)
66 66 if len(files) > self.maxfiles:
67 67 yield self.t("fileellipses")
68 68
69 69 def siblings(self, siblings=[], rev=None, hiderev=None, **args):
70 70 if not rev:
71 71 rev = lambda x: ""
72 72 siblings = [s for s in siblings if s != nullid]
73 73 if len(siblings) == 1 and rev(siblings[0]) == hiderev:
74 74 return
75 75 for s in siblings:
76 76 yield dict(node=hex(s), rev=rev(s), **args)
77 77
78 78 def renamelink(self, fl, node):
79 79 r = fl.renamed(node)
80 80 if r:
81 81 return [dict(file=r[0], node=hex(r[1]))]
82 82 return []
83 83
84 84 def showtag(self, t1, node=nullid, **args):
85 85 for t in self.repo.nodetags(node):
86 86 yield self.t(t1, tag=t, **args)
87 87
88 88 def diff(self, node1, node2, files):
89 89 def filterfiles(filters, files):
90 90 l = [x for x in files if x in filters]
91 91
92 92 for t in filters:
93 93 if t and t[-1] != os.sep:
94 94 t += os.sep
95 95 l += [x for x in files if x.startswith(t)]
96 96 return l
97 97
98 98 parity = [0]
99 99 def diffblock(diff, f, fn):
100 100 yield self.t("diffblock",
101 101 lines=prettyprintlines(diff),
102 102 parity=parity[0],
103 103 file=f,
104 104 filenode=hex(fn or nullid))
105 105 parity[0] = 1 - parity[0]
106 106
107 107 def prettyprintlines(diff):
108 108 for l in diff.splitlines(1):
109 109 if l.startswith('+'):
110 110 yield self.t("difflineplus", line=l)
111 111 elif l.startswith('-'):
112 112 yield self.t("difflineminus", line=l)
113 113 elif l.startswith('@'):
114 114 yield self.t("difflineat", line=l)
115 115 else:
116 116 yield self.t("diffline", line=l)
117 117
118 118 r = self.repo
119 119 cl = r.changelog
120 120 mf = r.manifest
121 121 change1 = cl.read(node1)
122 122 change2 = cl.read(node2)
123 123 mmap1 = mf.read(change1[0])
124 124 mmap2 = mf.read(change2[0])
125 125 date1 = util.datestr(change1[2])
126 126 date2 = util.datestr(change2[2])
127 127
128 128 modified, added, removed, deleted, unknown = r.changes(node1, node2)
129 129 if files:
130 130 modified, added, removed = map(lambda x: filterfiles(files, x),
131 131 (modified, added, removed))
132 132
133 133 diffopts = self.repo.ui.diffopts()
134 134 showfunc = diffopts['showfunc']
135 135 ignorews = diffopts['ignorews']
136 ignorewsamount = diffopts['ignorewsamount']
137 ignoreblanklines = diffopts['ignoreblanklines']
136 138 for f in modified:
137 139 to = r.file(f).read(mmap1[f])
138 140 tn = r.file(f).read(mmap2[f])
139 141 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f,
140 showfunc=showfunc, ignorews=ignorews), f, tn)
142 showfunc=showfunc, ignorews=ignorews,
143 ignorewsamount=ignorewsamount,
144 ignoreblanklines=ignoreblanklines), f, tn)
141 145 for f in added:
142 146 to = None
143 147 tn = r.file(f).read(mmap2[f])
144 148 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f,
145 showfunc=showfunc, ignorews=ignorews), f, tn)
149 showfunc=showfunc, ignorews=ignorews,
150 ignorewsamount=ignorewsamount,
151 ignoreblanklines=ignoreblanklines), f, tn)
146 152 for f in removed:
147 153 to = r.file(f).read(mmap1[f])
148 154 tn = None
149 155 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f,
150 showfunc=showfunc, ignorews=ignorews), f, tn)
156 showfunc=showfunc, ignorews=ignorews,
157 ignorewsamount=ignorewsamount,
158 ignoreblanklines=ignoreblanklines), f, tn)
151 159
152 160 def changelog(self, pos):
153 161 def changenav(**map):
154 162 def seq(factor, maxchanges=None):
155 163 if maxchanges:
156 164 yield maxchanges
157 165 if maxchanges >= 20 and maxchanges <= 40:
158 166 yield 50
159 167 else:
160 168 yield 1 * factor
161 169 yield 3 * factor
162 170 for f in seq(factor * 10):
163 171 yield f
164 172
165 173 l = []
166 174 last = 0
167 175 for f in seq(1, self.maxchanges):
168 176 if f < self.maxchanges or f <= last:
169 177 continue
170 178 if f > count:
171 179 break
172 180 last = f
173 181 r = "%d" % f
174 182 if pos + f < count:
175 183 l.append(("+" + r, pos + f))
176 184 if pos - f >= 0:
177 185 l.insert(0, ("-" + r, pos - f))
178 186
179 187 yield {"rev": 0, "label": "(0)"}
180 188
181 189 for label, rev in l:
182 190 yield {"label": label, "rev": rev}
183 191
184 192 yield {"label": "tip", "rev": "tip"}
185 193
186 194 def changelist(**map):
187 195 parity = (start - end) & 1
188 196 cl = self.repo.changelog
189 197 l = [] # build a list in forward order for efficiency
190 198 for i in range(start, end):
191 199 n = cl.node(i)
192 200 changes = cl.read(n)
193 201 hn = hex(n)
194 202
195 203 l.insert(0, {"parity": parity,
196 204 "author": changes[1],
197 205 "parent": self.siblings(cl.parents(n), cl.rev,
198 206 cl.rev(n) - 1),
199 207 "child": self.siblings(cl.children(n), cl.rev,
200 208 cl.rev(n) + 1),
201 209 "changelogtag": self.showtag("changelogtag",n),
202 210 "manifest": hex(changes[0]),
203 211 "desc": changes[4],
204 212 "date": changes[2],
205 213 "files": self.listfilediffs(changes[3], n),
206 214 "rev": i,
207 215 "node": hn})
208 216 parity = 1 - parity
209 217
210 218 for e in l:
211 219 yield e
212 220
213 221 cl = self.repo.changelog
214 222 mf = cl.read(cl.tip())[0]
215 223 count = cl.count()
216 224 start = max(0, pos - self.maxchanges + 1)
217 225 end = min(count, start + self.maxchanges)
218 226 pos = end - 1
219 227
220 228 yield self.t('changelog',
221 229 changenav=changenav,
222 230 manifest=hex(mf),
223 231 rev=pos, changesets=count, entries=changelist,
224 232 archives=self.archivelist("tip"))
225 233
226 234 def search(self, query):
227 235
228 236 def changelist(**map):
229 237 cl = self.repo.changelog
230 238 count = 0
231 239 qw = query.lower().split()
232 240
233 241 def revgen():
234 242 for i in range(cl.count() - 1, 0, -100):
235 243 l = []
236 244 for j in range(max(0, i - 100), i):
237 245 n = cl.node(j)
238 246 changes = cl.read(n)
239 247 l.append((n, j, changes))
240 248 l.reverse()
241 249 for e in l:
242 250 yield e
243 251
244 252 for n, i, changes in revgen():
245 253 miss = 0
246 254 for q in qw:
247 255 if not (q in changes[1].lower() or
248 256 q in changes[4].lower() or
249 257 q in " ".join(changes[3][:20]).lower()):
250 258 miss = 1
251 259 break
252 260 if miss:
253 261 continue
254 262
255 263 count += 1
256 264 hn = hex(n)
257 265
258 266 yield self.t('searchentry',
259 267 parity=count & 1,
260 268 author=changes[1],
261 269 parent=self.siblings(cl.parents(n), cl.rev),
262 270 child=self.siblings(cl.children(n), cl.rev),
263 271 changelogtag=self.showtag("changelogtag",n),
264 272 manifest=hex(changes[0]),
265 273 desc=changes[4],
266 274 date=changes[2],
267 275 files=self.listfilediffs(changes[3], n),
268 276 rev=i,
269 277 node=hn)
270 278
271 279 if count >= self.maxchanges:
272 280 break
273 281
274 282 cl = self.repo.changelog
275 283 mf = cl.read(cl.tip())[0]
276 284
277 285 yield self.t('search',
278 286 query=query,
279 287 manifest=hex(mf),
280 288 entries=changelist)
281 289
282 290 def changeset(self, nodeid):
283 291 cl = self.repo.changelog
284 292 n = self.repo.lookup(nodeid)
285 293 nodeid = hex(n)
286 294 changes = cl.read(n)
287 295 p1 = cl.parents(n)[0]
288 296
289 297 files = []
290 298 mf = self.repo.manifest.read(changes[0])
291 299 for f in changes[3]:
292 300 files.append(self.t("filenodelink",
293 301 filenode=hex(mf.get(f, nullid)), file=f))
294 302
295 303 def diff(**map):
296 304 yield self.diff(p1, n, None)
297 305
298 306 yield self.t('changeset',
299 307 diff=diff,
300 308 rev=cl.rev(n),
301 309 node=nodeid,
302 310 parent=self.siblings(cl.parents(n), cl.rev),
303 311 child=self.siblings(cl.children(n), cl.rev),
304 312 changesettag=self.showtag("changesettag",n),
305 313 manifest=hex(changes[0]),
306 314 author=changes[1],
307 315 desc=changes[4],
308 316 date=changes[2],
309 317 files=files,
310 318 archives=self.archivelist(nodeid))
311 319
312 320 def filelog(self, f, filenode):
313 321 cl = self.repo.changelog
314 322 fl = self.repo.file(f)
315 323 filenode = hex(fl.lookup(filenode))
316 324 count = fl.count()
317 325
318 326 def entries(**map):
319 327 l = []
320 328 parity = (count - 1) & 1
321 329
322 330 for i in range(count):
323 331 n = fl.node(i)
324 332 lr = fl.linkrev(n)
325 333 cn = cl.node(lr)
326 334 cs = cl.read(cl.node(lr))
327 335
328 336 l.insert(0, {"parity": parity,
329 337 "filenode": hex(n),
330 338 "filerev": i,
331 339 "file": f,
332 340 "node": hex(cn),
333 341 "author": cs[1],
334 342 "date": cs[2],
335 343 "rename": self.renamelink(fl, n),
336 344 "parent": self.siblings(fl.parents(n),
337 345 fl.rev, file=f),
338 346 "child": self.siblings(fl.children(n),
339 347 fl.rev, file=f),
340 348 "desc": cs[4]})
341 349 parity = 1 - parity
342 350
343 351 for e in l:
344 352 yield e
345 353
346 354 yield self.t("filelog", file=f, filenode=filenode, entries=entries)
347 355
348 356 def filerevision(self, f, node):
349 357 fl = self.repo.file(f)
350 358 n = fl.lookup(node)
351 359 node = hex(n)
352 360 text = fl.read(n)
353 361 changerev = fl.linkrev(n)
354 362 cl = self.repo.changelog
355 363 cn = cl.node(changerev)
356 364 cs = cl.read(cn)
357 365 mfn = cs[0]
358 366
359 367 mt = mimetypes.guess_type(f)[0]
360 368 rawtext = text
361 369 if util.binary(text):
362 370 mt = mt or 'application/octet-stream'
363 371 text = "(binary:%s)" % mt
364 372 mt = mt or 'text/plain'
365 373
366 374 def lines():
367 375 for l, t in enumerate(text.splitlines(1)):
368 376 yield {"line": t,
369 377 "linenumber": "% 6d" % (l + 1),
370 378 "parity": l & 1}
371 379
372 380 yield self.t("filerevision",
373 381 file=f,
374 382 filenode=node,
375 383 path=_up(f),
376 384 text=lines(),
377 385 raw=rawtext,
378 386 mimetype=mt,
379 387 rev=changerev,
380 388 node=hex(cn),
381 389 manifest=hex(mfn),
382 390 author=cs[1],
383 391 date=cs[2],
384 392 parent=self.siblings(fl.parents(n), fl.rev, file=f),
385 393 child=self.siblings(fl.children(n), fl.rev, file=f),
386 394 rename=self.renamelink(fl, n),
387 395 permissions=self.repo.manifest.readflags(mfn)[f])
388 396
389 397 def fileannotate(self, f, node):
390 398 bcache = {}
391 399 ncache = {}
392 400 fl = self.repo.file(f)
393 401 n = fl.lookup(node)
394 402 node = hex(n)
395 403 changerev = fl.linkrev(n)
396 404
397 405 cl = self.repo.changelog
398 406 cn = cl.node(changerev)
399 407 cs = cl.read(cn)
400 408 mfn = cs[0]
401 409
402 410 def annotate(**map):
403 411 parity = 1
404 412 last = None
405 413 for r, l in fl.annotate(n):
406 414 try:
407 415 cnode = ncache[r]
408 416 except KeyError:
409 417 cnode = ncache[r] = self.repo.changelog.node(r)
410 418
411 419 try:
412 420 name = bcache[r]
413 421 except KeyError:
414 422 cl = self.repo.changelog.read(cnode)
415 423 bcache[r] = name = self.repo.ui.shortuser(cl[1])
416 424
417 425 if last != cnode:
418 426 parity = 1 - parity
419 427 last = cnode
420 428
421 429 yield {"parity": parity,
422 430 "node": hex(cnode),
423 431 "rev": r,
424 432 "author": name,
425 433 "file": f,
426 434 "line": l}
427 435
428 436 yield self.t("fileannotate",
429 437 file=f,
430 438 filenode=node,
431 439 annotate=annotate,
432 440 path=_up(f),
433 441 rev=changerev,
434 442 node=hex(cn),
435 443 manifest=hex(mfn),
436 444 author=cs[1],
437 445 date=cs[2],
438 446 rename=self.renamelink(fl, n),
439 447 parent=self.siblings(fl.parents(n), fl.rev, file=f),
440 448 child=self.siblings(fl.children(n), fl.rev, file=f),
441 449 permissions=self.repo.manifest.readflags(mfn)[f])
442 450
443 451 def manifest(self, mnode, path):
444 452 man = self.repo.manifest
445 453 mn = man.lookup(mnode)
446 454 mnode = hex(mn)
447 455 mf = man.read(mn)
448 456 rev = man.rev(mn)
449 457 changerev = man.linkrev(mn)
450 458 node = self.repo.changelog.node(changerev)
451 459 mff = man.readflags(mn)
452 460
453 461 files = {}
454 462
455 463 p = path[1:]
456 464 if p and p[-1] != "/":
457 465 p += "/"
458 466 l = len(p)
459 467
460 468 for f,n in mf.items():
461 469 if f[:l] != p:
462 470 continue
463 471 remain = f[l:]
464 472 if "/" in remain:
465 473 short = remain[:remain.index("/") + 1] # bleah
466 474 files[short] = (f, None)
467 475 else:
468 476 short = os.path.basename(remain)
469 477 files[short] = (f, n)
470 478
471 479 def filelist(**map):
472 480 parity = 0
473 481 fl = files.keys()
474 482 fl.sort()
475 483 for f in fl:
476 484 full, fnode = files[f]
477 485 if not fnode:
478 486 continue
479 487
480 488 yield {"file": full,
481 489 "manifest": mnode,
482 490 "filenode": hex(fnode),
483 491 "parity": parity,
484 492 "basename": f,
485 493 "permissions": mff[full]}
486 494 parity = 1 - parity
487 495
488 496 def dirlist(**map):
489 497 parity = 0
490 498 fl = files.keys()
491 499 fl.sort()
492 500 for f in fl:
493 501 full, fnode = files[f]
494 502 if fnode:
495 503 continue
496 504
497 505 yield {"parity": parity,
498 506 "path": os.path.join(path, f),
499 507 "manifest": mnode,
500 508 "basename": f[:-1]}
501 509 parity = 1 - parity
502 510
503 511 yield self.t("manifest",
504 512 manifest=mnode,
505 513 rev=rev,
506 514 node=hex(node),
507 515 path=path,
508 516 up=_up(path),
509 517 fentries=filelist,
510 518 dentries=dirlist,
511 519 archives=self.archivelist(hex(node)))
512 520
513 521 def tags(self):
514 522 cl = self.repo.changelog
515 523 mf = cl.read(cl.tip())[0]
516 524
517 525 i = self.repo.tagslist()
518 526 i.reverse()
519 527
520 528 def entries(notip=False, **map):
521 529 parity = 0
522 530 for k,n in i:
523 531 if notip and k == "tip": continue
524 532 yield {"parity": parity,
525 533 "tag": k,
526 534 "tagmanifest": hex(cl.read(n)[0]),
527 535 "date": cl.read(n)[2],
528 536 "node": hex(n)}
529 537 parity = 1 - parity
530 538
531 539 yield self.t("tags",
532 540 manifest=hex(mf),
533 541 entries=lambda **x: entries(False, **x),
534 542 entriesnotip=lambda **x: entries(True, **x))
535 543
536 544 def summary(self):
537 545 cl = self.repo.changelog
538 546 mf = cl.read(cl.tip())[0]
539 547
540 548 i = self.repo.tagslist()
541 549 i.reverse()
542 550
543 551 def tagentries(**map):
544 552 parity = 0
545 553 count = 0
546 554 for k,n in i:
547 555 if k == "tip": # skip tip
548 556 continue;
549 557
550 558 count += 1
551 559 if count > 10: # limit to 10 tags
552 560 break;
553 561
554 562 c = cl.read(n)
555 563 m = c[0]
556 564 t = c[2]
557 565
558 566 yield self.t("tagentry",
559 567 parity = parity,
560 568 tag = k,
561 569 node = hex(n),
562 570 date = t,
563 571 tagmanifest = hex(m))
564 572 parity = 1 - parity
565 573
566 574 def changelist(**map):
567 575 parity = 0
568 576 cl = self.repo.changelog
569 577 l = [] # build a list in forward order for efficiency
570 578 for i in range(start, end):
571 579 n = cl.node(i)
572 580 changes = cl.read(n)
573 581 hn = hex(n)
574 582 t = changes[2]
575 583
576 584 l.insert(0, self.t(
577 585 'shortlogentry',
578 586 parity = parity,
579 587 author = changes[1],
580 588 manifest = hex(changes[0]),
581 589 desc = changes[4],
582 590 date = t,
583 591 rev = i,
584 592 node = hn))
585 593 parity = 1 - parity
586 594
587 595 yield l
588 596
589 597 cl = self.repo.changelog
590 598 mf = cl.read(cl.tip())[0]
591 599 count = cl.count()
592 600 start = max(0, count - self.maxchanges)
593 601 end = min(count, start + self.maxchanges)
594 602
595 603 yield self.t("summary",
596 604 desc = self.repo.ui.config("web", "description", "unknown"),
597 605 owner = (self.repo.ui.config("ui", "username") or # preferred
598 606 self.repo.ui.config("web", "contact") or # deprecated
599 607 self.repo.ui.config("web", "author", "unknown")), # also
600 608 lastchange = (0, 0), # FIXME
601 609 manifest = hex(mf),
602 610 tags = tagentries,
603 611 shortlog = changelist)
604 612
605 613 def filediff(self, file, changeset):
606 614 cl = self.repo.changelog
607 615 n = self.repo.lookup(changeset)
608 616 changeset = hex(n)
609 617 p1 = cl.parents(n)[0]
610 618 cs = cl.read(n)
611 619 mf = self.repo.manifest.read(cs[0])
612 620
613 621 def diff(**map):
614 622 yield self.diff(p1, n, [file])
615 623
616 624 yield self.t("filediff",
617 625 file=file,
618 626 filenode=hex(mf.get(file, nullid)),
619 627 node=changeset,
620 628 rev=self.repo.changelog.rev(n),
621 629 parent=self.siblings(cl.parents(n), cl.rev),
622 630 child=self.siblings(cl.children(n), cl.rev),
623 631 diff=diff)
624 632
625 633 archive_specs = {
626 634 'bz2': ('application/x-tar', 'tbz2', '.tar.bz2', None),
627 635 'gz': ('application/x-tar', 'tgz', '.tar.gz', None),
628 636 'zip': ('application/zip', 'zip', '.zip', None),
629 637 }
630 638
631 639 def archive(self, req, cnode, type_):
632 640 reponame = re.sub(r"\W+", "-", os.path.basename(self.reponame))
633 641 name = "%s-%s" % (reponame, short(cnode))
634 642 mimetype, artype, extension, encoding = self.archive_specs[type_]
635 643 headers = [('Content-type', mimetype),
636 644 ('Content-disposition', 'attachment; filename=%s%s' %
637 645 (name, extension))]
638 646 if encoding:
639 647 headers.append(('Content-encoding', encoding))
640 648 req.header(headers)
641 649 archival.archive(self.repo, req.out, cnode, artype, prefix=name)
642 650
643 651 # add tags to things
644 652 # tags -> list of changesets corresponding to tags
645 653 # find tag, changeset, file
646 654
647 655 def cleanpath(self, path):
648 656 p = util.normpath(path)
649 657 if p[:2] == "..":
650 658 raise Exception("suspicious path")
651 659 return p
652 660
653 661 def run(self):
654 662 if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."):
655 663 raise RuntimeError("This function is only intended to be called while running as a CGI script.")
656 664 import mercurial.hgweb.wsgicgi as wsgicgi
657 665 from request import wsgiapplication
658 666 def make_web_app():
659 667 return self
660 668 wsgicgi.launch(wsgiapplication(make_web_app))
661 669
662 670 def run_wsgi(self, req):
663 671 def header(**map):
664 672 header_file = cStringIO.StringIO(''.join(self.t("header", **map)))
665 673 msg = mimetools.Message(header_file, 0)
666 674 req.header(msg.items())
667 675 yield header_file.read()
668 676
669 677 def rawfileheader(**map):
670 678 req.header([('Content-type', map['mimetype']),
671 679 ('Content-disposition', 'filename=%s' % map['file']),
672 680 ('Content-length', str(len(map['raw'])))])
673 681 yield ''
674 682
675 683 def footer(**map):
676 684 yield self.t("footer",
677 685 motd=self.repo.ui.config("web", "motd", ""),
678 686 **map)
679 687
680 688 def expand_form(form):
681 689 shortcuts = {
682 690 'cl': [('cmd', ['changelog']), ('rev', None)],
683 691 'cs': [('cmd', ['changeset']), ('node', None)],
684 692 'f': [('cmd', ['file']), ('filenode', None)],
685 693 'fl': [('cmd', ['filelog']), ('filenode', None)],
686 694 'fd': [('cmd', ['filediff']), ('node', None)],
687 695 'fa': [('cmd', ['annotate']), ('filenode', None)],
688 696 'mf': [('cmd', ['manifest']), ('manifest', None)],
689 697 'ca': [('cmd', ['archive']), ('node', None)],
690 698 'tags': [('cmd', ['tags'])],
691 699 'tip': [('cmd', ['changeset']), ('node', ['tip'])],
692 700 'static': [('cmd', ['static']), ('file', None)]
693 701 }
694 702
695 703 for k in shortcuts.iterkeys():
696 704 if form.has_key(k):
697 705 for name, value in shortcuts[k]:
698 706 if value is None:
699 707 value = form[k]
700 708 form[name] = value
701 709 del form[k]
702 710
703 711 self.refresh()
704 712
705 713 expand_form(req.form)
706 714
707 715 m = os.path.join(self.templatepath, "map")
708 716 style = self.repo.ui.config("web", "style", "")
709 717 if req.form.has_key('style'):
710 718 style = req.form['style'][0]
711 719 if style:
712 720 b = os.path.basename("map-" + style)
713 721 p = os.path.join(self.templatepath, b)
714 722 if os.path.isfile(p):
715 723 m = p
716 724
717 725 port = req.env["SERVER_PORT"]
718 726 port = port != "80" and (":" + port) or ""
719 727 uri = req.env["REQUEST_URI"]
720 728 if "?" in uri:
721 729 uri = uri.split("?")[0]
722 730 url = "http://%s%s%s" % (req.env["SERVER_NAME"], port, uri)
723 731 if not self.reponame:
724 732 self.reponame = (self.repo.ui.config("web", "name")
725 733 or uri.strip('/') or self.repo.root)
726 734
727 735 self.t = templater.templater(m, templater.common_filters,
728 736 defaults={"url": url,
729 737 "repo": self.reponame,
730 738 "header": header,
731 739 "footer": footer,
732 740 "rawfileheader": rawfileheader,
733 741 })
734 742
735 743 if not req.form.has_key('cmd'):
736 744 req.form['cmd'] = [self.t.cache['default'],]
737 745
738 746 cmd = req.form['cmd'][0]
739 747
740 748 method = getattr(self, 'do_' + cmd, None)
741 749 if method:
742 750 method(req)
743 751 else:
744 752 req.write(self.t("error"))
745 753
746 754 def do_changelog(self, req):
747 755 hi = self.repo.changelog.count() - 1
748 756 if req.form.has_key('rev'):
749 757 hi = req.form['rev'][0]
750 758 try:
751 759 hi = self.repo.changelog.rev(self.repo.lookup(hi))
752 760 except hg.RepoError:
753 761 req.write(self.search(hi)) # XXX redirect to 404 page?
754 762 return
755 763
756 764 req.write(self.changelog(hi))
757 765
758 766 def do_changeset(self, req):
759 767 req.write(self.changeset(req.form['node'][0]))
760 768
761 769 def do_manifest(self, req):
762 770 req.write(self.manifest(req.form['manifest'][0],
763 771 self.cleanpath(req.form['path'][0])))
764 772
765 773 def do_tags(self, req):
766 774 req.write(self.tags())
767 775
768 776 def do_summary(self, req):
769 777 req.write(self.summary())
770 778
771 779 def do_filediff(self, req):
772 780 req.write(self.filediff(self.cleanpath(req.form['file'][0]),
773 781 req.form['node'][0]))
774 782
775 783 def do_file(self, req):
776 784 req.write(self.filerevision(self.cleanpath(req.form['file'][0]),
777 785 req.form['filenode'][0]))
778 786
779 787 def do_annotate(self, req):
780 788 req.write(self.fileannotate(self.cleanpath(req.form['file'][0]),
781 789 req.form['filenode'][0]))
782 790
783 791 def do_filelog(self, req):
784 792 req.write(self.filelog(self.cleanpath(req.form['file'][0]),
785 793 req.form['filenode'][0]))
786 794
787 795 def do_heads(self, req):
788 796 resp = " ".join(map(hex, self.repo.heads())) + "\n"
789 797 req.httphdr("application/mercurial-0.1", length=len(resp))
790 798 req.write(resp)
791 799
792 800 def do_branches(self, req):
793 801 nodes = []
794 802 if req.form.has_key('nodes'):
795 803 nodes = map(bin, req.form['nodes'][0].split(" "))
796 804 resp = cStringIO.StringIO()
797 805 for b in self.repo.branches(nodes):
798 806 resp.write(" ".join(map(hex, b)) + "\n")
799 807 resp = resp.getvalue()
800 808 req.httphdr("application/mercurial-0.1", length=len(resp))
801 809 req.write(resp)
802 810
803 811 def do_between(self, req):
804 812 nodes = []
805 813 if req.form.has_key('pairs'):
806 814 pairs = [map(bin, p.split("-"))
807 815 for p in req.form['pairs'][0].split(" ")]
808 816 resp = cStringIO.StringIO()
809 817 for b in self.repo.between(pairs):
810 818 resp.write(" ".join(map(hex, b)) + "\n")
811 819 resp = resp.getvalue()
812 820 req.httphdr("application/mercurial-0.1", length=len(resp))
813 821 req.write(resp)
814 822
815 823 def do_changegroup(self, req):
816 824 req.httphdr("application/mercurial-0.1")
817 825 nodes = []
818 826 if not self.allowpull:
819 827 return
820 828
821 829 if req.form.has_key('roots'):
822 830 nodes = map(bin, req.form['roots'][0].split(" "))
823 831
824 832 z = zlib.compressobj()
825 833 f = self.repo.changegroup(nodes, 'serve')
826 834 while 1:
827 835 chunk = f.read(4096)
828 836 if not chunk:
829 837 break
830 838 req.write(z.compress(chunk))
831 839
832 840 req.write(z.flush())
833 841
834 842 def do_archive(self, req):
835 843 changeset = self.repo.lookup(req.form['node'][0])
836 844 type_ = req.form['type'][0]
837 845 allowed = self.repo.ui.configlist("web", "allow_archive")
838 846 if (type_ in self.archives and (type_ in allowed or
839 847 self.repo.ui.configbool("web", "allow" + type_, False))):
840 848 self.archive(req, changeset, type_)
841 849 return
842 850
843 851 req.write(self.t("error"))
844 852
845 853 def do_static(self, req):
846 854 fname = req.form['file'][0]
847 855 static = self.repo.ui.config("web", "static",
848 856 os.path.join(self.templatepath,
849 857 "static"))
850 858 req.write(staticfile(static, fname, req)
851 859 or self.t("error", error="%r not found" % fname))
852 860
853 861 def do_capabilities(self, req):
854 862 resp = 'unbundle'
855 863 req.httphdr("application/mercurial-0.1", length=len(resp))
856 864 req.write(resp)
857 865
858 866 def check_perm(self, req, op, default):
859 867 '''check permission for operation based on user auth.
860 868 return true if op allowed, else false.
861 869 default is policy to use if no config given.'''
862 870
863 871 user = req.env.get('REMOTE_USER')
864 872
865 873 deny = self.repo.ui.configlist('web', 'deny_' + op)
866 874 if deny and (not user or deny == ['*'] or user in deny):
867 875 return False
868 876
869 877 allow = self.repo.ui.configlist('web', 'allow_' + op)
870 878 return (allow and (allow == ['*'] or user in allow)) or default
871 879
872 880 def do_unbundle(self, req):
873 881 def bail(response, headers={}):
874 882 length = int(req.env['CONTENT_LENGTH'])
875 883 for s in util.filechunkiter(req, limit=length):
876 884 # drain incoming bundle, else client will not see
877 885 # response when run outside cgi script
878 886 pass
879 887 req.httphdr("application/mercurial-0.1", headers=headers)
880 888 req.write('0\n')
881 889 req.write(response)
882 890
883 891 # require ssl by default, auth info cannot be sniffed and
884 892 # replayed
885 893 ssl_req = self.repo.ui.configbool('web', 'push_ssl', True)
886 894 if ssl_req and not req.env.get('HTTPS'):
887 895 bail(_('ssl required\n'))
888 896 return
889 897
890 898 # do not allow push unless explicitly allowed
891 899 if not self.check_perm(req, 'push', False):
892 900 bail(_('push not authorized\n'),
893 901 headers={'status': '401 Unauthorized'})
894 902 return
895 903
896 904 req.httphdr("application/mercurial-0.1")
897 905
898 906 their_heads = req.form['heads'][0].split(' ')
899 907
900 908 def check_heads():
901 909 heads = map(hex, self.repo.heads())
902 910 return their_heads == [hex('force')] or their_heads == heads
903 911
904 912 # fail early if possible
905 913 if not check_heads():
906 914 bail(_('unsynced changes\n'))
907 915 return
908 916
909 917 # do not lock repo until all changegroup data is
910 918 # streamed. save to temporary file.
911 919
912 920 fd, tempname = tempfile.mkstemp(prefix='hg-unbundle-')
913 921 fp = os.fdopen(fd, 'wb+')
914 922 try:
915 923 length = int(req.env['CONTENT_LENGTH'])
916 924 for s in util.filechunkiter(req, limit=length):
917 925 fp.write(s)
918 926
919 927 lock = self.repo.lock()
920 928 try:
921 929 if not check_heads():
922 930 req.write('0\n')
923 931 req.write(_('unsynced changes\n'))
924 932 return
925 933
926 934 fp.seek(0)
927 935
928 936 # send addchangegroup output to client
929 937
930 938 old_stdout = sys.stdout
931 939 sys.stdout = cStringIO.StringIO()
932 940
933 941 try:
934 942 ret = self.repo.addchangegroup(fp, 'serve')
935 943 finally:
936 944 val = sys.stdout.getvalue()
937 945 sys.stdout = old_stdout
938 946 req.write('%d\n' % ret)
939 947 req.write(val)
940 948 finally:
941 949 lock.release()
942 950 finally:
943 951 fp.close()
944 952 os.unlink(tempname)
@@ -1,205 +1,229 b''
1 1 # mdiff.py - diff and patch routines 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 import bdiff, mpatch
10 10 demandload(globals(), "re struct util")
11 11
12 12 def splitnewlines(text):
13 13 '''like str.splitlines, but only split on newlines.'''
14 14 lines = [l + '\n' for l in text.split('\n')]
15 15 if lines:
16 16 if lines[-1] == '\n':
17 17 lines.pop()
18 18 else:
19 19 lines[-1] = lines[-1][:-1]
20 20 return lines
21 21
22 22 def unidiff(a, ad, b, bd, fn, r=None, text=False,
23 showfunc=False, ignorews=False):
23 showfunc=False, ignorews=False, ignorewsamount=False,
24 ignoreblanklines=False):
24 25
25 26 if not a and not b: return ""
26 27 epoch = util.datestr((0, 0))
27 28
28 29 if not text and (util.binary(a) or util.binary(b)):
29 30 l = ['Binary file %s has changed\n' % fn]
30 31 elif not a:
31 32 b = splitnewlines(b)
32 33 if a is None:
33 34 l1 = "--- %s\t%s\n" % ("/dev/null", epoch)
34 35 else:
35 36 l1 = "--- %s\t%s\n" % ("a/" + fn, ad)
36 37 l2 = "+++ %s\t%s\n" % ("b/" + fn, bd)
37 38 l3 = "@@ -0,0 +1,%d @@\n" % len(b)
38 39 l = [l1, l2, l3] + ["+" + e for e in b]
39 40 elif not b:
40 41 a = splitnewlines(a)
41 42 l1 = "--- %s\t%s\n" % ("a/" + fn, ad)
42 43 if b is None:
43 44 l2 = "+++ %s\t%s\n" % ("/dev/null", epoch)
44 45 else:
45 46 l2 = "+++ %s\t%s\n" % ("b/" + fn, bd)
46 47 l3 = "@@ -1,%d +0,0 @@\n" % len(a)
47 48 l = [l1, l2, l3] + ["-" + e for e in a]
48 49 else:
49 50 al = splitnewlines(a)
50 51 bl = splitnewlines(b)
51 52 l = list(bunidiff(a, b, al, bl, "a/" + fn, "b/" + fn,
52 showfunc=showfunc, ignorews=ignorews))
53 showfunc=showfunc, ignorews=ignorews,
54 ignorewsamount=ignorewsamount,
55 ignoreblanklines=ignoreblanklines))
53 56 if not l: return ""
54 57 # difflib uses a space, rather than a tab
55 58 l[0] = "%s\t%s\n" % (l[0][:-2], ad)
56 59 l[1] = "%s\t%s\n" % (l[1][:-2], bd)
57 60
58 61 for ln in xrange(len(l)):
59 62 if l[ln][-1] != '\n':
60 63 l[ln] += "\n\ No newline at end of file\n"
61 64
62 65 if r:
63 66 l.insert(0, "diff %s %s\n" %
64 67 (' '.join(["-r %s" % rev for rev in r]), fn))
65 68
66 69 return "".join(l)
67 70
68 71 # somewhat self contained replacement for difflib.unified_diff
69 72 # t1 and t2 are the text to be diffed
70 73 # l1 and l2 are the text broken up into lines
71 74 # header1 and header2 are the filenames for the diff output
72 75 # context is the number of context lines
73 76 # showfunc enables diff -p output
74 77 # ignorews ignores all whitespace changes in the diff
78 # ignorewsamount ignores changes in the amount of whitespace
79 # ignoreblanklines ignores changes whose lines are all blank
75 80 def bunidiff(t1, t2, l1, l2, header1, header2, context=3, showfunc=False,
76 ignorews=False):
81 ignorews=False, ignorewsamount=False, ignoreblanklines=False):
77 82 def contextend(l, len):
78 83 ret = l + context
79 84 if ret > len:
80 85 ret = len
81 86 return ret
82 87
83 88 def contextstart(l):
84 89 ret = l - context
85 90 if ret < 0:
86 91 return 0
87 92 return ret
88 93
89 94 def yieldhunk(hunk, header):
90 95 if header:
91 96 for x in header:
92 97 yield x
93 98 (astart, a2, bstart, b2, delta) = hunk
94 99 aend = contextend(a2, len(l1))
95 100 alen = aend - astart
96 101 blen = b2 - bstart + aend - a2
97 102
98 103 func = ""
99 104 if showfunc:
100 105 # walk backwards from the start of the context
101 106 # to find a line starting with an alphanumeric char.
102 107 for x in xrange(astart, -1, -1):
103 108 t = l1[x].rstrip()
104 109 if funcre.match(t):
105 110 func = ' ' + t[:40]
106 111 break
107 112
108 113 yield "@@ -%d,%d +%d,%d @@%s\n" % (astart + 1, alen,
109 114 bstart + 1, blen, func)
110 115 for x in delta:
111 116 yield x
112 117 for x in xrange(a2, aend):
113 118 yield ' ' + l1[x]
114 119
115 120 header = [ "--- %s\t\n" % header1, "+++ %s\t\n" % header2 ]
116 121
117 122 if showfunc:
118 123 funcre = re.compile('\w')
124 if ignorewsamount:
125 wsamountre = re.compile('[ \t]+')
126 wsappendedre = re.compile(' \n')
127 if ignoreblanklines:
128 wsblanklinesre = re.compile('\n')
119 129 if ignorews:
120 130 wsre = re.compile('[ \t]')
121 131
122 132 # bdiff.blocks gives us the matching sequences in the files. The loop
123 133 # below finds the spaces between those matching sequences and translates
124 134 # them into diff output.
125 135 #
126 136 diff = bdiff.blocks(t1, t2)
127 137 hunk = None
128 138 for i in xrange(len(diff)):
129 139 # The first match is special.
130 140 # we've either found a match starting at line 0 or a match later
131 141 # in the file. If it starts later, old and new below will both be
132 142 # empty and we'll continue to the next match.
133 143 if i > 0:
134 144 s = diff[i-1]
135 145 else:
136 146 s = [0, 0, 0, 0]
137 147 delta = []
138 148 s1 = diff[i]
139 149 a1 = s[1]
140 150 a2 = s1[0]
141 151 b1 = s[3]
142 152 b2 = s1[2]
143 153
144 154 old = l1[a1:a2]
145 155 new = l2[b1:b2]
146 156
147 157 # bdiff sometimes gives huge matches past eof, this check eats them,
148 158 # and deals with the special first match case described above
149 159 if not old and not new:
150 160 continue
151 161
162 if ignoreblanklines:
163 wsold = wsblanklinesre.sub('', "".join(old))
164 wsnew = wsblanklinesre.sub('', "".join(new))
165 if wsold == wsnew:
166 continue
167
168 if ignorewsamount:
169 wsold = wsamountre.sub(' ', "".join(old))
170 wsold = wsappendedre.sub('\n', wsold)
171 wsnew = wsamountre.sub(' ', "".join(new))
172 wsnew = wsappendedre.sub('\n', wsnew)
173 if wsold == wsnew:
174 continue
175
152 176 if ignorews:
153 177 wsold = wsre.sub('', "".join(old))
154 178 wsnew = wsre.sub('', "".join(new))
155 179 if wsold == wsnew:
156 180 continue
157 181
158 182 astart = contextstart(a1)
159 183 bstart = contextstart(b1)
160 184 prev = None
161 185 if hunk:
162 186 # join with the previous hunk if it falls inside the context
163 187 if astart < hunk[1] + context + 1:
164 188 prev = hunk
165 189 astart = hunk[1]
166 190 bstart = hunk[3]
167 191 else:
168 192 for x in yieldhunk(hunk, header):
169 193 yield x
170 194 # we only want to yield the header if the files differ, and
171 195 # we only want to yield it once.
172 196 header = None
173 197 if prev:
174 198 # we've joined the previous hunk, record the new ending points.
175 199 hunk[1] = a2
176 200 hunk[3] = b2
177 201 delta = hunk[4]
178 202 else:
179 203 # create a new hunk
180 204 hunk = [ astart, a2, bstart, b2, delta ]
181 205
182 206 delta[len(delta):] = [ ' ' + x for x in l1[astart:a1] ]
183 207 delta[len(delta):] = [ '-' + x for x in old ]
184 208 delta[len(delta):] = [ '+' + x for x in new ]
185 209
186 210 if hunk:
187 211 for x in yieldhunk(hunk, header):
188 212 yield x
189 213
190 214 def patchtext(bin):
191 215 pos = 0
192 216 t = []
193 217 while pos < len(bin):
194 218 p1, p2, l = struct.unpack(">lll", bin[pos:pos + 12])
195 219 pos += 12
196 220 t.append(bin[pos:pos + l])
197 221 pos += l
198 222 return "".join(t)
199 223
200 224 def patch(a, bin):
201 225 return mpatch.patches(a, [bin])
202 226
203 227 patches = mpatch.patches
204 228 patchedsize = mpatch.patchedsize
205 229 textdiff = bdiff.bdiff
@@ -1,355 +1,356 b''
1 1 # ui.py - user interface bits 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 i18n import gettext as _
9 9 from demandload import *
10 10 demandload(globals(), "errno getpass os re smtplib socket sys tempfile")
11 11 demandload(globals(), "ConfigParser templater traceback util")
12 12
13 13 class ui(object):
14 14 def __init__(self, verbose=False, debug=False, quiet=False,
15 15 interactive=True, traceback=False, parentui=None):
16 16 self.overlay = {}
17 17 if parentui is None:
18 18 # this is the parent of all ui children
19 19 self.parentui = None
20 20 self.cdata = ConfigParser.SafeConfigParser()
21 21 self.readconfig(util.rcpath())
22 22
23 23 self.quiet = self.configbool("ui", "quiet")
24 24 self.verbose = self.configbool("ui", "verbose")
25 25 self.debugflag = self.configbool("ui", "debug")
26 26 self.interactive = self.configbool("ui", "interactive", True)
27 27 self.traceback = traceback
28 28
29 29 self.updateopts(verbose, debug, quiet, interactive)
30 30 self.diffcache = None
31 31 self.header = []
32 32 self.prev_header = []
33 33 self.revlogopts = self.configrevlog()
34 34 else:
35 35 # parentui may point to an ui object which is already a child
36 36 self.parentui = parentui.parentui or parentui
37 37 parent_cdata = self.parentui.cdata
38 38 self.cdata = ConfigParser.SafeConfigParser(parent_cdata.defaults())
39 39 # make interpolation work
40 40 for section in parent_cdata.sections():
41 41 self.cdata.add_section(section)
42 42 for name, value in parent_cdata.items(section, raw=True):
43 43 self.cdata.set(section, name, value)
44 44
45 45 def __getattr__(self, key):
46 46 return getattr(self.parentui, key)
47 47
48 48 def updateopts(self, verbose=False, debug=False, quiet=False,
49 49 interactive=True, traceback=False, config=[]):
50 50 self.quiet = (self.quiet or quiet) and not verbose and not debug
51 51 self.verbose = (self.verbose or verbose) or debug
52 52 self.debugflag = (self.debugflag or debug)
53 53 self.interactive = (self.interactive and interactive)
54 54 self.traceback = self.traceback or traceback
55 55 for cfg in config:
56 56 try:
57 57 name, value = cfg.split('=', 1)
58 58 section, name = name.split('.', 1)
59 59 if not self.cdata.has_section(section):
60 60 self.cdata.add_section(section)
61 61 if not section or not name:
62 62 raise IndexError
63 63 self.cdata.set(section, name, value)
64 64 except (IndexError, ValueError):
65 65 raise util.Abort(_('malformed --config option: %s') % cfg)
66 66
67 67 def readconfig(self, fn, root=None):
68 68 if isinstance(fn, basestring):
69 69 fn = [fn]
70 70 for f in fn:
71 71 try:
72 72 self.cdata.read(f)
73 73 except ConfigParser.ParsingError, inst:
74 74 raise util.Abort(_("Failed to parse %s\n%s") % (f, inst))
75 75 # translate paths relative to root (or home) into absolute paths
76 76 if root is None:
77 77 root = os.path.expanduser('~')
78 78 for name, path in self.configitems("paths"):
79 79 if path and "://" not in path and not os.path.isabs(path):
80 80 self.cdata.set("paths", name, os.path.join(root, path))
81 81
82 82 def setconfig(self, section, name, val):
83 83 self.overlay[(section, name)] = val
84 84
85 85 def config(self, section, name, default=None):
86 86 if self.overlay.has_key((section, name)):
87 87 return self.overlay[(section, name)]
88 88 if self.cdata.has_option(section, name):
89 89 try:
90 90 return self.cdata.get(section, name)
91 91 except ConfigParser.InterpolationError, inst:
92 92 raise util.Abort(_("Error in configuration:\n%s") % inst)
93 93 if self.parentui is None:
94 94 return default
95 95 else:
96 96 return self.parentui.config(section, name, default)
97 97
98 98 def configlist(self, section, name, default=None):
99 99 """Return a list of comma/space separated strings"""
100 100 result = self.config(section, name)
101 101 if result is None:
102 102 result = default or []
103 103 if isinstance(result, basestring):
104 104 result = result.replace(",", " ").split()
105 105 return result
106 106
107 107 def configbool(self, section, name, default=False):
108 108 if self.overlay.has_key((section, name)):
109 109 return self.overlay[(section, name)]
110 110 if self.cdata.has_option(section, name):
111 111 try:
112 112 return self.cdata.getboolean(section, name)
113 113 except ConfigParser.InterpolationError, inst:
114 114 raise util.Abort(_("Error in configuration:\n%s") % inst)
115 115 if self.parentui is None:
116 116 return default
117 117 else:
118 118 return self.parentui.configbool(section, name, default)
119 119
120 120 def has_config(self, section):
121 121 '''tell whether section exists in config.'''
122 122 return self.cdata.has_section(section)
123 123
124 124 def configitems(self, section):
125 125 items = {}
126 126 if self.parentui is not None:
127 127 items = dict(self.parentui.configitems(section))
128 128 if self.cdata.has_section(section):
129 129 try:
130 130 items.update(dict(self.cdata.items(section)))
131 131 except ConfigParser.InterpolationError, inst:
132 132 raise util.Abort(_("Error in configuration:\n%s") % inst)
133 133 x = items.items()
134 134 x.sort()
135 135 return x
136 136
137 137 def walkconfig(self, seen=None):
138 138 if seen is None:
139 139 seen = {}
140 140 for (section, name), value in self.overlay.iteritems():
141 141 yield section, name, value
142 142 seen[section, name] = 1
143 143 for section in self.cdata.sections():
144 144 for name, value in self.cdata.items(section):
145 145 if (section, name) in seen: continue
146 146 yield section, name, value.replace('\n', '\\n')
147 147 seen[section, name] = 1
148 148 if self.parentui is not None:
149 149 for parent in self.parentui.walkconfig(seen):
150 150 yield parent
151 151
152 152 def extensions(self):
153 153 result = self.configitems("extensions")
154 154 for i, (key, value) in enumerate(result):
155 155 if value:
156 156 result[i] = (key, os.path.expanduser(value))
157 157 return result
158 158
159 159 def hgignorefiles(self):
160 160 result = []
161 161 for key, value in self.configitems("ui"):
162 162 if key == 'ignore' or key.startswith('ignore.'):
163 163 result.append(os.path.expanduser(value))
164 164 return result
165 165
166 166 def configrevlog(self):
167 167 result = {}
168 168 for key, value in self.configitems("revlog"):
169 169 result[key.lower()] = value
170 170 return result
171 171
172 172 def diffopts(self):
173 173 if self.diffcache:
174 174 return self.diffcache
175 result = {'showfunc': True, 'ignorews': False}
175 result = {'showfunc': True, 'ignorews': False,
176 'ignorewsamount': False, 'ignoreblanklines': False}
176 177 for key, value in self.configitems("diff"):
177 178 if value:
178 179 result[key.lower()] = (value.lower() == 'true')
179 180 self.diffcache = result
180 181 return result
181 182
182 183 def username(self):
183 184 """Return default username to be used in commits.
184 185
185 186 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
186 187 and stop searching if one of these is set.
187 188 Abort if found username is an empty string to force specifying
188 189 the commit user elsewhere, e.g. with line option or repo hgrc.
189 190 If not found, use ($LOGNAME or $USER or $LNAME or
190 191 $USERNAME) +"@full.hostname".
191 192 """
192 193 user = os.environ.get("HGUSER")
193 194 if user is None:
194 195 user = self.config("ui", "username")
195 196 if user is None:
196 197 user = os.environ.get("EMAIL")
197 198 if user is None:
198 199 try:
199 200 user = '%s@%s' % (getpass.getuser(), socket.getfqdn())
200 201 except KeyError:
201 202 raise util.Abort(_("Please specify a username."))
202 203 return user
203 204
204 205 def shortuser(self, user):
205 206 """Return a short representation of a user name or email address."""
206 207 if not self.verbose: user = util.shortuser(user)
207 208 return user
208 209
209 210 def expandpath(self, loc, default=None):
210 211 """Return repository location relative to cwd or from [paths]"""
211 212 if "://" in loc or os.path.exists(loc):
212 213 return loc
213 214
214 215 path = self.config("paths", loc)
215 216 if not path and default is not None:
216 217 path = self.config("paths", default)
217 218 return path or loc
218 219
219 220 def write(self, *args):
220 221 if self.header:
221 222 if self.header != self.prev_header:
222 223 self.prev_header = self.header
223 224 self.write(*self.header)
224 225 self.header = []
225 226 for a in args:
226 227 sys.stdout.write(str(a))
227 228
228 229 def write_header(self, *args):
229 230 for a in args:
230 231 self.header.append(str(a))
231 232
232 233 def write_err(self, *args):
233 234 try:
234 235 if not sys.stdout.closed: sys.stdout.flush()
235 236 for a in args:
236 237 sys.stderr.write(str(a))
237 238 except IOError, inst:
238 239 if inst.errno != errno.EPIPE:
239 240 raise
240 241
241 242 def flush(self):
242 243 try: sys.stdout.flush()
243 244 except: pass
244 245 try: sys.stderr.flush()
245 246 except: pass
246 247
247 248 def readline(self):
248 249 return sys.stdin.readline()[:-1]
249 250 def prompt(self, msg, pat=None, default="y"):
250 251 if not self.interactive: return default
251 252 while 1:
252 253 self.write(msg, " ")
253 254 r = self.readline()
254 255 if not pat or re.match(pat, r):
255 256 return r
256 257 else:
257 258 self.write(_("unrecognized response\n"))
258 259 def getpass(self, prompt=None, default=None):
259 260 if not self.interactive: return default
260 261 return getpass.getpass(prompt or _('password: '))
261 262 def status(self, *msg):
262 263 if not self.quiet: self.write(*msg)
263 264 def warn(self, *msg):
264 265 self.write_err(*msg)
265 266 def note(self, *msg):
266 267 if self.verbose: self.write(*msg)
267 268 def debug(self, *msg):
268 269 if self.debugflag: self.write(*msg)
269 270 def edit(self, text, user):
270 271 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
271 272 text=True)
272 273 try:
273 274 f = os.fdopen(fd, "w")
274 275 f.write(text)
275 276 f.close()
276 277
277 278 editor = (os.environ.get("HGEDITOR") or
278 279 self.config("ui", "editor") or
279 280 os.environ.get("EDITOR", "vi"))
280 281
281 282 util.system("%s \"%s\"" % (editor, name),
282 283 environ={'HGUSER': user},
283 284 onerr=util.Abort, errprefix=_("edit failed"))
284 285
285 286 f = open(name)
286 287 t = f.read()
287 288 f.close()
288 289 t = re.sub("(?m)^HG:.*\n", "", t)
289 290 finally:
290 291 os.unlink(name)
291 292
292 293 return t
293 294
294 295 def sendmail(self):
295 296 '''send mail message. object returned has one method, sendmail.
296 297 call as sendmail(sender, list-of-recipients, msg).'''
297 298
298 299 def smtp():
299 300 '''send mail using smtp.'''
300 301
301 302 s = smtplib.SMTP()
302 303 mailhost = self.config('smtp', 'host')
303 304 if not mailhost:
304 305 raise util.Abort(_('no [smtp]host in hgrc - cannot send mail'))
305 306 mailport = int(self.config('smtp', 'port', 25))
306 307 self.note(_('sending mail: smtp host %s, port %s\n') %
307 308 (mailhost, mailport))
308 309 s.connect(host=mailhost, port=mailport)
309 310 if self.configbool('smtp', 'tls'):
310 311 self.note(_('(using tls)\n'))
311 312 s.ehlo()
312 313 s.starttls()
313 314 s.ehlo()
314 315 username = self.config('smtp', 'username')
315 316 password = self.config('smtp', 'password')
316 317 if username and password:
317 318 self.note(_('(authenticating to mail server as %s)\n') %
318 319 (username))
319 320 s.login(username, password)
320 321 return s
321 322
322 323 class sendmail(object):
323 324 '''send mail using sendmail.'''
324 325
325 326 def __init__(self, ui, program):
326 327 self.ui = ui
327 328 self.program = program
328 329
329 330 def sendmail(self, sender, recipients, msg):
330 331 cmdline = '%s -f %s %s' % (
331 332 self.program, templater.email(sender),
332 333 ' '.join(map(templater.email, recipients)))
333 334 self.ui.note(_('sending mail: %s\n') % cmdline)
334 335 fp = os.popen(cmdline, 'w')
335 336 fp.write(msg)
336 337 ret = fp.close()
337 338 if ret:
338 339 raise util.Abort('%s %s' % (
339 340 os.path.basename(self.program.split(None, 1)[0]),
340 341 util.explain_exit(ret)[0]))
341 342
342 343 method = self.config('email', 'method', 'smtp')
343 344 if method == 'smtp':
344 345 mail = smtp()
345 346 else:
346 347 mail = sendmail(self, method)
347 348 return mail
348 349
349 350 def print_exc(self):
350 351 '''print exception traceback if traceback printing enabled.
351 352 only to call in exception handler. returns true if traceback
352 353 printed.'''
353 354 if self.traceback:
354 355 traceback.print_exc()
355 356 return self.traceback
@@ -1,255 +1,257 b''
1 1 Mercurial Distributed SCM
2 2
3 3 basic commands (use "hg help" for the full list or option "-v" for details):
4 4
5 5 add add the specified files on the next commit
6 6 annotate show changeset information per file line
7 7 clone make a copy of an existing repository
8 8 commit commit the specified files or all outstanding changes
9 9 diff diff repository (or selected files)
10 10 export dump the header and diffs for one or more changesets
11 11 init create a new repository in the given directory
12 12 log show revision history of entire repository or files
13 13 parents show the parents of the working dir or revision
14 14 pull pull changes from the specified source
15 15 push push changes to the specified destination
16 16 remove remove the specified files on the next commit
17 17 revert revert files or dirs to their states as of some revision
18 18 serve export the repository via HTTP
19 19 status show changed files in the working directory
20 20 update update or merge working directory
21 21 add add the specified files on the next commit
22 22 annotate show changeset information per file line
23 23 clone make a copy of an existing repository
24 24 commit commit the specified files or all outstanding changes
25 25 diff diff repository (or selected files)
26 26 export dump the header and diffs for one or more changesets
27 27 init create a new repository in the given directory
28 28 log show revision history of entire repository or files
29 29 parents show the parents of the working dir or revision
30 30 pull pull changes from the specified source
31 31 push push changes to the specified destination
32 32 remove remove the specified files on the next commit
33 33 revert revert files or dirs to their states as of some revision
34 34 serve export the repository via HTTP
35 35 status show changed files in the working directory
36 36 update update or merge working directory
37 37 Mercurial Distributed SCM
38 38
39 39 list of commands (use "hg help -v" to show aliases and global options):
40 40
41 41 add add the specified files on the next commit
42 42 annotate show changeset information per file line
43 43 archive create unversioned archive of a repository revision
44 44 backout reverse effect of earlier changeset
45 45 bundle create a changegroup file
46 46 cat output the latest or given revisions of files
47 47 clone make a copy of an existing repository
48 48 commit commit the specified files or all outstanding changes
49 49 copy mark files as copied for the next commit
50 50 diff diff repository (or selected files)
51 51 export dump the header and diffs for one or more changesets
52 52 grep search for a pattern in specified files and revisions
53 53 heads show current repository heads
54 54 help show help for a command, extension, or list of commands
55 55 identify print information about the working copy
56 56 import import an ordered set of patches
57 57 incoming show new changesets found in source
58 58 init create a new repository in the given directory
59 59 locate locate files matching specific patterns
60 60 log show revision history of entire repository or files
61 61 manifest output the latest or given revision of the project manifest
62 62 merge Merge working directory with another revision
63 63 outgoing show changesets not found in destination
64 64 parents show the parents of the working dir or revision
65 65 paths show definition of symbolic path names
66 66 pull pull changes from the specified source
67 67 push push changes to the specified destination
68 68 recover roll back an interrupted transaction
69 69 remove remove the specified files on the next commit
70 70 rename rename files; equivalent of copy + remove
71 71 revert revert files or dirs to their states as of some revision
72 72 rollback roll back the last transaction in this repository
73 73 root print the root (top) of the current working dir
74 74 serve export the repository via HTTP
75 75 status show changed files in the working directory
76 76 tag add a tag for the current tip or a given revision
77 77 tags list repository tags
78 78 tip show the tip revision
79 79 unbundle apply a changegroup file
80 80 update update or merge working directory
81 81 verify verify the integrity of the repository
82 82 version output version and copyright information
83 83 add add the specified files on the next commit
84 84 annotate show changeset information per file line
85 85 archive create unversioned archive of a repository revision
86 86 backout reverse effect of earlier changeset
87 87 bundle create a changegroup file
88 88 cat output the latest or given revisions of files
89 89 clone make a copy of an existing repository
90 90 commit commit the specified files or all outstanding changes
91 91 copy mark files as copied for the next commit
92 92 diff diff repository (or selected files)
93 93 export dump the header and diffs for one or more changesets
94 94 grep search for a pattern in specified files and revisions
95 95 heads show current repository heads
96 96 help show help for a command, extension, or list of commands
97 97 identify print information about the working copy
98 98 import import an ordered set of patches
99 99 incoming show new changesets found in source
100 100 init create a new repository in the given directory
101 101 locate locate files matching specific patterns
102 102 log show revision history of entire repository or files
103 103 manifest output the latest or given revision of the project manifest
104 104 merge Merge working directory with another revision
105 105 outgoing show changesets not found in destination
106 106 parents show the parents of the working dir or revision
107 107 paths show definition of symbolic path names
108 108 pull pull changes from the specified source
109 109 push push changes to the specified destination
110 110 recover roll back an interrupted transaction
111 111 remove remove the specified files on the next commit
112 112 rename rename files; equivalent of copy + remove
113 113 revert revert files or dirs to their states as of some revision
114 114 rollback roll back the last transaction in this repository
115 115 root print the root (top) of the current working dir
116 116 serve export the repository via HTTP
117 117 status show changed files in the working directory
118 118 tag add a tag for the current tip or a given revision
119 119 tags list repository tags
120 120 tip show the tip revision
121 121 unbundle apply a changegroup file
122 122 update update or merge working directory
123 123 verify verify the integrity of the repository
124 124 version output version and copyright information
125 125 hg add [OPTION]... [FILE]...
126 126
127 127 add the specified files on the next commit
128 128
129 129 Schedule files to be version controlled and added to the repository.
130 130
131 131 The files will be added to the repository at the next commit.
132 132
133 133 If no names are given, add all files in the repository.
134 134
135 135 options:
136 136
137 137 -I --include include names matching the given patterns
138 138 -X --exclude exclude names matching the given patterns
139 139 -n --dry-run do not perform actions, just print output
140 140 hg add: option --skjdfks not recognized
141 141 hg add [OPTION]... [FILE]...
142 142
143 143 add the specified files on the next commit
144 144
145 145 Schedule files to be version controlled and added to the repository.
146 146
147 147 The files will be added to the repository at the next commit.
148 148
149 149 If no names are given, add all files in the repository.
150 150
151 151 options:
152 152
153 153 -I --include include names matching the given patterns
154 154 -X --exclude exclude names matching the given patterns
155 155 -n --dry-run do not perform actions, just print output
156 156 hg diff [-a] [-I] [-X] [-r REV1 [-r REV2]] [FILE]...
157 157
158 158 diff repository (or selected files)
159 159
160 160 Show differences between revisions for the specified files.
161 161
162 162 Differences between files are shown using the unified diff format.
163 163
164 164 When two revision arguments are given, then changes are shown
165 165 between those revisions. If only one revision is specified then
166 166 that revision is compared to the working directory, and, when no
167 167 revisions are specified, the working directory files are compared
168 168 to its parent.
169 169
170 170 Without the -a option, diff will avoid generating diffs of files
171 171 it detects as binary. With -a, diff will generate a diff anyway,
172 172 probably with undesirable results.
173 173
174 174 options:
175 175
176 -r --rev revision
177 -a --text treat all files as text
178 -p --show-function show which function each change is in
179 -w --ignore-all-space ignore white space when comparing lines
180 -I --include include names matching the given patterns
181 -X --exclude exclude names matching the given patterns
176 -r --rev revision
177 -a --text treat all files as text
178 -p --show-function show which function each change is in
179 -w --ignore-all-space ignore white space when comparing lines
180 -b --ignore-space-change ignore changes in the amount of white space
181 -B --ignore-blank-lines ignore changes whose lines are all blank
182 -I --include include names matching the given patterns
183 -X --exclude exclude names matching the given patterns
182 184 hg status [OPTION]... [FILE]...
183 185
184 186 show changed files in the working directory
185 187
186 188 Show changed files in the repository. If names are
187 189 given, only files that match are shown.
188 190
189 191 The codes used to show the status of files are:
190 192 M = modified
191 193 A = added
192 194 R = removed
193 195 ! = deleted, but still tracked
194 196 ? = not tracked
195 197 I = ignored (not shown by default)
196 198
197 199 aliases: st
198 200
199 201 options:
200 202
201 203 -m --modified show only modified files
202 204 -a --added show only added files
203 205 -r --removed show only removed files
204 206 -d --deleted show only deleted (but tracked) files
205 207 -u --unknown show only unknown (not tracked) files
206 208 -i --ignored show ignored files
207 209 -n --no-status hide status prefix
208 210 -0 --print0 end filenames with NUL, for use with xargs
209 211 -I --include include names matching the given patterns
210 212 -X --exclude exclude names matching the given patterns
211 213 hg status [OPTION]... [FILE]...
212 214
213 215 show changed files in the working directory
214 216 hg: unknown command 'foo'
215 217 Mercurial Distributed SCM
216 218
217 219 basic commands (use "hg help" for the full list or option "-v" for details):
218 220
219 221 add add the specified files on the next commit
220 222 annotate show changeset information per file line
221 223 clone make a copy of an existing repository
222 224 commit commit the specified files or all outstanding changes
223 225 diff diff repository (or selected files)
224 226 export dump the header and diffs for one or more changesets
225 227 init create a new repository in the given directory
226 228 log show revision history of entire repository or files
227 229 parents show the parents of the working dir or revision
228 230 pull pull changes from the specified source
229 231 push push changes to the specified destination
230 232 remove remove the specified files on the next commit
231 233 revert revert files or dirs to their states as of some revision
232 234 serve export the repository via HTTP
233 235 status show changed files in the working directory
234 236 update update or merge working directory
235 237 hg: unknown command 'skjdfks'
236 238 Mercurial Distributed SCM
237 239
238 240 basic commands (use "hg help" for the full list or option "-v" for details):
239 241
240 242 add add the specified files on the next commit
241 243 annotate show changeset information per file line
242 244 clone make a copy of an existing repository
243 245 commit commit the specified files or all outstanding changes
244 246 diff diff repository (or selected files)
245 247 export dump the header and diffs for one or more changesets
246 248 init create a new repository in the given directory
247 249 log show revision history of entire repository or files
248 250 parents show the parents of the working dir or revision
249 251 pull pull changes from the specified source
250 252 push push changes to the specified destination
251 253 remove remove the specified files on the next commit
252 254 revert revert files or dirs to their states as of some revision
253 255 serve export the repository via HTTP
254 256 status show changed files in the working directory
255 257 update update or merge working directory
General Comments 0
You need to be logged in to leave comments. Login now