##// END OF EJS Templates
bookmarks: backout locking change in 12dea4d998ec...
Matt Mackall -
r15908:60cb4f38 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 lock = repo.lock()
87 wlock = repo.wlock()
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 lock.release()
102 wlock.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 lock = repo.lock()
120 wlock = repo.wlock()
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 lock.release()
126 wlock.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 lock = repo.lock()
165 w = repo.wlock()
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 lock.release()
179 w.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,589 +1,584 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 = destwlock = destlock = dircleanup = None
282 srclock = 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()
333 srcrepo.hook('outgoing', source='clone',
328 srcrepo.hook('outgoing', source='clone',
334 node=node.hex(node.nullid))
329 node=node.hex(node.nullid))
335 else:
330 else:
336 try:
331 try:
337 destrepo = repository(remoteui(ui, peeropts), dest,
332 destrepo = repository(remoteui(ui, peeropts), dest,
338 create=True)
333 create=True)
339 except OSError, inst:
334 except OSError, inst:
340 if inst.errno == errno.EEXIST:
335 if inst.errno == errno.EEXIST:
341 dircleanup.close()
336 dircleanup.close()
342 raise util.Abort(_("destination '%s' already exists")
337 raise util.Abort(_("destination '%s' already exists")
343 % dest)
338 % dest)
344 raise
339 raise
345
340
346 revs = None
341 revs = None
347 if rev:
342 if rev:
348 if not srcrepo.capable('lookup'):
343 if not srcrepo.capable('lookup'):
349 raise util.Abort(_("src repository does not support "
344 raise util.Abort(_("src repository does not support "
350 "revision lookup and so doesn't "
345 "revision lookup and so doesn't "
351 "support clone by revision"))
346 "support clone by revision"))
352 revs = [srcrepo.lookup(r) for r in rev]
347 revs = [srcrepo.lookup(r) for r in rev]
353 checkout = revs[0]
348 checkout = revs[0]
354 if destrepo.local():
349 if destrepo.local():
355 destrepo.clone(srcrepo, heads=revs, stream=stream)
350 destrepo.clone(srcrepo, heads=revs, stream=stream)
356 elif srcrepo.local():
351 elif srcrepo.local():
357 srcrepo.push(destrepo, revs=revs)
352 srcrepo.push(destrepo, revs=revs)
358 else:
353 else:
359 raise util.Abort(_("clone from remote to remote not supported"))
354 raise util.Abort(_("clone from remote to remote not supported"))
360
355
361 if dircleanup:
356 if dircleanup:
362 dircleanup.close()
357 dircleanup.close()
363
358
364 # clone all bookmarks
359 # clone all bookmarks
365 if destrepo.local() and srcrepo.capable("pushkey"):
360 if destrepo.local() and srcrepo.capable("pushkey"):
366 rb = srcrepo.listkeys('bookmarks')
361 rb = srcrepo.listkeys('bookmarks')
367 for k, n in rb.iteritems():
362 for k, n in rb.iteritems():
368 try:
363 try:
369 m = destrepo.lookup(n)
364 m = destrepo.lookup(n)
370 destrepo._bookmarks[k] = m
365 destrepo._bookmarks[k] = m
371 except error.RepoLookupError:
366 except error.RepoLookupError:
372 pass
367 pass
373 if rb:
368 if rb:
374 bookmarks.write(destrepo)
369 bookmarks.write(destrepo)
375 elif srcrepo.local() and destrepo.capable("pushkey"):
370 elif srcrepo.local() and destrepo.capable("pushkey"):
376 for k, n in srcrepo._bookmarks.iteritems():
371 for k, n in srcrepo._bookmarks.iteritems():
377 destrepo.pushkey('bookmarks', k, '', hex(n))
372 destrepo.pushkey('bookmarks', k, '', hex(n))
378
373
379 if destrepo.local():
374 if destrepo.local():
380 fp = destrepo.opener("hgrc", "w", text=True)
375 fp = destrepo.opener("hgrc", "w", text=True)
381 fp.write("[paths]\n")
376 fp.write("[paths]\n")
382 u = util.url(abspath)
377 u = util.url(abspath)
383 u.passwd = None
378 u.passwd = None
384 defaulturl = str(u)
379 defaulturl = str(u)
385 fp.write("default = %s\n" % defaulturl)
380 fp.write("default = %s\n" % defaulturl)
386 fp.close()
381 fp.close()
387
382
388 destrepo.ui.setconfig('paths', 'default', defaulturl)
383 destrepo.ui.setconfig('paths', 'default', defaulturl)
389
384
390 if update:
385 if update:
391 if update is not True:
386 if update is not True:
392 checkout = update
387 checkout = update
393 if srcrepo.local():
388 if srcrepo.local():
394 checkout = srcrepo.lookup(update)
389 checkout = srcrepo.lookup(update)
395 for test in (checkout, 'default', 'tip'):
390 for test in (checkout, 'default', 'tip'):
396 if test is None:
391 if test is None:
397 continue
392 continue
398 try:
393 try:
399 uprev = destrepo.lookup(test)
394 uprev = destrepo.lookup(test)
400 break
395 break
401 except error.RepoLookupError:
396 except error.RepoLookupError:
402 continue
397 continue
403 bn = destrepo[uprev].branch()
398 bn = destrepo[uprev].branch()
404 destrepo.ui.status(_("updating to branch %s\n") % bn)
399 destrepo.ui.status(_("updating to branch %s\n") % bn)
405 _update(destrepo, uprev)
400 _update(destrepo, uprev)
406
401
407 return srcrepo, destrepo
402 return srcrepo, destrepo
408 finally:
403 finally:
409 release(srclock, destlock, destwlock)
404 release(srclock, destlock)
410 if dircleanup is not None:
405 if dircleanup is not None:
411 dircleanup.cleanup()
406 dircleanup.cleanup()
412
407
413 def _showstats(repo, stats):
408 def _showstats(repo, stats):
414 repo.ui.status(_("%d files updated, %d files merged, "
409 repo.ui.status(_("%d files updated, %d files merged, "
415 "%d files removed, %d files unresolved\n") % stats)
410 "%d files removed, %d files unresolved\n") % stats)
416
411
417 def update(repo, node):
412 def update(repo, node):
418 """update the working directory to node, merging linear changes"""
413 """update the working directory to node, merging linear changes"""
419 stats = mergemod.update(repo, node, False, False, None)
414 stats = mergemod.update(repo, node, False, False, None)
420 _showstats(repo, stats)
415 _showstats(repo, stats)
421 if stats[3]:
416 if stats[3]:
422 repo.ui.status(_("use 'hg resolve' to retry unresolved file merges\n"))
417 repo.ui.status(_("use 'hg resolve' to retry unresolved file merges\n"))
423 return stats[3] > 0
418 return stats[3] > 0
424
419
425 # naming conflict in clone()
420 # naming conflict in clone()
426 _update = update
421 _update = update
427
422
428 def clean(repo, node, show_stats=True):
423 def clean(repo, node, show_stats=True):
429 """forcibly switch the working directory to node, clobbering changes"""
424 """forcibly switch the working directory to node, clobbering changes"""
430 stats = mergemod.update(repo, node, False, True, None)
425 stats = mergemod.update(repo, node, False, True, None)
431 if show_stats:
426 if show_stats:
432 _showstats(repo, stats)
427 _showstats(repo, stats)
433 return stats[3] > 0
428 return stats[3] > 0
434
429
435 def merge(repo, node, force=None, remind=True):
430 def merge(repo, node, force=None, remind=True):
436 """Branch merge with node, resolving changes. Return true if any
431 """Branch merge with node, resolving changes. Return true if any
437 unresolved conflicts."""
432 unresolved conflicts."""
438 stats = mergemod.update(repo, node, True, force, False)
433 stats = mergemod.update(repo, node, True, force, False)
439 _showstats(repo, stats)
434 _showstats(repo, stats)
440 if stats[3]:
435 if stats[3]:
441 repo.ui.status(_("use 'hg resolve' to retry unresolved file merges "
436 repo.ui.status(_("use 'hg resolve' to retry unresolved file merges "
442 "or 'hg update -C .' to abandon\n"))
437 "or 'hg update -C .' to abandon\n"))
443 elif remind:
438 elif remind:
444 repo.ui.status(_("(branch merge, don't forget to commit)\n"))
439 repo.ui.status(_("(branch merge, don't forget to commit)\n"))
445 return stats[3] > 0
440 return stats[3] > 0
446
441
447 def _incoming(displaychlist, subreporecurse, ui, repo, source,
442 def _incoming(displaychlist, subreporecurse, ui, repo, source,
448 opts, buffered=False):
443 opts, buffered=False):
449 """
444 """
450 Helper for incoming / gincoming.
445 Helper for incoming / gincoming.
451 displaychlist gets called with
446 displaychlist gets called with
452 (remoterepo, incomingchangesetlist, displayer) parameters,
447 (remoterepo, incomingchangesetlist, displayer) parameters,
453 and is supposed to contain only code that can't be unified.
448 and is supposed to contain only code that can't be unified.
454 """
449 """
455 source, branches = parseurl(ui.expandpath(source), opts.get('branch'))
450 source, branches = parseurl(ui.expandpath(source), opts.get('branch'))
456 other = peer(repo, opts, source)
451 other = peer(repo, opts, source)
457 ui.status(_('comparing with %s\n') % util.hidepassword(source))
452 ui.status(_('comparing with %s\n') % util.hidepassword(source))
458 revs, checkout = addbranchrevs(repo, other, branches, opts.get('rev'))
453 revs, checkout = addbranchrevs(repo, other, branches, opts.get('rev'))
459
454
460 if revs:
455 if revs:
461 revs = [other.lookup(rev) for rev in revs]
456 revs = [other.lookup(rev) for rev in revs]
462 other, chlist, cleanupfn = bundlerepo.getremotechanges(ui, repo, other,
457 other, chlist, cleanupfn = bundlerepo.getremotechanges(ui, repo, other,
463 revs, opts["bundle"], opts["force"])
458 revs, opts["bundle"], opts["force"])
464 try:
459 try:
465 if not chlist:
460 if not chlist:
466 ui.status(_("no changes found\n"))
461 ui.status(_("no changes found\n"))
467 return subreporecurse()
462 return subreporecurse()
468
463
469 displayer = cmdutil.show_changeset(ui, other, opts, buffered)
464 displayer = cmdutil.show_changeset(ui, other, opts, buffered)
470
465
471 # XXX once graphlog extension makes it into core,
466 # XXX once graphlog extension makes it into core,
472 # should be replaced by a if graph/else
467 # should be replaced by a if graph/else
473 displaychlist(other, chlist, displayer)
468 displaychlist(other, chlist, displayer)
474
469
475 displayer.close()
470 displayer.close()
476 finally:
471 finally:
477 cleanupfn()
472 cleanupfn()
478 subreporecurse()
473 subreporecurse()
479 return 0 # exit code is zero since we found incoming changes
474 return 0 # exit code is zero since we found incoming changes
480
475
481 def incoming(ui, repo, source, opts):
476 def incoming(ui, repo, source, opts):
482 def subreporecurse():
477 def subreporecurse():
483 ret = 1
478 ret = 1
484 if opts.get('subrepos'):
479 if opts.get('subrepos'):
485 ctx = repo[None]
480 ctx = repo[None]
486 for subpath in sorted(ctx.substate):
481 for subpath in sorted(ctx.substate):
487 sub = ctx.sub(subpath)
482 sub = ctx.sub(subpath)
488 ret = min(ret, sub.incoming(ui, source, opts))
483 ret = min(ret, sub.incoming(ui, source, opts))
489 return ret
484 return ret
490
485
491 def display(other, chlist, displayer):
486 def display(other, chlist, displayer):
492 limit = cmdutil.loglimit(opts)
487 limit = cmdutil.loglimit(opts)
493 if opts.get('newest_first'):
488 if opts.get('newest_first'):
494 chlist.reverse()
489 chlist.reverse()
495 count = 0
490 count = 0
496 for n in chlist:
491 for n in chlist:
497 if limit is not None and count >= limit:
492 if limit is not None and count >= limit:
498 break
493 break
499 parents = [p for p in other.changelog.parents(n) if p != nullid]
494 parents = [p for p in other.changelog.parents(n) if p != nullid]
500 if opts.get('no_merges') and len(parents) == 2:
495 if opts.get('no_merges') and len(parents) == 2:
501 continue
496 continue
502 count += 1
497 count += 1
503 displayer.show(other[n])
498 displayer.show(other[n])
504 return _incoming(display, subreporecurse, ui, repo, source, opts)
499 return _incoming(display, subreporecurse, ui, repo, source, opts)
505
500
506 def _outgoing(ui, repo, dest, opts):
501 def _outgoing(ui, repo, dest, opts):
507 dest = ui.expandpath(dest or 'default-push', dest or 'default')
502 dest = ui.expandpath(dest or 'default-push', dest or 'default')
508 dest, branches = parseurl(dest, opts.get('branch'))
503 dest, branches = parseurl(dest, opts.get('branch'))
509 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
504 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
510 revs, checkout = addbranchrevs(repo, repo, branches, opts.get('rev'))
505 revs, checkout = addbranchrevs(repo, repo, branches, opts.get('rev'))
511 if revs:
506 if revs:
512 revs = [repo.lookup(rev) for rev in revs]
507 revs = [repo.lookup(rev) for rev in revs]
513
508
514 other = peer(repo, opts, dest)
509 other = peer(repo, opts, dest)
515 outgoing = discovery.findcommonoutgoing(repo, other, revs,
510 outgoing = discovery.findcommonoutgoing(repo, other, revs,
516 force=opts.get('force'))
511 force=opts.get('force'))
517 o = outgoing.missing
512 o = outgoing.missing
518 if not o:
513 if not o:
519 ui.status(_("no changes found\n"))
514 ui.status(_("no changes found\n"))
520 return None
515 return None
521 return o
516 return o
522
517
523 def outgoing(ui, repo, dest, opts):
518 def outgoing(ui, repo, dest, opts):
524 def recurse():
519 def recurse():
525 ret = 1
520 ret = 1
526 if opts.get('subrepos'):
521 if opts.get('subrepos'):
527 ctx = repo[None]
522 ctx = repo[None]
528 for subpath in sorted(ctx.substate):
523 for subpath in sorted(ctx.substate):
529 sub = ctx.sub(subpath)
524 sub = ctx.sub(subpath)
530 ret = min(ret, sub.outgoing(ui, dest, opts))
525 ret = min(ret, sub.outgoing(ui, dest, opts))
531 return ret
526 return ret
532
527
533 limit = cmdutil.loglimit(opts)
528 limit = cmdutil.loglimit(opts)
534 o = _outgoing(ui, repo, dest, opts)
529 o = _outgoing(ui, repo, dest, opts)
535 if o is None:
530 if o is None:
536 return recurse()
531 return recurse()
537
532
538 if opts.get('newest_first'):
533 if opts.get('newest_first'):
539 o.reverse()
534 o.reverse()
540 displayer = cmdutil.show_changeset(ui, repo, opts)
535 displayer = cmdutil.show_changeset(ui, repo, opts)
541 count = 0
536 count = 0
542 for n in o:
537 for n in o:
543 if limit is not None and count >= limit:
538 if limit is not None and count >= limit:
544 break
539 break
545 parents = [p for p in repo.changelog.parents(n) if p != nullid]
540 parents = [p for p in repo.changelog.parents(n) if p != nullid]
546 if opts.get('no_merges') and len(parents) == 2:
541 if opts.get('no_merges') and len(parents) == 2:
547 continue
542 continue
548 count += 1
543 count += 1
549 displayer.show(repo[n])
544 displayer.show(repo[n])
550 displayer.close()
545 displayer.close()
551 recurse()
546 recurse()
552 return 0 # exit code is zero since we found outgoing changes
547 return 0 # exit code is zero since we found outgoing changes
553
548
554 def revert(repo, node, choose):
549 def revert(repo, node, choose):
555 """revert changes to revision in node without updating dirstate"""
550 """revert changes to revision in node without updating dirstate"""
556 return mergemod.update(repo, node, False, True, choose)[3] > 0
551 return mergemod.update(repo, node, False, True, choose)[3] > 0
557
552
558 def verify(repo):
553 def verify(repo):
559 """verify the consistency of a repository"""
554 """verify the consistency of a repository"""
560 return verifymod.verify(repo)
555 return verifymod.verify(repo)
561
556
562 def remoteui(src, opts):
557 def remoteui(src, opts):
563 'build a remote ui from ui or repo and opts'
558 'build a remote ui from ui or repo and opts'
564 if util.safehasattr(src, 'baseui'): # looks like a repository
559 if util.safehasattr(src, 'baseui'): # looks like a repository
565 dst = src.baseui.copy() # drop repo-specific config
560 dst = src.baseui.copy() # drop repo-specific config
566 src = src.ui # copy target options from repo
561 src = src.ui # copy target options from repo
567 else: # assume it's a global ui object
562 else: # assume it's a global ui object
568 dst = src.copy() # keep all global options
563 dst = src.copy() # keep all global options
569
564
570 # copy ssh-specific options
565 # copy ssh-specific options
571 for o in 'ssh', 'remotecmd':
566 for o in 'ssh', 'remotecmd':
572 v = opts.get(o) or src.config('ui', o)
567 v = opts.get(o) or src.config('ui', o)
573 if v:
568 if v:
574 dst.setconfig("ui", o, v)
569 dst.setconfig("ui", o, v)
575
570
576 # copy bundle-specific options
571 # copy bundle-specific options
577 r = src.config('bundle', 'mainreporoot')
572 r = src.config('bundle', 'mainreporoot')
578 if r:
573 if r:
579 dst.setconfig('bundle', 'mainreporoot', r)
574 dst.setconfig('bundle', 'mainreporoot', r)
580
575
581 # copy selected local settings to the remote ui
576 # copy selected local settings to the remote ui
582 for sect in ('auth', 'hostfingerprints', 'http_proxy'):
577 for sect in ('auth', 'hostfingerprints', 'http_proxy'):
583 for key, val in src.configitems(sect):
578 for key, val in src.configitems(sect):
584 dst.setconfig(sect, key, val)
579 dst.setconfig(sect, key, val)
585 v = src.config('web', 'cacerts')
580 v = src.config('web', 'cacerts')
586 if v:
581 if v:
587 dst.setconfig('web', 'cacerts', util.expandpath(v))
582 dst.setconfig('web', 'cacerts', util.expandpath(v))
588
583
589 return dst
584 return dst
General Comments 0
You need to be logged in to leave comments. Login now