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