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