##// END OF EJS Templates
hg: use capable instead of capabilities
Peter Arrenbrecht -
r14552:3417954c default
parent child Browse files
Show More
@@ -1,559 +1,559 b''
1 1 # hg.py - repository classes for mercurial
2 2 #
3 3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 4 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
5 5 #
6 6 # This software may be used and distributed according to the terms of the
7 7 # GNU General Public License version 2 or any later version.
8 8
9 9 from i18n import _
10 10 from lock import release
11 11 from node import hex, nullid
12 12 import localrepo, bundlerepo, httprepo, sshrepo, statichttprepo, bookmarks
13 13 import lock, util, extensions, error, node
14 14 import cmdutil, discovery
15 15 import merge as mergemod
16 16 import verify as verifymod
17 17 import errno, os, shutil
18 18
19 19 def _local(path):
20 20 path = util.expandpath(util.localpath(path))
21 21 return (os.path.isfile(path) and bundlerepo or localrepo)
22 22
23 23 def addbranchrevs(lrepo, repo, branches, revs):
24 24 hashbranch, branches = branches
25 25 if not hashbranch and not branches:
26 26 return revs or None, revs and revs[0] or None
27 27 revs = revs and list(revs) or []
28 28 if not repo.capable('branchmap'):
29 29 if branches:
30 30 raise util.Abort(_("remote branch lookup not supported"))
31 31 revs.append(hashbranch)
32 32 return revs, revs[0]
33 33 branchmap = repo.branchmap()
34 34
35 35 def primary(branch):
36 36 if branch == '.':
37 37 if not lrepo or not lrepo.local():
38 38 raise util.Abort(_("dirstate branch not accessible"))
39 39 branch = lrepo.dirstate.branch()
40 40 if branch in branchmap:
41 41 revs.extend(node.hex(r) for r in reversed(branchmap[branch]))
42 42 return True
43 43 else:
44 44 return False
45 45
46 46 for branch in branches:
47 47 if not primary(branch):
48 48 raise error.RepoLookupError(_("unknown branch '%s'") % branch)
49 49 if hashbranch:
50 50 if not primary(hashbranch):
51 51 revs.append(hashbranch)
52 52 return revs, revs[0]
53 53
54 54 def parseurl(path, branches=None):
55 55 '''parse url#branch, returning (url, (branch, branches))'''
56 56
57 57 u = util.url(path)
58 58 branch = None
59 59 if u.fragment:
60 60 branch = u.fragment
61 61 u.fragment = None
62 62 return str(u), (branch, branches or [])
63 63
64 64 schemes = {
65 65 'bundle': bundlerepo,
66 66 'file': _local,
67 67 'http': httprepo,
68 68 'https': httprepo,
69 69 'ssh': sshrepo,
70 70 'static-http': statichttprepo,
71 71 }
72 72
73 73 def _lookup(path):
74 74 u = util.url(path)
75 75 scheme = u.scheme or 'file'
76 76 thing = schemes.get(scheme) or schemes['file']
77 77 try:
78 78 return thing(path)
79 79 except TypeError:
80 80 return thing
81 81
82 82 def islocal(repo):
83 83 '''return true if repo or path is local'''
84 84 if isinstance(repo, str):
85 85 try:
86 86 return _lookup(repo).islocal(repo)
87 87 except AttributeError:
88 88 return False
89 89 return repo.local()
90 90
91 91 def repository(ui, path='', create=False):
92 92 """return a repository object for the specified path"""
93 93 repo = _lookup(path).instance(ui, path, create)
94 94 ui = getattr(repo, "ui", ui)
95 95 for name, module in extensions.extensions():
96 96 hook = getattr(module, 'reposetup', None)
97 97 if hook:
98 98 hook(ui, repo)
99 99 return repo
100 100
101 101 def defaultdest(source):
102 102 '''return default destination of clone if none is given'''
103 103 return os.path.basename(os.path.normpath(source))
104 104
105 105 def share(ui, source, dest=None, update=True):
106 106 '''create a shared repository'''
107 107
108 108 if not islocal(source):
109 109 raise util.Abort(_('can only share local repositories'))
110 110
111 111 if not dest:
112 112 dest = defaultdest(source)
113 113 else:
114 114 dest = ui.expandpath(dest)
115 115
116 116 if isinstance(source, str):
117 117 origsource = ui.expandpath(source)
118 118 source, branches = parseurl(origsource)
119 119 srcrepo = repository(ui, source)
120 120 rev, checkout = addbranchrevs(srcrepo, srcrepo, branches, None)
121 121 else:
122 122 srcrepo = source
123 123 origsource = source = srcrepo.url()
124 124 checkout = None
125 125
126 126 sharedpath = srcrepo.sharedpath # if our source is already sharing
127 127
128 128 root = os.path.realpath(dest)
129 129 roothg = os.path.join(root, '.hg')
130 130
131 131 if os.path.exists(roothg):
132 132 raise util.Abort(_('destination already exists'))
133 133
134 134 if not os.path.isdir(root):
135 135 os.mkdir(root)
136 136 util.makedir(roothg, notindexed=True)
137 137
138 138 requirements = ''
139 139 try:
140 140 requirements = srcrepo.opener.read('requires')
141 141 except IOError, inst:
142 142 if inst.errno != errno.ENOENT:
143 143 raise
144 144
145 145 requirements += 'shared\n'
146 146 util.writefile(os.path.join(roothg, 'requires'), requirements)
147 147 util.writefile(os.path.join(roothg, 'sharedpath'), sharedpath)
148 148
149 149 r = repository(ui, root)
150 150
151 151 default = srcrepo.ui.config('paths', 'default')
152 152 if default:
153 153 fp = r.opener("hgrc", "w", text=True)
154 154 fp.write("[paths]\n")
155 155 fp.write("default = %s\n" % default)
156 156 fp.close()
157 157
158 158 if update:
159 159 r.ui.status(_("updating working directory\n"))
160 160 if update is not True:
161 161 checkout = update
162 162 for test in (checkout, 'default', 'tip'):
163 163 if test is None:
164 164 continue
165 165 try:
166 166 uprev = r.lookup(test)
167 167 break
168 168 except error.RepoLookupError:
169 169 continue
170 170 _update(r, uprev)
171 171
172 172 def clone(ui, source, dest=None, pull=False, rev=None, update=True,
173 173 stream=False, branch=None):
174 174 """Make a copy of an existing repository.
175 175
176 176 Create a copy of an existing repository in a new directory. The
177 177 source and destination are URLs, as passed to the repository
178 178 function. Returns a pair of repository objects, the source and
179 179 newly created destination.
180 180
181 181 The location of the source is added to the new repository's
182 182 .hg/hgrc file, as the default to be used for future pulls and
183 183 pushes.
184 184
185 185 If an exception is raised, the partly cloned/updated destination
186 186 repository will be deleted.
187 187
188 188 Arguments:
189 189
190 190 source: repository object or URL
191 191
192 192 dest: URL of destination repository to create (defaults to base
193 193 name of source repository)
194 194
195 195 pull: always pull from source repository, even in local case
196 196
197 197 stream: stream raw data uncompressed from repository (fast over
198 198 LAN, slow over WAN)
199 199
200 200 rev: revision to clone up to (implies pull=True)
201 201
202 202 update: update working directory after clone completes, if
203 203 destination is local repository (True means update to default rev,
204 204 anything else is treated as a revision)
205 205
206 206 branch: branches to clone
207 207 """
208 208
209 209 if isinstance(source, str):
210 210 origsource = ui.expandpath(source)
211 211 source, branch = parseurl(origsource, branch)
212 212 srcrepo = repository(ui, source)
213 213 else:
214 214 srcrepo = source
215 215 branch = (None, branch or [])
216 216 origsource = source = srcrepo.url()
217 217 rev, checkout = addbranchrevs(srcrepo, srcrepo, branch, rev)
218 218
219 219 if dest is None:
220 220 dest = defaultdest(source)
221 221 ui.status(_("destination directory: %s\n") % dest)
222 222 else:
223 223 dest = ui.expandpath(dest)
224 224
225 225 dest = util.localpath(dest)
226 226 source = util.localpath(source)
227 227
228 228 if os.path.exists(dest):
229 229 if not os.path.isdir(dest):
230 230 raise util.Abort(_("destination '%s' already exists") % dest)
231 231 elif os.listdir(dest):
232 232 raise util.Abort(_("destination '%s' is not empty") % dest)
233 233
234 234 class DirCleanup(object):
235 235 def __init__(self, dir_):
236 236 self.rmtree = shutil.rmtree
237 237 self.dir_ = dir_
238 238 def close(self):
239 239 self.dir_ = None
240 240 def cleanup(self):
241 241 if self.dir_:
242 242 self.rmtree(self.dir_, True)
243 243
244 244 srclock = destlock = dircleanup = None
245 245 try:
246 246 abspath = origsource
247 247 if islocal(origsource):
248 248 abspath = os.path.abspath(util.localpath(origsource))
249 249
250 250 if islocal(dest):
251 251 dircleanup = DirCleanup(dest)
252 252
253 253 copy = False
254 254 if srcrepo.cancopy() and islocal(dest):
255 255 copy = not pull and not rev
256 256
257 257 if copy:
258 258 try:
259 259 # we use a lock here because if we race with commit, we
260 260 # can end up with extra data in the cloned revlogs that's
261 261 # not pointed to by changesets, thus causing verify to
262 262 # fail
263 263 srclock = srcrepo.lock(wait=False)
264 264 except error.LockError:
265 265 copy = False
266 266
267 267 if copy:
268 268 srcrepo.hook('preoutgoing', throw=True, source='clone')
269 269 hgdir = os.path.realpath(os.path.join(dest, ".hg"))
270 270 if not os.path.exists(dest):
271 271 os.mkdir(dest)
272 272 else:
273 273 # only clean up directories we create ourselves
274 274 dircleanup.dir_ = hgdir
275 275 try:
276 276 destpath = hgdir
277 277 util.makedir(destpath, notindexed=True)
278 278 except OSError, inst:
279 279 if inst.errno == errno.EEXIST:
280 280 dircleanup.close()
281 281 raise util.Abort(_("destination '%s' already exists")
282 282 % dest)
283 283 raise
284 284
285 285 hardlink = None
286 286 num = 0
287 287 for f in srcrepo.store.copylist():
288 288 src = os.path.join(srcrepo.sharedpath, f)
289 289 dst = os.path.join(destpath, f)
290 290 dstbase = os.path.dirname(dst)
291 291 if dstbase and not os.path.exists(dstbase):
292 292 os.mkdir(dstbase)
293 293 if os.path.exists(src):
294 294 if dst.endswith('data'):
295 295 # lock to avoid premature writing to the target
296 296 destlock = lock.lock(os.path.join(dstbase, "lock"))
297 297 hardlink, n = util.copyfiles(src, dst, hardlink)
298 298 num += n
299 299 if hardlink:
300 300 ui.debug("linked %d files\n" % num)
301 301 else:
302 302 ui.debug("copied %d files\n" % num)
303 303
304 304 # we need to re-init the repo after manually copying the data
305 305 # into it
306 306 destrepo = repository(ui, dest)
307 307 srcrepo.hook('outgoing', source='clone',
308 308 node=node.hex(node.nullid))
309 309 else:
310 310 try:
311 311 destrepo = repository(ui, dest, create=True)
312 312 except OSError, inst:
313 313 if inst.errno == errno.EEXIST:
314 314 dircleanup.close()
315 315 raise util.Abort(_("destination '%s' already exists")
316 316 % dest)
317 317 raise
318 318
319 319 revs = None
320 320 if rev:
321 if 'lookup' not in srcrepo.capabilities:
321 if not srcrepo.capable('lookup'):
322 322 raise util.Abort(_("src repository does not support "
323 323 "revision lookup and so doesn't "
324 324 "support clone by revision"))
325 325 revs = [srcrepo.lookup(r) for r in rev]
326 326 checkout = revs[0]
327 327 if destrepo.local():
328 328 destrepo.clone(srcrepo, heads=revs, stream=stream)
329 329 elif srcrepo.local():
330 330 srcrepo.push(destrepo, revs=revs)
331 331 else:
332 332 raise util.Abort(_("clone from remote to remote not supported"))
333 333
334 334 if dircleanup:
335 335 dircleanup.close()
336 336
337 337 if destrepo.local():
338 338 fp = destrepo.opener("hgrc", "w", text=True)
339 339 fp.write("[paths]\n")
340 340 fp.write("default = %s\n" % abspath)
341 341 fp.close()
342 342
343 343 destrepo.ui.setconfig('paths', 'default', abspath)
344 344
345 345 if update:
346 346 if update is not True:
347 347 checkout = update
348 348 if srcrepo.local():
349 349 checkout = srcrepo.lookup(update)
350 350 for test in (checkout, 'default', 'tip'):
351 351 if test is None:
352 352 continue
353 353 try:
354 354 uprev = destrepo.lookup(test)
355 355 break
356 356 except error.RepoLookupError:
357 357 continue
358 358 bn = destrepo[uprev].branch()
359 359 destrepo.ui.status(_("updating to branch %s\n") % bn)
360 360 _update(destrepo, uprev)
361 361
362 362 # clone all bookmarks
363 363 if destrepo.local() and srcrepo.capable("pushkey"):
364 364 rb = srcrepo.listkeys('bookmarks')
365 365 for k, n in rb.iteritems():
366 366 try:
367 367 m = destrepo.lookup(n)
368 368 destrepo._bookmarks[k] = m
369 369 except error.RepoLookupError:
370 370 pass
371 371 if rb:
372 372 bookmarks.write(destrepo)
373 373 elif srcrepo.local() and destrepo.capable("pushkey"):
374 374 for k, n in srcrepo._bookmarks.iteritems():
375 375 destrepo.pushkey('bookmarks', k, '', hex(n))
376 376
377 377 return srcrepo, destrepo
378 378 finally:
379 379 release(srclock, destlock)
380 380 if dircleanup is not None:
381 381 dircleanup.cleanup()
382 382
383 383 def _showstats(repo, stats):
384 384 repo.ui.status(_("%d files updated, %d files merged, "
385 385 "%d files removed, %d files unresolved\n") % stats)
386 386
387 387 def update(repo, node):
388 388 """update the working directory to node, merging linear changes"""
389 389 stats = mergemod.update(repo, node, False, False, None)
390 390 _showstats(repo, stats)
391 391 if stats[3]:
392 392 repo.ui.status(_("use 'hg resolve' to retry unresolved file merges\n"))
393 393 return stats[3] > 0
394 394
395 395 # naming conflict in clone()
396 396 _update = update
397 397
398 398 def clean(repo, node, show_stats=True):
399 399 """forcibly switch the working directory to node, clobbering changes"""
400 400 stats = mergemod.update(repo, node, False, True, None)
401 401 if show_stats:
402 402 _showstats(repo, stats)
403 403 return stats[3] > 0
404 404
405 405 def merge(repo, node, force=None, remind=True):
406 406 """Branch merge with node, resolving changes. Return true if any
407 407 unresolved conflicts."""
408 408 stats = mergemod.update(repo, node, True, force, False)
409 409 _showstats(repo, stats)
410 410 if stats[3]:
411 411 repo.ui.status(_("use 'hg resolve' to retry unresolved file merges "
412 412 "or 'hg update -C .' to abandon\n"))
413 413 elif remind:
414 414 repo.ui.status(_("(branch merge, don't forget to commit)\n"))
415 415 return stats[3] > 0
416 416
417 417 def _incoming(displaychlist, subreporecurse, ui, repo, source,
418 418 opts, buffered=False):
419 419 """
420 420 Helper for incoming / gincoming.
421 421 displaychlist gets called with
422 422 (remoterepo, incomingchangesetlist, displayer) parameters,
423 423 and is supposed to contain only code that can't be unified.
424 424 """
425 425 source, branches = parseurl(ui.expandpath(source), opts.get('branch'))
426 426 other = repository(remoteui(repo, opts), source)
427 427 ui.status(_('comparing with %s\n') % util.hidepassword(source))
428 428 revs, checkout = addbranchrevs(repo, other, branches, opts.get('rev'))
429 429
430 430 if revs:
431 431 revs = [other.lookup(rev) for rev in revs]
432 432 other, chlist, cleanupfn = bundlerepo.getremotechanges(ui, repo, other,
433 433 revs, opts["bundle"], opts["force"])
434 434 try:
435 435 if not chlist:
436 436 ui.status(_("no changes found\n"))
437 437 return subreporecurse()
438 438
439 439 displayer = cmdutil.show_changeset(ui, other, opts, buffered)
440 440
441 441 # XXX once graphlog extension makes it into core,
442 442 # should be replaced by a if graph/else
443 443 displaychlist(other, chlist, displayer)
444 444
445 445 displayer.close()
446 446 finally:
447 447 cleanupfn()
448 448 subreporecurse()
449 449 return 0 # exit code is zero since we found incoming changes
450 450
451 451 def incoming(ui, repo, source, opts):
452 452 def subreporecurse():
453 453 ret = 1
454 454 if opts.get('subrepos'):
455 455 ctx = repo[None]
456 456 for subpath in sorted(ctx.substate):
457 457 sub = ctx.sub(subpath)
458 458 ret = min(ret, sub.incoming(ui, source, opts))
459 459 return ret
460 460
461 461 def display(other, chlist, displayer):
462 462 limit = cmdutil.loglimit(opts)
463 463 if opts.get('newest_first'):
464 464 chlist.reverse()
465 465 count = 0
466 466 for n in chlist:
467 467 if limit is not None and count >= limit:
468 468 break
469 469 parents = [p for p in other.changelog.parents(n) if p != nullid]
470 470 if opts.get('no_merges') and len(parents) == 2:
471 471 continue
472 472 count += 1
473 473 displayer.show(other[n])
474 474 return _incoming(display, subreporecurse, ui, repo, source, opts)
475 475
476 476 def _outgoing(ui, repo, dest, opts):
477 477 dest = ui.expandpath(dest or 'default-push', dest or 'default')
478 478 dest, branches = parseurl(dest, opts.get('branch'))
479 479 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
480 480 revs, checkout = addbranchrevs(repo, repo, branches, opts.get('rev'))
481 481 if revs:
482 482 revs = [repo.lookup(rev) for rev in revs]
483 483
484 484 other = repository(remoteui(repo, opts), dest)
485 485 common, outheads = discovery.findcommonoutgoing(repo, other, revs,
486 486 force=opts.get('force'))
487 487 o = repo.changelog.findmissing(common, outheads)
488 488 if not o:
489 489 ui.status(_("no changes found\n"))
490 490 return None
491 491 return o
492 492
493 493 def outgoing(ui, repo, dest, opts):
494 494 def recurse():
495 495 ret = 1
496 496 if opts.get('subrepos'):
497 497 ctx = repo[None]
498 498 for subpath in sorted(ctx.substate):
499 499 sub = ctx.sub(subpath)
500 500 ret = min(ret, sub.outgoing(ui, dest, opts))
501 501 return ret
502 502
503 503 limit = cmdutil.loglimit(opts)
504 504 o = _outgoing(ui, repo, dest, opts)
505 505 if o is None:
506 506 return recurse()
507 507
508 508 if opts.get('newest_first'):
509 509 o.reverse()
510 510 displayer = cmdutil.show_changeset(ui, repo, opts)
511 511 count = 0
512 512 for n in o:
513 513 if limit is not None and count >= limit:
514 514 break
515 515 parents = [p for p in repo.changelog.parents(n) if p != nullid]
516 516 if opts.get('no_merges') and len(parents) == 2:
517 517 continue
518 518 count += 1
519 519 displayer.show(repo[n])
520 520 displayer.close()
521 521 recurse()
522 522 return 0 # exit code is zero since we found outgoing changes
523 523
524 524 def revert(repo, node, choose):
525 525 """revert changes to revision in node without updating dirstate"""
526 526 return mergemod.update(repo, node, False, True, choose)[3] > 0
527 527
528 528 def verify(repo):
529 529 """verify the consistency of a repository"""
530 530 return verifymod.verify(repo)
531 531
532 532 def remoteui(src, opts):
533 533 'build a remote ui from ui or repo and opts'
534 534 if hasattr(src, 'baseui'): # looks like a repository
535 535 dst = src.baseui.copy() # drop repo-specific config
536 536 src = src.ui # copy target options from repo
537 537 else: # assume it's a global ui object
538 538 dst = src.copy() # keep all global options
539 539
540 540 # copy ssh-specific options
541 541 for o in 'ssh', 'remotecmd':
542 542 v = opts.get(o) or src.config('ui', o)
543 543 if v:
544 544 dst.setconfig("ui", o, v)
545 545
546 546 # copy bundle-specific options
547 547 r = src.config('bundle', 'mainreporoot')
548 548 if r:
549 549 dst.setconfig('bundle', 'mainreporoot', r)
550 550
551 551 # copy selected local settings to the remote ui
552 552 for sect in ('auth', 'hostfingerprints', 'http_proxy'):
553 553 for key, val in src.configitems(sect):
554 554 dst.setconfig(sect, key, val)
555 555 v = src.config('web', 'cacerts')
556 556 if v:
557 557 dst.setconfig('web', 'cacerts', util.expandpath(v))
558 558
559 559 return dst
General Comments 0
You need to be logged in to leave comments. Login now