##// END OF EJS Templates
hg-mod: directly use repo.vfs.join...
Pierre-Yves David -
r31322:4282feb7 default
parent child Browse files
Show More
@@ -1,1056 +1,1056 b''
1 # hg.py - repository classes for mercurial
1 # hg.py - repository classes for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
4 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version.
7 # GNU General Public License version 2 or any later version.
8
8
9 from __future__ import absolute_import
9 from __future__ import absolute_import
10
10
11 import errno
11 import errno
12 import hashlib
12 import hashlib
13 import os
13 import os
14 import shutil
14 import shutil
15
15
16 from .i18n import _
16 from .i18n import _
17 from .node import nullid
17 from .node import nullid
18
18
19 from . import (
19 from . import (
20 bookmarks,
20 bookmarks,
21 bundlerepo,
21 bundlerepo,
22 cmdutil,
22 cmdutil,
23 destutil,
23 destutil,
24 discovery,
24 discovery,
25 error,
25 error,
26 exchange,
26 exchange,
27 extensions,
27 extensions,
28 httppeer,
28 httppeer,
29 localrepo,
29 localrepo,
30 lock,
30 lock,
31 merge as mergemod,
31 merge as mergemod,
32 node,
32 node,
33 phases,
33 phases,
34 repoview,
34 repoview,
35 scmutil,
35 scmutil,
36 sshpeer,
36 sshpeer,
37 statichttprepo,
37 statichttprepo,
38 ui as uimod,
38 ui as uimod,
39 unionrepo,
39 unionrepo,
40 url,
40 url,
41 util,
41 util,
42 verify as verifymod,
42 verify as verifymod,
43 vfs as vfsmod,
43 vfs as vfsmod,
44 )
44 )
45
45
46 release = lock.release
46 release = lock.release
47
47
48 # shared features
48 # shared features
49 sharedbookmarks = 'bookmarks'
49 sharedbookmarks = 'bookmarks'
50
50
51 def _local(path):
51 def _local(path):
52 path = util.expandpath(util.urllocalpath(path))
52 path = util.expandpath(util.urllocalpath(path))
53 return (os.path.isfile(path) and bundlerepo or localrepo)
53 return (os.path.isfile(path) and bundlerepo or localrepo)
54
54
55 def addbranchrevs(lrepo, other, branches, revs):
55 def addbranchrevs(lrepo, other, branches, revs):
56 peer = other.peer() # a courtesy to callers using a localrepo for other
56 peer = other.peer() # a courtesy to callers using a localrepo for other
57 hashbranch, branches = branches
57 hashbranch, branches = branches
58 if not hashbranch and not branches:
58 if not hashbranch and not branches:
59 x = revs or None
59 x = revs or None
60 if util.safehasattr(revs, 'first'):
60 if util.safehasattr(revs, 'first'):
61 y = revs.first()
61 y = revs.first()
62 elif revs:
62 elif revs:
63 y = revs[0]
63 y = revs[0]
64 else:
64 else:
65 y = None
65 y = None
66 return x, y
66 return x, y
67 if revs:
67 if revs:
68 revs = list(revs)
68 revs = list(revs)
69 else:
69 else:
70 revs = []
70 revs = []
71
71
72 if not peer.capable('branchmap'):
72 if not peer.capable('branchmap'):
73 if branches:
73 if branches:
74 raise error.Abort(_("remote branch lookup not supported"))
74 raise error.Abort(_("remote branch lookup not supported"))
75 revs.append(hashbranch)
75 revs.append(hashbranch)
76 return revs, revs[0]
76 return revs, revs[0]
77 branchmap = peer.branchmap()
77 branchmap = peer.branchmap()
78
78
79 def primary(branch):
79 def primary(branch):
80 if branch == '.':
80 if branch == '.':
81 if not lrepo:
81 if not lrepo:
82 raise error.Abort(_("dirstate branch not accessible"))
82 raise error.Abort(_("dirstate branch not accessible"))
83 branch = lrepo.dirstate.branch()
83 branch = lrepo.dirstate.branch()
84 if branch in branchmap:
84 if branch in branchmap:
85 revs.extend(node.hex(r) for r in reversed(branchmap[branch]))
85 revs.extend(node.hex(r) for r in reversed(branchmap[branch]))
86 return True
86 return True
87 else:
87 else:
88 return False
88 return False
89
89
90 for branch in branches:
90 for branch in branches:
91 if not primary(branch):
91 if not primary(branch):
92 raise error.RepoLookupError(_("unknown branch '%s'") % branch)
92 raise error.RepoLookupError(_("unknown branch '%s'") % branch)
93 if hashbranch:
93 if hashbranch:
94 if not primary(hashbranch):
94 if not primary(hashbranch):
95 revs.append(hashbranch)
95 revs.append(hashbranch)
96 return revs, revs[0]
96 return revs, revs[0]
97
97
98 def parseurl(path, branches=None):
98 def parseurl(path, branches=None):
99 '''parse url#branch, returning (url, (branch, branches))'''
99 '''parse url#branch, returning (url, (branch, branches))'''
100
100
101 u = util.url(path)
101 u = util.url(path)
102 branch = None
102 branch = None
103 if u.fragment:
103 if u.fragment:
104 branch = u.fragment
104 branch = u.fragment
105 u.fragment = None
105 u.fragment = None
106 return str(u), (branch, branches or [])
106 return str(u), (branch, branches or [])
107
107
108 schemes = {
108 schemes = {
109 'bundle': bundlerepo,
109 'bundle': bundlerepo,
110 'union': unionrepo,
110 'union': unionrepo,
111 'file': _local,
111 'file': _local,
112 'http': httppeer,
112 'http': httppeer,
113 'https': httppeer,
113 'https': httppeer,
114 'ssh': sshpeer,
114 'ssh': sshpeer,
115 'static-http': statichttprepo,
115 'static-http': statichttprepo,
116 }
116 }
117
117
118 def _peerlookup(path):
118 def _peerlookup(path):
119 u = util.url(path)
119 u = util.url(path)
120 scheme = u.scheme or 'file'
120 scheme = u.scheme or 'file'
121 thing = schemes.get(scheme) or schemes['file']
121 thing = schemes.get(scheme) or schemes['file']
122 try:
122 try:
123 return thing(path)
123 return thing(path)
124 except TypeError:
124 except TypeError:
125 # we can't test callable(thing) because 'thing' can be an unloaded
125 # we can't test callable(thing) because 'thing' can be an unloaded
126 # module that implements __call__
126 # module that implements __call__
127 if not util.safehasattr(thing, 'instance'):
127 if not util.safehasattr(thing, 'instance'):
128 raise
128 raise
129 return thing
129 return thing
130
130
131 def islocal(repo):
131 def islocal(repo):
132 '''return true if repo (or path pointing to repo) is local'''
132 '''return true if repo (or path pointing to repo) is local'''
133 if isinstance(repo, str):
133 if isinstance(repo, str):
134 try:
134 try:
135 return _peerlookup(repo).islocal(repo)
135 return _peerlookup(repo).islocal(repo)
136 except AttributeError:
136 except AttributeError:
137 return False
137 return False
138 return repo.local()
138 return repo.local()
139
139
140 def openpath(ui, path):
140 def openpath(ui, path):
141 '''open path with open if local, url.open if remote'''
141 '''open path with open if local, url.open if remote'''
142 pathurl = util.url(path, parsequery=False, parsefragment=False)
142 pathurl = util.url(path, parsequery=False, parsefragment=False)
143 if pathurl.islocal():
143 if pathurl.islocal():
144 return util.posixfile(pathurl.localpath(), 'rb')
144 return util.posixfile(pathurl.localpath(), 'rb')
145 else:
145 else:
146 return url.open(ui, path)
146 return url.open(ui, path)
147
147
148 # a list of (ui, repo) functions called for wire peer initialization
148 # a list of (ui, repo) functions called for wire peer initialization
149 wirepeersetupfuncs = []
149 wirepeersetupfuncs = []
150
150
151 def _peerorrepo(ui, path, create=False):
151 def _peerorrepo(ui, path, create=False):
152 """return a repository object for the specified path"""
152 """return a repository object for the specified path"""
153 obj = _peerlookup(path).instance(ui, path, create)
153 obj = _peerlookup(path).instance(ui, path, create)
154 ui = getattr(obj, "ui", ui)
154 ui = getattr(obj, "ui", ui)
155 for name, module in extensions.extensions(ui):
155 for name, module in extensions.extensions(ui):
156 hook = getattr(module, 'reposetup', None)
156 hook = getattr(module, 'reposetup', None)
157 if hook:
157 if hook:
158 hook(ui, obj)
158 hook(ui, obj)
159 if not obj.local():
159 if not obj.local():
160 for f in wirepeersetupfuncs:
160 for f in wirepeersetupfuncs:
161 f(ui, obj)
161 f(ui, obj)
162 return obj
162 return obj
163
163
164 def repository(ui, path='', create=False):
164 def repository(ui, path='', create=False):
165 """return a repository object for the specified path"""
165 """return a repository object for the specified path"""
166 peer = _peerorrepo(ui, path, create)
166 peer = _peerorrepo(ui, path, create)
167 repo = peer.local()
167 repo = peer.local()
168 if not repo:
168 if not repo:
169 raise error.Abort(_("repository '%s' is not local") %
169 raise error.Abort(_("repository '%s' is not local") %
170 (path or peer.url()))
170 (path or peer.url()))
171 return repo.filtered('visible')
171 return repo.filtered('visible')
172
172
173 def peer(uiorrepo, opts, path, create=False):
173 def peer(uiorrepo, opts, path, create=False):
174 '''return a repository peer for the specified path'''
174 '''return a repository peer for the specified path'''
175 rui = remoteui(uiorrepo, opts)
175 rui = remoteui(uiorrepo, opts)
176 return _peerorrepo(rui, path, create).peer()
176 return _peerorrepo(rui, path, create).peer()
177
177
178 def defaultdest(source):
178 def defaultdest(source):
179 '''return default destination of clone if none is given
179 '''return default destination of clone if none is given
180
180
181 >>> defaultdest('foo')
181 >>> defaultdest('foo')
182 'foo'
182 'foo'
183 >>> defaultdest('/foo/bar')
183 >>> defaultdest('/foo/bar')
184 'bar'
184 'bar'
185 >>> defaultdest('/')
185 >>> defaultdest('/')
186 ''
186 ''
187 >>> defaultdest('')
187 >>> defaultdest('')
188 ''
188 ''
189 >>> defaultdest('http://example.org/')
189 >>> defaultdest('http://example.org/')
190 ''
190 ''
191 >>> defaultdest('http://example.org/foo/')
191 >>> defaultdest('http://example.org/foo/')
192 'foo'
192 'foo'
193 '''
193 '''
194 path = util.url(source).path
194 path = util.url(source).path
195 if not path:
195 if not path:
196 return ''
196 return ''
197 return os.path.basename(os.path.normpath(path))
197 return os.path.basename(os.path.normpath(path))
198
198
199 def share(ui, source, dest=None, update=True, bookmarks=True, defaultpath=None,
199 def share(ui, source, dest=None, update=True, bookmarks=True, defaultpath=None,
200 relative=False):
200 relative=False):
201 '''create a shared repository'''
201 '''create a shared repository'''
202
202
203 if not islocal(source):
203 if not islocal(source):
204 raise error.Abort(_('can only share local repositories'))
204 raise error.Abort(_('can only share local repositories'))
205
205
206 if not dest:
206 if not dest:
207 dest = defaultdest(source)
207 dest = defaultdest(source)
208 else:
208 else:
209 dest = ui.expandpath(dest)
209 dest = ui.expandpath(dest)
210
210
211 if isinstance(source, str):
211 if isinstance(source, str):
212 origsource = ui.expandpath(source)
212 origsource = ui.expandpath(source)
213 source, branches = parseurl(origsource)
213 source, branches = parseurl(origsource)
214 srcrepo = repository(ui, source)
214 srcrepo = repository(ui, source)
215 rev, checkout = addbranchrevs(srcrepo, srcrepo, branches, None)
215 rev, checkout = addbranchrevs(srcrepo, srcrepo, branches, None)
216 else:
216 else:
217 srcrepo = source.local()
217 srcrepo = source.local()
218 origsource = source = srcrepo.url()
218 origsource = source = srcrepo.url()
219 checkout = None
219 checkout = None
220
220
221 sharedpath = srcrepo.sharedpath # if our source is already sharing
221 sharedpath = srcrepo.sharedpath # if our source is already sharing
222
222
223 destwvfs = vfsmod.vfs(dest, realpath=True)
223 destwvfs = vfsmod.vfs(dest, realpath=True)
224 destvfs = vfsmod.vfs(os.path.join(destwvfs.base, '.hg'), realpath=True)
224 destvfs = vfsmod.vfs(os.path.join(destwvfs.base, '.hg'), realpath=True)
225
225
226 if destvfs.lexists():
226 if destvfs.lexists():
227 raise error.Abort(_('destination already exists'))
227 raise error.Abort(_('destination already exists'))
228
228
229 if not destwvfs.isdir():
229 if not destwvfs.isdir():
230 destwvfs.mkdir()
230 destwvfs.mkdir()
231 destvfs.makedir()
231 destvfs.makedir()
232
232
233 requirements = ''
233 requirements = ''
234 try:
234 try:
235 requirements = srcrepo.vfs.read('requires')
235 requirements = srcrepo.vfs.read('requires')
236 except IOError as inst:
236 except IOError as inst:
237 if inst.errno != errno.ENOENT:
237 if inst.errno != errno.ENOENT:
238 raise
238 raise
239
239
240 if relative:
240 if relative:
241 try:
241 try:
242 sharedpath = os.path.relpath(sharedpath, destvfs.base)
242 sharedpath = os.path.relpath(sharedpath, destvfs.base)
243 requirements += 'relshared\n'
243 requirements += 'relshared\n'
244 except IOError as e:
244 except IOError as e:
245 raise error.Abort(_('cannot calculate relative path'),
245 raise error.Abort(_('cannot calculate relative path'),
246 hint=str(e))
246 hint=str(e))
247 else:
247 else:
248 requirements += 'shared\n'
248 requirements += 'shared\n'
249
249
250 destvfs.write('requires', requirements)
250 destvfs.write('requires', requirements)
251 destvfs.write('sharedpath', sharedpath)
251 destvfs.write('sharedpath', sharedpath)
252
252
253 r = repository(ui, destwvfs.base)
253 r = repository(ui, destwvfs.base)
254 postshare(srcrepo, r, bookmarks=bookmarks, defaultpath=defaultpath)
254 postshare(srcrepo, r, bookmarks=bookmarks, defaultpath=defaultpath)
255 _postshareupdate(r, update, checkout=checkout)
255 _postshareupdate(r, update, checkout=checkout)
256
256
257 def postshare(sourcerepo, destrepo, bookmarks=True, defaultpath=None):
257 def postshare(sourcerepo, destrepo, bookmarks=True, defaultpath=None):
258 """Called after a new shared repo is created.
258 """Called after a new shared repo is created.
259
259
260 The new repo only has a requirements file and pointer to the source.
260 The new repo only has a requirements file and pointer to the source.
261 This function configures additional shared data.
261 This function configures additional shared data.
262
262
263 Extensions can wrap this function and write additional entries to
263 Extensions can wrap this function and write additional entries to
264 destrepo/.hg/shared to indicate additional pieces of data to be shared.
264 destrepo/.hg/shared to indicate additional pieces of data to be shared.
265 """
265 """
266 default = defaultpath or sourcerepo.ui.config('paths', 'default')
266 default = defaultpath or sourcerepo.ui.config('paths', 'default')
267 if default:
267 if default:
268 fp = destrepo.vfs("hgrc", "w", text=True)
268 fp = destrepo.vfs("hgrc", "w", text=True)
269 fp.write("[paths]\n")
269 fp.write("[paths]\n")
270 fp.write("default = %s\n" % default)
270 fp.write("default = %s\n" % default)
271 fp.close()
271 fp.close()
272
272
273 with destrepo.wlock():
273 with destrepo.wlock():
274 if bookmarks:
274 if bookmarks:
275 fp = destrepo.vfs('shared', 'w')
275 fp = destrepo.vfs('shared', 'w')
276 fp.write(sharedbookmarks + '\n')
276 fp.write(sharedbookmarks + '\n')
277 fp.close()
277 fp.close()
278
278
279 def _postshareupdate(repo, update, checkout=None):
279 def _postshareupdate(repo, update, checkout=None):
280 """Maybe perform a working directory update after a shared repo is created.
280 """Maybe perform a working directory update after a shared repo is created.
281
281
282 ``update`` can be a boolean or a revision to update to.
282 ``update`` can be a boolean or a revision to update to.
283 """
283 """
284 if not update:
284 if not update:
285 return
285 return
286
286
287 repo.ui.status(_("updating working directory\n"))
287 repo.ui.status(_("updating working directory\n"))
288 if update is not True:
288 if update is not True:
289 checkout = update
289 checkout = update
290 for test in (checkout, 'default', 'tip'):
290 for test in (checkout, 'default', 'tip'):
291 if test is None:
291 if test is None:
292 continue
292 continue
293 try:
293 try:
294 uprev = repo.lookup(test)
294 uprev = repo.lookup(test)
295 break
295 break
296 except error.RepoLookupError:
296 except error.RepoLookupError:
297 continue
297 continue
298 _update(repo, uprev)
298 _update(repo, uprev)
299
299
300 def copystore(ui, srcrepo, destpath):
300 def copystore(ui, srcrepo, destpath):
301 '''copy files from store of srcrepo in destpath
301 '''copy files from store of srcrepo in destpath
302
302
303 returns destlock
303 returns destlock
304 '''
304 '''
305 destlock = None
305 destlock = None
306 try:
306 try:
307 hardlink = None
307 hardlink = None
308 num = 0
308 num = 0
309 closetopic = [None]
309 closetopic = [None]
310 def prog(topic, pos):
310 def prog(topic, pos):
311 if pos is None:
311 if pos is None:
312 closetopic[0] = topic
312 closetopic[0] = topic
313 else:
313 else:
314 ui.progress(topic, pos + num)
314 ui.progress(topic, pos + num)
315 srcpublishing = srcrepo.publishing()
315 srcpublishing = srcrepo.publishing()
316 srcvfs = vfsmod.vfs(srcrepo.sharedpath)
316 srcvfs = vfsmod.vfs(srcrepo.sharedpath)
317 dstvfs = vfsmod.vfs(destpath)
317 dstvfs = vfsmod.vfs(destpath)
318 for f in srcrepo.store.copylist():
318 for f in srcrepo.store.copylist():
319 if srcpublishing and f.endswith('phaseroots'):
319 if srcpublishing and f.endswith('phaseroots'):
320 continue
320 continue
321 dstbase = os.path.dirname(f)
321 dstbase = os.path.dirname(f)
322 if dstbase and not dstvfs.exists(dstbase):
322 if dstbase and not dstvfs.exists(dstbase):
323 dstvfs.mkdir(dstbase)
323 dstvfs.mkdir(dstbase)
324 if srcvfs.exists(f):
324 if srcvfs.exists(f):
325 if f.endswith('data'):
325 if f.endswith('data'):
326 # 'dstbase' may be empty (e.g. revlog format 0)
326 # 'dstbase' may be empty (e.g. revlog format 0)
327 lockfile = os.path.join(dstbase, "lock")
327 lockfile = os.path.join(dstbase, "lock")
328 # lock to avoid premature writing to the target
328 # lock to avoid premature writing to the target
329 destlock = lock.lock(dstvfs, lockfile)
329 destlock = lock.lock(dstvfs, lockfile)
330 hardlink, n = util.copyfiles(srcvfs.join(f), dstvfs.join(f),
330 hardlink, n = util.copyfiles(srcvfs.join(f), dstvfs.join(f),
331 hardlink, progress=prog)
331 hardlink, progress=prog)
332 num += n
332 num += n
333 if hardlink:
333 if hardlink:
334 ui.debug("linked %d files\n" % num)
334 ui.debug("linked %d files\n" % num)
335 if closetopic[0]:
335 if closetopic[0]:
336 ui.progress(closetopic[0], None)
336 ui.progress(closetopic[0], None)
337 else:
337 else:
338 ui.debug("copied %d files\n" % num)
338 ui.debug("copied %d files\n" % num)
339 if closetopic[0]:
339 if closetopic[0]:
340 ui.progress(closetopic[0], None)
340 ui.progress(closetopic[0], None)
341 return destlock
341 return destlock
342 except: # re-raises
342 except: # re-raises
343 release(destlock)
343 release(destlock)
344 raise
344 raise
345
345
346 def clonewithshare(ui, peeropts, sharepath, source, srcpeer, dest, pull=False,
346 def clonewithshare(ui, peeropts, sharepath, source, srcpeer, dest, pull=False,
347 rev=None, update=True, stream=False):
347 rev=None, update=True, stream=False):
348 """Perform a clone using a shared repo.
348 """Perform a clone using a shared repo.
349
349
350 The store for the repository will be located at <sharepath>/.hg. The
350 The store for the repository will be located at <sharepath>/.hg. The
351 specified revisions will be cloned or pulled from "source". A shared repo
351 specified revisions will be cloned or pulled from "source". A shared repo
352 will be created at "dest" and a working copy will be created if "update" is
352 will be created at "dest" and a working copy will be created if "update" is
353 True.
353 True.
354 """
354 """
355 revs = None
355 revs = None
356 if rev:
356 if rev:
357 if not srcpeer.capable('lookup'):
357 if not srcpeer.capable('lookup'):
358 raise error.Abort(_("src repository does not support "
358 raise error.Abort(_("src repository does not support "
359 "revision lookup and so doesn't "
359 "revision lookup and so doesn't "
360 "support clone by revision"))
360 "support clone by revision"))
361 revs = [srcpeer.lookup(r) for r in rev]
361 revs = [srcpeer.lookup(r) for r in rev]
362
362
363 # Obtain a lock before checking for or cloning the pooled repo otherwise
363 # Obtain a lock before checking for or cloning the pooled repo otherwise
364 # 2 clients may race creating or populating it.
364 # 2 clients may race creating or populating it.
365 pooldir = os.path.dirname(sharepath)
365 pooldir = os.path.dirname(sharepath)
366 # lock class requires the directory to exist.
366 # lock class requires the directory to exist.
367 try:
367 try:
368 util.makedir(pooldir, False)
368 util.makedir(pooldir, False)
369 except OSError as e:
369 except OSError as e:
370 if e.errno != errno.EEXIST:
370 if e.errno != errno.EEXIST:
371 raise
371 raise
372
372
373 poolvfs = vfsmod.vfs(pooldir)
373 poolvfs = vfsmod.vfs(pooldir)
374 basename = os.path.basename(sharepath)
374 basename = os.path.basename(sharepath)
375
375
376 with lock.lock(poolvfs, '%s.lock' % basename):
376 with lock.lock(poolvfs, '%s.lock' % basename):
377 if os.path.exists(sharepath):
377 if os.path.exists(sharepath):
378 ui.status(_('(sharing from existing pooled repository %s)\n') %
378 ui.status(_('(sharing from existing pooled repository %s)\n') %
379 basename)
379 basename)
380 else:
380 else:
381 ui.status(_('(sharing from new pooled repository %s)\n') % basename)
381 ui.status(_('(sharing from new pooled repository %s)\n') % basename)
382 # Always use pull mode because hardlinks in share mode don't work
382 # Always use pull mode because hardlinks in share mode don't work
383 # well. Never update because working copies aren't necessary in
383 # well. Never update because working copies aren't necessary in
384 # share mode.
384 # share mode.
385 clone(ui, peeropts, source, dest=sharepath, pull=True,
385 clone(ui, peeropts, source, dest=sharepath, pull=True,
386 rev=rev, update=False, stream=stream)
386 rev=rev, update=False, stream=stream)
387
387
388 # Resolve the value to put in [paths] section for the source.
388 # Resolve the value to put in [paths] section for the source.
389 if islocal(source):
389 if islocal(source):
390 defaultpath = os.path.abspath(util.urllocalpath(source))
390 defaultpath = os.path.abspath(util.urllocalpath(source))
391 else:
391 else:
392 defaultpath = source
392 defaultpath = source
393
393
394 sharerepo = repository(ui, path=sharepath)
394 sharerepo = repository(ui, path=sharepath)
395 share(ui, sharerepo, dest=dest, update=False, bookmarks=False,
395 share(ui, sharerepo, dest=dest, update=False, bookmarks=False,
396 defaultpath=defaultpath)
396 defaultpath=defaultpath)
397
397
398 # We need to perform a pull against the dest repo to fetch bookmarks
398 # We need to perform a pull against the dest repo to fetch bookmarks
399 # and other non-store data that isn't shared by default. In the case of
399 # and other non-store data that isn't shared by default. In the case of
400 # non-existing shared repo, this means we pull from the remote twice. This
400 # non-existing shared repo, this means we pull from the remote twice. This
401 # is a bit weird. But at the time it was implemented, there wasn't an easy
401 # is a bit weird. But at the time it was implemented, there wasn't an easy
402 # way to pull just non-changegroup data.
402 # way to pull just non-changegroup data.
403 destrepo = repository(ui, path=dest)
403 destrepo = repository(ui, path=dest)
404 exchange.pull(destrepo, srcpeer, heads=revs)
404 exchange.pull(destrepo, srcpeer, heads=revs)
405
405
406 _postshareupdate(destrepo, update)
406 _postshareupdate(destrepo, update)
407
407
408 return srcpeer, peer(ui, peeropts, dest)
408 return srcpeer, peer(ui, peeropts, dest)
409
409
410 def clone(ui, peeropts, source, dest=None, pull=False, rev=None,
410 def clone(ui, peeropts, source, dest=None, pull=False, rev=None,
411 update=True, stream=False, branch=None, shareopts=None):
411 update=True, stream=False, branch=None, shareopts=None):
412 """Make a copy of an existing repository.
412 """Make a copy of an existing repository.
413
413
414 Create a copy of an existing repository in a new directory. The
414 Create a copy of an existing repository in a new directory. The
415 source and destination are URLs, as passed to the repository
415 source and destination are URLs, as passed to the repository
416 function. Returns a pair of repository peers, the source and
416 function. Returns a pair of repository peers, the source and
417 newly created destination.
417 newly created destination.
418
418
419 The location of the source is added to the new repository's
419 The location of the source is added to the new repository's
420 .hg/hgrc file, as the default to be used for future pulls and
420 .hg/hgrc file, as the default to be used for future pulls and
421 pushes.
421 pushes.
422
422
423 If an exception is raised, the partly cloned/updated destination
423 If an exception is raised, the partly cloned/updated destination
424 repository will be deleted.
424 repository will be deleted.
425
425
426 Arguments:
426 Arguments:
427
427
428 source: repository object or URL
428 source: repository object or URL
429
429
430 dest: URL of destination repository to create (defaults to base
430 dest: URL of destination repository to create (defaults to base
431 name of source repository)
431 name of source repository)
432
432
433 pull: always pull from source repository, even in local case or if the
433 pull: always pull from source repository, even in local case or if the
434 server prefers streaming
434 server prefers streaming
435
435
436 stream: stream raw data uncompressed from repository (fast over
436 stream: stream raw data uncompressed from repository (fast over
437 LAN, slow over WAN)
437 LAN, slow over WAN)
438
438
439 rev: revision to clone up to (implies pull=True)
439 rev: revision to clone up to (implies pull=True)
440
440
441 update: update working directory after clone completes, if
441 update: update working directory after clone completes, if
442 destination is local repository (True means update to default rev,
442 destination is local repository (True means update to default rev,
443 anything else is treated as a revision)
443 anything else is treated as a revision)
444
444
445 branch: branches to clone
445 branch: branches to clone
446
446
447 shareopts: dict of options to control auto sharing behavior. The "pool" key
447 shareopts: dict of options to control auto sharing behavior. The "pool" key
448 activates auto sharing mode and defines the directory for stores. The
448 activates auto sharing mode and defines the directory for stores. The
449 "mode" key determines how to construct the directory name of the shared
449 "mode" key determines how to construct the directory name of the shared
450 repository. "identity" means the name is derived from the node of the first
450 repository. "identity" means the name is derived from the node of the first
451 changeset in the repository. "remote" means the name is derived from the
451 changeset in the repository. "remote" means the name is derived from the
452 remote's path/URL. Defaults to "identity."
452 remote's path/URL. Defaults to "identity."
453 """
453 """
454
454
455 if isinstance(source, str):
455 if isinstance(source, str):
456 origsource = ui.expandpath(source)
456 origsource = ui.expandpath(source)
457 source, branch = parseurl(origsource, branch)
457 source, branch = parseurl(origsource, branch)
458 srcpeer = peer(ui, peeropts, source)
458 srcpeer = peer(ui, peeropts, source)
459 else:
459 else:
460 srcpeer = source.peer() # in case we were called with a localrepo
460 srcpeer = source.peer() # in case we were called with a localrepo
461 branch = (None, branch or [])
461 branch = (None, branch or [])
462 origsource = source = srcpeer.url()
462 origsource = source = srcpeer.url()
463 rev, checkout = addbranchrevs(srcpeer, srcpeer, branch, rev)
463 rev, checkout = addbranchrevs(srcpeer, srcpeer, branch, rev)
464
464
465 if dest is None:
465 if dest is None:
466 dest = defaultdest(source)
466 dest = defaultdest(source)
467 if dest:
467 if dest:
468 ui.status(_("destination directory: %s\n") % dest)
468 ui.status(_("destination directory: %s\n") % dest)
469 else:
469 else:
470 dest = ui.expandpath(dest)
470 dest = ui.expandpath(dest)
471
471
472 dest = util.urllocalpath(dest)
472 dest = util.urllocalpath(dest)
473 source = util.urllocalpath(source)
473 source = util.urllocalpath(source)
474
474
475 if not dest:
475 if not dest:
476 raise error.Abort(_("empty destination path is not valid"))
476 raise error.Abort(_("empty destination path is not valid"))
477
477
478 destvfs = vfsmod.vfs(dest, expandpath=True)
478 destvfs = vfsmod.vfs(dest, expandpath=True)
479 if destvfs.lexists():
479 if destvfs.lexists():
480 if not destvfs.isdir():
480 if not destvfs.isdir():
481 raise error.Abort(_("destination '%s' already exists") % dest)
481 raise error.Abort(_("destination '%s' already exists") % dest)
482 elif destvfs.listdir():
482 elif destvfs.listdir():
483 raise error.Abort(_("destination '%s' is not empty") % dest)
483 raise error.Abort(_("destination '%s' is not empty") % dest)
484
484
485 shareopts = shareopts or {}
485 shareopts = shareopts or {}
486 sharepool = shareopts.get('pool')
486 sharepool = shareopts.get('pool')
487 sharenamemode = shareopts.get('mode')
487 sharenamemode = shareopts.get('mode')
488 if sharepool and islocal(dest):
488 if sharepool and islocal(dest):
489 sharepath = None
489 sharepath = None
490 if sharenamemode == 'identity':
490 if sharenamemode == 'identity':
491 # Resolve the name from the initial changeset in the remote
491 # Resolve the name from the initial changeset in the remote
492 # repository. This returns nullid when the remote is empty. It
492 # repository. This returns nullid when the remote is empty. It
493 # raises RepoLookupError if revision 0 is filtered or otherwise
493 # raises RepoLookupError if revision 0 is filtered or otherwise
494 # not available. If we fail to resolve, sharing is not enabled.
494 # not available. If we fail to resolve, sharing is not enabled.
495 try:
495 try:
496 rootnode = srcpeer.lookup('0')
496 rootnode = srcpeer.lookup('0')
497 if rootnode != node.nullid:
497 if rootnode != node.nullid:
498 sharepath = os.path.join(sharepool, node.hex(rootnode))
498 sharepath = os.path.join(sharepool, node.hex(rootnode))
499 else:
499 else:
500 ui.status(_('(not using pooled storage: '
500 ui.status(_('(not using pooled storage: '
501 'remote appears to be empty)\n'))
501 'remote appears to be empty)\n'))
502 except error.RepoLookupError:
502 except error.RepoLookupError:
503 ui.status(_('(not using pooled storage: '
503 ui.status(_('(not using pooled storage: '
504 'unable to resolve identity of remote)\n'))
504 'unable to resolve identity of remote)\n'))
505 elif sharenamemode == 'remote':
505 elif sharenamemode == 'remote':
506 sharepath = os.path.join(
506 sharepath = os.path.join(
507 sharepool, hashlib.sha1(source).hexdigest())
507 sharepool, hashlib.sha1(source).hexdigest())
508 else:
508 else:
509 raise error.Abort(_('unknown share naming mode: %s') %
509 raise error.Abort(_('unknown share naming mode: %s') %
510 sharenamemode)
510 sharenamemode)
511
511
512 if sharepath:
512 if sharepath:
513 return clonewithshare(ui, peeropts, sharepath, source, srcpeer,
513 return clonewithshare(ui, peeropts, sharepath, source, srcpeer,
514 dest, pull=pull, rev=rev, update=update,
514 dest, pull=pull, rev=rev, update=update,
515 stream=stream)
515 stream=stream)
516
516
517 srclock = destlock = cleandir = None
517 srclock = destlock = cleandir = None
518 srcrepo = srcpeer.local()
518 srcrepo = srcpeer.local()
519 try:
519 try:
520 abspath = origsource
520 abspath = origsource
521 if islocal(origsource):
521 if islocal(origsource):
522 abspath = os.path.abspath(util.urllocalpath(origsource))
522 abspath = os.path.abspath(util.urllocalpath(origsource))
523
523
524 if islocal(dest):
524 if islocal(dest):
525 cleandir = dest
525 cleandir = dest
526
526
527 copy = False
527 copy = False
528 if (srcrepo and srcrepo.cancopy() and islocal(dest)
528 if (srcrepo and srcrepo.cancopy() and islocal(dest)
529 and not phases.hassecret(srcrepo)):
529 and not phases.hassecret(srcrepo)):
530 copy = not pull and not rev
530 copy = not pull and not rev
531
531
532 if copy:
532 if copy:
533 try:
533 try:
534 # we use a lock here because if we race with commit, we
534 # we use a lock here because if we race with commit, we
535 # can end up with extra data in the cloned revlogs that's
535 # can end up with extra data in the cloned revlogs that's
536 # not pointed to by changesets, thus causing verify to
536 # not pointed to by changesets, thus causing verify to
537 # fail
537 # fail
538 srclock = srcrepo.lock(wait=False)
538 srclock = srcrepo.lock(wait=False)
539 except error.LockError:
539 except error.LockError:
540 copy = False
540 copy = False
541
541
542 if copy:
542 if copy:
543 srcrepo.hook('preoutgoing', throw=True, source='clone')
543 srcrepo.hook('preoutgoing', throw=True, source='clone')
544 hgdir = os.path.realpath(os.path.join(dest, ".hg"))
544 hgdir = os.path.realpath(os.path.join(dest, ".hg"))
545 if not os.path.exists(dest):
545 if not os.path.exists(dest):
546 os.mkdir(dest)
546 os.mkdir(dest)
547 else:
547 else:
548 # only clean up directories we create ourselves
548 # only clean up directories we create ourselves
549 cleandir = hgdir
549 cleandir = hgdir
550 try:
550 try:
551 destpath = hgdir
551 destpath = hgdir
552 util.makedir(destpath, notindexed=True)
552 util.makedir(destpath, notindexed=True)
553 except OSError as inst:
553 except OSError as inst:
554 if inst.errno == errno.EEXIST:
554 if inst.errno == errno.EEXIST:
555 cleandir = None
555 cleandir = None
556 raise error.Abort(_("destination '%s' already exists")
556 raise error.Abort(_("destination '%s' already exists")
557 % dest)
557 % dest)
558 raise
558 raise
559
559
560 destlock = copystore(ui, srcrepo, destpath)
560 destlock = copystore(ui, srcrepo, destpath)
561 # copy bookmarks over
561 # copy bookmarks over
562 srcbookmarks = srcrepo.join('bookmarks')
562 srcbookmarks = srcrepo.vfs.join('bookmarks')
563 dstbookmarks = os.path.join(destpath, 'bookmarks')
563 dstbookmarks = os.path.join(destpath, 'bookmarks')
564 if os.path.exists(srcbookmarks):
564 if os.path.exists(srcbookmarks):
565 util.copyfile(srcbookmarks, dstbookmarks)
565 util.copyfile(srcbookmarks, dstbookmarks)
566
566
567 # Recomputing branch cache might be slow on big repos,
567 # Recomputing branch cache might be slow on big repos,
568 # so just copy it
568 # so just copy it
569 def copybranchcache(fname):
569 def copybranchcache(fname):
570 srcbranchcache = srcrepo.join('cache/%s' % fname)
570 srcbranchcache = srcrepo.vfs.join('cache/%s' % fname)
571 dstbranchcache = os.path.join(dstcachedir, fname)
571 dstbranchcache = os.path.join(dstcachedir, fname)
572 if os.path.exists(srcbranchcache):
572 if os.path.exists(srcbranchcache):
573 if not os.path.exists(dstcachedir):
573 if not os.path.exists(dstcachedir):
574 os.mkdir(dstcachedir)
574 os.mkdir(dstcachedir)
575 util.copyfile(srcbranchcache, dstbranchcache)
575 util.copyfile(srcbranchcache, dstbranchcache)
576
576
577 dstcachedir = os.path.join(destpath, 'cache')
577 dstcachedir = os.path.join(destpath, 'cache')
578 # In local clones we're copying all nodes, not just served
578 # In local clones we're copying all nodes, not just served
579 # ones. Therefore copy all branch caches over.
579 # ones. Therefore copy all branch caches over.
580 copybranchcache('branch2')
580 copybranchcache('branch2')
581 for cachename in repoview.filtertable:
581 for cachename in repoview.filtertable:
582 copybranchcache('branch2-%s' % cachename)
582 copybranchcache('branch2-%s' % cachename)
583
583
584 # we need to re-init the repo after manually copying the data
584 # we need to re-init the repo after manually copying the data
585 # into it
585 # into it
586 destpeer = peer(srcrepo, peeropts, dest)
586 destpeer = peer(srcrepo, peeropts, dest)
587 srcrepo.hook('outgoing', source='clone',
587 srcrepo.hook('outgoing', source='clone',
588 node=node.hex(node.nullid))
588 node=node.hex(node.nullid))
589 else:
589 else:
590 try:
590 try:
591 destpeer = peer(srcrepo or ui, peeropts, dest, create=True)
591 destpeer = peer(srcrepo or ui, peeropts, dest, create=True)
592 # only pass ui when no srcrepo
592 # only pass ui when no srcrepo
593 except OSError as inst:
593 except OSError as inst:
594 if inst.errno == errno.EEXIST:
594 if inst.errno == errno.EEXIST:
595 cleandir = None
595 cleandir = None
596 raise error.Abort(_("destination '%s' already exists")
596 raise error.Abort(_("destination '%s' already exists")
597 % dest)
597 % dest)
598 raise
598 raise
599
599
600 revs = None
600 revs = None
601 if rev:
601 if rev:
602 if not srcpeer.capable('lookup'):
602 if not srcpeer.capable('lookup'):
603 raise error.Abort(_("src repository does not support "
603 raise error.Abort(_("src repository does not support "
604 "revision lookup and so doesn't "
604 "revision lookup and so doesn't "
605 "support clone by revision"))
605 "support clone by revision"))
606 revs = [srcpeer.lookup(r) for r in rev]
606 revs = [srcpeer.lookup(r) for r in rev]
607 checkout = revs[0]
607 checkout = revs[0]
608 local = destpeer.local()
608 local = destpeer.local()
609 if local:
609 if local:
610 if not stream:
610 if not stream:
611 if pull:
611 if pull:
612 stream = False
612 stream = False
613 else:
613 else:
614 stream = None
614 stream = None
615 # internal config: ui.quietbookmarkmove
615 # internal config: ui.quietbookmarkmove
616 quiet = local.ui.backupconfig('ui', 'quietbookmarkmove')
616 quiet = local.ui.backupconfig('ui', 'quietbookmarkmove')
617 try:
617 try:
618 local.ui.setconfig(
618 local.ui.setconfig(
619 'ui', 'quietbookmarkmove', True, 'clone')
619 'ui', 'quietbookmarkmove', True, 'clone')
620 exchange.pull(local, srcpeer, revs,
620 exchange.pull(local, srcpeer, revs,
621 streamclonerequested=stream)
621 streamclonerequested=stream)
622 finally:
622 finally:
623 local.ui.restoreconfig(quiet)
623 local.ui.restoreconfig(quiet)
624 elif srcrepo:
624 elif srcrepo:
625 exchange.push(srcrepo, destpeer, revs=revs,
625 exchange.push(srcrepo, destpeer, revs=revs,
626 bookmarks=srcrepo._bookmarks.keys())
626 bookmarks=srcrepo._bookmarks.keys())
627 else:
627 else:
628 raise error.Abort(_("clone from remote to remote not supported")
628 raise error.Abort(_("clone from remote to remote not supported")
629 )
629 )
630
630
631 cleandir = None
631 cleandir = None
632
632
633 destrepo = destpeer.local()
633 destrepo = destpeer.local()
634 if destrepo:
634 if destrepo:
635 template = uimod.samplehgrcs['cloned']
635 template = uimod.samplehgrcs['cloned']
636 fp = destrepo.vfs("hgrc", "w", text=True)
636 fp = destrepo.vfs("hgrc", "w", text=True)
637 u = util.url(abspath)
637 u = util.url(abspath)
638 u.passwd = None
638 u.passwd = None
639 defaulturl = str(u)
639 defaulturl = str(u)
640 fp.write(template % defaulturl)
640 fp.write(template % defaulturl)
641 fp.close()
641 fp.close()
642
642
643 destrepo.ui.setconfig('paths', 'default', defaulturl, 'clone')
643 destrepo.ui.setconfig('paths', 'default', defaulturl, 'clone')
644
644
645 if update:
645 if update:
646 if update is not True:
646 if update is not True:
647 checkout = srcpeer.lookup(update)
647 checkout = srcpeer.lookup(update)
648 uprev = None
648 uprev = None
649 status = None
649 status = None
650 if checkout is not None:
650 if checkout is not None:
651 try:
651 try:
652 uprev = destrepo.lookup(checkout)
652 uprev = destrepo.lookup(checkout)
653 except error.RepoLookupError:
653 except error.RepoLookupError:
654 if update is not True:
654 if update is not True:
655 try:
655 try:
656 uprev = destrepo.lookup(update)
656 uprev = destrepo.lookup(update)
657 except error.RepoLookupError:
657 except error.RepoLookupError:
658 pass
658 pass
659 if uprev is None:
659 if uprev is None:
660 try:
660 try:
661 uprev = destrepo._bookmarks['@']
661 uprev = destrepo._bookmarks['@']
662 update = '@'
662 update = '@'
663 bn = destrepo[uprev].branch()
663 bn = destrepo[uprev].branch()
664 if bn == 'default':
664 if bn == 'default':
665 status = _("updating to bookmark @\n")
665 status = _("updating to bookmark @\n")
666 else:
666 else:
667 status = (_("updating to bookmark @ on branch %s\n")
667 status = (_("updating to bookmark @ on branch %s\n")
668 % bn)
668 % bn)
669 except KeyError:
669 except KeyError:
670 try:
670 try:
671 uprev = destrepo.branchtip('default')
671 uprev = destrepo.branchtip('default')
672 except error.RepoLookupError:
672 except error.RepoLookupError:
673 uprev = destrepo.lookup('tip')
673 uprev = destrepo.lookup('tip')
674 if not status:
674 if not status:
675 bn = destrepo[uprev].branch()
675 bn = destrepo[uprev].branch()
676 status = _("updating to branch %s\n") % bn
676 status = _("updating to branch %s\n") % bn
677 destrepo.ui.status(status)
677 destrepo.ui.status(status)
678 _update(destrepo, uprev)
678 _update(destrepo, uprev)
679 if update in destrepo._bookmarks:
679 if update in destrepo._bookmarks:
680 bookmarks.activate(destrepo, update)
680 bookmarks.activate(destrepo, update)
681 finally:
681 finally:
682 release(srclock, destlock)
682 release(srclock, destlock)
683 if cleandir is not None:
683 if cleandir is not None:
684 shutil.rmtree(cleandir, True)
684 shutil.rmtree(cleandir, True)
685 if srcpeer is not None:
685 if srcpeer is not None:
686 srcpeer.close()
686 srcpeer.close()
687 return srcpeer, destpeer
687 return srcpeer, destpeer
688
688
689 def _showstats(repo, stats, quietempty=False):
689 def _showstats(repo, stats, quietempty=False):
690 if quietempty and not any(stats):
690 if quietempty and not any(stats):
691 return
691 return
692 repo.ui.status(_("%d files updated, %d files merged, "
692 repo.ui.status(_("%d files updated, %d files merged, "
693 "%d files removed, %d files unresolved\n") % stats)
693 "%d files removed, %d files unresolved\n") % stats)
694
694
695 def updaterepo(repo, node, overwrite, updatecheck=None):
695 def updaterepo(repo, node, overwrite, updatecheck=None):
696 """Update the working directory to node.
696 """Update the working directory to node.
697
697
698 When overwrite is set, changes are clobbered, merged else
698 When overwrite is set, changes are clobbered, merged else
699
699
700 returns stats (see pydoc mercurial.merge.applyupdates)"""
700 returns stats (see pydoc mercurial.merge.applyupdates)"""
701 return mergemod.update(repo, node, False, overwrite,
701 return mergemod.update(repo, node, False, overwrite,
702 labels=['working copy', 'destination'],
702 labels=['working copy', 'destination'],
703 updatecheck=updatecheck)
703 updatecheck=updatecheck)
704
704
705 def update(repo, node, quietempty=False, updatecheck=None):
705 def update(repo, node, quietempty=False, updatecheck=None):
706 """update the working directory to node"""
706 """update the working directory to node"""
707 stats = updaterepo(repo, node, False, updatecheck=updatecheck)
707 stats = updaterepo(repo, node, False, updatecheck=updatecheck)
708 _showstats(repo, stats, quietempty)
708 _showstats(repo, stats, quietempty)
709 if stats[3]:
709 if stats[3]:
710 repo.ui.status(_("use 'hg resolve' to retry unresolved file merges\n"))
710 repo.ui.status(_("use 'hg resolve' to retry unresolved file merges\n"))
711 return stats[3] > 0
711 return stats[3] > 0
712
712
713 # naming conflict in clone()
713 # naming conflict in clone()
714 _update = update
714 _update = update
715
715
716 def clean(repo, node, show_stats=True, quietempty=False):
716 def clean(repo, node, show_stats=True, quietempty=False):
717 """forcibly switch the working directory to node, clobbering changes"""
717 """forcibly switch the working directory to node, clobbering changes"""
718 stats = updaterepo(repo, node, True)
718 stats = updaterepo(repo, node, True)
719 repo.vfs.unlinkpath('graftstate', ignoremissing=True)
719 repo.vfs.unlinkpath('graftstate', ignoremissing=True)
720 if show_stats:
720 if show_stats:
721 _showstats(repo, stats, quietempty)
721 _showstats(repo, stats, quietempty)
722 return stats[3] > 0
722 return stats[3] > 0
723
723
724 # naming conflict in updatetotally()
724 # naming conflict in updatetotally()
725 _clean = clean
725 _clean = clean
726
726
727 def updatetotally(ui, repo, checkout, brev, clean=False, updatecheck=None):
727 def updatetotally(ui, repo, checkout, brev, clean=False, updatecheck=None):
728 """Update the working directory with extra care for non-file components
728 """Update the working directory with extra care for non-file components
729
729
730 This takes care of non-file components below:
730 This takes care of non-file components below:
731
731
732 :bookmark: might be advanced or (in)activated
732 :bookmark: might be advanced or (in)activated
733
733
734 This takes arguments below:
734 This takes arguments below:
735
735
736 :checkout: to which revision the working directory is updated
736 :checkout: to which revision the working directory is updated
737 :brev: a name, which might be a bookmark to be activated after updating
737 :brev: a name, which might be a bookmark to be activated after updating
738 :clean: whether changes in the working directory can be discarded
738 :clean: whether changes in the working directory can be discarded
739 :updatecheck: how to deal with a dirty working directory
739 :updatecheck: how to deal with a dirty working directory
740
740
741 Valid values for updatecheck are (None => linear):
741 Valid values for updatecheck are (None => linear):
742
742
743 * abort: abort if the working directory is dirty
743 * abort: abort if the working directory is dirty
744 * none: don't check (merge working directory changes into destination)
744 * none: don't check (merge working directory changes into destination)
745 * linear: check that update is linear before merging working directory
745 * linear: check that update is linear before merging working directory
746 changes into destination
746 changes into destination
747 * noconflict: check that the update does not result in file merges
747 * noconflict: check that the update does not result in file merges
748
748
749 This returns whether conflict is detected at updating or not.
749 This returns whether conflict is detected at updating or not.
750 """
750 """
751 if updatecheck is None:
751 if updatecheck is None:
752 updatecheck = ui.config('experimental', 'updatecheck')
752 updatecheck = ui.config('experimental', 'updatecheck')
753 if updatecheck not in ('abort', 'none', 'linear', 'noconflict'):
753 if updatecheck not in ('abort', 'none', 'linear', 'noconflict'):
754 # If not configured, or invalid value configured
754 # If not configured, or invalid value configured
755 updatecheck = 'linear'
755 updatecheck = 'linear'
756 with repo.wlock():
756 with repo.wlock():
757 movemarkfrom = None
757 movemarkfrom = None
758 warndest = False
758 warndest = False
759 if checkout is None:
759 if checkout is None:
760 updata = destutil.destupdate(repo, clean=clean)
760 updata = destutil.destupdate(repo, clean=clean)
761 checkout, movemarkfrom, brev = updata
761 checkout, movemarkfrom, brev = updata
762 warndest = True
762 warndest = True
763
763
764 if clean:
764 if clean:
765 ret = _clean(repo, checkout)
765 ret = _clean(repo, checkout)
766 else:
766 else:
767 if updatecheck == 'abort':
767 if updatecheck == 'abort':
768 cmdutil.bailifchanged(repo, merge=False)
768 cmdutil.bailifchanged(repo, merge=False)
769 updatecheck = 'none'
769 updatecheck = 'none'
770 ret = _update(repo, checkout, updatecheck=updatecheck)
770 ret = _update(repo, checkout, updatecheck=updatecheck)
771
771
772 if not ret and movemarkfrom:
772 if not ret and movemarkfrom:
773 if movemarkfrom == repo['.'].node():
773 if movemarkfrom == repo['.'].node():
774 pass # no-op update
774 pass # no-op update
775 elif bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
775 elif bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
776 b = ui.label(repo._activebookmark, 'bookmarks.active')
776 b = ui.label(repo._activebookmark, 'bookmarks.active')
777 ui.status(_("updating bookmark %s\n") % b)
777 ui.status(_("updating bookmark %s\n") % b)
778 else:
778 else:
779 # this can happen with a non-linear update
779 # this can happen with a non-linear update
780 b = ui.label(repo._activebookmark, 'bookmarks')
780 b = ui.label(repo._activebookmark, 'bookmarks')
781 ui.status(_("(leaving bookmark %s)\n") % b)
781 ui.status(_("(leaving bookmark %s)\n") % b)
782 bookmarks.deactivate(repo)
782 bookmarks.deactivate(repo)
783 elif brev in repo._bookmarks:
783 elif brev in repo._bookmarks:
784 if brev != repo._activebookmark:
784 if brev != repo._activebookmark:
785 b = ui.label(brev, 'bookmarks.active')
785 b = ui.label(brev, 'bookmarks.active')
786 ui.status(_("(activating bookmark %s)\n") % b)
786 ui.status(_("(activating bookmark %s)\n") % b)
787 bookmarks.activate(repo, brev)
787 bookmarks.activate(repo, brev)
788 elif brev:
788 elif brev:
789 if repo._activebookmark:
789 if repo._activebookmark:
790 b = ui.label(repo._activebookmark, 'bookmarks')
790 b = ui.label(repo._activebookmark, 'bookmarks')
791 ui.status(_("(leaving bookmark %s)\n") % b)
791 ui.status(_("(leaving bookmark %s)\n") % b)
792 bookmarks.deactivate(repo)
792 bookmarks.deactivate(repo)
793
793
794 if warndest:
794 if warndest:
795 destutil.statusotherdests(ui, repo)
795 destutil.statusotherdests(ui, repo)
796
796
797 return ret
797 return ret
798
798
799 def merge(repo, node, force=None, remind=True, mergeforce=False, labels=None):
799 def merge(repo, node, force=None, remind=True, mergeforce=False, labels=None):
800 """Branch merge with node, resolving changes. Return true if any
800 """Branch merge with node, resolving changes. Return true if any
801 unresolved conflicts."""
801 unresolved conflicts."""
802 stats = mergemod.update(repo, node, True, force, mergeforce=mergeforce,
802 stats = mergemod.update(repo, node, True, force, mergeforce=mergeforce,
803 labels=labels)
803 labels=labels)
804 _showstats(repo, stats)
804 _showstats(repo, stats)
805 if stats[3]:
805 if stats[3]:
806 repo.ui.status(_("use 'hg resolve' to retry unresolved file merges "
806 repo.ui.status(_("use 'hg resolve' to retry unresolved file merges "
807 "or 'hg update -C .' to abandon\n"))
807 "or 'hg update -C .' to abandon\n"))
808 elif remind:
808 elif remind:
809 repo.ui.status(_("(branch merge, don't forget to commit)\n"))
809 repo.ui.status(_("(branch merge, don't forget to commit)\n"))
810 return stats[3] > 0
810 return stats[3] > 0
811
811
812 def _incoming(displaychlist, subreporecurse, ui, repo, source,
812 def _incoming(displaychlist, subreporecurse, ui, repo, source,
813 opts, buffered=False):
813 opts, buffered=False):
814 """
814 """
815 Helper for incoming / gincoming.
815 Helper for incoming / gincoming.
816 displaychlist gets called with
816 displaychlist gets called with
817 (remoterepo, incomingchangesetlist, displayer) parameters,
817 (remoterepo, incomingchangesetlist, displayer) parameters,
818 and is supposed to contain only code that can't be unified.
818 and is supposed to contain only code that can't be unified.
819 """
819 """
820 source, branches = parseurl(ui.expandpath(source), opts.get('branch'))
820 source, branches = parseurl(ui.expandpath(source), opts.get('branch'))
821 other = peer(repo, opts, source)
821 other = peer(repo, opts, source)
822 ui.status(_('comparing with %s\n') % util.hidepassword(source))
822 ui.status(_('comparing with %s\n') % util.hidepassword(source))
823 revs, checkout = addbranchrevs(repo, other, branches, opts.get('rev'))
823 revs, checkout = addbranchrevs(repo, other, branches, opts.get('rev'))
824
824
825 if revs:
825 if revs:
826 revs = [other.lookup(rev) for rev in revs]
826 revs = [other.lookup(rev) for rev in revs]
827 other, chlist, cleanupfn = bundlerepo.getremotechanges(ui, repo, other,
827 other, chlist, cleanupfn = bundlerepo.getremotechanges(ui, repo, other,
828 revs, opts["bundle"], opts["force"])
828 revs, opts["bundle"], opts["force"])
829 try:
829 try:
830 if not chlist:
830 if not chlist:
831 ui.status(_("no changes found\n"))
831 ui.status(_("no changes found\n"))
832 return subreporecurse()
832 return subreporecurse()
833 ui.pager('incoming')
833 ui.pager('incoming')
834 displayer = cmdutil.show_changeset(ui, other, opts, buffered)
834 displayer = cmdutil.show_changeset(ui, other, opts, buffered)
835 displaychlist(other, chlist, displayer)
835 displaychlist(other, chlist, displayer)
836 displayer.close()
836 displayer.close()
837 finally:
837 finally:
838 cleanupfn()
838 cleanupfn()
839 subreporecurse()
839 subreporecurse()
840 return 0 # exit code is zero since we found incoming changes
840 return 0 # exit code is zero since we found incoming changes
841
841
842 def incoming(ui, repo, source, opts):
842 def incoming(ui, repo, source, opts):
843 def subreporecurse():
843 def subreporecurse():
844 ret = 1
844 ret = 1
845 if opts.get('subrepos'):
845 if opts.get('subrepos'):
846 ctx = repo[None]
846 ctx = repo[None]
847 for subpath in sorted(ctx.substate):
847 for subpath in sorted(ctx.substate):
848 sub = ctx.sub(subpath)
848 sub = ctx.sub(subpath)
849 ret = min(ret, sub.incoming(ui, source, opts))
849 ret = min(ret, sub.incoming(ui, source, opts))
850 return ret
850 return ret
851
851
852 def display(other, chlist, displayer):
852 def display(other, chlist, displayer):
853 limit = cmdutil.loglimit(opts)
853 limit = cmdutil.loglimit(opts)
854 if opts.get('newest_first'):
854 if opts.get('newest_first'):
855 chlist.reverse()
855 chlist.reverse()
856 count = 0
856 count = 0
857 for n in chlist:
857 for n in chlist:
858 if limit is not None and count >= limit:
858 if limit is not None and count >= limit:
859 break
859 break
860 parents = [p for p in other.changelog.parents(n) if p != nullid]
860 parents = [p for p in other.changelog.parents(n) if p != nullid]
861 if opts.get('no_merges') and len(parents) == 2:
861 if opts.get('no_merges') and len(parents) == 2:
862 continue
862 continue
863 count += 1
863 count += 1
864 displayer.show(other[n])
864 displayer.show(other[n])
865 return _incoming(display, subreporecurse, ui, repo, source, opts)
865 return _incoming(display, subreporecurse, ui, repo, source, opts)
866
866
867 def _outgoing(ui, repo, dest, opts):
867 def _outgoing(ui, repo, dest, opts):
868 dest = ui.expandpath(dest or 'default-push', dest or 'default')
868 dest = ui.expandpath(dest or 'default-push', dest or 'default')
869 dest, branches = parseurl(dest, opts.get('branch'))
869 dest, branches = parseurl(dest, opts.get('branch'))
870 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
870 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
871 revs, checkout = addbranchrevs(repo, repo, branches, opts.get('rev'))
871 revs, checkout = addbranchrevs(repo, repo, branches, opts.get('rev'))
872 if revs:
872 if revs:
873 revs = [repo.lookup(rev) for rev in scmutil.revrange(repo, revs)]
873 revs = [repo.lookup(rev) for rev in scmutil.revrange(repo, revs)]
874
874
875 other = peer(repo, opts, dest)
875 other = peer(repo, opts, dest)
876 outgoing = discovery.findcommonoutgoing(repo.unfiltered(), other, revs,
876 outgoing = discovery.findcommonoutgoing(repo.unfiltered(), other, revs,
877 force=opts.get('force'))
877 force=opts.get('force'))
878 o = outgoing.missing
878 o = outgoing.missing
879 if not o:
879 if not o:
880 scmutil.nochangesfound(repo.ui, repo, outgoing.excluded)
880 scmutil.nochangesfound(repo.ui, repo, outgoing.excluded)
881 return o, other
881 return o, other
882
882
883 def outgoing(ui, repo, dest, opts):
883 def outgoing(ui, repo, dest, opts):
884 def recurse():
884 def recurse():
885 ret = 1
885 ret = 1
886 if opts.get('subrepos'):
886 if opts.get('subrepos'):
887 ctx = repo[None]
887 ctx = repo[None]
888 for subpath in sorted(ctx.substate):
888 for subpath in sorted(ctx.substate):
889 sub = ctx.sub(subpath)
889 sub = ctx.sub(subpath)
890 ret = min(ret, sub.outgoing(ui, dest, opts))
890 ret = min(ret, sub.outgoing(ui, dest, opts))
891 return ret
891 return ret
892
892
893 limit = cmdutil.loglimit(opts)
893 limit = cmdutil.loglimit(opts)
894 o, other = _outgoing(ui, repo, dest, opts)
894 o, other = _outgoing(ui, repo, dest, opts)
895 if not o:
895 if not o:
896 cmdutil.outgoinghooks(ui, repo, other, opts, o)
896 cmdutil.outgoinghooks(ui, repo, other, opts, o)
897 return recurse()
897 return recurse()
898
898
899 if opts.get('newest_first'):
899 if opts.get('newest_first'):
900 o.reverse()
900 o.reverse()
901 ui.pager('outgoing')
901 ui.pager('outgoing')
902 displayer = cmdutil.show_changeset(ui, repo, opts)
902 displayer = cmdutil.show_changeset(ui, repo, opts)
903 count = 0
903 count = 0
904 for n in o:
904 for n in o:
905 if limit is not None and count >= limit:
905 if limit is not None and count >= limit:
906 break
906 break
907 parents = [p for p in repo.changelog.parents(n) if p != nullid]
907 parents = [p for p in repo.changelog.parents(n) if p != nullid]
908 if opts.get('no_merges') and len(parents) == 2:
908 if opts.get('no_merges') and len(parents) == 2:
909 continue
909 continue
910 count += 1
910 count += 1
911 displayer.show(repo[n])
911 displayer.show(repo[n])
912 displayer.close()
912 displayer.close()
913 cmdutil.outgoinghooks(ui, repo, other, opts, o)
913 cmdutil.outgoinghooks(ui, repo, other, opts, o)
914 recurse()
914 recurse()
915 return 0 # exit code is zero since we found outgoing changes
915 return 0 # exit code is zero since we found outgoing changes
916
916
917 def verify(repo):
917 def verify(repo):
918 """verify the consistency of a repository"""
918 """verify the consistency of a repository"""
919 ret = verifymod.verify(repo)
919 ret = verifymod.verify(repo)
920
920
921 # Broken subrepo references in hidden csets don't seem worth worrying about,
921 # Broken subrepo references in hidden csets don't seem worth worrying about,
922 # since they can't be pushed/pulled, and --hidden can be used if they are a
922 # since they can't be pushed/pulled, and --hidden can be used if they are a
923 # concern.
923 # concern.
924
924
925 # pathto() is needed for -R case
925 # pathto() is needed for -R case
926 revs = repo.revs("filelog(%s)",
926 revs = repo.revs("filelog(%s)",
927 util.pathto(repo.root, repo.getcwd(), '.hgsubstate'))
927 util.pathto(repo.root, repo.getcwd(), '.hgsubstate'))
928
928
929 if revs:
929 if revs:
930 repo.ui.status(_('checking subrepo links\n'))
930 repo.ui.status(_('checking subrepo links\n'))
931 for rev in revs:
931 for rev in revs:
932 ctx = repo[rev]
932 ctx = repo[rev]
933 try:
933 try:
934 for subpath in ctx.substate:
934 for subpath in ctx.substate:
935 try:
935 try:
936 ret = (ctx.sub(subpath, allowcreate=False).verify()
936 ret = (ctx.sub(subpath, allowcreate=False).verify()
937 or ret)
937 or ret)
938 except error.RepoError as e:
938 except error.RepoError as e:
939 repo.ui.warn(('%s: %s\n') % (rev, e))
939 repo.ui.warn(('%s: %s\n') % (rev, e))
940 except Exception:
940 except Exception:
941 repo.ui.warn(_('.hgsubstate is corrupt in revision %s\n') %
941 repo.ui.warn(_('.hgsubstate is corrupt in revision %s\n') %
942 node.short(ctx.node()))
942 node.short(ctx.node()))
943
943
944 return ret
944 return ret
945
945
946 def remoteui(src, opts):
946 def remoteui(src, opts):
947 'build a remote ui from ui or repo and opts'
947 'build a remote ui from ui or repo and opts'
948 if util.safehasattr(src, 'baseui'): # looks like a repository
948 if util.safehasattr(src, 'baseui'): # looks like a repository
949 dst = src.baseui.copy() # drop repo-specific config
949 dst = src.baseui.copy() # drop repo-specific config
950 src = src.ui # copy target options from repo
950 src = src.ui # copy target options from repo
951 else: # assume it's a global ui object
951 else: # assume it's a global ui object
952 dst = src.copy() # keep all global options
952 dst = src.copy() # keep all global options
953
953
954 # copy ssh-specific options
954 # copy ssh-specific options
955 for o in 'ssh', 'remotecmd':
955 for o in 'ssh', 'remotecmd':
956 v = opts.get(o) or src.config('ui', o)
956 v = opts.get(o) or src.config('ui', o)
957 if v:
957 if v:
958 dst.setconfig("ui", o, v, 'copied')
958 dst.setconfig("ui", o, v, 'copied')
959
959
960 # copy bundle-specific options
960 # copy bundle-specific options
961 r = src.config('bundle', 'mainreporoot')
961 r = src.config('bundle', 'mainreporoot')
962 if r:
962 if r:
963 dst.setconfig('bundle', 'mainreporoot', r, 'copied')
963 dst.setconfig('bundle', 'mainreporoot', r, 'copied')
964
964
965 # copy selected local settings to the remote ui
965 # copy selected local settings to the remote ui
966 for sect in ('auth', 'hostfingerprints', 'hostsecurity', 'http_proxy'):
966 for sect in ('auth', 'hostfingerprints', 'hostsecurity', 'http_proxy'):
967 for key, val in src.configitems(sect):
967 for key, val in src.configitems(sect):
968 dst.setconfig(sect, key, val, 'copied')
968 dst.setconfig(sect, key, val, 'copied')
969 v = src.config('web', 'cacerts')
969 v = src.config('web', 'cacerts')
970 if v:
970 if v:
971 dst.setconfig('web', 'cacerts', util.expandpath(v), 'copied')
971 dst.setconfig('web', 'cacerts', util.expandpath(v), 'copied')
972
972
973 return dst
973 return dst
974
974
975 # Files of interest
975 # Files of interest
976 # Used to check if the repository has changed looking at mtime and size of
976 # Used to check if the repository has changed looking at mtime and size of
977 # these files.
977 # these files.
978 foi = [('spath', '00changelog.i'),
978 foi = [('spath', '00changelog.i'),
979 ('spath', 'phaseroots'), # ! phase can change content at the same size
979 ('spath', 'phaseroots'), # ! phase can change content at the same size
980 ('spath', 'obsstore'),
980 ('spath', 'obsstore'),
981 ('path', 'bookmarks'), # ! bookmark can change content at the same size
981 ('path', 'bookmarks'), # ! bookmark can change content at the same size
982 ]
982 ]
983
983
984 class cachedlocalrepo(object):
984 class cachedlocalrepo(object):
985 """Holds a localrepository that can be cached and reused."""
985 """Holds a localrepository that can be cached and reused."""
986
986
987 def __init__(self, repo):
987 def __init__(self, repo):
988 """Create a new cached repo from an existing repo.
988 """Create a new cached repo from an existing repo.
989
989
990 We assume the passed in repo was recently created. If the
990 We assume the passed in repo was recently created. If the
991 repo has changed between when it was created and when it was
991 repo has changed between when it was created and when it was
992 turned into a cache, it may not refresh properly.
992 turned into a cache, it may not refresh properly.
993 """
993 """
994 assert isinstance(repo, localrepo.localrepository)
994 assert isinstance(repo, localrepo.localrepository)
995 self._repo = repo
995 self._repo = repo
996 self._state, self.mtime = self._repostate()
996 self._state, self.mtime = self._repostate()
997 self._filtername = repo.filtername
997 self._filtername = repo.filtername
998
998
999 def fetch(self):
999 def fetch(self):
1000 """Refresh (if necessary) and return a repository.
1000 """Refresh (if necessary) and return a repository.
1001
1001
1002 If the cached instance is out of date, it will be recreated
1002 If the cached instance is out of date, it will be recreated
1003 automatically and returned.
1003 automatically and returned.
1004
1004
1005 Returns a tuple of the repo and a boolean indicating whether a new
1005 Returns a tuple of the repo and a boolean indicating whether a new
1006 repo instance was created.
1006 repo instance was created.
1007 """
1007 """
1008 # We compare the mtimes and sizes of some well-known files to
1008 # We compare the mtimes and sizes of some well-known files to
1009 # determine if the repo changed. This is not precise, as mtimes
1009 # determine if the repo changed. This is not precise, as mtimes
1010 # are susceptible to clock skew and imprecise filesystems and
1010 # are susceptible to clock skew and imprecise filesystems and
1011 # file content can change while maintaining the same size.
1011 # file content can change while maintaining the same size.
1012
1012
1013 state, mtime = self._repostate()
1013 state, mtime = self._repostate()
1014 if state == self._state:
1014 if state == self._state:
1015 return self._repo, False
1015 return self._repo, False
1016
1016
1017 repo = repository(self._repo.baseui, self._repo.url())
1017 repo = repository(self._repo.baseui, self._repo.url())
1018 if self._filtername:
1018 if self._filtername:
1019 self._repo = repo.filtered(self._filtername)
1019 self._repo = repo.filtered(self._filtername)
1020 else:
1020 else:
1021 self._repo = repo.unfiltered()
1021 self._repo = repo.unfiltered()
1022 self._state = state
1022 self._state = state
1023 self.mtime = mtime
1023 self.mtime = mtime
1024
1024
1025 return self._repo, True
1025 return self._repo, True
1026
1026
1027 def _repostate(self):
1027 def _repostate(self):
1028 state = []
1028 state = []
1029 maxmtime = -1
1029 maxmtime = -1
1030 for attr, fname in foi:
1030 for attr, fname in foi:
1031 prefix = getattr(self._repo, attr)
1031 prefix = getattr(self._repo, attr)
1032 p = os.path.join(prefix, fname)
1032 p = os.path.join(prefix, fname)
1033 try:
1033 try:
1034 st = os.stat(p)
1034 st = os.stat(p)
1035 except OSError:
1035 except OSError:
1036 st = os.stat(prefix)
1036 st = os.stat(prefix)
1037 state.append((st.st_mtime, st.st_size))
1037 state.append((st.st_mtime, st.st_size))
1038 maxmtime = max(maxmtime, st.st_mtime)
1038 maxmtime = max(maxmtime, st.st_mtime)
1039
1039
1040 return tuple(state), maxmtime
1040 return tuple(state), maxmtime
1041
1041
1042 def copy(self):
1042 def copy(self):
1043 """Obtain a copy of this class instance.
1043 """Obtain a copy of this class instance.
1044
1044
1045 A new localrepository instance is obtained. The new instance should be
1045 A new localrepository instance is obtained. The new instance should be
1046 completely independent of the original.
1046 completely independent of the original.
1047 """
1047 """
1048 repo = repository(self._repo.baseui, self._repo.origroot)
1048 repo = repository(self._repo.baseui, self._repo.origroot)
1049 if self._filtername:
1049 if self._filtername:
1050 repo = repo.filtered(self._filtername)
1050 repo = repo.filtered(self._filtername)
1051 else:
1051 else:
1052 repo = repo.unfiltered()
1052 repo = repo.unfiltered()
1053 c = cachedlocalrepo(repo)
1053 c = cachedlocalrepo(repo)
1054 c._state = self._state
1054 c._state = self._state
1055 c.mtime = self.mtime
1055 c.mtime = self.mtime
1056 return c
1056 return c
General Comments 0
You need to be logged in to leave comments. Login now