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