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