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