##// END OF EJS Templates
bookmarks: primarily use repo lock, not wlock...
Mads Kiilerich -
r15887:12dea4d9 default
parent child Browse files
Show More
@@ -1,229 +1,229 b''
1 # Mercurial bookmark support code
1 # Mercurial bookmark support code
2 #
2 #
3 # Copyright 2008 David Soria Parra <dsp@php.net>
3 # Copyright 2008 David Soria Parra <dsp@php.net>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from mercurial.i18n import _
8 from mercurial.i18n import _
9 from mercurial.node import hex
9 from mercurial.node import hex
10 from mercurial import encoding, error, util
10 from mercurial import encoding, error, util
11 import errno, os
11 import errno, os
12
12
13 def valid(mark):
13 def valid(mark):
14 for c in (':', '\0', '\n', '\r'):
14 for c in (':', '\0', '\n', '\r'):
15 if c in mark:
15 if c in mark:
16 return False
16 return False
17 return True
17 return True
18
18
19 def read(repo):
19 def read(repo):
20 '''Parse .hg/bookmarks file and return a dictionary
20 '''Parse .hg/bookmarks file and return a dictionary
21
21
22 Bookmarks are stored as {HASH}\\s{NAME}\\n (localtags format) values
22 Bookmarks are stored as {HASH}\\s{NAME}\\n (localtags format) values
23 in the .hg/bookmarks file.
23 in the .hg/bookmarks file.
24 Read the file and return a (name=>nodeid) dictionary
24 Read the file and return a (name=>nodeid) dictionary
25 '''
25 '''
26 bookmarks = {}
26 bookmarks = {}
27 try:
27 try:
28 for line in repo.opener('bookmarks'):
28 for line in repo.opener('bookmarks'):
29 line = line.strip()
29 line = line.strip()
30 if not line:
30 if not line:
31 continue
31 continue
32 if ' ' not in line:
32 if ' ' not in line:
33 repo.ui.warn(_('malformed line in .hg/bookmarks: %r\n') % line)
33 repo.ui.warn(_('malformed line in .hg/bookmarks: %r\n') % line)
34 continue
34 continue
35 sha, refspec = line.split(' ', 1)
35 sha, refspec = line.split(' ', 1)
36 refspec = encoding.tolocal(refspec)
36 refspec = encoding.tolocal(refspec)
37 try:
37 try:
38 bookmarks[refspec] = repo.changelog.lookup(sha)
38 bookmarks[refspec] = repo.changelog.lookup(sha)
39 except error.RepoLookupError:
39 except error.RepoLookupError:
40 pass
40 pass
41 except IOError, inst:
41 except IOError, inst:
42 if inst.errno != errno.ENOENT:
42 if inst.errno != errno.ENOENT:
43 raise
43 raise
44 return bookmarks
44 return bookmarks
45
45
46 def readcurrent(repo):
46 def readcurrent(repo):
47 '''Get the current bookmark
47 '''Get the current bookmark
48
48
49 If we use gittishsh branches we have a current bookmark that
49 If we use gittishsh branches we have a current bookmark that
50 we are on. This function returns the name of the bookmark. It
50 we are on. This function returns the name of the bookmark. It
51 is stored in .hg/bookmarks.current
51 is stored in .hg/bookmarks.current
52 '''
52 '''
53 mark = None
53 mark = None
54 try:
54 try:
55 file = repo.opener('bookmarks.current')
55 file = repo.opener('bookmarks.current')
56 except IOError, inst:
56 except IOError, inst:
57 if inst.errno != errno.ENOENT:
57 if inst.errno != errno.ENOENT:
58 raise
58 raise
59 return None
59 return None
60 try:
60 try:
61 # No readline() in posixfile_nt, reading everything is cheap
61 # No readline() in posixfile_nt, reading everything is cheap
62 mark = encoding.tolocal((file.readlines() or [''])[0])
62 mark = encoding.tolocal((file.readlines() or [''])[0])
63 if mark == '' or mark not in repo._bookmarks:
63 if mark == '' or mark not in repo._bookmarks:
64 mark = None
64 mark = None
65 finally:
65 finally:
66 file.close()
66 file.close()
67 return mark
67 return mark
68
68
69 def write(repo):
69 def write(repo):
70 '''Write bookmarks
70 '''Write bookmarks
71
71
72 Write the given bookmark => hash dictionary to the .hg/bookmarks file
72 Write the given bookmark => hash dictionary to the .hg/bookmarks file
73 in a format equal to those of localtags.
73 in a format equal to those of localtags.
74
74
75 We also store a backup of the previous state in undo.bookmarks that
75 We also store a backup of the previous state in undo.bookmarks that
76 can be copied back on rollback.
76 can be copied back on rollback.
77 '''
77 '''
78 refs = repo._bookmarks
78 refs = repo._bookmarks
79
79
80 if repo._bookmarkcurrent not in refs:
80 if repo._bookmarkcurrent not in refs:
81 setcurrent(repo, None)
81 setcurrent(repo, None)
82 for mark in refs.keys():
82 for mark in refs.keys():
83 if not valid(mark):
83 if not valid(mark):
84 raise util.Abort(_("bookmark '%s' contains illegal "
84 raise util.Abort(_("bookmark '%s' contains illegal "
85 "character" % mark))
85 "character" % mark))
86
86
87 wlock = repo.wlock()
87 lock = repo.lock()
88 try:
88 try:
89
89
90 file = repo.opener('bookmarks', 'w', atomictemp=True)
90 file = repo.opener('bookmarks', 'w', atomictemp=True)
91 for refspec, node in refs.iteritems():
91 for refspec, node in refs.iteritems():
92 file.write("%s %s\n" % (hex(node), encoding.fromlocal(refspec)))
92 file.write("%s %s\n" % (hex(node), encoding.fromlocal(refspec)))
93 file.close()
93 file.close()
94
94
95 # touch 00changelog.i so hgweb reloads bookmarks (no lock needed)
95 # touch 00changelog.i so hgweb reloads bookmarks (no lock needed)
96 try:
96 try:
97 os.utime(repo.sjoin('00changelog.i'), None)
97 os.utime(repo.sjoin('00changelog.i'), None)
98 except OSError:
98 except OSError:
99 pass
99 pass
100
100
101 finally:
101 finally:
102 wlock.release()
102 lock.release()
103
103
104 def setcurrent(repo, mark):
104 def setcurrent(repo, mark):
105 '''Set the name of the bookmark that we are currently on
105 '''Set the name of the bookmark that we are currently on
106
106
107 Set the name of the bookmark that we are on (hg update <bookmark>).
107 Set the name of the bookmark that we are on (hg update <bookmark>).
108 The name is recorded in .hg/bookmarks.current
108 The name is recorded in .hg/bookmarks.current
109 '''
109 '''
110 current = repo._bookmarkcurrent
110 current = repo._bookmarkcurrent
111 if current == mark:
111 if current == mark:
112 return
112 return
113
113
114 if mark not in repo._bookmarks:
114 if mark not in repo._bookmarks:
115 mark = ''
115 mark = ''
116 if not valid(mark):
116 if not valid(mark):
117 raise util.Abort(_("bookmark '%s' contains illegal "
117 raise util.Abort(_("bookmark '%s' contains illegal "
118 "character" % mark))
118 "character" % mark))
119
119
120 wlock = repo.wlock()
120 lock = repo.lock()
121 try:
121 try:
122 file = repo.opener('bookmarks.current', 'w', atomictemp=True)
122 file = repo.opener('bookmarks.current', 'w', atomictemp=True)
123 file.write(encoding.fromlocal(mark))
123 file.write(encoding.fromlocal(mark))
124 file.close()
124 file.close()
125 finally:
125 finally:
126 wlock.release()
126 lock.release()
127 repo._bookmarkcurrent = mark
127 repo._bookmarkcurrent = mark
128
128
129 def updatecurrentbookmark(repo, oldnode, curbranch):
129 def updatecurrentbookmark(repo, oldnode, curbranch):
130 try:
130 try:
131 return update(repo, oldnode, repo.branchtags()[curbranch])
131 return update(repo, oldnode, repo.branchtags()[curbranch])
132 except KeyError:
132 except KeyError:
133 if curbranch == "default": # no default branch!
133 if curbranch == "default": # no default branch!
134 return update(repo, oldnode, repo.lookup("tip"))
134 return update(repo, oldnode, repo.lookup("tip"))
135 else:
135 else:
136 raise util.Abort(_("branch %s not found") % curbranch)
136 raise util.Abort(_("branch %s not found") % curbranch)
137
137
138 def update(repo, parents, node):
138 def update(repo, parents, node):
139 marks = repo._bookmarks
139 marks = repo._bookmarks
140 update = False
140 update = False
141 mark = repo._bookmarkcurrent
141 mark = repo._bookmarkcurrent
142 if mark and marks[mark] in parents:
142 if mark and marks[mark] in parents:
143 old = repo[marks[mark]]
143 old = repo[marks[mark]]
144 new = repo[node]
144 new = repo[node]
145 if new in old.descendants():
145 if new in old.descendants():
146 marks[mark] = new.node()
146 marks[mark] = new.node()
147 update = True
147 update = True
148 if update:
148 if update:
149 repo._writebookmarks(marks)
149 repo._writebookmarks(marks)
150 return update
150 return update
151
151
152 def listbookmarks(repo):
152 def listbookmarks(repo):
153 # We may try to list bookmarks on a repo type that does not
153 # We may try to list bookmarks on a repo type that does not
154 # support it (e.g., statichttprepository).
154 # support it (e.g., statichttprepository).
155 marks = getattr(repo, '_bookmarks', {})
155 marks = getattr(repo, '_bookmarks', {})
156
156
157 d = {}
157 d = {}
158 for k, v in marks.iteritems():
158 for k, v in marks.iteritems():
159 # don't expose local divergent bookmarks
159 # don't expose local divergent bookmarks
160 if '@' not in k and not k.endswith('@'):
160 if '@' not in k and not k.endswith('@'):
161 d[k] = hex(v)
161 d[k] = hex(v)
162 return d
162 return d
163
163
164 def pushbookmark(repo, key, old, new):
164 def pushbookmark(repo, key, old, new):
165 w = repo.wlock()
165 lock = repo.lock()
166 try:
166 try:
167 marks = repo._bookmarks
167 marks = repo._bookmarks
168 if hex(marks.get(key, '')) != old:
168 if hex(marks.get(key, '')) != old:
169 return False
169 return False
170 if new == '':
170 if new == '':
171 del marks[key]
171 del marks[key]
172 else:
172 else:
173 if new not in repo:
173 if new not in repo:
174 return False
174 return False
175 marks[key] = repo[new].node()
175 marks[key] = repo[new].node()
176 write(repo)
176 write(repo)
177 return True
177 return True
178 finally:
178 finally:
179 w.release()
179 lock.release()
180
180
181 def updatefromremote(ui, repo, remote, path):
181 def updatefromremote(ui, repo, remote, path):
182 ui.debug("checking for updated bookmarks\n")
182 ui.debug("checking for updated bookmarks\n")
183 rb = remote.listkeys('bookmarks')
183 rb = remote.listkeys('bookmarks')
184 changed = False
184 changed = False
185 for k in rb.keys():
185 for k in rb.keys():
186 if k in repo._bookmarks:
186 if k in repo._bookmarks:
187 nr, nl = rb[k], repo._bookmarks[k]
187 nr, nl = rb[k], repo._bookmarks[k]
188 if nr in repo:
188 if nr in repo:
189 cr = repo[nr]
189 cr = repo[nr]
190 cl = repo[nl]
190 cl = repo[nl]
191 if cl.rev() >= cr.rev():
191 if cl.rev() >= cr.rev():
192 continue
192 continue
193 if cr in cl.descendants():
193 if cr in cl.descendants():
194 repo._bookmarks[k] = cr.node()
194 repo._bookmarks[k] = cr.node()
195 changed = True
195 changed = True
196 ui.status(_("updating bookmark %s\n") % k)
196 ui.status(_("updating bookmark %s\n") % k)
197 else:
197 else:
198 # find a unique @ suffix
198 # find a unique @ suffix
199 for x in range(1, 100):
199 for x in range(1, 100):
200 n = '%s@%d' % (k, x)
200 n = '%s@%d' % (k, x)
201 if n not in repo._bookmarks:
201 if n not in repo._bookmarks:
202 break
202 break
203 # try to use an @pathalias suffix
203 # try to use an @pathalias suffix
204 # if an @pathalias already exists, we overwrite (update) it
204 # if an @pathalias already exists, we overwrite (update) it
205 for p, u in ui.configitems("paths"):
205 for p, u in ui.configitems("paths"):
206 if path == u:
206 if path == u:
207 n = '%s@%s' % (k, p)
207 n = '%s@%s' % (k, p)
208
208
209 repo._bookmarks[n] = cr.node()
209 repo._bookmarks[n] = cr.node()
210 changed = True
210 changed = True
211 ui.warn(_("divergent bookmark %s stored as %s\n") % (k, n))
211 ui.warn(_("divergent bookmark %s stored as %s\n") % (k, n))
212
212
213 if changed:
213 if changed:
214 write(repo)
214 write(repo)
215
215
216 def diff(ui, repo, remote):
216 def diff(ui, repo, remote):
217 ui.status(_("searching for changed bookmarks\n"))
217 ui.status(_("searching for changed bookmarks\n"))
218
218
219 lmarks = repo.listkeys('bookmarks')
219 lmarks = repo.listkeys('bookmarks')
220 rmarks = remote.listkeys('bookmarks')
220 rmarks = remote.listkeys('bookmarks')
221
221
222 diff = sorted(set(rmarks) - set(lmarks))
222 diff = sorted(set(rmarks) - set(lmarks))
223 for k in diff:
223 for k in diff:
224 ui.write(" %-25s %s\n" % (k, rmarks[k][:12]))
224 ui.write(" %-25s %s\n" % (k, rmarks[k][:12]))
225
225
226 if len(diff) <= 0:
226 if len(diff) <= 0:
227 ui.status(_("no changed bookmarks found\n"))
227 ui.status(_("no changed bookmarks found\n"))
228 return 1
228 return 1
229 return 0
229 return 0
@@ -1,584 +1,589 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(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 srcpublishing = srcrepo.ui.configbool('phases', 'publish', True)
186 srcpublishing = srcrepo.ui.configbool('phases', 'publish', True)
187 for f in srcrepo.store.copylist():
187 for f in srcrepo.store.copylist():
188 if srcpublishing and f.endswith('phaseroots'):
188 if srcpublishing and f.endswith('phaseroots'):
189 continue
189 continue
190 src = os.path.join(srcrepo.sharedpath, f)
190 src = os.path.join(srcrepo.sharedpath, f)
191 dst = os.path.join(destpath, f)
191 dst = os.path.join(destpath, f)
192 dstbase = os.path.dirname(dst)
192 dstbase = os.path.dirname(dst)
193 if dstbase and not os.path.exists(dstbase):
193 if dstbase and not os.path.exists(dstbase):
194 os.mkdir(dstbase)
194 os.mkdir(dstbase)
195 if os.path.exists(src):
195 if os.path.exists(src):
196 if dst.endswith('data'):
196 if dst.endswith('data'):
197 # lock to avoid premature writing to the target
197 # lock to avoid premature writing to the target
198 destlock = lock.lock(os.path.join(dstbase, "lock"))
198 destlock = lock.lock(os.path.join(dstbase, "lock"))
199 hardlink, n = util.copyfiles(src, dst, hardlink)
199 hardlink, n = util.copyfiles(src, dst, hardlink)
200 num += n
200 num += n
201 if hardlink:
201 if hardlink:
202 ui.debug("linked %d files\n" % num)
202 ui.debug("linked %d files\n" % num)
203 else:
203 else:
204 ui.debug("copied %d files\n" % num)
204 ui.debug("copied %d files\n" % num)
205 return destlock
205 return destlock
206 except:
206 except:
207 release(destlock)
207 release(destlock)
208 raise
208 raise
209
209
210 def clone(ui, peeropts, source, dest=None, pull=False, rev=None,
210 def clone(ui, peeropts, source, dest=None, pull=False, rev=None,
211 update=True, stream=False, branch=None):
211 update=True, stream=False, branch=None):
212 """Make a copy of an existing repository.
212 """Make a copy of an existing repository.
213
213
214 Create a copy of an existing repository in a new directory. The
214 Create a copy of an existing repository in a new directory. The
215 source and destination are URLs, as passed to the repository
215 source and destination are URLs, as passed to the repository
216 function. Returns a pair of repository objects, the source and
216 function. Returns a pair of repository objects, the source and
217 newly created destination.
217 newly created destination.
218
218
219 The location of the source is added to the new repository's
219 The location of the source is added to the new repository's
220 .hg/hgrc file, as the default to be used for future pulls and
220 .hg/hgrc file, as the default to be used for future pulls and
221 pushes.
221 pushes.
222
222
223 If an exception is raised, the partly cloned/updated destination
223 If an exception is raised, the partly cloned/updated destination
224 repository will be deleted.
224 repository will be deleted.
225
225
226 Arguments:
226 Arguments:
227
227
228 source: repository object or URL
228 source: repository object or URL
229
229
230 dest: URL of destination repository to create (defaults to base
230 dest: URL of destination repository to create (defaults to base
231 name of source repository)
231 name of source repository)
232
232
233 pull: always pull from source repository, even in local case
233 pull: always pull from source repository, even in local case
234
234
235 stream: stream raw data uncompressed from repository (fast over
235 stream: stream raw data uncompressed from repository (fast over
236 LAN, slow over WAN)
236 LAN, slow over WAN)
237
237
238 rev: revision to clone up to (implies pull=True)
238 rev: revision to clone up to (implies pull=True)
239
239
240 update: update working directory after clone completes, if
240 update: update working directory after clone completes, if
241 destination is local repository (True means update to default rev,
241 destination is local repository (True means update to default rev,
242 anything else is treated as a revision)
242 anything else is treated as a revision)
243
243
244 branch: branches to clone
244 branch: branches to clone
245 """
245 """
246
246
247 if isinstance(source, str):
247 if isinstance(source, str):
248 origsource = ui.expandpath(source)
248 origsource = ui.expandpath(source)
249 source, branch = parseurl(origsource, branch)
249 source, branch = parseurl(origsource, branch)
250 srcrepo = repository(remoteui(ui, peeropts), source)
250 srcrepo = repository(remoteui(ui, peeropts), source)
251 else:
251 else:
252 srcrepo = source
252 srcrepo = source
253 branch = (None, branch or [])
253 branch = (None, branch or [])
254 origsource = source = srcrepo.url()
254 origsource = source = srcrepo.url()
255 rev, checkout = addbranchrevs(srcrepo, srcrepo, branch, rev)
255 rev, checkout = addbranchrevs(srcrepo, srcrepo, branch, rev)
256
256
257 if dest is None:
257 if dest is None:
258 dest = defaultdest(source)
258 dest = defaultdest(source)
259 ui.status(_("destination directory: %s\n") % dest)
259 ui.status(_("destination directory: %s\n") % dest)
260 else:
260 else:
261 dest = ui.expandpath(dest)
261 dest = ui.expandpath(dest)
262
262
263 dest = util.urllocalpath(dest)
263 dest = util.urllocalpath(dest)
264 source = util.urllocalpath(source)
264 source = util.urllocalpath(source)
265
265
266 if os.path.exists(dest):
266 if os.path.exists(dest):
267 if not os.path.isdir(dest):
267 if not os.path.isdir(dest):
268 raise util.Abort(_("destination '%s' already exists") % dest)
268 raise util.Abort(_("destination '%s' already exists") % dest)
269 elif os.listdir(dest):
269 elif os.listdir(dest):
270 raise util.Abort(_("destination '%s' is not empty") % dest)
270 raise util.Abort(_("destination '%s' is not empty") % dest)
271
271
272 class DirCleanup(object):
272 class DirCleanup(object):
273 def __init__(self, dir_):
273 def __init__(self, dir_):
274 self.rmtree = shutil.rmtree
274 self.rmtree = shutil.rmtree
275 self.dir_ = dir_
275 self.dir_ = dir_
276 def close(self):
276 def close(self):
277 self.dir_ = None
277 self.dir_ = None
278 def cleanup(self):
278 def cleanup(self):
279 if self.dir_:
279 if self.dir_:
280 self.rmtree(self.dir_, True)
280 self.rmtree(self.dir_, True)
281
281
282 srclock = destlock = dircleanup = None
282 srclock = destwlock = destlock = dircleanup = None
283 try:
283 try:
284 abspath = origsource
284 abspath = origsource
285 if islocal(origsource):
285 if islocal(origsource):
286 abspath = os.path.abspath(util.urllocalpath(origsource))
286 abspath = os.path.abspath(util.urllocalpath(origsource))
287
287
288 if islocal(dest):
288 if islocal(dest):
289 dircleanup = DirCleanup(dest)
289 dircleanup = DirCleanup(dest)
290
290
291 copy = False
291 copy = False
292 if srcrepo.cancopy() and islocal(dest):
292 if srcrepo.cancopy() and islocal(dest):
293 copy = not pull and not rev
293 copy = not pull and not rev
294
294
295 if copy:
295 if copy:
296 try:
296 try:
297 # we use a lock here because if we race with commit, we
297 # we use a lock here because if we race with commit, we
298 # can end up with extra data in the cloned revlogs that's
298 # can end up with extra data in the cloned revlogs that's
299 # not pointed to by changesets, thus causing verify to
299 # not pointed to by changesets, thus causing verify to
300 # fail
300 # fail
301 srclock = srcrepo.lock(wait=False)
301 srclock = srcrepo.lock(wait=False)
302 except error.LockError:
302 except error.LockError:
303 copy = False
303 copy = False
304
304
305 if copy:
305 if copy:
306 srcrepo.hook('preoutgoing', throw=True, source='clone')
306 srcrepo.hook('preoutgoing', throw=True, source='clone')
307 hgdir = os.path.realpath(os.path.join(dest, ".hg"))
307 hgdir = os.path.realpath(os.path.join(dest, ".hg"))
308 if not os.path.exists(dest):
308 if not os.path.exists(dest):
309 os.mkdir(dest)
309 os.mkdir(dest)
310 else:
310 else:
311 # only clean up directories we create ourselves
311 # only clean up directories we create ourselves
312 dircleanup.dir_ = hgdir
312 dircleanup.dir_ = hgdir
313 try:
313 try:
314 destpath = hgdir
314 destpath = hgdir
315 util.makedir(destpath, notindexed=True)
315 util.makedir(destpath, notindexed=True)
316 except OSError, inst:
316 except OSError, inst:
317 if inst.errno == errno.EEXIST:
317 if inst.errno == errno.EEXIST:
318 dircleanup.close()
318 dircleanup.close()
319 raise util.Abort(_("destination '%s' already exists")
319 raise util.Abort(_("destination '%s' already exists")
320 % dest)
320 % dest)
321 raise
321 raise
322
322
323 destlock = copystore(ui, srcrepo, destpath)
323 destlock = copystore(ui, srcrepo, destpath)
324
324
325 # we need to re-init the repo after manually copying the data
325 # we need to re-init the repo after manually copying the data
326 # into it
326 # into it
327 destrepo = repository(remoteui(ui, peeropts), dest)
327 destrepo = repository(remoteui(ui, peeropts), dest)
328 # we need full recursive locking of the new repo instance
329 destwlock = destrepo.wlock()
330 if destlock:
331 destlock.release() # a little race condition - but no deadlock
332 destlock = destrepo.lock()
328 srcrepo.hook('outgoing', source='clone',
333 srcrepo.hook('outgoing', source='clone',
329 node=node.hex(node.nullid))
334 node=node.hex(node.nullid))
330 else:
335 else:
331 try:
336 try:
332 destrepo = repository(remoteui(ui, peeropts), dest,
337 destrepo = repository(remoteui(ui, peeropts), dest,
333 create=True)
338 create=True)
334 except OSError, inst:
339 except OSError, inst:
335 if inst.errno == errno.EEXIST:
340 if inst.errno == errno.EEXIST:
336 dircleanup.close()
341 dircleanup.close()
337 raise util.Abort(_("destination '%s' already exists")
342 raise util.Abort(_("destination '%s' already exists")
338 % dest)
343 % dest)
339 raise
344 raise
340
345
341 revs = None
346 revs = None
342 if rev:
347 if rev:
343 if not srcrepo.capable('lookup'):
348 if not srcrepo.capable('lookup'):
344 raise util.Abort(_("src repository does not support "
349 raise util.Abort(_("src repository does not support "
345 "revision lookup and so doesn't "
350 "revision lookup and so doesn't "
346 "support clone by revision"))
351 "support clone by revision"))
347 revs = [srcrepo.lookup(r) for r in rev]
352 revs = [srcrepo.lookup(r) for r in rev]
348 checkout = revs[0]
353 checkout = revs[0]
349 if destrepo.local():
354 if destrepo.local():
350 destrepo.clone(srcrepo, heads=revs, stream=stream)
355 destrepo.clone(srcrepo, heads=revs, stream=stream)
351 elif srcrepo.local():
356 elif srcrepo.local():
352 srcrepo.push(destrepo, revs=revs)
357 srcrepo.push(destrepo, revs=revs)
353 else:
358 else:
354 raise util.Abort(_("clone from remote to remote not supported"))
359 raise util.Abort(_("clone from remote to remote not supported"))
355
360
356 if dircleanup:
361 if dircleanup:
357 dircleanup.close()
362 dircleanup.close()
358
363
359 # clone all bookmarks
364 # clone all bookmarks
360 if destrepo.local() and srcrepo.capable("pushkey"):
365 if destrepo.local() and srcrepo.capable("pushkey"):
361 rb = srcrepo.listkeys('bookmarks')
366 rb = srcrepo.listkeys('bookmarks')
362 for k, n in rb.iteritems():
367 for k, n in rb.iteritems():
363 try:
368 try:
364 m = destrepo.lookup(n)
369 m = destrepo.lookup(n)
365 destrepo._bookmarks[k] = m
370 destrepo._bookmarks[k] = m
366 except error.RepoLookupError:
371 except error.RepoLookupError:
367 pass
372 pass
368 if rb:
373 if rb:
369 bookmarks.write(destrepo)
374 bookmarks.write(destrepo)
370 elif srcrepo.local() and destrepo.capable("pushkey"):
375 elif srcrepo.local() and destrepo.capable("pushkey"):
371 for k, n in srcrepo._bookmarks.iteritems():
376 for k, n in srcrepo._bookmarks.iteritems():
372 destrepo.pushkey('bookmarks', k, '', hex(n))
377 destrepo.pushkey('bookmarks', k, '', hex(n))
373
378
374 if destrepo.local():
379 if destrepo.local():
375 fp = destrepo.opener("hgrc", "w", text=True)
380 fp = destrepo.opener("hgrc", "w", text=True)
376 fp.write("[paths]\n")
381 fp.write("[paths]\n")
377 u = util.url(abspath)
382 u = util.url(abspath)
378 u.passwd = None
383 u.passwd = None
379 defaulturl = str(u)
384 defaulturl = str(u)
380 fp.write("default = %s\n" % defaulturl)
385 fp.write("default = %s\n" % defaulturl)
381 fp.close()
386 fp.close()
382
387
383 destrepo.ui.setconfig('paths', 'default', defaulturl)
388 destrepo.ui.setconfig('paths', 'default', defaulturl)
384
389
385 if update:
390 if update:
386 if update is not True:
391 if update is not True:
387 checkout = update
392 checkout = update
388 if srcrepo.local():
393 if srcrepo.local():
389 checkout = srcrepo.lookup(update)
394 checkout = srcrepo.lookup(update)
390 for test in (checkout, 'default', 'tip'):
395 for test in (checkout, 'default', 'tip'):
391 if test is None:
396 if test is None:
392 continue
397 continue
393 try:
398 try:
394 uprev = destrepo.lookup(test)
399 uprev = destrepo.lookup(test)
395 break
400 break
396 except error.RepoLookupError:
401 except error.RepoLookupError:
397 continue
402 continue
398 bn = destrepo[uprev].branch()
403 bn = destrepo[uprev].branch()
399 destrepo.ui.status(_("updating to branch %s\n") % bn)
404 destrepo.ui.status(_("updating to branch %s\n") % bn)
400 _update(destrepo, uprev)
405 _update(destrepo, uprev)
401
406
402 return srcrepo, destrepo
407 return srcrepo, destrepo
403 finally:
408 finally:
404 release(srclock, destlock)
409 release(srclock, destlock, destwlock)
405 if dircleanup is not None:
410 if dircleanup is not None:
406 dircleanup.cleanup()
411 dircleanup.cleanup()
407
412
408 def _showstats(repo, stats):
413 def _showstats(repo, stats):
409 repo.ui.status(_("%d files updated, %d files merged, "
414 repo.ui.status(_("%d files updated, %d files merged, "
410 "%d files removed, %d files unresolved\n") % stats)
415 "%d files removed, %d files unresolved\n") % stats)
411
416
412 def update(repo, node):
417 def update(repo, node):
413 """update the working directory to node, merging linear changes"""
418 """update the working directory to node, merging linear changes"""
414 stats = mergemod.update(repo, node, False, False, None)
419 stats = mergemod.update(repo, node, False, False, None)
415 _showstats(repo, stats)
420 _showstats(repo, stats)
416 if stats[3]:
421 if stats[3]:
417 repo.ui.status(_("use 'hg resolve' to retry unresolved file merges\n"))
422 repo.ui.status(_("use 'hg resolve' to retry unresolved file merges\n"))
418 return stats[3] > 0
423 return stats[3] > 0
419
424
420 # naming conflict in clone()
425 # naming conflict in clone()
421 _update = update
426 _update = update
422
427
423 def clean(repo, node, show_stats=True):
428 def clean(repo, node, show_stats=True):
424 """forcibly switch the working directory to node, clobbering changes"""
429 """forcibly switch the working directory to node, clobbering changes"""
425 stats = mergemod.update(repo, node, False, True, None)
430 stats = mergemod.update(repo, node, False, True, None)
426 if show_stats:
431 if show_stats:
427 _showstats(repo, stats)
432 _showstats(repo, stats)
428 return stats[3] > 0
433 return stats[3] > 0
429
434
430 def merge(repo, node, force=None, remind=True):
435 def merge(repo, node, force=None, remind=True):
431 """Branch merge with node, resolving changes. Return true if any
436 """Branch merge with node, resolving changes. Return true if any
432 unresolved conflicts."""
437 unresolved conflicts."""
433 stats = mergemod.update(repo, node, True, force, False)
438 stats = mergemod.update(repo, node, True, force, False)
434 _showstats(repo, stats)
439 _showstats(repo, stats)
435 if stats[3]:
440 if stats[3]:
436 repo.ui.status(_("use 'hg resolve' to retry unresolved file merges "
441 repo.ui.status(_("use 'hg resolve' to retry unresolved file merges "
437 "or 'hg update -C .' to abandon\n"))
442 "or 'hg update -C .' to abandon\n"))
438 elif remind:
443 elif remind:
439 repo.ui.status(_("(branch merge, don't forget to commit)\n"))
444 repo.ui.status(_("(branch merge, don't forget to commit)\n"))
440 return stats[3] > 0
445 return stats[3] > 0
441
446
442 def _incoming(displaychlist, subreporecurse, ui, repo, source,
447 def _incoming(displaychlist, subreporecurse, ui, repo, source,
443 opts, buffered=False):
448 opts, buffered=False):
444 """
449 """
445 Helper for incoming / gincoming.
450 Helper for incoming / gincoming.
446 displaychlist gets called with
451 displaychlist gets called with
447 (remoterepo, incomingchangesetlist, displayer) parameters,
452 (remoterepo, incomingchangesetlist, displayer) parameters,
448 and is supposed to contain only code that can't be unified.
453 and is supposed to contain only code that can't be unified.
449 """
454 """
450 source, branches = parseurl(ui.expandpath(source), opts.get('branch'))
455 source, branches = parseurl(ui.expandpath(source), opts.get('branch'))
451 other = peer(repo, opts, source)
456 other = peer(repo, opts, source)
452 ui.status(_('comparing with %s\n') % util.hidepassword(source))
457 ui.status(_('comparing with %s\n') % util.hidepassword(source))
453 revs, checkout = addbranchrevs(repo, other, branches, opts.get('rev'))
458 revs, checkout = addbranchrevs(repo, other, branches, opts.get('rev'))
454
459
455 if revs:
460 if revs:
456 revs = [other.lookup(rev) for rev in revs]
461 revs = [other.lookup(rev) for rev in revs]
457 other, chlist, cleanupfn = bundlerepo.getremotechanges(ui, repo, other,
462 other, chlist, cleanupfn = bundlerepo.getremotechanges(ui, repo, other,
458 revs, opts["bundle"], opts["force"])
463 revs, opts["bundle"], opts["force"])
459 try:
464 try:
460 if not chlist:
465 if not chlist:
461 ui.status(_("no changes found\n"))
466 ui.status(_("no changes found\n"))
462 return subreporecurse()
467 return subreporecurse()
463
468
464 displayer = cmdutil.show_changeset(ui, other, opts, buffered)
469 displayer = cmdutil.show_changeset(ui, other, opts, buffered)
465
470
466 # XXX once graphlog extension makes it into core,
471 # XXX once graphlog extension makes it into core,
467 # should be replaced by a if graph/else
472 # should be replaced by a if graph/else
468 displaychlist(other, chlist, displayer)
473 displaychlist(other, chlist, displayer)
469
474
470 displayer.close()
475 displayer.close()
471 finally:
476 finally:
472 cleanupfn()
477 cleanupfn()
473 subreporecurse()
478 subreporecurse()
474 return 0 # exit code is zero since we found incoming changes
479 return 0 # exit code is zero since we found incoming changes
475
480
476 def incoming(ui, repo, source, opts):
481 def incoming(ui, repo, source, opts):
477 def subreporecurse():
482 def subreporecurse():
478 ret = 1
483 ret = 1
479 if opts.get('subrepos'):
484 if opts.get('subrepos'):
480 ctx = repo[None]
485 ctx = repo[None]
481 for subpath in sorted(ctx.substate):
486 for subpath in sorted(ctx.substate):
482 sub = ctx.sub(subpath)
487 sub = ctx.sub(subpath)
483 ret = min(ret, sub.incoming(ui, source, opts))
488 ret = min(ret, sub.incoming(ui, source, opts))
484 return ret
489 return ret
485
490
486 def display(other, chlist, displayer):
491 def display(other, chlist, displayer):
487 limit = cmdutil.loglimit(opts)
492 limit = cmdutil.loglimit(opts)
488 if opts.get('newest_first'):
493 if opts.get('newest_first'):
489 chlist.reverse()
494 chlist.reverse()
490 count = 0
495 count = 0
491 for n in chlist:
496 for n in chlist:
492 if limit is not None and count >= limit:
497 if limit is not None and count >= limit:
493 break
498 break
494 parents = [p for p in other.changelog.parents(n) if p != nullid]
499 parents = [p for p in other.changelog.parents(n) if p != nullid]
495 if opts.get('no_merges') and len(parents) == 2:
500 if opts.get('no_merges') and len(parents) == 2:
496 continue
501 continue
497 count += 1
502 count += 1
498 displayer.show(other[n])
503 displayer.show(other[n])
499 return _incoming(display, subreporecurse, ui, repo, source, opts)
504 return _incoming(display, subreporecurse, ui, repo, source, opts)
500
505
501 def _outgoing(ui, repo, dest, opts):
506 def _outgoing(ui, repo, dest, opts):
502 dest = ui.expandpath(dest or 'default-push', dest or 'default')
507 dest = ui.expandpath(dest or 'default-push', dest or 'default')
503 dest, branches = parseurl(dest, opts.get('branch'))
508 dest, branches = parseurl(dest, opts.get('branch'))
504 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
509 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
505 revs, checkout = addbranchrevs(repo, repo, branches, opts.get('rev'))
510 revs, checkout = addbranchrevs(repo, repo, branches, opts.get('rev'))
506 if revs:
511 if revs:
507 revs = [repo.lookup(rev) for rev in revs]
512 revs = [repo.lookup(rev) for rev in revs]
508
513
509 other = peer(repo, opts, dest)
514 other = peer(repo, opts, dest)
510 outgoing = discovery.findcommonoutgoing(repo, other, revs,
515 outgoing = discovery.findcommonoutgoing(repo, other, revs,
511 force=opts.get('force'))
516 force=opts.get('force'))
512 o = outgoing.missing
517 o = outgoing.missing
513 if not o:
518 if not o:
514 ui.status(_("no changes found\n"))
519 ui.status(_("no changes found\n"))
515 return None
520 return None
516 return o
521 return o
517
522
518 def outgoing(ui, repo, dest, opts):
523 def outgoing(ui, repo, dest, opts):
519 def recurse():
524 def recurse():
520 ret = 1
525 ret = 1
521 if opts.get('subrepos'):
526 if opts.get('subrepos'):
522 ctx = repo[None]
527 ctx = repo[None]
523 for subpath in sorted(ctx.substate):
528 for subpath in sorted(ctx.substate):
524 sub = ctx.sub(subpath)
529 sub = ctx.sub(subpath)
525 ret = min(ret, sub.outgoing(ui, dest, opts))
530 ret = min(ret, sub.outgoing(ui, dest, opts))
526 return ret
531 return ret
527
532
528 limit = cmdutil.loglimit(opts)
533 limit = cmdutil.loglimit(opts)
529 o = _outgoing(ui, repo, dest, opts)
534 o = _outgoing(ui, repo, dest, opts)
530 if o is None:
535 if o is None:
531 return recurse()
536 return recurse()
532
537
533 if opts.get('newest_first'):
538 if opts.get('newest_first'):
534 o.reverse()
539 o.reverse()
535 displayer = cmdutil.show_changeset(ui, repo, opts)
540 displayer = cmdutil.show_changeset(ui, repo, opts)
536 count = 0
541 count = 0
537 for n in o:
542 for n in o:
538 if limit is not None and count >= limit:
543 if limit is not None and count >= limit:
539 break
544 break
540 parents = [p for p in repo.changelog.parents(n) if p != nullid]
545 parents = [p for p in repo.changelog.parents(n) if p != nullid]
541 if opts.get('no_merges') and len(parents) == 2:
546 if opts.get('no_merges') and len(parents) == 2:
542 continue
547 continue
543 count += 1
548 count += 1
544 displayer.show(repo[n])
549 displayer.show(repo[n])
545 displayer.close()
550 displayer.close()
546 recurse()
551 recurse()
547 return 0 # exit code is zero since we found outgoing changes
552 return 0 # exit code is zero since we found outgoing changes
548
553
549 def revert(repo, node, choose):
554 def revert(repo, node, choose):
550 """revert changes to revision in node without updating dirstate"""
555 """revert changes to revision in node without updating dirstate"""
551 return mergemod.update(repo, node, False, True, choose)[3] > 0
556 return mergemod.update(repo, node, False, True, choose)[3] > 0
552
557
553 def verify(repo):
558 def verify(repo):
554 """verify the consistency of a repository"""
559 """verify the consistency of a repository"""
555 return verifymod.verify(repo)
560 return verifymod.verify(repo)
556
561
557 def remoteui(src, opts):
562 def remoteui(src, opts):
558 'build a remote ui from ui or repo and opts'
563 'build a remote ui from ui or repo and opts'
559 if util.safehasattr(src, 'baseui'): # looks like a repository
564 if util.safehasattr(src, 'baseui'): # looks like a repository
560 dst = src.baseui.copy() # drop repo-specific config
565 dst = src.baseui.copy() # drop repo-specific config
561 src = src.ui # copy target options from repo
566 src = src.ui # copy target options from repo
562 else: # assume it's a global ui object
567 else: # assume it's a global ui object
563 dst = src.copy() # keep all global options
568 dst = src.copy() # keep all global options
564
569
565 # copy ssh-specific options
570 # copy ssh-specific options
566 for o in 'ssh', 'remotecmd':
571 for o in 'ssh', 'remotecmd':
567 v = opts.get(o) or src.config('ui', o)
572 v = opts.get(o) or src.config('ui', o)
568 if v:
573 if v:
569 dst.setconfig("ui", o, v)
574 dst.setconfig("ui", o, v)
570
575
571 # copy bundle-specific options
576 # copy bundle-specific options
572 r = src.config('bundle', 'mainreporoot')
577 r = src.config('bundle', 'mainreporoot')
573 if r:
578 if r:
574 dst.setconfig('bundle', 'mainreporoot', r)
579 dst.setconfig('bundle', 'mainreporoot', r)
575
580
576 # copy selected local settings to the remote ui
581 # copy selected local settings to the remote ui
577 for sect in ('auth', 'hostfingerprints', 'http_proxy'):
582 for sect in ('auth', 'hostfingerprints', 'http_proxy'):
578 for key, val in src.configitems(sect):
583 for key, val in src.configitems(sect):
579 dst.setconfig(sect, key, val)
584 dst.setconfig(sect, key, val)
580 v = src.config('web', 'cacerts')
585 v = src.config('web', 'cacerts')
581 if v:
586 if v:
582 dst.setconfig('web', 'cacerts', util.expandpath(v))
587 dst.setconfig('web', 'cacerts', util.expandpath(v))
583
588
584 return dst
589 return dst
General Comments 0
You need to be logged in to leave comments. Login now