##// END OF EJS Templates
bookmarks: factor out bookmark file opening for easier extensibility
Ryan McElroy -
r23458:756376ec default
parent child Browse files
Show More
@@ -1,452 +1,456
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 import os
8 import os
9 from mercurial.i18n import _
9 from mercurial.i18n import _
10 from mercurial.node import hex, bin
10 from mercurial.node import hex, bin
11 from mercurial import encoding, error, util, obsolete, lock as lockmod
11 from mercurial import encoding, error, util, obsolete, lock as lockmod
12 import errno
12 import errno
13
13
14 class bmstore(dict):
14 class bmstore(dict):
15 """Storage for bookmarks.
15 """Storage for bookmarks.
16
16
17 This object should do all bookmark reads and writes, so that it's
17 This object should do all bookmark reads and writes, so that it's
18 fairly simple to replace the storage underlying bookmarks without
18 fairly simple to replace the storage underlying bookmarks without
19 having to clone the logic surrounding bookmarks.
19 having to clone the logic surrounding bookmarks.
20
20
21 This particular bmstore implementation stores bookmarks as
21 This particular bmstore implementation stores bookmarks as
22 {hash}\s{name}\n (the same format as localtags) in
22 {hash}\s{name}\n (the same format as localtags) in
23 .hg/bookmarks. The mapping is stored as {name: nodeid}.
23 .hg/bookmarks. The mapping is stored as {name: nodeid}.
24
24
25 This class does NOT handle the "current" bookmark state at this
25 This class does NOT handle the "current" bookmark state at this
26 time.
26 time.
27 """
27 """
28
28
29 def __init__(self, repo):
29 def __init__(self, repo):
30 dict.__init__(self)
30 dict.__init__(self)
31 self._repo = repo
31 self._repo = repo
32 try:
32 try:
33 bkfile = None
33 bkfile = self.getbkfile(repo)
34 if 'HG_PENDING' in os.environ:
35 try:
36 bkfile = repo.vfs('bookmarks.pending')
37 except IOError, inst:
38 if inst.errno != errno.ENOENT:
39 raise
40 if bkfile is None:
41 bkfile = repo.vfs('bookmarks')
42 for line in bkfile:
34 for line in bkfile:
43 line = line.strip()
35 line = line.strip()
44 if not line:
36 if not line:
45 continue
37 continue
46 if ' ' not in line:
38 if ' ' not in line:
47 repo.ui.warn(_('malformed line in .hg/bookmarks: %r\n')
39 repo.ui.warn(_('malformed line in .hg/bookmarks: %r\n')
48 % line)
40 % line)
49 continue
41 continue
50 sha, refspec = line.split(' ', 1)
42 sha, refspec = line.split(' ', 1)
51 refspec = encoding.tolocal(refspec)
43 refspec = encoding.tolocal(refspec)
52 try:
44 try:
53 self[refspec] = repo.changelog.lookup(sha)
45 self[refspec] = repo.changelog.lookup(sha)
54 except LookupError:
46 except LookupError:
55 pass
47 pass
56 except IOError, inst:
48 except IOError, inst:
57 if inst.errno != errno.ENOENT:
49 if inst.errno != errno.ENOENT:
58 raise
50 raise
59
51
52 def getbkfile(self, repo):
53 bkfile = None
54 if 'HG_PENDING' in os.environ:
55 try:
56 bkfile = repo.vfs('bookmarks.pending')
57 except IOError, inst:
58 if inst.errno != errno.ENOENT:
59 raise
60 if bkfile is None:
61 bkfile = repo.vfs('bookmarks')
62 return bkfile
63
60 def recordchange(self, tr):
64 def recordchange(self, tr):
61 """record that bookmarks have been changed in a transaction
65 """record that bookmarks have been changed in a transaction
62
66
63 The transaction is then responsible for updating the file content."""
67 The transaction is then responsible for updating the file content."""
64 tr.addfilegenerator('bookmarks', ('bookmarks',), self._write,
68 tr.addfilegenerator('bookmarks', ('bookmarks',), self._write,
65 location='plain')
69 location='plain')
66 tr.hookargs['bookmark_moved'] = '1'
70 tr.hookargs['bookmark_moved'] = '1'
67
71
68 def write(self):
72 def write(self):
69 '''Write bookmarks
73 '''Write bookmarks
70
74
71 Write the given bookmark => hash dictionary to the .hg/bookmarks file
75 Write the given bookmark => hash dictionary to the .hg/bookmarks file
72 in a format equal to those of localtags.
76 in a format equal to those of localtags.
73
77
74 We also store a backup of the previous state in undo.bookmarks that
78 We also store a backup of the previous state in undo.bookmarks that
75 can be copied back on rollback.
79 can be copied back on rollback.
76 '''
80 '''
77 repo = self._repo
81 repo = self._repo
78 if repo._bookmarkcurrent not in self:
82 if repo._bookmarkcurrent not in self:
79 unsetcurrent(repo)
83 unsetcurrent(repo)
80
84
81 wlock = repo.wlock()
85 wlock = repo.wlock()
82 try:
86 try:
83
87
84 file = repo.vfs('bookmarks', 'w', atomictemp=True)
88 file = repo.vfs('bookmarks', 'w', atomictemp=True)
85 self._write(file)
89 self._write(file)
86 file.close()
90 file.close()
87
91
88 # touch 00changelog.i so hgweb reloads bookmarks (no lock needed)
92 # touch 00changelog.i so hgweb reloads bookmarks (no lock needed)
89 try:
93 try:
90 repo.svfs.utime('00changelog.i', None)
94 repo.svfs.utime('00changelog.i', None)
91 except OSError:
95 except OSError:
92 pass
96 pass
93
97
94 finally:
98 finally:
95 wlock.release()
99 wlock.release()
96
100
97 def _write(self, fp):
101 def _write(self, fp):
98 for name, node in self.iteritems():
102 for name, node in self.iteritems():
99 fp.write("%s %s\n" % (hex(node), encoding.fromlocal(name)))
103 fp.write("%s %s\n" % (hex(node), encoding.fromlocal(name)))
100
104
101 def readcurrent(repo):
105 def readcurrent(repo):
102 '''Get the current bookmark
106 '''Get the current bookmark
103
107
104 If we use gittish branches we have a current bookmark that
108 If we use gittish branches we have a current bookmark that
105 we are on. This function returns the name of the bookmark. It
109 we are on. This function returns the name of the bookmark. It
106 is stored in .hg/bookmarks.current
110 is stored in .hg/bookmarks.current
107 '''
111 '''
108 mark = None
112 mark = None
109 try:
113 try:
110 file = repo.opener('bookmarks.current')
114 file = repo.opener('bookmarks.current')
111 except IOError, inst:
115 except IOError, inst:
112 if inst.errno != errno.ENOENT:
116 if inst.errno != errno.ENOENT:
113 raise
117 raise
114 return None
118 return None
115 try:
119 try:
116 # No readline() in osutil.posixfile, reading everything is cheap
120 # No readline() in osutil.posixfile, reading everything is cheap
117 mark = encoding.tolocal((file.readlines() or [''])[0])
121 mark = encoding.tolocal((file.readlines() or [''])[0])
118 if mark == '' or mark not in repo._bookmarks:
122 if mark == '' or mark not in repo._bookmarks:
119 mark = None
123 mark = None
120 finally:
124 finally:
121 file.close()
125 file.close()
122 return mark
126 return mark
123
127
124 def setcurrent(repo, mark):
128 def setcurrent(repo, mark):
125 '''Set the name of the bookmark that we are currently on
129 '''Set the name of the bookmark that we are currently on
126
130
127 Set the name of the bookmark that we are on (hg update <bookmark>).
131 Set the name of the bookmark that we are on (hg update <bookmark>).
128 The name is recorded in .hg/bookmarks.current
132 The name is recorded in .hg/bookmarks.current
129 '''
133 '''
130 if mark not in repo._bookmarks:
134 if mark not in repo._bookmarks:
131 raise AssertionError('bookmark %s does not exist!' % mark)
135 raise AssertionError('bookmark %s does not exist!' % mark)
132
136
133 current = repo._bookmarkcurrent
137 current = repo._bookmarkcurrent
134 if current == mark:
138 if current == mark:
135 return
139 return
136
140
137 wlock = repo.wlock()
141 wlock = repo.wlock()
138 try:
142 try:
139 file = repo.opener('bookmarks.current', 'w', atomictemp=True)
143 file = repo.opener('bookmarks.current', 'w', atomictemp=True)
140 file.write(encoding.fromlocal(mark))
144 file.write(encoding.fromlocal(mark))
141 file.close()
145 file.close()
142 finally:
146 finally:
143 wlock.release()
147 wlock.release()
144 repo._bookmarkcurrent = mark
148 repo._bookmarkcurrent = mark
145
149
146 def unsetcurrent(repo):
150 def unsetcurrent(repo):
147 wlock = repo.wlock()
151 wlock = repo.wlock()
148 try:
152 try:
149 try:
153 try:
150 repo.vfs.unlink('bookmarks.current')
154 repo.vfs.unlink('bookmarks.current')
151 repo._bookmarkcurrent = None
155 repo._bookmarkcurrent = None
152 except OSError, inst:
156 except OSError, inst:
153 if inst.errno != errno.ENOENT:
157 if inst.errno != errno.ENOENT:
154 raise
158 raise
155 finally:
159 finally:
156 wlock.release()
160 wlock.release()
157
161
158 def iscurrent(repo, mark=None, parents=None):
162 def iscurrent(repo, mark=None, parents=None):
159 '''Tell whether the current bookmark is also active
163 '''Tell whether the current bookmark is also active
160
164
161 I.e., the bookmark listed in .hg/bookmarks.current also points to a
165 I.e., the bookmark listed in .hg/bookmarks.current also points to a
162 parent of the working directory.
166 parent of the working directory.
163 '''
167 '''
164 if not mark:
168 if not mark:
165 mark = repo._bookmarkcurrent
169 mark = repo._bookmarkcurrent
166 if not parents:
170 if not parents:
167 parents = [p.node() for p in repo[None].parents()]
171 parents = [p.node() for p in repo[None].parents()]
168 marks = repo._bookmarks
172 marks = repo._bookmarks
169 return (mark in marks and marks[mark] in parents)
173 return (mark in marks and marks[mark] in parents)
170
174
171 def updatecurrentbookmark(repo, oldnode, curbranch):
175 def updatecurrentbookmark(repo, oldnode, curbranch):
172 try:
176 try:
173 return update(repo, oldnode, repo.branchtip(curbranch))
177 return update(repo, oldnode, repo.branchtip(curbranch))
174 except error.RepoLookupError:
178 except error.RepoLookupError:
175 if curbranch == "default": # no default branch!
179 if curbranch == "default": # no default branch!
176 return update(repo, oldnode, repo.lookup("tip"))
180 return update(repo, oldnode, repo.lookup("tip"))
177 else:
181 else:
178 raise util.Abort(_("branch %s not found") % curbranch)
182 raise util.Abort(_("branch %s not found") % curbranch)
179
183
180 def deletedivergent(repo, deletefrom, bm):
184 def deletedivergent(repo, deletefrom, bm):
181 '''Delete divergent versions of bm on nodes in deletefrom.
185 '''Delete divergent versions of bm on nodes in deletefrom.
182
186
183 Return True if at least one bookmark was deleted, False otherwise.'''
187 Return True if at least one bookmark was deleted, False otherwise.'''
184 deleted = False
188 deleted = False
185 marks = repo._bookmarks
189 marks = repo._bookmarks
186 divergent = [b for b in marks if b.split('@', 1)[0] == bm.split('@', 1)[0]]
190 divergent = [b for b in marks if b.split('@', 1)[0] == bm.split('@', 1)[0]]
187 for mark in divergent:
191 for mark in divergent:
188 if mark == '@' or '@' not in mark:
192 if mark == '@' or '@' not in mark:
189 # can't be divergent by definition
193 # can't be divergent by definition
190 continue
194 continue
191 if mark and marks[mark] in deletefrom:
195 if mark and marks[mark] in deletefrom:
192 if mark != bm:
196 if mark != bm:
193 del marks[mark]
197 del marks[mark]
194 deleted = True
198 deleted = True
195 return deleted
199 return deleted
196
200
197 def calculateupdate(ui, repo, checkout):
201 def calculateupdate(ui, repo, checkout):
198 '''Return a tuple (targetrev, movemarkfrom) indicating the rev to
202 '''Return a tuple (targetrev, movemarkfrom) indicating the rev to
199 check out and where to move the active bookmark from, if needed.'''
203 check out and where to move the active bookmark from, if needed.'''
200 movemarkfrom = None
204 movemarkfrom = None
201 if checkout is None:
205 if checkout is None:
202 curmark = repo._bookmarkcurrent
206 curmark = repo._bookmarkcurrent
203 if iscurrent(repo):
207 if iscurrent(repo):
204 movemarkfrom = repo['.'].node()
208 movemarkfrom = repo['.'].node()
205 elif curmark:
209 elif curmark:
206 ui.status(_("updating to active bookmark %s\n") % curmark)
210 ui.status(_("updating to active bookmark %s\n") % curmark)
207 checkout = curmark
211 checkout = curmark
208 return (checkout, movemarkfrom)
212 return (checkout, movemarkfrom)
209
213
210 def update(repo, parents, node):
214 def update(repo, parents, node):
211 deletefrom = parents
215 deletefrom = parents
212 marks = repo._bookmarks
216 marks = repo._bookmarks
213 update = False
217 update = False
214 cur = repo._bookmarkcurrent
218 cur = repo._bookmarkcurrent
215 if not cur:
219 if not cur:
216 return False
220 return False
217
221
218 if marks[cur] in parents:
222 if marks[cur] in parents:
219 new = repo[node]
223 new = repo[node]
220 divs = [repo[b] for b in marks
224 divs = [repo[b] for b in marks
221 if b.split('@', 1)[0] == cur.split('@', 1)[0]]
225 if b.split('@', 1)[0] == cur.split('@', 1)[0]]
222 anc = repo.changelog.ancestors([new.rev()])
226 anc = repo.changelog.ancestors([new.rev()])
223 deletefrom = [b.node() for b in divs if b.rev() in anc or b == new]
227 deletefrom = [b.node() for b in divs if b.rev() in anc or b == new]
224 if validdest(repo, repo[marks[cur]], new):
228 if validdest(repo, repo[marks[cur]], new):
225 marks[cur] = new.node()
229 marks[cur] = new.node()
226 update = True
230 update = True
227
231
228 if deletedivergent(repo, deletefrom, cur):
232 if deletedivergent(repo, deletefrom, cur):
229 update = True
233 update = True
230
234
231 if update:
235 if update:
232 marks.write()
236 marks.write()
233 return update
237 return update
234
238
235 def listbookmarks(repo):
239 def listbookmarks(repo):
236 # We may try to list bookmarks on a repo type that does not
240 # We may try to list bookmarks on a repo type that does not
237 # support it (e.g., statichttprepository).
241 # support it (e.g., statichttprepository).
238 marks = getattr(repo, '_bookmarks', {})
242 marks = getattr(repo, '_bookmarks', {})
239
243
240 d = {}
244 d = {}
241 hasnode = repo.changelog.hasnode
245 hasnode = repo.changelog.hasnode
242 for k, v in marks.iteritems():
246 for k, v in marks.iteritems():
243 # don't expose local divergent bookmarks
247 # don't expose local divergent bookmarks
244 if hasnode(v) and ('@' not in k or k.endswith('@')):
248 if hasnode(v) and ('@' not in k or k.endswith('@')):
245 d[k] = hex(v)
249 d[k] = hex(v)
246 return d
250 return d
247
251
248 def pushbookmark(repo, key, old, new):
252 def pushbookmark(repo, key, old, new):
249 w = l = tr = None
253 w = l = tr = None
250 try:
254 try:
251 w = repo.wlock()
255 w = repo.wlock()
252 l = repo.lock()
256 l = repo.lock()
253 tr = repo.transaction('bookmarks')
257 tr = repo.transaction('bookmarks')
254 marks = repo._bookmarks
258 marks = repo._bookmarks
255 existing = hex(marks.get(key, ''))
259 existing = hex(marks.get(key, ''))
256 if existing != old and existing != new:
260 if existing != old and existing != new:
257 return False
261 return False
258 if new == '':
262 if new == '':
259 del marks[key]
263 del marks[key]
260 else:
264 else:
261 if new not in repo:
265 if new not in repo:
262 return False
266 return False
263 marks[key] = repo[new].node()
267 marks[key] = repo[new].node()
264 marks.recordchange(tr)
268 marks.recordchange(tr)
265 tr.close()
269 tr.close()
266 return True
270 return True
267 finally:
271 finally:
268 lockmod.release(tr, l, w)
272 lockmod.release(tr, l, w)
269
273
270 def compare(repo, srcmarks, dstmarks,
274 def compare(repo, srcmarks, dstmarks,
271 srchex=None, dsthex=None, targets=None):
275 srchex=None, dsthex=None, targets=None):
272 '''Compare bookmarks between srcmarks and dstmarks
276 '''Compare bookmarks between srcmarks and dstmarks
273
277
274 This returns tuple "(addsrc, adddst, advsrc, advdst, diverge,
278 This returns tuple "(addsrc, adddst, advsrc, advdst, diverge,
275 differ, invalid)", each are list of bookmarks below:
279 differ, invalid)", each are list of bookmarks below:
276
280
277 :addsrc: added on src side (removed on dst side, perhaps)
281 :addsrc: added on src side (removed on dst side, perhaps)
278 :adddst: added on dst side (removed on src side, perhaps)
282 :adddst: added on dst side (removed on src side, perhaps)
279 :advsrc: advanced on src side
283 :advsrc: advanced on src side
280 :advdst: advanced on dst side
284 :advdst: advanced on dst side
281 :diverge: diverge
285 :diverge: diverge
282 :differ: changed, but changeset referred on src is unknown on dst
286 :differ: changed, but changeset referred on src is unknown on dst
283 :invalid: unknown on both side
287 :invalid: unknown on both side
284 :same: same on both side
288 :same: same on both side
285
289
286 Each elements of lists in result tuple is tuple "(bookmark name,
290 Each elements of lists in result tuple is tuple "(bookmark name,
287 changeset ID on source side, changeset ID on destination
291 changeset ID on source side, changeset ID on destination
288 side)". Each changeset IDs are 40 hexadecimal digit string or
292 side)". Each changeset IDs are 40 hexadecimal digit string or
289 None.
293 None.
290
294
291 Changeset IDs of tuples in "addsrc", "adddst", "differ" or
295 Changeset IDs of tuples in "addsrc", "adddst", "differ" or
292 "invalid" list may be unknown for repo.
296 "invalid" list may be unknown for repo.
293
297
294 This function expects that "srcmarks" and "dstmarks" return
298 This function expects that "srcmarks" and "dstmarks" return
295 changeset ID in 40 hexadecimal digit string for specified
299 changeset ID in 40 hexadecimal digit string for specified
296 bookmark. If not so (e.g. bmstore "repo._bookmarks" returning
300 bookmark. If not so (e.g. bmstore "repo._bookmarks" returning
297 binary value), "srchex" or "dsthex" should be specified to convert
301 binary value), "srchex" or "dsthex" should be specified to convert
298 into such form.
302 into such form.
299
303
300 If "targets" is specified, only bookmarks listed in it are
304 If "targets" is specified, only bookmarks listed in it are
301 examined.
305 examined.
302 '''
306 '''
303 if not srchex:
307 if not srchex:
304 srchex = lambda x: x
308 srchex = lambda x: x
305 if not dsthex:
309 if not dsthex:
306 dsthex = lambda x: x
310 dsthex = lambda x: x
307
311
308 if targets:
312 if targets:
309 bset = set(targets)
313 bset = set(targets)
310 else:
314 else:
311 srcmarkset = set(srcmarks)
315 srcmarkset = set(srcmarks)
312 dstmarkset = set(dstmarks)
316 dstmarkset = set(dstmarks)
313 bset = srcmarkset | dstmarkset
317 bset = srcmarkset | dstmarkset
314
318
315 results = ([], [], [], [], [], [], [], [])
319 results = ([], [], [], [], [], [], [], [])
316 addsrc = results[0].append
320 addsrc = results[0].append
317 adddst = results[1].append
321 adddst = results[1].append
318 advsrc = results[2].append
322 advsrc = results[2].append
319 advdst = results[3].append
323 advdst = results[3].append
320 diverge = results[4].append
324 diverge = results[4].append
321 differ = results[5].append
325 differ = results[5].append
322 invalid = results[6].append
326 invalid = results[6].append
323 same = results[7].append
327 same = results[7].append
324
328
325 for b in sorted(bset):
329 for b in sorted(bset):
326 if b not in srcmarks:
330 if b not in srcmarks:
327 if b in dstmarks:
331 if b in dstmarks:
328 adddst((b, None, dsthex(dstmarks[b])))
332 adddst((b, None, dsthex(dstmarks[b])))
329 else:
333 else:
330 invalid((b, None, None))
334 invalid((b, None, None))
331 elif b not in dstmarks:
335 elif b not in dstmarks:
332 addsrc((b, srchex(srcmarks[b]), None))
336 addsrc((b, srchex(srcmarks[b]), None))
333 else:
337 else:
334 scid = srchex(srcmarks[b])
338 scid = srchex(srcmarks[b])
335 dcid = dsthex(dstmarks[b])
339 dcid = dsthex(dstmarks[b])
336 if scid == dcid:
340 if scid == dcid:
337 same((b, scid, dcid))
341 same((b, scid, dcid))
338 elif scid in repo and dcid in repo:
342 elif scid in repo and dcid in repo:
339 sctx = repo[scid]
343 sctx = repo[scid]
340 dctx = repo[dcid]
344 dctx = repo[dcid]
341 if sctx.rev() < dctx.rev():
345 if sctx.rev() < dctx.rev():
342 if validdest(repo, sctx, dctx):
346 if validdest(repo, sctx, dctx):
343 advdst((b, scid, dcid))
347 advdst((b, scid, dcid))
344 else:
348 else:
345 diverge((b, scid, dcid))
349 diverge((b, scid, dcid))
346 else:
350 else:
347 if validdest(repo, dctx, sctx):
351 if validdest(repo, dctx, sctx):
348 advsrc((b, scid, dcid))
352 advsrc((b, scid, dcid))
349 else:
353 else:
350 diverge((b, scid, dcid))
354 diverge((b, scid, dcid))
351 else:
355 else:
352 # it is too expensive to examine in detail, in this case
356 # it is too expensive to examine in detail, in this case
353 differ((b, scid, dcid))
357 differ((b, scid, dcid))
354
358
355 return results
359 return results
356
360
357 def _diverge(ui, b, path, localmarks):
361 def _diverge(ui, b, path, localmarks):
358 if b == '@':
362 if b == '@':
359 b = ''
363 b = ''
360 # find a unique @ suffix
364 # find a unique @ suffix
361 for x in range(1, 100):
365 for x in range(1, 100):
362 n = '%s@%d' % (b, x)
366 n = '%s@%d' % (b, x)
363 if n not in localmarks:
367 if n not in localmarks:
364 break
368 break
365 # try to use an @pathalias suffix
369 # try to use an @pathalias suffix
366 # if an @pathalias already exists, we overwrite (update) it
370 # if an @pathalias already exists, we overwrite (update) it
367 if path.startswith("file:"):
371 if path.startswith("file:"):
368 path = util.url(path).path
372 path = util.url(path).path
369 for p, u in ui.configitems("paths"):
373 for p, u in ui.configitems("paths"):
370 if u.startswith("file:"):
374 if u.startswith("file:"):
371 u = util.url(u).path
375 u = util.url(u).path
372 if path == u:
376 if path == u:
373 n = '%s@%s' % (b, p)
377 n = '%s@%s' % (b, p)
374 return n
378 return n
375
379
376 def updatefromremote(ui, repo, remotemarks, path, trfunc, explicit=()):
380 def updatefromremote(ui, repo, remotemarks, path, trfunc, explicit=()):
377 ui.debug("checking for updated bookmarks\n")
381 ui.debug("checking for updated bookmarks\n")
378 localmarks = repo._bookmarks
382 localmarks = repo._bookmarks
379 (addsrc, adddst, advsrc, advdst, diverge, differ, invalid, same
383 (addsrc, adddst, advsrc, advdst, diverge, differ, invalid, same
380 ) = compare(repo, remotemarks, localmarks, dsthex=hex)
384 ) = compare(repo, remotemarks, localmarks, dsthex=hex)
381
385
382 status = ui.status
386 status = ui.status
383 warn = ui.warn
387 warn = ui.warn
384 if ui.configbool('ui', 'quietbookmarkmove', False):
388 if ui.configbool('ui', 'quietbookmarkmove', False):
385 status = warn = ui.debug
389 status = warn = ui.debug
386
390
387 explicit = set(explicit)
391 explicit = set(explicit)
388 changed = []
392 changed = []
389 for b, scid, dcid in addsrc:
393 for b, scid, dcid in addsrc:
390 if scid in repo: # add remote bookmarks for changes we already have
394 if scid in repo: # add remote bookmarks for changes we already have
391 changed.append((b, bin(scid), status,
395 changed.append((b, bin(scid), status,
392 _("adding remote bookmark %s\n") % (b)))
396 _("adding remote bookmark %s\n") % (b)))
393 for b, scid, dcid in advsrc:
397 for b, scid, dcid in advsrc:
394 changed.append((b, bin(scid), status,
398 changed.append((b, bin(scid), status,
395 _("updating bookmark %s\n") % (b)))
399 _("updating bookmark %s\n") % (b)))
396 # remove normal movement from explicit set
400 # remove normal movement from explicit set
397 explicit.difference_update(d[0] for d in changed)
401 explicit.difference_update(d[0] for d in changed)
398
402
399 for b, scid, dcid in diverge:
403 for b, scid, dcid in diverge:
400 if b in explicit:
404 if b in explicit:
401 explicit.discard(b)
405 explicit.discard(b)
402 changed.append((b, bin(scid), status,
406 changed.append((b, bin(scid), status,
403 _("importing bookmark %s\n") % (b)))
407 _("importing bookmark %s\n") % (b)))
404 else:
408 else:
405 db = _diverge(ui, b, path, localmarks)
409 db = _diverge(ui, b, path, localmarks)
406 changed.append((db, bin(scid), warn,
410 changed.append((db, bin(scid), warn,
407 _("divergent bookmark %s stored as %s\n")
411 _("divergent bookmark %s stored as %s\n")
408 % (b, db)))
412 % (b, db)))
409 for b, scid, dcid in adddst + advdst:
413 for b, scid, dcid in adddst + advdst:
410 if b in explicit:
414 if b in explicit:
411 explicit.discard(b)
415 explicit.discard(b)
412 changed.append((b, bin(scid), status,
416 changed.append((b, bin(scid), status,
413 _("importing bookmark %s\n") % (b)))
417 _("importing bookmark %s\n") % (b)))
414
418
415 if changed:
419 if changed:
416 tr = trfunc()
420 tr = trfunc()
417 for b, node, writer, msg in sorted(changed):
421 for b, node, writer, msg in sorted(changed):
418 localmarks[b] = node
422 localmarks[b] = node
419 writer(msg)
423 writer(msg)
420 localmarks.recordchange(tr)
424 localmarks.recordchange(tr)
421
425
422 def diff(ui, dst, src):
426 def diff(ui, dst, src):
423 ui.status(_("searching for changed bookmarks\n"))
427 ui.status(_("searching for changed bookmarks\n"))
424
428
425 smarks = src.listkeys('bookmarks')
429 smarks = src.listkeys('bookmarks')
426 dmarks = dst.listkeys('bookmarks')
430 dmarks = dst.listkeys('bookmarks')
427
431
428 diff = sorted(set(smarks) - set(dmarks))
432 diff = sorted(set(smarks) - set(dmarks))
429 for k in diff:
433 for k in diff:
430 mark = ui.debugflag and smarks[k] or smarks[k][:12]
434 mark = ui.debugflag and smarks[k] or smarks[k][:12]
431 ui.write(" %-25s %s\n" % (k, mark))
435 ui.write(" %-25s %s\n" % (k, mark))
432
436
433 if len(diff) <= 0:
437 if len(diff) <= 0:
434 ui.status(_("no changed bookmarks found\n"))
438 ui.status(_("no changed bookmarks found\n"))
435 return 1
439 return 1
436 return 0
440 return 0
437
441
438 def validdest(repo, old, new):
442 def validdest(repo, old, new):
439 """Is the new bookmark destination a valid update from the old one"""
443 """Is the new bookmark destination a valid update from the old one"""
440 repo = repo.unfiltered()
444 repo = repo.unfiltered()
441 if old == new:
445 if old == new:
442 # Old == new -> nothing to update.
446 # Old == new -> nothing to update.
443 return False
447 return False
444 elif not old:
448 elif not old:
445 # old is nullrev, anything is valid.
449 # old is nullrev, anything is valid.
446 # (new != nullrev has been excluded by the previous check)
450 # (new != nullrev has been excluded by the previous check)
447 return True
451 return True
448 elif repo.obsstore:
452 elif repo.obsstore:
449 return new.node() in obsolete.foreground(repo, [old.node()])
453 return new.node() in obsolete.foreground(repo, [old.node()])
450 else:
454 else:
451 # still an independent clause as it is lazyer (and therefore faster)
455 # still an independent clause as it is lazyer (and therefore faster)
452 return old.descendant(new)
456 return old.descendant(new)
General Comments 0
You need to be logged in to leave comments. Login now