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