##// END OF EJS Templates
Allow "file://localhost/" in addition to "file:///" (issue728)...
Thomas Arendsen Hein -
r6165:0d36de68 default
parent child Browse files
Show More
@@ -1,311 +1,313 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
6 # This software may be used and distributed according to the terms
7 # of the GNU General Public License, incorporated herein by reference.
7 # of the GNU General Public License, incorporated herein by reference.
8
8
9 from node import *
9 from node import *
10 from repo import *
10 from repo import *
11 from i18n import _
11 from i18n import _
12 import localrepo, bundlerepo, httprepo, sshrepo, statichttprepo
12 import localrepo, bundlerepo, httprepo, sshrepo, statichttprepo
13 import errno, lock, os, shutil, util, extensions
13 import errno, lock, os, shutil, util, extensions
14 import merge as _merge
14 import merge as _merge
15 import verify as _verify
15 import verify as _verify
16
16
17 def _local(path):
17 def _local(path):
18 return (os.path.isfile(util.drop_scheme('file', path)) and
18 return (os.path.isfile(util.drop_scheme('file', path)) and
19 bundlerepo or localrepo)
19 bundlerepo or localrepo)
20
20
21 def parseurl(url, revs):
21 def parseurl(url, revs):
22 '''parse url#branch, returning url, branch + revs'''
22 '''parse url#branch, returning url, branch + revs'''
23
23
24 if '#' not in url:
24 if '#' not in url:
25 return url, (revs or None), None
25 return url, (revs or None), None
26
26
27 url, rev = url.split('#', 1)
27 url, rev = url.split('#', 1)
28 return url, revs + [rev], rev
28 return url, revs + [rev], rev
29
29
30 schemes = {
30 schemes = {
31 'bundle': bundlerepo,
31 'bundle': bundlerepo,
32 'file': _local,
32 'file': _local,
33 'http': httprepo,
33 'http': httprepo,
34 'https': httprepo,
34 'https': httprepo,
35 'ssh': sshrepo,
35 'ssh': sshrepo,
36 'static-http': statichttprepo,
36 'static-http': statichttprepo,
37 }
37 }
38
38
39 def _lookup(path):
39 def _lookup(path):
40 scheme = 'file'
40 scheme = 'file'
41 if path:
41 if path:
42 c = path.find(':')
42 c = path.find(':')
43 if c > 0:
43 if c > 0:
44 scheme = path[:c]
44 scheme = path[:c]
45 thing = schemes.get(scheme) or schemes['file']
45 thing = schemes.get(scheme) or schemes['file']
46 try:
46 try:
47 return thing(path)
47 return thing(path)
48 except TypeError:
48 except TypeError:
49 return thing
49 return thing
50
50
51 def islocal(repo):
51 def islocal(repo):
52 '''return true if repo or path is local'''
52 '''return true if repo or path is local'''
53 if isinstance(repo, str):
53 if isinstance(repo, str):
54 try:
54 try:
55 return _lookup(repo).islocal(repo)
55 return _lookup(repo).islocal(repo)
56 except AttributeError:
56 except AttributeError:
57 return False
57 return False
58 return repo.local()
58 return repo.local()
59
59
60 def repository(ui, path='', create=False):
60 def repository(ui, path='', create=False):
61 """return a repository object for the specified path"""
61 """return a repository object for the specified path"""
62 repo = _lookup(path).instance(ui, path, create)
62 repo = _lookup(path).instance(ui, path, create)
63 ui = getattr(repo, "ui", ui)
63 ui = getattr(repo, "ui", ui)
64 for name, module in extensions.extensions():
64 for name, module in extensions.extensions():
65 hook = getattr(module, 'reposetup', None)
65 hook = getattr(module, 'reposetup', None)
66 if hook:
66 if hook:
67 hook(ui, repo)
67 hook(ui, repo)
68 return repo
68 return repo
69
69
70 def defaultdest(source):
70 def defaultdest(source):
71 '''return default destination of clone if none is given'''
71 '''return default destination of clone if none is given'''
72 return os.path.basename(os.path.normpath(source))
72 return os.path.basename(os.path.normpath(source))
73
73
74 def clone(ui, source, dest=None, pull=False, rev=None, update=True,
74 def clone(ui, source, dest=None, pull=False, rev=None, update=True,
75 stream=False):
75 stream=False):
76 """Make a copy of an existing repository.
76 """Make a copy of an existing repository.
77
77
78 Create a copy of an existing repository in a new directory. The
78 Create a copy of an existing repository in a new directory. The
79 source and destination are URLs, as passed to the repository
79 source and destination are URLs, as passed to the repository
80 function. Returns a pair of repository objects, the source and
80 function. Returns a pair of repository objects, the source and
81 newly created destination.
81 newly created destination.
82
82
83 The location of the source is added to the new repository's
83 The location of the source is added to the new repository's
84 .hg/hgrc file, as the default to be used for future pulls and
84 .hg/hgrc file, as the default to be used for future pulls and
85 pushes.
85 pushes.
86
86
87 If an exception is raised, the partly cloned/updated destination
87 If an exception is raised, the partly cloned/updated destination
88 repository will be deleted.
88 repository will be deleted.
89
89
90 Arguments:
90 Arguments:
91
91
92 source: repository object or URL
92 source: repository object or URL
93
93
94 dest: URL of destination repository to create (defaults to base
94 dest: URL of destination repository to create (defaults to base
95 name of source repository)
95 name of source repository)
96
96
97 pull: always pull from source repository, even in local case
97 pull: always pull from source repository, even in local case
98
98
99 stream: stream raw data uncompressed from repository (fast over
99 stream: stream raw data uncompressed from repository (fast over
100 LAN, slow over WAN)
100 LAN, slow over WAN)
101
101
102 rev: revision to clone up to (implies pull=True)
102 rev: revision to clone up to (implies pull=True)
103
103
104 update: update working directory after clone completes, if
104 update: update working directory after clone completes, if
105 destination is local repository
105 destination is local repository
106 """
106 """
107
107
108 if isinstance(source, str):
108 if isinstance(source, str):
109 origsource = ui.expandpath(source)
109 origsource = ui.expandpath(source)
110 source, rev, checkout = parseurl(origsource, rev)
110 source, rev, checkout = parseurl(origsource, rev)
111 src_repo = repository(ui, source)
111 src_repo = repository(ui, source)
112 else:
112 else:
113 src_repo = source
113 src_repo = source
114 origsource = source = src_repo.url()
114 origsource = source = src_repo.url()
115 checkout = None
115 checkout = None
116
116
117 if dest is None:
117 if dest is None:
118 dest = defaultdest(source)
118 dest = defaultdest(source)
119 ui.status(_("destination directory: %s\n") % dest)
119 ui.status(_("destination directory: %s\n") % dest)
120
120
121 def localpath(path):
121 def localpath(path):
122 if path.startswith('file://localhost/'):
123 return path[16:]
122 if path.startswith('file://'):
124 if path.startswith('file://'):
123 return path[7:]
125 return path[7:]
124 if path.startswith('file:'):
126 if path.startswith('file:'):
125 return path[5:]
127 return path[5:]
126 return path
128 return path
127
129
128 dest = localpath(dest)
130 dest = localpath(dest)
129 source = localpath(source)
131 source = localpath(source)
130
132
131 if os.path.exists(dest):
133 if os.path.exists(dest):
132 raise util.Abort(_("destination '%s' already exists") % dest)
134 raise util.Abort(_("destination '%s' already exists") % dest)
133
135
134 class DirCleanup(object):
136 class DirCleanup(object):
135 def __init__(self, dir_):
137 def __init__(self, dir_):
136 self.rmtree = shutil.rmtree
138 self.rmtree = shutil.rmtree
137 self.dir_ = dir_
139 self.dir_ = dir_
138 def close(self):
140 def close(self):
139 self.dir_ = None
141 self.dir_ = None
140 def __del__(self):
142 def __del__(self):
141 if self.dir_:
143 if self.dir_:
142 self.rmtree(self.dir_, True)
144 self.rmtree(self.dir_, True)
143
145
144 src_lock = dest_lock = dir_cleanup = None
146 src_lock = dest_lock = dir_cleanup = None
145 try:
147 try:
146 if islocal(dest):
148 if islocal(dest):
147 dir_cleanup = DirCleanup(dest)
149 dir_cleanup = DirCleanup(dest)
148
150
149 abspath = origsource
151 abspath = origsource
150 copy = False
152 copy = False
151 if src_repo.local() and islocal(dest):
153 if src_repo.local() and islocal(dest):
152 abspath = os.path.abspath(util.drop_scheme('file', origsource))
154 abspath = os.path.abspath(util.drop_scheme('file', origsource))
153 copy = not pull and not rev
155 copy = not pull and not rev
154
156
155 if copy:
157 if copy:
156 try:
158 try:
157 # we use a lock here because if we race with commit, we
159 # we use a lock here because if we race with commit, we
158 # can end up with extra data in the cloned revlogs that's
160 # can end up with extra data in the cloned revlogs that's
159 # not pointed to by changesets, thus causing verify to
161 # not pointed to by changesets, thus causing verify to
160 # fail
162 # fail
161 src_lock = src_repo.lock()
163 src_lock = src_repo.lock()
162 except lock.LockException:
164 except lock.LockException:
163 copy = False
165 copy = False
164
166
165 if copy:
167 if copy:
166 def force_copy(src, dst):
168 def force_copy(src, dst):
167 if not os.path.exists(src):
169 if not os.path.exists(src):
168 # Tolerate empty source repository and optional files
170 # Tolerate empty source repository and optional files
169 return
171 return
170 util.copyfiles(src, dst)
172 util.copyfiles(src, dst)
171
173
172 src_store = os.path.realpath(src_repo.spath)
174 src_store = os.path.realpath(src_repo.spath)
173 if not os.path.exists(dest):
175 if not os.path.exists(dest):
174 os.mkdir(dest)
176 os.mkdir(dest)
175 try:
177 try:
176 dest_path = os.path.realpath(os.path.join(dest, ".hg"))
178 dest_path = os.path.realpath(os.path.join(dest, ".hg"))
177 os.mkdir(dest_path)
179 os.mkdir(dest_path)
178 except OSError, inst:
180 except OSError, inst:
179 if inst.errno == errno.EEXIST:
181 if inst.errno == errno.EEXIST:
180 dir_cleanup.close()
182 dir_cleanup.close()
181 raise util.Abort(_("destination '%s' already exists")
183 raise util.Abort(_("destination '%s' already exists")
182 % dest)
184 % dest)
183 raise
185 raise
184 if src_repo.spath != src_repo.path:
186 if src_repo.spath != src_repo.path:
185 # XXX racy
187 # XXX racy
186 dummy_changelog = os.path.join(dest_path, "00changelog.i")
188 dummy_changelog = os.path.join(dest_path, "00changelog.i")
187 # copy the dummy changelog
189 # copy the dummy changelog
188 force_copy(src_repo.join("00changelog.i"), dummy_changelog)
190 force_copy(src_repo.join("00changelog.i"), dummy_changelog)
189 dest_store = os.path.join(dest_path, "store")
191 dest_store = os.path.join(dest_path, "store")
190 os.mkdir(dest_store)
192 os.mkdir(dest_store)
191 else:
193 else:
192 dest_store = dest_path
194 dest_store = dest_path
193 # copy the requires file
195 # copy the requires file
194 force_copy(src_repo.join("requires"),
196 force_copy(src_repo.join("requires"),
195 os.path.join(dest_path, "requires"))
197 os.path.join(dest_path, "requires"))
196 # we lock here to avoid premature writing to the target
198 # we lock here to avoid premature writing to the target
197 dest_lock = lock.lock(os.path.join(dest_store, "lock"))
199 dest_lock = lock.lock(os.path.join(dest_store, "lock"))
198
200
199 files = ("data",
201 files = ("data",
200 "00manifest.d", "00manifest.i",
202 "00manifest.d", "00manifest.i",
201 "00changelog.d", "00changelog.i")
203 "00changelog.d", "00changelog.i")
202 for f in files:
204 for f in files:
203 src = os.path.join(src_store, f)
205 src = os.path.join(src_store, f)
204 dst = os.path.join(dest_store, f)
206 dst = os.path.join(dest_store, f)
205 force_copy(src, dst)
207 force_copy(src, dst)
206
208
207 # we need to re-init the repo after manually copying the data
209 # we need to re-init the repo after manually copying the data
208 # into it
210 # into it
209 dest_repo = repository(ui, dest)
211 dest_repo = repository(ui, dest)
210
212
211 else:
213 else:
212 try:
214 try:
213 dest_repo = repository(ui, dest, create=True)
215 dest_repo = repository(ui, dest, create=True)
214 except OSError, inst:
216 except OSError, inst:
215 if inst.errno == errno.EEXIST:
217 if inst.errno == errno.EEXIST:
216 dir_cleanup.close()
218 dir_cleanup.close()
217 raise util.Abort(_("destination '%s' already exists")
219 raise util.Abort(_("destination '%s' already exists")
218 % dest)
220 % dest)
219 raise
221 raise
220
222
221 revs = None
223 revs = None
222 if rev:
224 if rev:
223 if 'lookup' not in src_repo.capabilities:
225 if 'lookup' not in src_repo.capabilities:
224 raise util.Abort(_("src repository does not support revision "
226 raise util.Abort(_("src repository does not support revision "
225 "lookup and so doesn't support clone by "
227 "lookup and so doesn't support clone by "
226 "revision"))
228 "revision"))
227 revs = [src_repo.lookup(r) for r in rev]
229 revs = [src_repo.lookup(r) for r in rev]
228
230
229 if dest_repo.local():
231 if dest_repo.local():
230 dest_repo.clone(src_repo, heads=revs, stream=stream)
232 dest_repo.clone(src_repo, heads=revs, stream=stream)
231 elif src_repo.local():
233 elif src_repo.local():
232 src_repo.push(dest_repo, revs=revs)
234 src_repo.push(dest_repo, revs=revs)
233 else:
235 else:
234 raise util.Abort(_("clone from remote to remote not supported"))
236 raise util.Abort(_("clone from remote to remote not supported"))
235
237
236 if dir_cleanup:
238 if dir_cleanup:
237 dir_cleanup.close()
239 dir_cleanup.close()
238
240
239 if dest_repo.local():
241 if dest_repo.local():
240 fp = dest_repo.opener("hgrc", "w", text=True)
242 fp = dest_repo.opener("hgrc", "w", text=True)
241 fp.write("[paths]\n")
243 fp.write("[paths]\n")
242 fp.write("default = %s\n" % abspath)
244 fp.write("default = %s\n" % abspath)
243 fp.close()
245 fp.close()
244
246
245 if update:
247 if update:
246 if not checkout:
248 if not checkout:
247 try:
249 try:
248 checkout = dest_repo.lookup("default")
250 checkout = dest_repo.lookup("default")
249 except:
251 except:
250 checkout = dest_repo.changelog.tip()
252 checkout = dest_repo.changelog.tip()
251 _update(dest_repo, checkout)
253 _update(dest_repo, checkout)
252
254
253 return src_repo, dest_repo
255 return src_repo, dest_repo
254 finally:
256 finally:
255 del src_lock, dest_lock, dir_cleanup
257 del src_lock, dest_lock, dir_cleanup
256
258
257 def _showstats(repo, stats):
259 def _showstats(repo, stats):
258 stats = ((stats[0], _("updated")),
260 stats = ((stats[0], _("updated")),
259 (stats[1], _("merged")),
261 (stats[1], _("merged")),
260 (stats[2], _("removed")),
262 (stats[2], _("removed")),
261 (stats[3], _("unresolved")))
263 (stats[3], _("unresolved")))
262 note = ", ".join([_("%d files %s") % s for s in stats])
264 note = ", ".join([_("%d files %s") % s for s in stats])
263 repo.ui.status("%s\n" % note)
265 repo.ui.status("%s\n" % note)
264
266
265 def _update(repo, node): return update(repo, node)
267 def _update(repo, node): return update(repo, node)
266
268
267 def update(repo, node):
269 def update(repo, node):
268 """update the working directory to node, merging linear changes"""
270 """update the working directory to node, merging linear changes"""
269 pl = repo.parents()
271 pl = repo.parents()
270 stats = _merge.update(repo, node, False, False, None)
272 stats = _merge.update(repo, node, False, False, None)
271 _showstats(repo, stats)
273 _showstats(repo, stats)
272 if stats[3]:
274 if stats[3]:
273 repo.ui.status(_("There are unresolved merges with"
275 repo.ui.status(_("There are unresolved merges with"
274 " locally modified files.\n"))
276 " locally modified files.\n"))
275 if stats[1]:
277 if stats[1]:
276 repo.ui.status(_("You can finish the partial merge using:\n"))
278 repo.ui.status(_("You can finish the partial merge using:\n"))
277 else:
279 else:
278 repo.ui.status(_("You can redo the full merge using:\n"))
280 repo.ui.status(_("You can redo the full merge using:\n"))
279 # len(pl)==1, otherwise _merge.update() would have raised util.Abort:
281 # len(pl)==1, otherwise _merge.update() would have raised util.Abort:
280 repo.ui.status(_(" hg update %s\n hg update %s\n")
282 repo.ui.status(_(" hg update %s\n hg update %s\n")
281 % (pl[0].rev(), repo.changectx(node).rev()))
283 % (pl[0].rev(), repo.changectx(node).rev()))
282 return stats[3] > 0
284 return stats[3] > 0
283
285
284 def clean(repo, node, show_stats=True):
286 def clean(repo, node, show_stats=True):
285 """forcibly switch the working directory to node, clobbering changes"""
287 """forcibly switch the working directory to node, clobbering changes"""
286 stats = _merge.update(repo, node, False, True, None)
288 stats = _merge.update(repo, node, False, True, None)
287 if show_stats: _showstats(repo, stats)
289 if show_stats: _showstats(repo, stats)
288 return stats[3] > 0
290 return stats[3] > 0
289
291
290 def merge(repo, node, force=None, remind=True):
292 def merge(repo, node, force=None, remind=True):
291 """branch merge with node, resolving changes"""
293 """branch merge with node, resolving changes"""
292 stats = _merge.update(repo, node, True, force, False)
294 stats = _merge.update(repo, node, True, force, False)
293 _showstats(repo, stats)
295 _showstats(repo, stats)
294 if stats[3]:
296 if stats[3]:
295 pl = repo.parents()
297 pl = repo.parents()
296 repo.ui.status(_("There are unresolved merges,"
298 repo.ui.status(_("There are unresolved merges,"
297 " you can redo the full merge using:\n"
299 " you can redo the full merge using:\n"
298 " hg update -C %s\n"
300 " hg update -C %s\n"
299 " hg merge %s\n")
301 " hg merge %s\n")
300 % (pl[0].rev(), pl[1].rev()))
302 % (pl[0].rev(), pl[1].rev()))
301 elif remind:
303 elif remind:
302 repo.ui.status(_("(branch merge, don't forget to commit)\n"))
304 repo.ui.status(_("(branch merge, don't forget to commit)\n"))
303 return stats[3] > 0
305 return stats[3] > 0
304
306
305 def revert(repo, node, choose):
307 def revert(repo, node, choose):
306 """revert changes to revision in node without updating dirstate"""
308 """revert changes to revision in node without updating dirstate"""
307 return _merge.update(repo, node, False, True, choose)[3] > 0
309 return _merge.update(repo, node, False, True, choose)[3] > 0
308
310
309 def verify(repo):
311 def verify(repo):
310 """verify the consistency of a repository"""
312 """verify the consistency of a repository"""
311 return _verify.verify(repo)
313 return _verify.verify(repo)
General Comments 0
You need to be logged in to leave comments. Login now