##// END OF EJS Templates
clfilter: `bookmark.validdest` should run on unfiltered repo...
Pierre-Yves David -
r18008:cf91b36f default
parent child Browse files
Show More
@@ -1,282 +1,283 b''
1 1 # Mercurial bookmark support code
2 2 #
3 3 # Copyright 2008 David Soria Parra <dsp@php.net>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from mercurial.i18n import _
9 9 from mercurial.node import hex
10 10 from mercurial import encoding, error, util, obsolete
11 11 import errno, os
12 12
13 13 class bmstore(dict):
14 14 """Storage for bookmarks.
15 15
16 16 This object should do all bookmark reads and writes, so that it's
17 17 fairly simple to replace the storage underlying bookmarks without
18 18 having to clone the logic surrounding bookmarks.
19 19
20 20 This particular bmstore implementation stores bookmarks as
21 21 {hash}\s{name}\n (the same format as localtags) in
22 22 .hg/bookmarks. The mapping is stored as {name: nodeid}.
23 23
24 24 This class does NOT handle the "current" bookmark state at this
25 25 time.
26 26 """
27 27
28 28 def __init__(self, repo):
29 29 dict.__init__(self)
30 30 self._repo = repo
31 31 try:
32 32 for line in repo.vfs('bookmarks'):
33 33 line = line.strip()
34 34 if not line:
35 35 continue
36 36 if ' ' not in line:
37 37 repo.ui.warn(_('malformed line in .hg/bookmarks: %r\n')
38 38 % line)
39 39 continue
40 40 sha, refspec = line.split(' ', 1)
41 41 refspec = encoding.tolocal(refspec)
42 42 try:
43 43 self[refspec] = repo.changelog.lookup(sha)
44 44 except LookupError:
45 45 pass
46 46 except IOError, inst:
47 47 if inst.errno != errno.ENOENT:
48 48 raise
49 49
50 50 def write(self):
51 51 '''Write bookmarks
52 52
53 53 Write the given bookmark => hash dictionary to the .hg/bookmarks file
54 54 in a format equal to those of localtags.
55 55
56 56 We also store a backup of the previous state in undo.bookmarks that
57 57 can be copied back on rollback.
58 58 '''
59 59 repo = self._repo
60 60 if repo._bookmarkcurrent not in self:
61 61 setcurrent(repo, None)
62 62
63 63 wlock = repo.wlock()
64 64 try:
65 65
66 66 file = repo.vfs('bookmarks', 'w', atomictemp=True)
67 67 for name, node in self.iteritems():
68 68 file.write("%s %s\n" % (hex(node), encoding.fromlocal(name)))
69 69 file.close()
70 70
71 71 # touch 00changelog.i so hgweb reloads bookmarks (no lock needed)
72 72 try:
73 73 os.utime(repo.sjoin('00changelog.i'), None)
74 74 except OSError:
75 75 pass
76 76
77 77 finally:
78 78 wlock.release()
79 79
80 80 def readcurrent(repo):
81 81 '''Get the current bookmark
82 82
83 83 If we use gittishsh branches we have a current bookmark that
84 84 we are on. This function returns the name of the bookmark. It
85 85 is stored in .hg/bookmarks.current
86 86 '''
87 87 mark = None
88 88 try:
89 89 file = repo.opener('bookmarks.current')
90 90 except IOError, inst:
91 91 if inst.errno != errno.ENOENT:
92 92 raise
93 93 return None
94 94 try:
95 95 # No readline() in osutil.posixfile, reading everything is cheap
96 96 mark = encoding.tolocal((file.readlines() or [''])[0])
97 97 if mark == '' or mark not in repo._bookmarks:
98 98 mark = None
99 99 finally:
100 100 file.close()
101 101 return mark
102 102
103 103 def setcurrent(repo, mark):
104 104 '''Set the name of the bookmark that we are currently on
105 105
106 106 Set the name of the bookmark that we are on (hg update <bookmark>).
107 107 The name is recorded in .hg/bookmarks.current
108 108 '''
109 109 current = repo._bookmarkcurrent
110 110 if current == mark:
111 111 return
112 112
113 113 if mark not in repo._bookmarks:
114 114 mark = ''
115 115
116 116 wlock = repo.wlock()
117 117 try:
118 118 file = repo.opener('bookmarks.current', 'w', atomictemp=True)
119 119 file.write(encoding.fromlocal(mark))
120 120 file.close()
121 121 finally:
122 122 wlock.release()
123 123 repo._bookmarkcurrent = mark
124 124
125 125 def unsetcurrent(repo):
126 126 wlock = repo.wlock()
127 127 try:
128 128 try:
129 129 util.unlink(repo.join('bookmarks.current'))
130 130 repo._bookmarkcurrent = None
131 131 except OSError, inst:
132 132 if inst.errno != errno.ENOENT:
133 133 raise
134 134 finally:
135 135 wlock.release()
136 136
137 137 def updatecurrentbookmark(repo, oldnode, curbranch):
138 138 try:
139 139 return update(repo, oldnode, repo.branchtip(curbranch))
140 140 except error.RepoLookupError:
141 141 if curbranch == "default": # no default branch!
142 142 return update(repo, oldnode, repo.lookup("tip"))
143 143 else:
144 144 raise util.Abort(_("branch %s not found") % curbranch)
145 145
146 146 def update(repo, parents, node):
147 147 marks = repo._bookmarks
148 148 update = False
149 149 cur = repo._bookmarkcurrent
150 150 if not cur:
151 151 return False
152 152
153 153 toupdate = [b for b in marks if b.split('@', 1)[0] == cur.split('@', 1)[0]]
154 154 for mark in toupdate:
155 155 if mark and marks[mark] in parents:
156 156 old = repo[marks[mark]]
157 157 new = repo[node]
158 158 if old.descendant(new) and mark == cur:
159 159 marks[cur] = new.node()
160 160 update = True
161 161 if mark != cur:
162 162 del marks[mark]
163 163 if update:
164 164 marks.write()
165 165 return update
166 166
167 167 def listbookmarks(repo):
168 168 # We may try to list bookmarks on a repo type that does not
169 169 # support it (e.g., statichttprepository).
170 170 marks = getattr(repo, '_bookmarks', {})
171 171
172 172 d = {}
173 173 for k, v in marks.iteritems():
174 174 # don't expose local divergent bookmarks
175 175 if '@' not in k or k.endswith('@'):
176 176 d[k] = hex(v)
177 177 return d
178 178
179 179 def pushbookmark(repo, key, old, new):
180 180 w = repo.wlock()
181 181 try:
182 182 marks = repo._bookmarks
183 183 if hex(marks.get(key, '')) != old:
184 184 return False
185 185 if new == '':
186 186 del marks[key]
187 187 else:
188 188 if new not in repo:
189 189 return False
190 190 marks[key] = repo[new].node()
191 191 marks.write()
192 192 return True
193 193 finally:
194 194 w.release()
195 195
196 196 def updatefromremote(ui, repo, remote, path):
197 197 ui.debug("checking for updated bookmarks\n")
198 198 rb = remote.listkeys('bookmarks')
199 199 changed = False
200 200 localmarks = repo._bookmarks
201 201 for k in rb.keys():
202 202 if k in localmarks:
203 203 nr, nl = rb[k], localmarks[k]
204 204 if nr in repo:
205 205 cr = repo[nr]
206 206 cl = repo[nl]
207 207 if cl.rev() >= cr.rev():
208 208 continue
209 209 if validdest(repo, cl, cr):
210 210 localmarks[k] = cr.node()
211 211 changed = True
212 212 ui.status(_("updating bookmark %s\n") % k)
213 213 else:
214 214 if k == '@':
215 215 kd = ''
216 216 else:
217 217 kd = k
218 218 # find a unique @ suffix
219 219 for x in range(1, 100):
220 220 n = '%s@%d' % (kd, x)
221 221 if n not in localmarks:
222 222 break
223 223 # try to use an @pathalias suffix
224 224 # if an @pathalias already exists, we overwrite (update) it
225 225 for p, u in ui.configitems("paths"):
226 226 if path == u:
227 227 n = '%s@%s' % (kd, p)
228 228
229 229 localmarks[n] = cr.node()
230 230 changed = True
231 231 ui.warn(_("divergent bookmark %s stored as %s\n") % (k, n))
232 232 elif rb[k] in repo:
233 233 # add remote bookmarks for changes we already have
234 234 localmarks[k] = repo[rb[k]].node()
235 235 changed = True
236 236 ui.status(_("adding remote bookmark %s\n") % k)
237 237
238 238 if changed:
239 239 localmarks.write()
240 240
241 241 def diff(ui, dst, src):
242 242 ui.status(_("searching for changed bookmarks\n"))
243 243
244 244 smarks = src.listkeys('bookmarks')
245 245 dmarks = dst.listkeys('bookmarks')
246 246
247 247 diff = sorted(set(smarks) - set(dmarks))
248 248 for k in diff:
249 249 mark = ui.debugflag and smarks[k] or smarks[k][:12]
250 250 ui.write(" %-25s %s\n" % (k, mark))
251 251
252 252 if len(diff) <= 0:
253 253 ui.status(_("no changed bookmarks found\n"))
254 254 return 1
255 255 return 0
256 256
257 257 def validdest(repo, old, new):
258 258 """Is the new bookmark destination a valid update from the old one"""
259 repo = repo.unfiltered()
259 260 if old == new:
260 261 # Old == new -> nothing to update.
261 262 return False
262 263 elif not old:
263 264 # old is nullrev, anything is valid.
264 265 # (new != nullrev has been excluded by the previous check)
265 266 return True
266 267 elif repo.obsstore:
267 268 # We only need this complicated logic if there is obsolescence
268 269 # XXX will probably deserve an optimised revset.
269 270 nm = repo.changelog.nodemap
270 271 validdests = set([old])
271 272 plen = -1
272 273 # compute the whole set of successors or descendants
273 274 while len(validdests) != plen:
274 275 plen = len(validdests)
275 276 succs = set(c.node() for c in validdests)
276 277 mutable = [c.node() for c in validdests if c.mutable()]
277 278 succs.update(obsolete.allsuccessors(repo.obsstore, mutable))
278 279 known = (n for n in succs if n in nm)
279 280 validdests = set(repo.set('%ln::', known))
280 281 return new in validdests
281 282 else:
282 283 return old.descendant(new)
General Comments 0
You need to be logged in to leave comments. Login now