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