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