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