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