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