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