##// END OF EJS Templates
hg: better wrapping of string literal
Martin Geisler -
r9171:a458b9bf default
parent child Browse files
Show More
@@ -1,363 +1,363
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, incorporated herein by reference.
7 # GNU General Public License version 2, incorporated herein by reference.
8
8
9 from i18n import _
9 from i18n import _
10 from lock import release
10 from lock import release
11 import localrepo, bundlerepo, httprepo, sshrepo, statichttprepo
11 import localrepo, bundlerepo, httprepo, sshrepo, statichttprepo
12 import lock, util, extensions, error
12 import lock, util, extensions, error
13 import merge as _merge
13 import merge as _merge
14 import verify as _verify
14 import verify as _verify
15 import errno, os, shutil
15 import errno, os, shutil
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), revs and revs[-1] or None
25 return url, (revs or None), revs and revs[-1] or None
26
26
27 url, branch = url.split('#', 1)
27 url, branch = url.split('#', 1)
28 checkout = revs and revs[-1] or branch
28 checkout = revs and revs[-1] or branch
29 return url, (revs or []) + [branch], checkout
29 return url, (revs or []) + [branch], checkout
30
30
31 schemes = {
31 schemes = {
32 'bundle': bundlerepo,
32 'bundle': bundlerepo,
33 'file': _local,
33 'file': _local,
34 'http': httprepo,
34 'http': httprepo,
35 'https': httprepo,
35 'https': httprepo,
36 'ssh': sshrepo,
36 'ssh': sshrepo,
37 'static-http': statichttprepo,
37 'static-http': statichttprepo,
38 }
38 }
39
39
40 def _lookup(path):
40 def _lookup(path):
41 scheme = 'file'
41 scheme = 'file'
42 if path:
42 if path:
43 c = path.find(':')
43 c = path.find(':')
44 if c > 0:
44 if c > 0:
45 scheme = path[:c]
45 scheme = path[:c]
46 thing = schemes.get(scheme) or schemes['file']
46 thing = schemes.get(scheme) or schemes['file']
47 try:
47 try:
48 return thing(path)
48 return thing(path)
49 except TypeError:
49 except TypeError:
50 return thing
50 return thing
51
51
52 def islocal(repo):
52 def islocal(repo):
53 '''return true if repo or path is local'''
53 '''return true if repo or path is local'''
54 if isinstance(repo, str):
54 if isinstance(repo, str):
55 try:
55 try:
56 return _lookup(repo).islocal(repo)
56 return _lookup(repo).islocal(repo)
57 except AttributeError:
57 except AttributeError:
58 return False
58 return False
59 return repo.local()
59 return repo.local()
60
60
61 def repository(ui, path='', create=False):
61 def repository(ui, path='', create=False):
62 """return a repository object for the specified path"""
62 """return a repository object for the specified path"""
63 repo = _lookup(path).instance(ui, path, create)
63 repo = _lookup(path).instance(ui, path, create)
64 ui = getattr(repo, "ui", ui)
64 ui = getattr(repo, "ui", ui)
65 for name, module in extensions.extensions():
65 for name, module in extensions.extensions():
66 hook = getattr(module, 'reposetup', None)
66 hook = getattr(module, 'reposetup', None)
67 if hook:
67 if hook:
68 hook(ui, repo)
68 hook(ui, repo)
69 return repo
69 return repo
70
70
71 def defaultdest(source):
71 def defaultdest(source):
72 '''return default destination of clone if none is given'''
72 '''return default destination of clone if none is given'''
73 return os.path.basename(os.path.normpath(source))
73 return os.path.basename(os.path.normpath(source))
74
74
75 def localpath(path):
75 def localpath(path):
76 if path.startswith('file://localhost/'):
76 if path.startswith('file://localhost/'):
77 return path[16:]
77 return path[16:]
78 if path.startswith('file://'):
78 if path.startswith('file://'):
79 return path[7:]
79 return path[7:]
80 if path.startswith('file:'):
80 if path.startswith('file:'):
81 return path[5:]
81 return path[5:]
82 return path
82 return path
83
83
84 def share(ui, source, dest=None, update=True):
84 def share(ui, source, dest=None, update=True):
85 '''create a shared repository'''
85 '''create a shared repository'''
86
86
87 if not islocal(source):
87 if not islocal(source):
88 raise util.Abort(_('can only share local repositories'))
88 raise util.Abort(_('can only share local repositories'))
89
89
90 if not dest:
90 if not dest:
91 dest = os.path.basename(source)
91 dest = os.path.basename(source)
92
92
93 if isinstance(source, str):
93 if isinstance(source, str):
94 origsource = ui.expandpath(source)
94 origsource = ui.expandpath(source)
95 source, rev, checkout = parseurl(origsource, '')
95 source, rev, checkout = parseurl(origsource, '')
96 srcrepo = repository(ui, source)
96 srcrepo = repository(ui, source)
97 else:
97 else:
98 srcrepo = source
98 srcrepo = source
99 origsource = source = srcrepo.url()
99 origsource = source = srcrepo.url()
100 checkout = None
100 checkout = None
101
101
102 sharedpath = srcrepo.sharedpath # if our source is already sharing
102 sharedpath = srcrepo.sharedpath # if our source is already sharing
103
103
104 root = os.path.realpath(dest)
104 root = os.path.realpath(dest)
105 roothg = os.path.join(root, '.hg')
105 roothg = os.path.join(root, '.hg')
106
106
107 if os.path.exists(roothg):
107 if os.path.exists(roothg):
108 raise util.Abort(_('destination already exists'))
108 raise util.Abort(_('destination already exists'))
109
109
110 if not os.path.isdir(root):
110 if not os.path.isdir(root):
111 os.mkdir(root)
111 os.mkdir(root)
112 os.mkdir(roothg)
112 os.mkdir(roothg)
113
113
114 requirements = ''
114 requirements = ''
115 try:
115 try:
116 requirements = srcrepo.opener('requires').read()
116 requirements = srcrepo.opener('requires').read()
117 except IOError, inst:
117 except IOError, inst:
118 if inst.errno != errno.ENOENT:
118 if inst.errno != errno.ENOENT:
119 raise
119 raise
120
120
121 requirements += 'shared\n'
121 requirements += 'shared\n'
122 file(os.path.join(roothg, 'requires'), 'w').write(requirements)
122 file(os.path.join(roothg, 'requires'), 'w').write(requirements)
123 file(os.path.join(roothg, 'sharedpath'), 'w').write(sharedpath)
123 file(os.path.join(roothg, 'sharedpath'), 'w').write(sharedpath)
124
124
125 default = srcrepo.ui.config('paths', 'default')
125 default = srcrepo.ui.config('paths', 'default')
126 if default:
126 if default:
127 f = file(os.path.join(roothg, 'hgrc'), 'w')
127 f = file(os.path.join(roothg, 'hgrc'), 'w')
128 f.write('[paths]\ndefault = %s\n' % default)
128 f.write('[paths]\ndefault = %s\n' % default)
129 f.close()
129 f.close()
130
130
131 r = repository(ui, root)
131 r = repository(ui, root)
132
132
133 if update:
133 if update:
134 r.ui.status(_("updating working directory\n"))
134 r.ui.status(_("updating working directory\n"))
135 if update is not True:
135 if update is not True:
136 checkout = update
136 checkout = update
137 for test in (checkout, 'default', 'tip'):
137 for test in (checkout, 'default', 'tip'):
138 try:
138 try:
139 uprev = r.lookup(test)
139 uprev = r.lookup(test)
140 break
140 break
141 except:
141 except:
142 continue
142 continue
143 _update(r, uprev)
143 _update(r, uprev)
144
144
145 def clone(ui, source, dest=None, pull=False, rev=None, update=True,
145 def clone(ui, source, dest=None, pull=False, rev=None, update=True,
146 stream=False):
146 stream=False):
147 """Make a copy of an existing repository.
147 """Make a copy of an existing repository.
148
148
149 Create a copy of an existing repository in a new directory. The
149 Create a copy of an existing repository in a new directory. The
150 source and destination are URLs, as passed to the repository
150 source and destination are URLs, as passed to the repository
151 function. Returns a pair of repository objects, the source and
151 function. Returns a pair of repository objects, the source and
152 newly created destination.
152 newly created destination.
153
153
154 The location of the source is added to the new repository's
154 The location of the source is added to the new repository's
155 .hg/hgrc file, as the default to be used for future pulls and
155 .hg/hgrc file, as the default to be used for future pulls and
156 pushes.
156 pushes.
157
157
158 If an exception is raised, the partly cloned/updated destination
158 If an exception is raised, the partly cloned/updated destination
159 repository will be deleted.
159 repository will be deleted.
160
160
161 Arguments:
161 Arguments:
162
162
163 source: repository object or URL
163 source: repository object or URL
164
164
165 dest: URL of destination repository to create (defaults to base
165 dest: URL of destination repository to create (defaults to base
166 name of source repository)
166 name of source repository)
167
167
168 pull: always pull from source repository, even in local case
168 pull: always pull from source repository, even in local case
169
169
170 stream: stream raw data uncompressed from repository (fast over
170 stream: stream raw data uncompressed from repository (fast over
171 LAN, slow over WAN)
171 LAN, slow over WAN)
172
172
173 rev: revision to clone up to (implies pull=True)
173 rev: revision to clone up to (implies pull=True)
174
174
175 update: update working directory after clone completes, if
175 update: update working directory after clone completes, if
176 destination is local repository (True means update to default rev,
176 destination is local repository (True means update to default rev,
177 anything else is treated as a revision)
177 anything else is treated as a revision)
178 """
178 """
179
179
180 if isinstance(source, str):
180 if isinstance(source, str):
181 origsource = ui.expandpath(source)
181 origsource = ui.expandpath(source)
182 source, rev, checkout = parseurl(origsource, rev)
182 source, rev, checkout = parseurl(origsource, rev)
183 src_repo = repository(ui, source)
183 src_repo = repository(ui, source)
184 else:
184 else:
185 src_repo = source
185 src_repo = source
186 origsource = source = src_repo.url()
186 origsource = source = src_repo.url()
187 checkout = rev and rev[-1] or None
187 checkout = rev and rev[-1] or None
188
188
189 if dest is None:
189 if dest is None:
190 dest = defaultdest(source)
190 dest = defaultdest(source)
191 ui.status(_("destination directory: %s\n") % dest)
191 ui.status(_("destination directory: %s\n") % dest)
192
192
193 dest = localpath(dest)
193 dest = localpath(dest)
194 source = localpath(source)
194 source = localpath(source)
195
195
196 if os.path.exists(dest):
196 if os.path.exists(dest):
197 if not os.path.isdir(dest):
197 if not os.path.isdir(dest):
198 raise util.Abort(_("destination '%s' already exists") % dest)
198 raise util.Abort(_("destination '%s' already exists") % dest)
199 elif os.listdir(dest):
199 elif os.listdir(dest):
200 raise util.Abort(_("destination '%s' is not empty") % dest)
200 raise util.Abort(_("destination '%s' is not empty") % dest)
201
201
202 class DirCleanup(object):
202 class DirCleanup(object):
203 def __init__(self, dir_):
203 def __init__(self, dir_):
204 self.rmtree = shutil.rmtree
204 self.rmtree = shutil.rmtree
205 self.dir_ = dir_
205 self.dir_ = dir_
206 def close(self):
206 def close(self):
207 self.dir_ = None
207 self.dir_ = None
208 def cleanup(self):
208 def cleanup(self):
209 if self.dir_:
209 if self.dir_:
210 self.rmtree(self.dir_, True)
210 self.rmtree(self.dir_, True)
211
211
212 src_lock = dest_lock = dir_cleanup = None
212 src_lock = dest_lock = dir_cleanup = None
213 try:
213 try:
214 if islocal(dest):
214 if islocal(dest):
215 dir_cleanup = DirCleanup(dest)
215 dir_cleanup = DirCleanup(dest)
216
216
217 abspath = origsource
217 abspath = origsource
218 copy = False
218 copy = False
219 if src_repo.cancopy() and islocal(dest):
219 if src_repo.cancopy() and islocal(dest):
220 abspath = os.path.abspath(util.drop_scheme('file', origsource))
220 abspath = os.path.abspath(util.drop_scheme('file', origsource))
221 copy = not pull and not rev
221 copy = not pull and not rev
222
222
223 if copy:
223 if copy:
224 try:
224 try:
225 # we use a lock here because if we race with commit, we
225 # we use a lock here because if we race with commit, we
226 # can end up with extra data in the cloned revlogs that's
226 # can end up with extra data in the cloned revlogs that's
227 # not pointed to by changesets, thus causing verify to
227 # not pointed to by changesets, thus causing verify to
228 # fail
228 # fail
229 src_lock = src_repo.lock(wait=False)
229 src_lock = src_repo.lock(wait=False)
230 except error.LockError:
230 except error.LockError:
231 copy = False
231 copy = False
232
232
233 if copy:
233 if copy:
234 src_repo.hook('preoutgoing', throw=True, source='clone')
234 src_repo.hook('preoutgoing', throw=True, source='clone')
235 hgdir = os.path.realpath(os.path.join(dest, ".hg"))
235 hgdir = os.path.realpath(os.path.join(dest, ".hg"))
236 if not os.path.exists(dest):
236 if not os.path.exists(dest):
237 os.mkdir(dest)
237 os.mkdir(dest)
238 else:
238 else:
239 # only clean up directories we create ourselves
239 # only clean up directories we create ourselves
240 dir_cleanup.dir_ = hgdir
240 dir_cleanup.dir_ = hgdir
241 try:
241 try:
242 dest_path = hgdir
242 dest_path = hgdir
243 os.mkdir(dest_path)
243 os.mkdir(dest_path)
244 except OSError, inst:
244 except OSError, inst:
245 if inst.errno == errno.EEXIST:
245 if inst.errno == errno.EEXIST:
246 dir_cleanup.close()
246 dir_cleanup.close()
247 raise util.Abort(_("destination '%s' already exists")
247 raise util.Abort(_("destination '%s' already exists")
248 % dest)
248 % dest)
249 raise
249 raise
250
250
251 for f in src_repo.store.copylist():
251 for f in src_repo.store.copylist():
252 src = os.path.join(src_repo.path, f)
252 src = os.path.join(src_repo.path, f)
253 dst = os.path.join(dest_path, f)
253 dst = os.path.join(dest_path, f)
254 dstbase = os.path.dirname(dst)
254 dstbase = os.path.dirname(dst)
255 if dstbase and not os.path.exists(dstbase):
255 if dstbase and not os.path.exists(dstbase):
256 os.mkdir(dstbase)
256 os.mkdir(dstbase)
257 if os.path.exists(src):
257 if os.path.exists(src):
258 if dst.endswith('data'):
258 if dst.endswith('data'):
259 # lock to avoid premature writing to the target
259 # lock to avoid premature writing to the target
260 dest_lock = lock.lock(os.path.join(dstbase, "lock"))
260 dest_lock = lock.lock(os.path.join(dstbase, "lock"))
261 util.copyfiles(src, dst)
261 util.copyfiles(src, dst)
262
262
263 # we need to re-init the repo after manually copying the data
263 # we need to re-init the repo after manually copying the data
264 # into it
264 # into it
265 dest_repo = repository(ui, dest)
265 dest_repo = repository(ui, dest)
266 src_repo.hook('outgoing', source='clone', node='0'*40)
266 src_repo.hook('outgoing', source='clone', node='0'*40)
267 else:
267 else:
268 try:
268 try:
269 dest_repo = repository(ui, dest, create=True)
269 dest_repo = repository(ui, dest, create=True)
270 except OSError, inst:
270 except OSError, inst:
271 if inst.errno == errno.EEXIST:
271 if inst.errno == errno.EEXIST:
272 dir_cleanup.close()
272 dir_cleanup.close()
273 raise util.Abort(_("destination '%s' already exists")
273 raise util.Abort(_("destination '%s' already exists")
274 % dest)
274 % dest)
275 raise
275 raise
276
276
277 revs = None
277 revs = None
278 if rev:
278 if rev:
279 if 'lookup' not in src_repo.capabilities:
279 if 'lookup' not in src_repo.capabilities:
280 raise util.Abort(_("src repository does not support revision "
280 raise util.Abort(_("src repository does not support "
281 "lookup and so doesn't support clone by "
281 "revision lookup and so doesn't "
282 "revision"))
282 "support clone by revision"))
283 revs = [src_repo.lookup(r) for r in rev]
283 revs = [src_repo.lookup(r) for r in rev]
284 checkout = revs[0]
284 checkout = revs[0]
285 if dest_repo.local():
285 if dest_repo.local():
286 dest_repo.clone(src_repo, heads=revs, stream=stream)
286 dest_repo.clone(src_repo, heads=revs, stream=stream)
287 elif src_repo.local():
287 elif src_repo.local():
288 src_repo.push(dest_repo, revs=revs)
288 src_repo.push(dest_repo, revs=revs)
289 else:
289 else:
290 raise util.Abort(_("clone from remote to remote not supported"))
290 raise util.Abort(_("clone from remote to remote not supported"))
291
291
292 if dir_cleanup:
292 if dir_cleanup:
293 dir_cleanup.close()
293 dir_cleanup.close()
294
294
295 if dest_repo.local():
295 if dest_repo.local():
296 fp = dest_repo.opener("hgrc", "w", text=True)
296 fp = dest_repo.opener("hgrc", "w", text=True)
297 fp.write("[paths]\n")
297 fp.write("[paths]\n")
298 fp.write("default = %s\n" % abspath)
298 fp.write("default = %s\n" % abspath)
299 fp.close()
299 fp.close()
300
300
301 dest_repo.ui.setconfig('paths', 'default', abspath)
301 dest_repo.ui.setconfig('paths', 'default', abspath)
302
302
303 if update:
303 if update:
304 dest_repo.ui.status(_("updating working directory\n"))
304 dest_repo.ui.status(_("updating working directory\n"))
305 if update is not True:
305 if update is not True:
306 checkout = update
306 checkout = update
307 for test in (checkout, 'default', 'tip'):
307 for test in (checkout, 'default', 'tip'):
308 try:
308 try:
309 uprev = dest_repo.lookup(test)
309 uprev = dest_repo.lookup(test)
310 break
310 break
311 except:
311 except:
312 continue
312 continue
313 _update(dest_repo, uprev)
313 _update(dest_repo, uprev)
314
314
315 return src_repo, dest_repo
315 return src_repo, dest_repo
316 finally:
316 finally:
317 release(src_lock, dest_lock)
317 release(src_lock, dest_lock)
318 if dir_cleanup is not None:
318 if dir_cleanup is not None:
319 dir_cleanup.cleanup()
319 dir_cleanup.cleanup()
320
320
321 def _showstats(repo, stats):
321 def _showstats(repo, stats):
322 stats = ((stats[0], _("updated")),
322 stats = ((stats[0], _("updated")),
323 (stats[1], _("merged")),
323 (stats[1], _("merged")),
324 (stats[2], _("removed")),
324 (stats[2], _("removed")),
325 (stats[3], _("unresolved")))
325 (stats[3], _("unresolved")))
326 note = ", ".join([_("%d files %s") % s for s in stats])
326 note = ", ".join([_("%d files %s") % s for s in stats])
327 repo.ui.status("%s\n" % note)
327 repo.ui.status("%s\n" % note)
328
328
329 def update(repo, node):
329 def update(repo, node):
330 """update the working directory to node, merging linear changes"""
330 """update the working directory to node, merging linear changes"""
331 stats = _merge.update(repo, node, False, False, None)
331 stats = _merge.update(repo, node, False, False, None)
332 _showstats(repo, stats)
332 _showstats(repo, stats)
333 if stats[3]:
333 if stats[3]:
334 repo.ui.status(_("use 'hg resolve' to retry unresolved file merges\n"))
334 repo.ui.status(_("use 'hg resolve' to retry unresolved file merges\n"))
335 return stats[3] > 0
335 return stats[3] > 0
336
336
337 # naming conflict in clone()
337 # naming conflict in clone()
338 _update = update
338 _update = update
339
339
340 def clean(repo, node, show_stats=True):
340 def clean(repo, node, show_stats=True):
341 """forcibly switch the working directory to node, clobbering changes"""
341 """forcibly switch the working directory to node, clobbering changes"""
342 stats = _merge.update(repo, node, False, True, None)
342 stats = _merge.update(repo, node, False, True, None)
343 if show_stats: _showstats(repo, stats)
343 if show_stats: _showstats(repo, stats)
344 return stats[3] > 0
344 return stats[3] > 0
345
345
346 def merge(repo, node, force=None, remind=True):
346 def merge(repo, node, force=None, remind=True):
347 """branch merge with node, resolving changes"""
347 """branch merge with node, resolving changes"""
348 stats = _merge.update(repo, node, True, force, False)
348 stats = _merge.update(repo, node, True, force, False)
349 _showstats(repo, stats)
349 _showstats(repo, stats)
350 if stats[3]:
350 if stats[3]:
351 repo.ui.status(_("use 'hg resolve' to retry unresolved file merges "
351 repo.ui.status(_("use 'hg resolve' to retry unresolved file merges "
352 "or 'hg up --clean' to abandon\n"))
352 "or 'hg up --clean' to abandon\n"))
353 elif remind:
353 elif remind:
354 repo.ui.status(_("(branch merge, don't forget to commit)\n"))
354 repo.ui.status(_("(branch merge, don't forget to commit)\n"))
355 return stats[3] > 0
355 return stats[3] > 0
356
356
357 def revert(repo, node, choose):
357 def revert(repo, node, choose):
358 """revert changes to revision in node without updating dirstate"""
358 """revert changes to revision in node without updating dirstate"""
359 return _merge.update(repo, node, False, True, choose)[3] > 0
359 return _merge.update(repo, node, False, True, choose)[3] > 0
360
360
361 def verify(repo):
361 def verify(repo):
362 """verify the consistency of a repository"""
362 """verify the consistency of a repository"""
363 return _verify.verify(repo)
363 return _verify.verify(repo)
General Comments 0
You need to be logged in to leave comments. Login now