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