##// END OF EJS Templates
hg: update to use vfs functions in shared destination repository...
Chinmay Joshi -
r21801:2ccd71bb default
parent child Browse files
Show More
@@ -1,660 +1,661 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 i18n import _
9 from i18n import _
10 from lock import release
10 from lock import release
11 from node import hex, nullid
11 from node import hex, nullid
12 import localrepo, bundlerepo, unionrepo, httppeer, sshpeer, statichttprepo
12 import localrepo, bundlerepo, unionrepo, httppeer, sshpeer, statichttprepo
13 import bookmarks, lock, util, extensions, error, node, scmutil, phases, url
13 import bookmarks, lock, util, extensions, error, node, scmutil, phases, url
14 import cmdutil, discovery
14 import cmdutil, discovery
15 import merge as mergemod
15 import merge as mergemod
16 import verify as verifymod
16 import verify as verifymod
17 import errno, os, shutil
17 import errno, os, shutil
18
18
19 def _local(path):
19 def _local(path):
20 path = util.expandpath(util.urllocalpath(path))
20 path = util.expandpath(util.urllocalpath(path))
21 return (os.path.isfile(path) and bundlerepo or localrepo)
21 return (os.path.isfile(path) and bundlerepo or localrepo)
22
22
23 def addbranchrevs(lrepo, other, branches, revs):
23 def addbranchrevs(lrepo, other, branches, revs):
24 peer = other.peer() # a courtesy to callers using a localrepo for other
24 peer = other.peer() # a courtesy to callers using a localrepo for other
25 hashbranch, branches = branches
25 hashbranch, branches = branches
26 if not hashbranch and not branches:
26 if not hashbranch and not branches:
27 return revs or None, revs and revs[0] or None
27 return revs or None, revs and revs[0] or None
28 revs = revs and list(revs) or []
28 revs = revs and list(revs) or []
29 if not peer.capable('branchmap'):
29 if not peer.capable('branchmap'):
30 if branches:
30 if branches:
31 raise util.Abort(_("remote branch lookup not supported"))
31 raise util.Abort(_("remote branch lookup not supported"))
32 revs.append(hashbranch)
32 revs.append(hashbranch)
33 return revs, revs[0]
33 return revs, revs[0]
34 branchmap = peer.branchmap()
34 branchmap = peer.branchmap()
35
35
36 def primary(branch):
36 def primary(branch):
37 if branch == '.':
37 if branch == '.':
38 if not lrepo:
38 if not lrepo:
39 raise util.Abort(_("dirstate branch not accessible"))
39 raise util.Abort(_("dirstate branch not accessible"))
40 branch = lrepo.dirstate.branch()
40 branch = lrepo.dirstate.branch()
41 if branch in branchmap:
41 if branch in branchmap:
42 revs.extend(node.hex(r) for r in reversed(branchmap[branch]))
42 revs.extend(node.hex(r) for r in reversed(branchmap[branch]))
43 return True
43 return True
44 else:
44 else:
45 return False
45 return False
46
46
47 for branch in branches:
47 for branch in branches:
48 if not primary(branch):
48 if not primary(branch):
49 raise error.RepoLookupError(_("unknown branch '%s'") % branch)
49 raise error.RepoLookupError(_("unknown branch '%s'") % branch)
50 if hashbranch:
50 if hashbranch:
51 if not primary(hashbranch):
51 if not primary(hashbranch):
52 revs.append(hashbranch)
52 revs.append(hashbranch)
53 return revs, revs[0]
53 return revs, revs[0]
54
54
55 def parseurl(path, branches=None):
55 def parseurl(path, branches=None):
56 '''parse url#branch, returning (url, (branch, branches))'''
56 '''parse url#branch, returning (url, (branch, branches))'''
57
57
58 u = util.url(path)
58 u = util.url(path)
59 branch = None
59 branch = None
60 if u.fragment:
60 if u.fragment:
61 branch = u.fragment
61 branch = u.fragment
62 u.fragment = None
62 u.fragment = None
63 return str(u), (branch, branches or [])
63 return str(u), (branch, branches or [])
64
64
65 schemes = {
65 schemes = {
66 'bundle': bundlerepo,
66 'bundle': bundlerepo,
67 'union': unionrepo,
67 'union': unionrepo,
68 'file': _local,
68 'file': _local,
69 'http': httppeer,
69 'http': httppeer,
70 'https': httppeer,
70 'https': httppeer,
71 'ssh': sshpeer,
71 'ssh': sshpeer,
72 'static-http': statichttprepo,
72 'static-http': statichttprepo,
73 }
73 }
74
74
75 def _peerlookup(path):
75 def _peerlookup(path):
76 u = util.url(path)
76 u = util.url(path)
77 scheme = u.scheme or 'file'
77 scheme = u.scheme or 'file'
78 thing = schemes.get(scheme) or schemes['file']
78 thing = schemes.get(scheme) or schemes['file']
79 try:
79 try:
80 return thing(path)
80 return thing(path)
81 except TypeError:
81 except TypeError:
82 return thing
82 return thing
83
83
84 def islocal(repo):
84 def islocal(repo):
85 '''return true if repo (or path pointing to repo) is local'''
85 '''return true if repo (or path pointing to repo) is local'''
86 if isinstance(repo, str):
86 if isinstance(repo, str):
87 try:
87 try:
88 return _peerlookup(repo).islocal(repo)
88 return _peerlookup(repo).islocal(repo)
89 except AttributeError:
89 except AttributeError:
90 return False
90 return False
91 return repo.local()
91 return repo.local()
92
92
93 def openpath(ui, path):
93 def openpath(ui, path):
94 '''open path with open if local, url.open if remote'''
94 '''open path with open if local, url.open if remote'''
95 pathurl = util.url(path, parsequery=False, parsefragment=False)
95 pathurl = util.url(path, parsequery=False, parsefragment=False)
96 if pathurl.islocal():
96 if pathurl.islocal():
97 return util.posixfile(pathurl.localpath(), 'rb')
97 return util.posixfile(pathurl.localpath(), 'rb')
98 else:
98 else:
99 return url.open(ui, path)
99 return url.open(ui, path)
100
100
101 # a list of (ui, repo) functions called for wire peer initialization
101 # a list of (ui, repo) functions called for wire peer initialization
102 wirepeersetupfuncs = []
102 wirepeersetupfuncs = []
103
103
104 def _peerorrepo(ui, path, create=False):
104 def _peerorrepo(ui, path, create=False):
105 """return a repository object for the specified path"""
105 """return a repository object for the specified path"""
106 obj = _peerlookup(path).instance(ui, path, create)
106 obj = _peerlookup(path).instance(ui, path, create)
107 ui = getattr(obj, "ui", ui)
107 ui = getattr(obj, "ui", ui)
108 for name, module in extensions.extensions(ui):
108 for name, module in extensions.extensions(ui):
109 hook = getattr(module, 'reposetup', None)
109 hook = getattr(module, 'reposetup', None)
110 if hook:
110 if hook:
111 hook(ui, obj)
111 hook(ui, obj)
112 if not obj.local():
112 if not obj.local():
113 for f in wirepeersetupfuncs:
113 for f in wirepeersetupfuncs:
114 f(ui, obj)
114 f(ui, obj)
115 return obj
115 return obj
116
116
117 def repository(ui, path='', create=False):
117 def repository(ui, path='', create=False):
118 """return a repository object for the specified path"""
118 """return a repository object for the specified path"""
119 peer = _peerorrepo(ui, path, create)
119 peer = _peerorrepo(ui, path, create)
120 repo = peer.local()
120 repo = peer.local()
121 if not repo:
121 if not repo:
122 raise util.Abort(_("repository '%s' is not local") %
122 raise util.Abort(_("repository '%s' is not local") %
123 (path or peer.url()))
123 (path or peer.url()))
124 return repo.filtered('visible')
124 return repo.filtered('visible')
125
125
126 def peer(uiorrepo, opts, path, create=False):
126 def peer(uiorrepo, opts, path, create=False):
127 '''return a repository peer for the specified path'''
127 '''return a repository peer for the specified path'''
128 rui = remoteui(uiorrepo, opts)
128 rui = remoteui(uiorrepo, opts)
129 return _peerorrepo(rui, path, create).peer()
129 return _peerorrepo(rui, path, create).peer()
130
130
131 def defaultdest(source):
131 def defaultdest(source):
132 '''return default destination of clone if none is given
132 '''return default destination of clone if none is given
133
133
134 >>> defaultdest('foo')
134 >>> defaultdest('foo')
135 'foo'
135 'foo'
136 >>> defaultdest('/foo/bar')
136 >>> defaultdest('/foo/bar')
137 'bar'
137 'bar'
138 >>> defaultdest('/')
138 >>> defaultdest('/')
139 ''
139 ''
140 >>> defaultdest('')
140 >>> defaultdest('')
141 ''
141 ''
142 >>> defaultdest('http://example.org/')
142 >>> defaultdest('http://example.org/')
143 ''
143 ''
144 >>> defaultdest('http://example.org/foo/')
144 >>> defaultdest('http://example.org/foo/')
145 'foo'
145 'foo'
146 '''
146 '''
147 path = util.url(source).path
147 path = util.url(source).path
148 if not path:
148 if not path:
149 return ''
149 return ''
150 return os.path.basename(os.path.normpath(path))
150 return os.path.basename(os.path.normpath(path))
151
151
152 def share(ui, source, dest=None, update=True):
152 def share(ui, source, dest=None, update=True):
153 '''create a shared repository'''
153 '''create a shared repository'''
154
154
155 if not islocal(source):
155 if not islocal(source):
156 raise util.Abort(_('can only share local repositories'))
156 raise util.Abort(_('can only share local repositories'))
157
157
158 if not dest:
158 if not dest:
159 dest = defaultdest(source)
159 dest = defaultdest(source)
160 else:
160 else:
161 dest = ui.expandpath(dest)
161 dest = ui.expandpath(dest)
162
162
163 if isinstance(source, str):
163 if isinstance(source, str):
164 origsource = ui.expandpath(source)
164 origsource = ui.expandpath(source)
165 source, branches = parseurl(origsource)
165 source, branches = parseurl(origsource)
166 srcrepo = repository(ui, source)
166 srcrepo = repository(ui, source)
167 rev, checkout = addbranchrevs(srcrepo, srcrepo, branches, None)
167 rev, checkout = addbranchrevs(srcrepo, srcrepo, branches, None)
168 else:
168 else:
169 srcrepo = source.local()
169 srcrepo = source.local()
170 origsource = source = srcrepo.url()
170 origsource = source = srcrepo.url()
171 checkout = None
171 checkout = None
172
172
173 sharedpath = srcrepo.sharedpath # if our source is already sharing
173 sharedpath = srcrepo.sharedpath # if our source is already sharing
174
174
175 root = os.path.realpath(dest)
175 root = os.path.realpath(dest)
176 roothg = os.path.join(root, '.hg')
176 roothg = os.path.join(root, '.hg')
177 destwvfs = scmutil.vfs(dest, realpath=True)
177 destwvfs = scmutil.vfs(dest, realpath=True)
178 destvfs = scmutil.vfs(os.path.join(destwvfs.base, '.hg'), realpath=True)
178
179
179 if os.path.exists(roothg):
180 if destvfs.lexists():
180 raise util.Abort(_('destination already exists'))
181 raise util.Abort(_('destination already exists'))
181
182
182 if not destwvfs.isdir():
183 if not destwvfs.isdir():
183 destwvfs.mkdir()
184 destwvfs.mkdir()
184 util.makedir(roothg, notindexed=True)
185 destvfs.makedir()
185
186
186 requirements = ''
187 requirements = ''
187 try:
188 try:
188 requirements = srcrepo.opener.read('requires')
189 requirements = srcrepo.opener.read('requires')
189 except IOError, inst:
190 except IOError, inst:
190 if inst.errno != errno.ENOENT:
191 if inst.errno != errno.ENOENT:
191 raise
192 raise
192
193
193 requirements += 'shared\n'
194 requirements += 'shared\n'
194 util.writefile(os.path.join(roothg, 'requires'), requirements)
195 util.writefile(os.path.join(roothg, 'requires'), requirements)
195 util.writefile(os.path.join(roothg, 'sharedpath'), sharedpath)
196 util.writefile(os.path.join(roothg, 'sharedpath'), sharedpath)
196
197
197 r = repository(ui, destwvfs.base)
198 r = repository(ui, destwvfs.base)
198
199
199 default = srcrepo.ui.config('paths', 'default')
200 default = srcrepo.ui.config('paths', 'default')
200 if default:
201 if default:
201 fp = r.opener("hgrc", "w", text=True)
202 fp = r.opener("hgrc", "w", text=True)
202 fp.write("[paths]\n")
203 fp.write("[paths]\n")
203 fp.write("default = %s\n" % default)
204 fp.write("default = %s\n" % default)
204 fp.close()
205 fp.close()
205
206
206 if update:
207 if update:
207 r.ui.status(_("updating working directory\n"))
208 r.ui.status(_("updating working directory\n"))
208 if update is not True:
209 if update is not True:
209 checkout = update
210 checkout = update
210 for test in (checkout, 'default', 'tip'):
211 for test in (checkout, 'default', 'tip'):
211 if test is None:
212 if test is None:
212 continue
213 continue
213 try:
214 try:
214 uprev = r.lookup(test)
215 uprev = r.lookup(test)
215 break
216 break
216 except error.RepoLookupError:
217 except error.RepoLookupError:
217 continue
218 continue
218 _update(r, uprev)
219 _update(r, uprev)
219
220
220 def copystore(ui, srcrepo, destpath):
221 def copystore(ui, srcrepo, destpath):
221 '''copy files from store of srcrepo in destpath
222 '''copy files from store of srcrepo in destpath
222
223
223 returns destlock
224 returns destlock
224 '''
225 '''
225 destlock = None
226 destlock = None
226 try:
227 try:
227 hardlink = None
228 hardlink = None
228 num = 0
229 num = 0
229 srcpublishing = srcrepo.ui.configbool('phases', 'publish', True)
230 srcpublishing = srcrepo.ui.configbool('phases', 'publish', True)
230 srcvfs = scmutil.vfs(srcrepo.sharedpath)
231 srcvfs = scmutil.vfs(srcrepo.sharedpath)
231 dstvfs = scmutil.vfs(destpath)
232 dstvfs = scmutil.vfs(destpath)
232 for f in srcrepo.store.copylist():
233 for f in srcrepo.store.copylist():
233 if srcpublishing and f.endswith('phaseroots'):
234 if srcpublishing and f.endswith('phaseroots'):
234 continue
235 continue
235 dstbase = os.path.dirname(f)
236 dstbase = os.path.dirname(f)
236 if dstbase and not dstvfs.exists(dstbase):
237 if dstbase and not dstvfs.exists(dstbase):
237 dstvfs.mkdir(dstbase)
238 dstvfs.mkdir(dstbase)
238 if srcvfs.exists(f):
239 if srcvfs.exists(f):
239 if f.endswith('data'):
240 if f.endswith('data'):
240 # 'dstbase' may be empty (e.g. revlog format 0)
241 # 'dstbase' may be empty (e.g. revlog format 0)
241 lockfile = os.path.join(dstbase, "lock")
242 lockfile = os.path.join(dstbase, "lock")
242 # lock to avoid premature writing to the target
243 # lock to avoid premature writing to the target
243 destlock = lock.lock(dstvfs, lockfile)
244 destlock = lock.lock(dstvfs, lockfile)
244 hardlink, n = util.copyfiles(srcvfs.join(f), dstvfs.join(f),
245 hardlink, n = util.copyfiles(srcvfs.join(f), dstvfs.join(f),
245 hardlink)
246 hardlink)
246 num += n
247 num += n
247 if hardlink:
248 if hardlink:
248 ui.debug("linked %d files\n" % num)
249 ui.debug("linked %d files\n" % num)
249 else:
250 else:
250 ui.debug("copied %d files\n" % num)
251 ui.debug("copied %d files\n" % num)
251 return destlock
252 return destlock
252 except: # re-raises
253 except: # re-raises
253 release(destlock)
254 release(destlock)
254 raise
255 raise
255
256
256 def clone(ui, peeropts, source, dest=None, pull=False, rev=None,
257 def clone(ui, peeropts, source, dest=None, pull=False, rev=None,
257 update=True, stream=False, branch=None):
258 update=True, stream=False, branch=None):
258 """Make a copy of an existing repository.
259 """Make a copy of an existing repository.
259
260
260 Create a copy of an existing repository in a new directory. The
261 Create a copy of an existing repository in a new directory. The
261 source and destination are URLs, as passed to the repository
262 source and destination are URLs, as passed to the repository
262 function. Returns a pair of repository peers, the source and
263 function. Returns a pair of repository peers, the source and
263 newly created destination.
264 newly created destination.
264
265
265 The location of the source is added to the new repository's
266 The location of the source is added to the new repository's
266 .hg/hgrc file, as the default to be used for future pulls and
267 .hg/hgrc file, as the default to be used for future pulls and
267 pushes.
268 pushes.
268
269
269 If an exception is raised, the partly cloned/updated destination
270 If an exception is raised, the partly cloned/updated destination
270 repository will be deleted.
271 repository will be deleted.
271
272
272 Arguments:
273 Arguments:
273
274
274 source: repository object or URL
275 source: repository object or URL
275
276
276 dest: URL of destination repository to create (defaults to base
277 dest: URL of destination repository to create (defaults to base
277 name of source repository)
278 name of source repository)
278
279
279 pull: always pull from source repository, even in local case
280 pull: always pull from source repository, even in local case
280
281
281 stream: stream raw data uncompressed from repository (fast over
282 stream: stream raw data uncompressed from repository (fast over
282 LAN, slow over WAN)
283 LAN, slow over WAN)
283
284
284 rev: revision to clone up to (implies pull=True)
285 rev: revision to clone up to (implies pull=True)
285
286
286 update: update working directory after clone completes, if
287 update: update working directory after clone completes, if
287 destination is local repository (True means update to default rev,
288 destination is local repository (True means update to default rev,
288 anything else is treated as a revision)
289 anything else is treated as a revision)
289
290
290 branch: branches to clone
291 branch: branches to clone
291 """
292 """
292
293
293 if isinstance(source, str):
294 if isinstance(source, str):
294 origsource = ui.expandpath(source)
295 origsource = ui.expandpath(source)
295 source, branch = parseurl(origsource, branch)
296 source, branch = parseurl(origsource, branch)
296 srcpeer = peer(ui, peeropts, source)
297 srcpeer = peer(ui, peeropts, source)
297 else:
298 else:
298 srcpeer = source.peer() # in case we were called with a localrepo
299 srcpeer = source.peer() # in case we were called with a localrepo
299 branch = (None, branch or [])
300 branch = (None, branch or [])
300 origsource = source = srcpeer.url()
301 origsource = source = srcpeer.url()
301 rev, checkout = addbranchrevs(srcpeer, srcpeer, branch, rev)
302 rev, checkout = addbranchrevs(srcpeer, srcpeer, branch, rev)
302
303
303 if dest is None:
304 if dest is None:
304 dest = defaultdest(source)
305 dest = defaultdest(source)
305 if dest:
306 if dest:
306 ui.status(_("destination directory: %s\n") % dest)
307 ui.status(_("destination directory: %s\n") % dest)
307 else:
308 else:
308 dest = ui.expandpath(dest)
309 dest = ui.expandpath(dest)
309
310
310 dest = util.urllocalpath(dest)
311 dest = util.urllocalpath(dest)
311 source = util.urllocalpath(source)
312 source = util.urllocalpath(source)
312
313
313 if not dest:
314 if not dest:
314 raise util.Abort(_("empty destination path is not valid"))
315 raise util.Abort(_("empty destination path is not valid"))
315 if os.path.exists(dest):
316 if os.path.exists(dest):
316 if not os.path.isdir(dest):
317 if not os.path.isdir(dest):
317 raise util.Abort(_("destination '%s' already exists") % dest)
318 raise util.Abort(_("destination '%s' already exists") % dest)
318 elif os.listdir(dest):
319 elif os.listdir(dest):
319 raise util.Abort(_("destination '%s' is not empty") % dest)
320 raise util.Abort(_("destination '%s' is not empty") % dest)
320
321
321 srclock = destlock = cleandir = None
322 srclock = destlock = cleandir = None
322 srcrepo = srcpeer.local()
323 srcrepo = srcpeer.local()
323 try:
324 try:
324 abspath = origsource
325 abspath = origsource
325 if islocal(origsource):
326 if islocal(origsource):
326 abspath = os.path.abspath(util.urllocalpath(origsource))
327 abspath = os.path.abspath(util.urllocalpath(origsource))
327
328
328 if islocal(dest):
329 if islocal(dest):
329 cleandir = dest
330 cleandir = dest
330
331
331 copy = False
332 copy = False
332 if (srcrepo and srcrepo.cancopy() and islocal(dest)
333 if (srcrepo and srcrepo.cancopy() and islocal(dest)
333 and not phases.hassecret(srcrepo)):
334 and not phases.hassecret(srcrepo)):
334 copy = not pull and not rev
335 copy = not pull and not rev
335
336
336 if copy:
337 if copy:
337 try:
338 try:
338 # we use a lock here because if we race with commit, we
339 # we use a lock here because if we race with commit, we
339 # can end up with extra data in the cloned revlogs that's
340 # can end up with extra data in the cloned revlogs that's
340 # not pointed to by changesets, thus causing verify to
341 # not pointed to by changesets, thus causing verify to
341 # fail
342 # fail
342 srclock = srcrepo.lock(wait=False)
343 srclock = srcrepo.lock(wait=False)
343 except error.LockError:
344 except error.LockError:
344 copy = False
345 copy = False
345
346
346 if copy:
347 if copy:
347 srcrepo.hook('preoutgoing', throw=True, source='clone')
348 srcrepo.hook('preoutgoing', throw=True, source='clone')
348 hgdir = os.path.realpath(os.path.join(dest, ".hg"))
349 hgdir = os.path.realpath(os.path.join(dest, ".hg"))
349 if not os.path.exists(dest):
350 if not os.path.exists(dest):
350 os.mkdir(dest)
351 os.mkdir(dest)
351 else:
352 else:
352 # only clean up directories we create ourselves
353 # only clean up directories we create ourselves
353 cleandir = hgdir
354 cleandir = hgdir
354 try:
355 try:
355 destpath = hgdir
356 destpath = hgdir
356 util.makedir(destpath, notindexed=True)
357 util.makedir(destpath, notindexed=True)
357 except OSError, inst:
358 except OSError, inst:
358 if inst.errno == errno.EEXIST:
359 if inst.errno == errno.EEXIST:
359 cleandir = None
360 cleandir = None
360 raise util.Abort(_("destination '%s' already exists")
361 raise util.Abort(_("destination '%s' already exists")
361 % dest)
362 % dest)
362 raise
363 raise
363
364
364 destlock = copystore(ui, srcrepo, destpath)
365 destlock = copystore(ui, srcrepo, destpath)
365
366
366 # Recomputing branch cache might be slow on big repos,
367 # Recomputing branch cache might be slow on big repos,
367 # so just copy it
368 # so just copy it
368 dstcachedir = os.path.join(destpath, 'cache')
369 dstcachedir = os.path.join(destpath, 'cache')
369 srcbranchcache = srcrepo.sjoin('cache/branch2')
370 srcbranchcache = srcrepo.sjoin('cache/branch2')
370 dstbranchcache = os.path.join(dstcachedir, 'branch2')
371 dstbranchcache = os.path.join(dstcachedir, 'branch2')
371 if os.path.exists(srcbranchcache):
372 if os.path.exists(srcbranchcache):
372 if not os.path.exists(dstcachedir):
373 if not os.path.exists(dstcachedir):
373 os.mkdir(dstcachedir)
374 os.mkdir(dstcachedir)
374 util.copyfile(srcbranchcache, dstbranchcache)
375 util.copyfile(srcbranchcache, dstbranchcache)
375
376
376 # we need to re-init the repo after manually copying the data
377 # we need to re-init the repo after manually copying the data
377 # into it
378 # into it
378 destpeer = peer(srcrepo, peeropts, dest)
379 destpeer = peer(srcrepo, peeropts, dest)
379 srcrepo.hook('outgoing', source='clone',
380 srcrepo.hook('outgoing', source='clone',
380 node=node.hex(node.nullid))
381 node=node.hex(node.nullid))
381 else:
382 else:
382 try:
383 try:
383 destpeer = peer(srcrepo or ui, peeropts, dest, create=True)
384 destpeer = peer(srcrepo or ui, peeropts, dest, create=True)
384 # only pass ui when no srcrepo
385 # only pass ui when no srcrepo
385 except OSError, inst:
386 except OSError, inst:
386 if inst.errno == errno.EEXIST:
387 if inst.errno == errno.EEXIST:
387 cleandir = None
388 cleandir = None
388 raise util.Abort(_("destination '%s' already exists")
389 raise util.Abort(_("destination '%s' already exists")
389 % dest)
390 % dest)
390 raise
391 raise
391
392
392 revs = None
393 revs = None
393 if rev:
394 if rev:
394 if not srcpeer.capable('lookup'):
395 if not srcpeer.capable('lookup'):
395 raise util.Abort(_("src repository does not support "
396 raise util.Abort(_("src repository does not support "
396 "revision lookup and so doesn't "
397 "revision lookup and so doesn't "
397 "support clone by revision"))
398 "support clone by revision"))
398 revs = [srcpeer.lookup(r) for r in rev]
399 revs = [srcpeer.lookup(r) for r in rev]
399 checkout = revs[0]
400 checkout = revs[0]
400 if destpeer.local():
401 if destpeer.local():
401 destpeer.local().clone(srcpeer, heads=revs, stream=stream)
402 destpeer.local().clone(srcpeer, heads=revs, stream=stream)
402 elif srcrepo:
403 elif srcrepo:
403 srcrepo.push(destpeer, revs=revs)
404 srcrepo.push(destpeer, revs=revs)
404 else:
405 else:
405 raise util.Abort(_("clone from remote to remote not supported"))
406 raise util.Abort(_("clone from remote to remote not supported"))
406
407
407 cleandir = None
408 cleandir = None
408
409
409 # clone all bookmarks except divergent ones
410 # clone all bookmarks except divergent ones
410 destrepo = destpeer.local()
411 destrepo = destpeer.local()
411 if destrepo and srcpeer.capable("pushkey"):
412 if destrepo and srcpeer.capable("pushkey"):
412 rb = srcpeer.listkeys('bookmarks')
413 rb = srcpeer.listkeys('bookmarks')
413 marks = destrepo._bookmarks
414 marks = destrepo._bookmarks
414 for k, n in rb.iteritems():
415 for k, n in rb.iteritems():
415 try:
416 try:
416 m = destrepo.lookup(n)
417 m = destrepo.lookup(n)
417 marks[k] = m
418 marks[k] = m
418 except error.RepoLookupError:
419 except error.RepoLookupError:
419 pass
420 pass
420 if rb:
421 if rb:
421 marks.write()
422 marks.write()
422 elif srcrepo and destpeer.capable("pushkey"):
423 elif srcrepo and destpeer.capable("pushkey"):
423 for k, n in srcrepo._bookmarks.iteritems():
424 for k, n in srcrepo._bookmarks.iteritems():
424 destpeer.pushkey('bookmarks', k, '', hex(n))
425 destpeer.pushkey('bookmarks', k, '', hex(n))
425
426
426 if destrepo:
427 if destrepo:
427 fp = destrepo.opener("hgrc", "w", text=True)
428 fp = destrepo.opener("hgrc", "w", text=True)
428 fp.write("[paths]\n")
429 fp.write("[paths]\n")
429 u = util.url(abspath)
430 u = util.url(abspath)
430 u.passwd = None
431 u.passwd = None
431 defaulturl = str(u)
432 defaulturl = str(u)
432 fp.write("default = %s\n" % defaulturl)
433 fp.write("default = %s\n" % defaulturl)
433 fp.close()
434 fp.close()
434
435
435 destrepo.ui.setconfig('paths', 'default', defaulturl, 'clone')
436 destrepo.ui.setconfig('paths', 'default', defaulturl, 'clone')
436
437
437 if update:
438 if update:
438 if update is not True:
439 if update is not True:
439 checkout = srcpeer.lookup(update)
440 checkout = srcpeer.lookup(update)
440 uprev = None
441 uprev = None
441 status = None
442 status = None
442 if checkout is not None:
443 if checkout is not None:
443 try:
444 try:
444 uprev = destrepo.lookup(checkout)
445 uprev = destrepo.lookup(checkout)
445 except error.RepoLookupError:
446 except error.RepoLookupError:
446 pass
447 pass
447 if uprev is None:
448 if uprev is None:
448 try:
449 try:
449 uprev = destrepo._bookmarks['@']
450 uprev = destrepo._bookmarks['@']
450 update = '@'
451 update = '@'
451 bn = destrepo[uprev].branch()
452 bn = destrepo[uprev].branch()
452 if bn == 'default':
453 if bn == 'default':
453 status = _("updating to bookmark @\n")
454 status = _("updating to bookmark @\n")
454 else:
455 else:
455 status = (_("updating to bookmark @ on branch %s\n")
456 status = (_("updating to bookmark @ on branch %s\n")
456 % bn)
457 % bn)
457 except KeyError:
458 except KeyError:
458 try:
459 try:
459 uprev = destrepo.branchtip('default')
460 uprev = destrepo.branchtip('default')
460 except error.RepoLookupError:
461 except error.RepoLookupError:
461 uprev = destrepo.lookup('tip')
462 uprev = destrepo.lookup('tip')
462 if not status:
463 if not status:
463 bn = destrepo[uprev].branch()
464 bn = destrepo[uprev].branch()
464 status = _("updating to branch %s\n") % bn
465 status = _("updating to branch %s\n") % bn
465 destrepo.ui.status(status)
466 destrepo.ui.status(status)
466 _update(destrepo, uprev)
467 _update(destrepo, uprev)
467 if update in destrepo._bookmarks:
468 if update in destrepo._bookmarks:
468 bookmarks.setcurrent(destrepo, update)
469 bookmarks.setcurrent(destrepo, update)
469 finally:
470 finally:
470 release(srclock, destlock)
471 release(srclock, destlock)
471 if cleandir is not None:
472 if cleandir is not None:
472 shutil.rmtree(cleandir, True)
473 shutil.rmtree(cleandir, True)
473 if srcpeer is not None:
474 if srcpeer is not None:
474 srcpeer.close()
475 srcpeer.close()
475 return srcpeer, destpeer
476 return srcpeer, destpeer
476
477
477 def _showstats(repo, stats):
478 def _showstats(repo, stats):
478 repo.ui.status(_("%d files updated, %d files merged, "
479 repo.ui.status(_("%d files updated, %d files merged, "
479 "%d files removed, %d files unresolved\n") % stats)
480 "%d files removed, %d files unresolved\n") % stats)
480
481
481 def updaterepo(repo, node, overwrite):
482 def updaterepo(repo, node, overwrite):
482 """Update the working directory to node.
483 """Update the working directory to node.
483
484
484 When overwrite is set, changes are clobbered, merged else
485 When overwrite is set, changes are clobbered, merged else
485
486
486 returns stats (see pydoc mercurial.merge.applyupdates)"""
487 returns stats (see pydoc mercurial.merge.applyupdates)"""
487 return mergemod.update(repo, node, False, overwrite, None,
488 return mergemod.update(repo, node, False, overwrite, None,
488 labels=['working copy', 'destination'])
489 labels=['working copy', 'destination'])
489
490
490 def update(repo, node):
491 def update(repo, node):
491 """update the working directory to node, merging linear changes"""
492 """update the working directory to node, merging linear changes"""
492 stats = updaterepo(repo, node, False)
493 stats = updaterepo(repo, node, False)
493 _showstats(repo, stats)
494 _showstats(repo, stats)
494 if stats[3]:
495 if stats[3]:
495 repo.ui.status(_("use 'hg resolve' to retry unresolved file merges\n"))
496 repo.ui.status(_("use 'hg resolve' to retry unresolved file merges\n"))
496 return stats[3] > 0
497 return stats[3] > 0
497
498
498 # naming conflict in clone()
499 # naming conflict in clone()
499 _update = update
500 _update = update
500
501
501 def clean(repo, node, show_stats=True):
502 def clean(repo, node, show_stats=True):
502 """forcibly switch the working directory to node, clobbering changes"""
503 """forcibly switch the working directory to node, clobbering changes"""
503 stats = updaterepo(repo, node, True)
504 stats = updaterepo(repo, node, True)
504 util.unlinkpath(repo.join('graftstate'), ignoremissing=True)
505 util.unlinkpath(repo.join('graftstate'), ignoremissing=True)
505 if show_stats:
506 if show_stats:
506 _showstats(repo, stats)
507 _showstats(repo, stats)
507 return stats[3] > 0
508 return stats[3] > 0
508
509
509 def merge(repo, node, force=None, remind=True):
510 def merge(repo, node, force=None, remind=True):
510 """Branch merge with node, resolving changes. Return true if any
511 """Branch merge with node, resolving changes. Return true if any
511 unresolved conflicts."""
512 unresolved conflicts."""
512 stats = mergemod.update(repo, node, True, force, False)
513 stats = mergemod.update(repo, node, True, force, False)
513 _showstats(repo, stats)
514 _showstats(repo, stats)
514 if stats[3]:
515 if stats[3]:
515 repo.ui.status(_("use 'hg resolve' to retry unresolved file merges "
516 repo.ui.status(_("use 'hg resolve' to retry unresolved file merges "
516 "or 'hg update -C .' to abandon\n"))
517 "or 'hg update -C .' to abandon\n"))
517 elif remind:
518 elif remind:
518 repo.ui.status(_("(branch merge, don't forget to commit)\n"))
519 repo.ui.status(_("(branch merge, don't forget to commit)\n"))
519 return stats[3] > 0
520 return stats[3] > 0
520
521
521 def _incoming(displaychlist, subreporecurse, ui, repo, source,
522 def _incoming(displaychlist, subreporecurse, ui, repo, source,
522 opts, buffered=False):
523 opts, buffered=False):
523 """
524 """
524 Helper for incoming / gincoming.
525 Helper for incoming / gincoming.
525 displaychlist gets called with
526 displaychlist gets called with
526 (remoterepo, incomingchangesetlist, displayer) parameters,
527 (remoterepo, incomingchangesetlist, displayer) parameters,
527 and is supposed to contain only code that can't be unified.
528 and is supposed to contain only code that can't be unified.
528 """
529 """
529 source, branches = parseurl(ui.expandpath(source), opts.get('branch'))
530 source, branches = parseurl(ui.expandpath(source), opts.get('branch'))
530 other = peer(repo, opts, source)
531 other = peer(repo, opts, source)
531 ui.status(_('comparing with %s\n') % util.hidepassword(source))
532 ui.status(_('comparing with %s\n') % util.hidepassword(source))
532 revs, checkout = addbranchrevs(repo, other, branches, opts.get('rev'))
533 revs, checkout = addbranchrevs(repo, other, branches, opts.get('rev'))
533
534
534 if revs:
535 if revs:
535 revs = [other.lookup(rev) for rev in revs]
536 revs = [other.lookup(rev) for rev in revs]
536 other, chlist, cleanupfn = bundlerepo.getremotechanges(ui, repo, other,
537 other, chlist, cleanupfn = bundlerepo.getremotechanges(ui, repo, other,
537 revs, opts["bundle"], opts["force"])
538 revs, opts["bundle"], opts["force"])
538 try:
539 try:
539 if not chlist:
540 if not chlist:
540 ui.status(_("no changes found\n"))
541 ui.status(_("no changes found\n"))
541 return subreporecurse()
542 return subreporecurse()
542
543
543 displayer = cmdutil.show_changeset(ui, other, opts, buffered)
544 displayer = cmdutil.show_changeset(ui, other, opts, buffered)
544 displaychlist(other, chlist, displayer)
545 displaychlist(other, chlist, displayer)
545 displayer.close()
546 displayer.close()
546 finally:
547 finally:
547 cleanupfn()
548 cleanupfn()
548 subreporecurse()
549 subreporecurse()
549 return 0 # exit code is zero since we found incoming changes
550 return 0 # exit code is zero since we found incoming changes
550
551
551 def incoming(ui, repo, source, opts):
552 def incoming(ui, repo, source, opts):
552 def subreporecurse():
553 def subreporecurse():
553 ret = 1
554 ret = 1
554 if opts.get('subrepos'):
555 if opts.get('subrepos'):
555 ctx = repo[None]
556 ctx = repo[None]
556 for subpath in sorted(ctx.substate):
557 for subpath in sorted(ctx.substate):
557 sub = ctx.sub(subpath)
558 sub = ctx.sub(subpath)
558 ret = min(ret, sub.incoming(ui, source, opts))
559 ret = min(ret, sub.incoming(ui, source, opts))
559 return ret
560 return ret
560
561
561 def display(other, chlist, displayer):
562 def display(other, chlist, displayer):
562 limit = cmdutil.loglimit(opts)
563 limit = cmdutil.loglimit(opts)
563 if opts.get('newest_first'):
564 if opts.get('newest_first'):
564 chlist.reverse()
565 chlist.reverse()
565 count = 0
566 count = 0
566 for n in chlist:
567 for n in chlist:
567 if limit is not None and count >= limit:
568 if limit is not None and count >= limit:
568 break
569 break
569 parents = [p for p in other.changelog.parents(n) if p != nullid]
570 parents = [p for p in other.changelog.parents(n) if p != nullid]
570 if opts.get('no_merges') and len(parents) == 2:
571 if opts.get('no_merges') and len(parents) == 2:
571 continue
572 continue
572 count += 1
573 count += 1
573 displayer.show(other[n])
574 displayer.show(other[n])
574 return _incoming(display, subreporecurse, ui, repo, source, opts)
575 return _incoming(display, subreporecurse, ui, repo, source, opts)
575
576
576 def _outgoing(ui, repo, dest, opts):
577 def _outgoing(ui, repo, dest, opts):
577 dest = ui.expandpath(dest or 'default-push', dest or 'default')
578 dest = ui.expandpath(dest or 'default-push', dest or 'default')
578 dest, branches = parseurl(dest, opts.get('branch'))
579 dest, branches = parseurl(dest, opts.get('branch'))
579 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
580 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
580 revs, checkout = addbranchrevs(repo, repo, branches, opts.get('rev'))
581 revs, checkout = addbranchrevs(repo, repo, branches, opts.get('rev'))
581 if revs:
582 if revs:
582 revs = [repo.lookup(rev) for rev in scmutil.revrange(repo, revs)]
583 revs = [repo.lookup(rev) for rev in scmutil.revrange(repo, revs)]
583
584
584 other = peer(repo, opts, dest)
585 other = peer(repo, opts, dest)
585 outgoing = discovery.findcommonoutgoing(repo.unfiltered(), other, revs,
586 outgoing = discovery.findcommonoutgoing(repo.unfiltered(), other, revs,
586 force=opts.get('force'))
587 force=opts.get('force'))
587 o = outgoing.missing
588 o = outgoing.missing
588 if not o:
589 if not o:
589 scmutil.nochangesfound(repo.ui, repo, outgoing.excluded)
590 scmutil.nochangesfound(repo.ui, repo, outgoing.excluded)
590 return o, other
591 return o, other
591
592
592 def outgoing(ui, repo, dest, opts):
593 def outgoing(ui, repo, dest, opts):
593 def recurse():
594 def recurse():
594 ret = 1
595 ret = 1
595 if opts.get('subrepos'):
596 if opts.get('subrepos'):
596 ctx = repo[None]
597 ctx = repo[None]
597 for subpath in sorted(ctx.substate):
598 for subpath in sorted(ctx.substate):
598 sub = ctx.sub(subpath)
599 sub = ctx.sub(subpath)
599 ret = min(ret, sub.outgoing(ui, dest, opts))
600 ret = min(ret, sub.outgoing(ui, dest, opts))
600 return ret
601 return ret
601
602
602 limit = cmdutil.loglimit(opts)
603 limit = cmdutil.loglimit(opts)
603 o, other = _outgoing(ui, repo, dest, opts)
604 o, other = _outgoing(ui, repo, dest, opts)
604 if not o:
605 if not o:
605 cmdutil.outgoinghooks(ui, repo, other, opts, o)
606 cmdutil.outgoinghooks(ui, repo, other, opts, o)
606 return recurse()
607 return recurse()
607
608
608 if opts.get('newest_first'):
609 if opts.get('newest_first'):
609 o.reverse()
610 o.reverse()
610 displayer = cmdutil.show_changeset(ui, repo, opts)
611 displayer = cmdutil.show_changeset(ui, repo, opts)
611 count = 0
612 count = 0
612 for n in o:
613 for n in o:
613 if limit is not None and count >= limit:
614 if limit is not None and count >= limit:
614 break
615 break
615 parents = [p for p in repo.changelog.parents(n) if p != nullid]
616 parents = [p for p in repo.changelog.parents(n) if p != nullid]
616 if opts.get('no_merges') and len(parents) == 2:
617 if opts.get('no_merges') and len(parents) == 2:
617 continue
618 continue
618 count += 1
619 count += 1
619 displayer.show(repo[n])
620 displayer.show(repo[n])
620 displayer.close()
621 displayer.close()
621 cmdutil.outgoinghooks(ui, repo, other, opts, o)
622 cmdutil.outgoinghooks(ui, repo, other, opts, o)
622 recurse()
623 recurse()
623 return 0 # exit code is zero since we found outgoing changes
624 return 0 # exit code is zero since we found outgoing changes
624
625
625 def revert(repo, node, choose):
626 def revert(repo, node, choose):
626 """revert changes to revision in node without updating dirstate"""
627 """revert changes to revision in node without updating dirstate"""
627 return mergemod.update(repo, node, False, True, choose)[3] > 0
628 return mergemod.update(repo, node, False, True, choose)[3] > 0
628
629
629 def verify(repo):
630 def verify(repo):
630 """verify the consistency of a repository"""
631 """verify the consistency of a repository"""
631 return verifymod.verify(repo)
632 return verifymod.verify(repo)
632
633
633 def remoteui(src, opts):
634 def remoteui(src, opts):
634 'build a remote ui from ui or repo and opts'
635 'build a remote ui from ui or repo and opts'
635 if util.safehasattr(src, 'baseui'): # looks like a repository
636 if util.safehasattr(src, 'baseui'): # looks like a repository
636 dst = src.baseui.copy() # drop repo-specific config
637 dst = src.baseui.copy() # drop repo-specific config
637 src = src.ui # copy target options from repo
638 src = src.ui # copy target options from repo
638 else: # assume it's a global ui object
639 else: # assume it's a global ui object
639 dst = src.copy() # keep all global options
640 dst = src.copy() # keep all global options
640
641
641 # copy ssh-specific options
642 # copy ssh-specific options
642 for o in 'ssh', 'remotecmd':
643 for o in 'ssh', 'remotecmd':
643 v = opts.get(o) or src.config('ui', o)
644 v = opts.get(o) or src.config('ui', o)
644 if v:
645 if v:
645 dst.setconfig("ui", o, v, 'copied')
646 dst.setconfig("ui", o, v, 'copied')
646
647
647 # copy bundle-specific options
648 # copy bundle-specific options
648 r = src.config('bundle', 'mainreporoot')
649 r = src.config('bundle', 'mainreporoot')
649 if r:
650 if r:
650 dst.setconfig('bundle', 'mainreporoot', r, 'copied')
651 dst.setconfig('bundle', 'mainreporoot', r, 'copied')
651
652
652 # copy selected local settings to the remote ui
653 # copy selected local settings to the remote ui
653 for sect in ('auth', 'hostfingerprints', 'http_proxy'):
654 for sect in ('auth', 'hostfingerprints', 'http_proxy'):
654 for key, val in src.configitems(sect):
655 for key, val in src.configitems(sect):
655 dst.setconfig(sect, key, val, 'copied')
656 dst.setconfig(sect, key, val, 'copied')
656 v = src.config('web', 'cacerts')
657 v = src.config('web', 'cacerts')
657 if v:
658 if v:
658 dst.setconfig('web', 'cacerts', util.expandpath(v), 'copied')
659 dst.setconfig('web', 'cacerts', util.expandpath(v), 'copied')
659
660
660 return dst
661 return dst
General Comments 0
You need to be logged in to leave comments. Login now