##// END OF EJS Templates
bookmarks: use absolute_import
Gregory Szorc -
r25917:aa323b53 default
parent child Browse files
Show More
@@ -1,554 +1,565 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 __future__ import absolute_import
9
10 import errno
8 import os
11 import os
9 from mercurial.i18n import _
12
10 from mercurial.node import hex, bin
13 from .i18n import _
11 from mercurial import encoding, util, obsolete, lock as lockmod
14 from .node import (
12 import errno
15 bin,
16 hex,
17 )
18 from . import (
19 encoding,
20 lock as lockmod,
21 obsolete,
22 util,
23 )
13
24
14 class bmstore(dict):
25 class bmstore(dict):
15 """Storage for bookmarks.
26 """Storage for bookmarks.
16
27
17 This object should do all bookmark reads and writes, so that it's
28 This object should do all bookmark reads and writes, so that it's
18 fairly simple to replace the storage underlying bookmarks without
29 fairly simple to replace the storage underlying bookmarks without
19 having to clone the logic surrounding bookmarks.
30 having to clone the logic surrounding bookmarks.
20
31
21 This particular bmstore implementation stores bookmarks as
32 This particular bmstore implementation stores bookmarks as
22 {hash}\s{name}\n (the same format as localtags) in
33 {hash}\s{name}\n (the same format as localtags) in
23 .hg/bookmarks. The mapping is stored as {name: nodeid}.
34 .hg/bookmarks. The mapping is stored as {name: nodeid}.
24
35
25 This class does NOT handle the "active" bookmark state at this
36 This class does NOT handle the "active" bookmark state at this
26 time.
37 time.
27 """
38 """
28
39
29 def __init__(self, repo):
40 def __init__(self, repo):
30 dict.__init__(self)
41 dict.__init__(self)
31 self._repo = repo
42 self._repo = repo
32 try:
43 try:
33 bkfile = self.getbkfile(repo)
44 bkfile = self.getbkfile(repo)
34 for line in bkfile:
45 for line in bkfile:
35 line = line.strip()
46 line = line.strip()
36 if not line:
47 if not line:
37 continue
48 continue
38 if ' ' not in line:
49 if ' ' not in line:
39 repo.ui.warn(_('malformed line in .hg/bookmarks: %r\n')
50 repo.ui.warn(_('malformed line in .hg/bookmarks: %r\n')
40 % line)
51 % line)
41 continue
52 continue
42 sha, refspec = line.split(' ', 1)
53 sha, refspec = line.split(' ', 1)
43 refspec = encoding.tolocal(refspec)
54 refspec = encoding.tolocal(refspec)
44 try:
55 try:
45 self[refspec] = repo.changelog.lookup(sha)
56 self[refspec] = repo.changelog.lookup(sha)
46 except LookupError:
57 except LookupError:
47 pass
58 pass
48 except IOError as inst:
59 except IOError as inst:
49 if inst.errno != errno.ENOENT:
60 if inst.errno != errno.ENOENT:
50 raise
61 raise
51
62
52 def getbkfile(self, repo):
63 def getbkfile(self, repo):
53 bkfile = None
64 bkfile = None
54 if 'HG_PENDING' in os.environ:
65 if 'HG_PENDING' in os.environ:
55 try:
66 try:
56 bkfile = repo.vfs('bookmarks.pending')
67 bkfile = repo.vfs('bookmarks.pending')
57 except IOError as inst:
68 except IOError as inst:
58 if inst.errno != errno.ENOENT:
69 if inst.errno != errno.ENOENT:
59 raise
70 raise
60 if bkfile is None:
71 if bkfile is None:
61 bkfile = repo.vfs('bookmarks')
72 bkfile = repo.vfs('bookmarks')
62 return bkfile
73 return bkfile
63
74
64 def recordchange(self, tr):
75 def recordchange(self, tr):
65 """record that bookmarks have been changed in a transaction
76 """record that bookmarks have been changed in a transaction
66
77
67 The transaction is then responsible for updating the file content."""
78 The transaction is then responsible for updating the file content."""
68 tr.addfilegenerator('bookmarks', ('bookmarks',), self._write,
79 tr.addfilegenerator('bookmarks', ('bookmarks',), self._write,
69 location='plain')
80 location='plain')
70 tr.hookargs['bookmark_moved'] = '1'
81 tr.hookargs['bookmark_moved'] = '1'
71
82
72 def write(self):
83 def write(self):
73 '''Write bookmarks
84 '''Write bookmarks
74
85
75 Write the given bookmark => hash dictionary to the .hg/bookmarks file
86 Write the given bookmark => hash dictionary to the .hg/bookmarks file
76 in a format equal to those of localtags.
87 in a format equal to those of localtags.
77
88
78 We also store a backup of the previous state in undo.bookmarks that
89 We also store a backup of the previous state in undo.bookmarks that
79 can be copied back on rollback.
90 can be copied back on rollback.
80 '''
91 '''
81 repo = self._repo
92 repo = self._repo
82 self._writerepo(repo)
93 self._writerepo(repo)
83 repo.invalidatevolatilesets()
94 repo.invalidatevolatilesets()
84
95
85 def _writerepo(self, repo):
96 def _writerepo(self, repo):
86 """Factored out for extensibility"""
97 """Factored out for extensibility"""
87 if repo._activebookmark not in self:
98 if repo._activebookmark not in self:
88 deactivate(repo)
99 deactivate(repo)
89
100
90 wlock = repo.wlock()
101 wlock = repo.wlock()
91 try:
102 try:
92
103
93 file = repo.vfs('bookmarks', 'w', atomictemp=True)
104 file = repo.vfs('bookmarks', 'w', atomictemp=True)
94 self._write(file)
105 self._write(file)
95 file.close()
106 file.close()
96
107
97 finally:
108 finally:
98 wlock.release()
109 wlock.release()
99
110
100 def _write(self, fp):
111 def _write(self, fp):
101 for name, node in self.iteritems():
112 for name, node in self.iteritems():
102 fp.write("%s %s\n" % (hex(node), encoding.fromlocal(name)))
113 fp.write("%s %s\n" % (hex(node), encoding.fromlocal(name)))
103
114
104 def readactive(repo):
115 def readactive(repo):
105 """
116 """
106 Get the active bookmark. We can have an active bookmark that updates
117 Get the active bookmark. We can have an active bookmark that updates
107 itself as we commit. This function returns the name of that bookmark.
118 itself as we commit. This function returns the name of that bookmark.
108 It is stored in .hg/bookmarks.current
119 It is stored in .hg/bookmarks.current
109 """
120 """
110 mark = None
121 mark = None
111 try:
122 try:
112 file = repo.vfs('bookmarks.current')
123 file = repo.vfs('bookmarks.current')
113 except IOError as inst:
124 except IOError as inst:
114 if inst.errno != errno.ENOENT:
125 if inst.errno != errno.ENOENT:
115 raise
126 raise
116 return None
127 return None
117 try:
128 try:
118 # No readline() in osutil.posixfile, reading everything is cheap
129 # No readline() in osutil.posixfile, reading everything is cheap
119 mark = encoding.tolocal((file.readlines() or [''])[0])
130 mark = encoding.tolocal((file.readlines() or [''])[0])
120 if mark == '' or mark not in repo._bookmarks:
131 if mark == '' or mark not in repo._bookmarks:
121 mark = None
132 mark = None
122 finally:
133 finally:
123 file.close()
134 file.close()
124 return mark
135 return mark
125
136
126 def activate(repo, mark):
137 def activate(repo, mark):
127 """
138 """
128 Set the given bookmark to be 'active', meaning that this bookmark will
139 Set the given bookmark to be 'active', meaning that this bookmark will
129 follow new commits that are made.
140 follow new commits that are made.
130 The name is recorded in .hg/bookmarks.current
141 The name is recorded in .hg/bookmarks.current
131 """
142 """
132 if mark not in repo._bookmarks:
143 if mark not in repo._bookmarks:
133 raise AssertionError('bookmark %s does not exist!' % mark)
144 raise AssertionError('bookmark %s does not exist!' % mark)
134
145
135 active = repo._activebookmark
146 active = repo._activebookmark
136 if active == mark:
147 if active == mark:
137 return
148 return
138
149
139 wlock = repo.wlock()
150 wlock = repo.wlock()
140 try:
151 try:
141 file = repo.vfs('bookmarks.current', 'w', atomictemp=True)
152 file = repo.vfs('bookmarks.current', 'w', atomictemp=True)
142 file.write(encoding.fromlocal(mark))
153 file.write(encoding.fromlocal(mark))
143 file.close()
154 file.close()
144 finally:
155 finally:
145 wlock.release()
156 wlock.release()
146 repo._activebookmark = mark
157 repo._activebookmark = mark
147
158
148 def deactivate(repo):
159 def deactivate(repo):
149 """
160 """
150 Unset the active bookmark in this reposiotry.
161 Unset the active bookmark in this reposiotry.
151 """
162 """
152 wlock = repo.wlock()
163 wlock = repo.wlock()
153 try:
164 try:
154 repo.vfs.unlink('bookmarks.current')
165 repo.vfs.unlink('bookmarks.current')
155 repo._activebookmark = None
166 repo._activebookmark = None
156 except OSError as inst:
167 except OSError as inst:
157 if inst.errno != errno.ENOENT:
168 if inst.errno != errno.ENOENT:
158 raise
169 raise
159 finally:
170 finally:
160 wlock.release()
171 wlock.release()
161
172
162 def isactivewdirparent(repo):
173 def isactivewdirparent(repo):
163 """
174 """
164 Tell whether the 'active' bookmark (the one that follows new commits)
175 Tell whether the 'active' bookmark (the one that follows new commits)
165 points to one of the parents of the current working directory (wdir).
176 points to one of the parents of the current working directory (wdir).
166
177
167 While this is normally the case, it can on occasion be false; for example,
178 While this is normally the case, it can on occasion be false; for example,
168 immediately after a pull, the active bookmark can be moved to point
179 immediately after a pull, the active bookmark can be moved to point
169 to a place different than the wdir. This is solved by running `hg update`.
180 to a place different than the wdir. This is solved by running `hg update`.
170 """
181 """
171 mark = repo._activebookmark
182 mark = repo._activebookmark
172 marks = repo._bookmarks
183 marks = repo._bookmarks
173 parents = [p.node() for p in repo[None].parents()]
184 parents = [p.node() for p in repo[None].parents()]
174 return (mark in marks and marks[mark] in parents)
185 return (mark in marks and marks[mark] in parents)
175
186
176 def deletedivergent(repo, deletefrom, bm):
187 def deletedivergent(repo, deletefrom, bm):
177 '''Delete divergent versions of bm on nodes in deletefrom.
188 '''Delete divergent versions of bm on nodes in deletefrom.
178
189
179 Return True if at least one bookmark was deleted, False otherwise.'''
190 Return True if at least one bookmark was deleted, False otherwise.'''
180 deleted = False
191 deleted = False
181 marks = repo._bookmarks
192 marks = repo._bookmarks
182 divergent = [b for b in marks if b.split('@', 1)[0] == bm.split('@', 1)[0]]
193 divergent = [b for b in marks if b.split('@', 1)[0] == bm.split('@', 1)[0]]
183 for mark in divergent:
194 for mark in divergent:
184 if mark == '@' or '@' not in mark:
195 if mark == '@' or '@' not in mark:
185 # can't be divergent by definition
196 # can't be divergent by definition
186 continue
197 continue
187 if mark and marks[mark] in deletefrom:
198 if mark and marks[mark] in deletefrom:
188 if mark != bm:
199 if mark != bm:
189 del marks[mark]
200 del marks[mark]
190 deleted = True
201 deleted = True
191 return deleted
202 return deleted
192
203
193 def calculateupdate(ui, repo, checkout):
204 def calculateupdate(ui, repo, checkout):
194 '''Return a tuple (targetrev, movemarkfrom) indicating the rev to
205 '''Return a tuple (targetrev, movemarkfrom) indicating the rev to
195 check out and where to move the active bookmark from, if needed.'''
206 check out and where to move the active bookmark from, if needed.'''
196 movemarkfrom = None
207 movemarkfrom = None
197 if checkout is None:
208 if checkout is None:
198 activemark = repo._activebookmark
209 activemark = repo._activebookmark
199 if isactivewdirparent(repo):
210 if isactivewdirparent(repo):
200 movemarkfrom = repo['.'].node()
211 movemarkfrom = repo['.'].node()
201 elif activemark:
212 elif activemark:
202 ui.status(_("updating to active bookmark %s\n") % activemark)
213 ui.status(_("updating to active bookmark %s\n") % activemark)
203 checkout = activemark
214 checkout = activemark
204 return (checkout, movemarkfrom)
215 return (checkout, movemarkfrom)
205
216
206 def update(repo, parents, node):
217 def update(repo, parents, node):
207 deletefrom = parents
218 deletefrom = parents
208 marks = repo._bookmarks
219 marks = repo._bookmarks
209 update = False
220 update = False
210 active = repo._activebookmark
221 active = repo._activebookmark
211 if not active:
222 if not active:
212 return False
223 return False
213
224
214 if marks[active] in parents:
225 if marks[active] in parents:
215 new = repo[node]
226 new = repo[node]
216 divs = [repo[b] for b in marks
227 divs = [repo[b] for b in marks
217 if b.split('@', 1)[0] == active.split('@', 1)[0]]
228 if b.split('@', 1)[0] == active.split('@', 1)[0]]
218 anc = repo.changelog.ancestors([new.rev()])
229 anc = repo.changelog.ancestors([new.rev()])
219 deletefrom = [b.node() for b in divs if b.rev() in anc or b == new]
230 deletefrom = [b.node() for b in divs if b.rev() in anc or b == new]
220 if validdest(repo, repo[marks[active]], new):
231 if validdest(repo, repo[marks[active]], new):
221 marks[active] = new.node()
232 marks[active] = new.node()
222 update = True
233 update = True
223
234
224 if deletedivergent(repo, deletefrom, active):
235 if deletedivergent(repo, deletefrom, active):
225 update = True
236 update = True
226
237
227 if update:
238 if update:
228 marks.write()
239 marks.write()
229 return update
240 return update
230
241
231 def listbookmarks(repo):
242 def listbookmarks(repo):
232 # We may try to list bookmarks on a repo type that does not
243 # We may try to list bookmarks on a repo type that does not
233 # support it (e.g., statichttprepository).
244 # support it (e.g., statichttprepository).
234 marks = getattr(repo, '_bookmarks', {})
245 marks = getattr(repo, '_bookmarks', {})
235
246
236 d = {}
247 d = {}
237 hasnode = repo.changelog.hasnode
248 hasnode = repo.changelog.hasnode
238 for k, v in marks.iteritems():
249 for k, v in marks.iteritems():
239 # don't expose local divergent bookmarks
250 # don't expose local divergent bookmarks
240 if hasnode(v) and ('@' not in k or k.endswith('@')):
251 if hasnode(v) and ('@' not in k or k.endswith('@')):
241 d[k] = hex(v)
252 d[k] = hex(v)
242 return d
253 return d
243
254
244 def pushbookmark(repo, key, old, new):
255 def pushbookmark(repo, key, old, new):
245 w = l = tr = None
256 w = l = tr = None
246 try:
257 try:
247 w = repo.wlock()
258 w = repo.wlock()
248 l = repo.lock()
259 l = repo.lock()
249 tr = repo.transaction('bookmarks')
260 tr = repo.transaction('bookmarks')
250 marks = repo._bookmarks
261 marks = repo._bookmarks
251 existing = hex(marks.get(key, ''))
262 existing = hex(marks.get(key, ''))
252 if existing != old and existing != new:
263 if existing != old and existing != new:
253 return False
264 return False
254 if new == '':
265 if new == '':
255 del marks[key]
266 del marks[key]
256 else:
267 else:
257 if new not in repo:
268 if new not in repo:
258 return False
269 return False
259 marks[key] = repo[new].node()
270 marks[key] = repo[new].node()
260 marks.recordchange(tr)
271 marks.recordchange(tr)
261 tr.close()
272 tr.close()
262 return True
273 return True
263 finally:
274 finally:
264 lockmod.release(tr, l, w)
275 lockmod.release(tr, l, w)
265
276
266 def compare(repo, srcmarks, dstmarks,
277 def compare(repo, srcmarks, dstmarks,
267 srchex=None, dsthex=None, targets=None):
278 srchex=None, dsthex=None, targets=None):
268 '''Compare bookmarks between srcmarks and dstmarks
279 '''Compare bookmarks between srcmarks and dstmarks
269
280
270 This returns tuple "(addsrc, adddst, advsrc, advdst, diverge,
281 This returns tuple "(addsrc, adddst, advsrc, advdst, diverge,
271 differ, invalid)", each are list of bookmarks below:
282 differ, invalid)", each are list of bookmarks below:
272
283
273 :addsrc: added on src side (removed on dst side, perhaps)
284 :addsrc: added on src side (removed on dst side, perhaps)
274 :adddst: added on dst side (removed on src side, perhaps)
285 :adddst: added on dst side (removed on src side, perhaps)
275 :advsrc: advanced on src side
286 :advsrc: advanced on src side
276 :advdst: advanced on dst side
287 :advdst: advanced on dst side
277 :diverge: diverge
288 :diverge: diverge
278 :differ: changed, but changeset referred on src is unknown on dst
289 :differ: changed, but changeset referred on src is unknown on dst
279 :invalid: unknown on both side
290 :invalid: unknown on both side
280 :same: same on both side
291 :same: same on both side
281
292
282 Each elements of lists in result tuple is tuple "(bookmark name,
293 Each elements of lists in result tuple is tuple "(bookmark name,
283 changeset ID on source side, changeset ID on destination
294 changeset ID on source side, changeset ID on destination
284 side)". Each changeset IDs are 40 hexadecimal digit string or
295 side)". Each changeset IDs are 40 hexadecimal digit string or
285 None.
296 None.
286
297
287 Changeset IDs of tuples in "addsrc", "adddst", "differ" or
298 Changeset IDs of tuples in "addsrc", "adddst", "differ" or
288 "invalid" list may be unknown for repo.
299 "invalid" list may be unknown for repo.
289
300
290 This function expects that "srcmarks" and "dstmarks" return
301 This function expects that "srcmarks" and "dstmarks" return
291 changeset ID in 40 hexadecimal digit string for specified
302 changeset ID in 40 hexadecimal digit string for specified
292 bookmark. If not so (e.g. bmstore "repo._bookmarks" returning
303 bookmark. If not so (e.g. bmstore "repo._bookmarks" returning
293 binary value), "srchex" or "dsthex" should be specified to convert
304 binary value), "srchex" or "dsthex" should be specified to convert
294 into such form.
305 into such form.
295
306
296 If "targets" is specified, only bookmarks listed in it are
307 If "targets" is specified, only bookmarks listed in it are
297 examined.
308 examined.
298 '''
309 '''
299 if not srchex:
310 if not srchex:
300 srchex = lambda x: x
311 srchex = lambda x: x
301 if not dsthex:
312 if not dsthex:
302 dsthex = lambda x: x
313 dsthex = lambda x: x
303
314
304 if targets:
315 if targets:
305 bset = set(targets)
316 bset = set(targets)
306 else:
317 else:
307 srcmarkset = set(srcmarks)
318 srcmarkset = set(srcmarks)
308 dstmarkset = set(dstmarks)
319 dstmarkset = set(dstmarks)
309 bset = srcmarkset | dstmarkset
320 bset = srcmarkset | dstmarkset
310
321
311 results = ([], [], [], [], [], [], [], [])
322 results = ([], [], [], [], [], [], [], [])
312 addsrc = results[0].append
323 addsrc = results[0].append
313 adddst = results[1].append
324 adddst = results[1].append
314 advsrc = results[2].append
325 advsrc = results[2].append
315 advdst = results[3].append
326 advdst = results[3].append
316 diverge = results[4].append
327 diverge = results[4].append
317 differ = results[5].append
328 differ = results[5].append
318 invalid = results[6].append
329 invalid = results[6].append
319 same = results[7].append
330 same = results[7].append
320
331
321 for b in sorted(bset):
332 for b in sorted(bset):
322 if b not in srcmarks:
333 if b not in srcmarks:
323 if b in dstmarks:
334 if b in dstmarks:
324 adddst((b, None, dsthex(dstmarks[b])))
335 adddst((b, None, dsthex(dstmarks[b])))
325 else:
336 else:
326 invalid((b, None, None))
337 invalid((b, None, None))
327 elif b not in dstmarks:
338 elif b not in dstmarks:
328 addsrc((b, srchex(srcmarks[b]), None))
339 addsrc((b, srchex(srcmarks[b]), None))
329 else:
340 else:
330 scid = srchex(srcmarks[b])
341 scid = srchex(srcmarks[b])
331 dcid = dsthex(dstmarks[b])
342 dcid = dsthex(dstmarks[b])
332 if scid == dcid:
343 if scid == dcid:
333 same((b, scid, dcid))
344 same((b, scid, dcid))
334 elif scid in repo and dcid in repo:
345 elif scid in repo and dcid in repo:
335 sctx = repo[scid]
346 sctx = repo[scid]
336 dctx = repo[dcid]
347 dctx = repo[dcid]
337 if sctx.rev() < dctx.rev():
348 if sctx.rev() < dctx.rev():
338 if validdest(repo, sctx, dctx):
349 if validdest(repo, sctx, dctx):
339 advdst((b, scid, dcid))
350 advdst((b, scid, dcid))
340 else:
351 else:
341 diverge((b, scid, dcid))
352 diverge((b, scid, dcid))
342 else:
353 else:
343 if validdest(repo, dctx, sctx):
354 if validdest(repo, dctx, sctx):
344 advsrc((b, scid, dcid))
355 advsrc((b, scid, dcid))
345 else:
356 else:
346 diverge((b, scid, dcid))
357 diverge((b, scid, dcid))
347 else:
358 else:
348 # it is too expensive to examine in detail, in this case
359 # it is too expensive to examine in detail, in this case
349 differ((b, scid, dcid))
360 differ((b, scid, dcid))
350
361
351 return results
362 return results
352
363
353 def _diverge(ui, b, path, localmarks, remotenode):
364 def _diverge(ui, b, path, localmarks, remotenode):
354 '''Return appropriate diverged bookmark for specified ``path``
365 '''Return appropriate diverged bookmark for specified ``path``
355
366
356 This returns None, if it is failed to assign any divergent
367 This returns None, if it is failed to assign any divergent
357 bookmark name.
368 bookmark name.
358
369
359 This reuses already existing one with "@number" suffix, if it
370 This reuses already existing one with "@number" suffix, if it
360 refers ``remotenode``.
371 refers ``remotenode``.
361 '''
372 '''
362 if b == '@':
373 if b == '@':
363 b = ''
374 b = ''
364 # try to use an @pathalias suffix
375 # try to use an @pathalias suffix
365 # if an @pathalias already exists, we overwrite (update) it
376 # if an @pathalias already exists, we overwrite (update) it
366 if path.startswith("file:"):
377 if path.startswith("file:"):
367 path = util.url(path).path
378 path = util.url(path).path
368 for p, u in ui.configitems("paths"):
379 for p, u in ui.configitems("paths"):
369 if u.startswith("file:"):
380 if u.startswith("file:"):
370 u = util.url(u).path
381 u = util.url(u).path
371 if path == u:
382 if path == u:
372 return '%s@%s' % (b, p)
383 return '%s@%s' % (b, p)
373
384
374 # assign a unique "@number" suffix newly
385 # assign a unique "@number" suffix newly
375 for x in range(1, 100):
386 for x in range(1, 100):
376 n = '%s@%d' % (b, x)
387 n = '%s@%d' % (b, x)
377 if n not in localmarks or localmarks[n] == remotenode:
388 if n not in localmarks or localmarks[n] == remotenode:
378 return n
389 return n
379
390
380 return None
391 return None
381
392
382 def updatefromremote(ui, repo, remotemarks, path, trfunc, explicit=()):
393 def updatefromremote(ui, repo, remotemarks, path, trfunc, explicit=()):
383 ui.debug("checking for updated bookmarks\n")
394 ui.debug("checking for updated bookmarks\n")
384 localmarks = repo._bookmarks
395 localmarks = repo._bookmarks
385 (addsrc, adddst, advsrc, advdst, diverge, differ, invalid, same
396 (addsrc, adddst, advsrc, advdst, diverge, differ, invalid, same
386 ) = compare(repo, remotemarks, localmarks, dsthex=hex)
397 ) = compare(repo, remotemarks, localmarks, dsthex=hex)
387
398
388 status = ui.status
399 status = ui.status
389 warn = ui.warn
400 warn = ui.warn
390 if ui.configbool('ui', 'quietbookmarkmove', False):
401 if ui.configbool('ui', 'quietbookmarkmove', False):
391 status = warn = ui.debug
402 status = warn = ui.debug
392
403
393 explicit = set(explicit)
404 explicit = set(explicit)
394 changed = []
405 changed = []
395 for b, scid, dcid in addsrc:
406 for b, scid, dcid in addsrc:
396 if scid in repo: # add remote bookmarks for changes we already have
407 if scid in repo: # add remote bookmarks for changes we already have
397 changed.append((b, bin(scid), status,
408 changed.append((b, bin(scid), status,
398 _("adding remote bookmark %s\n") % (b)))
409 _("adding remote bookmark %s\n") % (b)))
399 elif b in explicit:
410 elif b in explicit:
400 explicit.remove(b)
411 explicit.remove(b)
401 ui.warn(_("remote bookmark %s points to locally missing %s\n")
412 ui.warn(_("remote bookmark %s points to locally missing %s\n")
402 % (b, scid[:12]))
413 % (b, scid[:12]))
403
414
404 for b, scid, dcid in advsrc:
415 for b, scid, dcid in advsrc:
405 changed.append((b, bin(scid), status,
416 changed.append((b, bin(scid), status,
406 _("updating bookmark %s\n") % (b)))
417 _("updating bookmark %s\n") % (b)))
407 # remove normal movement from explicit set
418 # remove normal movement from explicit set
408 explicit.difference_update(d[0] for d in changed)
419 explicit.difference_update(d[0] for d in changed)
409
420
410 for b, scid, dcid in diverge:
421 for b, scid, dcid in diverge:
411 if b in explicit:
422 if b in explicit:
412 explicit.discard(b)
423 explicit.discard(b)
413 changed.append((b, bin(scid), status,
424 changed.append((b, bin(scid), status,
414 _("importing bookmark %s\n") % (b)))
425 _("importing bookmark %s\n") % (b)))
415 else:
426 else:
416 snode = bin(scid)
427 snode = bin(scid)
417 db = _diverge(ui, b, path, localmarks, snode)
428 db = _diverge(ui, b, path, localmarks, snode)
418 if db:
429 if db:
419 changed.append((db, snode, warn,
430 changed.append((db, snode, warn,
420 _("divergent bookmark %s stored as %s\n") %
431 _("divergent bookmark %s stored as %s\n") %
421 (b, db)))
432 (b, db)))
422 else:
433 else:
423 warn(_("warning: failed to assign numbered name "
434 warn(_("warning: failed to assign numbered name "
424 "to divergent bookmark %s\n") % (b))
435 "to divergent bookmark %s\n") % (b))
425 for b, scid, dcid in adddst + advdst:
436 for b, scid, dcid in adddst + advdst:
426 if b in explicit:
437 if b in explicit:
427 explicit.discard(b)
438 explicit.discard(b)
428 changed.append((b, bin(scid), status,
439 changed.append((b, bin(scid), status,
429 _("importing bookmark %s\n") % (b)))
440 _("importing bookmark %s\n") % (b)))
430 for b, scid, dcid in differ:
441 for b, scid, dcid in differ:
431 if b in explicit:
442 if b in explicit:
432 explicit.remove(b)
443 explicit.remove(b)
433 ui.warn(_("remote bookmark %s points to locally missing %s\n")
444 ui.warn(_("remote bookmark %s points to locally missing %s\n")
434 % (b, scid[:12]))
445 % (b, scid[:12]))
435
446
436 if changed:
447 if changed:
437 tr = trfunc()
448 tr = trfunc()
438 for b, node, writer, msg in sorted(changed):
449 for b, node, writer, msg in sorted(changed):
439 localmarks[b] = node
450 localmarks[b] = node
440 writer(msg)
451 writer(msg)
441 localmarks.recordchange(tr)
452 localmarks.recordchange(tr)
442
453
443 def incoming(ui, repo, other):
454 def incoming(ui, repo, other):
444 '''Show bookmarks incoming from other to repo
455 '''Show bookmarks incoming from other to repo
445 '''
456 '''
446 ui.status(_("searching for changed bookmarks\n"))
457 ui.status(_("searching for changed bookmarks\n"))
447
458
448 r = compare(repo, other.listkeys('bookmarks'), repo._bookmarks,
459 r = compare(repo, other.listkeys('bookmarks'), repo._bookmarks,
449 dsthex=hex)
460 dsthex=hex)
450 addsrc, adddst, advsrc, advdst, diverge, differ, invalid, same = r
461 addsrc, adddst, advsrc, advdst, diverge, differ, invalid, same = r
451
462
452 incomings = []
463 incomings = []
453 if ui.debugflag:
464 if ui.debugflag:
454 getid = lambda id: id
465 getid = lambda id: id
455 else:
466 else:
456 getid = lambda id: id[:12]
467 getid = lambda id: id[:12]
457 if ui.verbose:
468 if ui.verbose:
458 def add(b, id, st):
469 def add(b, id, st):
459 incomings.append(" %-25s %s %s\n" % (b, getid(id), st))
470 incomings.append(" %-25s %s %s\n" % (b, getid(id), st))
460 else:
471 else:
461 def add(b, id, st):
472 def add(b, id, st):
462 incomings.append(" %-25s %s\n" % (b, getid(id)))
473 incomings.append(" %-25s %s\n" % (b, getid(id)))
463 for b, scid, dcid in addsrc:
474 for b, scid, dcid in addsrc:
464 # i18n: "added" refers to a bookmark
475 # i18n: "added" refers to a bookmark
465 add(b, scid, _('added'))
476 add(b, scid, _('added'))
466 for b, scid, dcid in advsrc:
477 for b, scid, dcid in advsrc:
467 # i18n: "advanced" refers to a bookmark
478 # i18n: "advanced" refers to a bookmark
468 add(b, scid, _('advanced'))
479 add(b, scid, _('advanced'))
469 for b, scid, dcid in diverge:
480 for b, scid, dcid in diverge:
470 # i18n: "diverged" refers to a bookmark
481 # i18n: "diverged" refers to a bookmark
471 add(b, scid, _('diverged'))
482 add(b, scid, _('diverged'))
472 for b, scid, dcid in differ:
483 for b, scid, dcid in differ:
473 # i18n: "changed" refers to a bookmark
484 # i18n: "changed" refers to a bookmark
474 add(b, scid, _('changed'))
485 add(b, scid, _('changed'))
475
486
476 if not incomings:
487 if not incomings:
477 ui.status(_("no changed bookmarks found\n"))
488 ui.status(_("no changed bookmarks found\n"))
478 return 1
489 return 1
479
490
480 for s in sorted(incomings):
491 for s in sorted(incomings):
481 ui.write(s)
492 ui.write(s)
482
493
483 return 0
494 return 0
484
495
485 def outgoing(ui, repo, other):
496 def outgoing(ui, repo, other):
486 '''Show bookmarks outgoing from repo to other
497 '''Show bookmarks outgoing from repo to other
487 '''
498 '''
488 ui.status(_("searching for changed bookmarks\n"))
499 ui.status(_("searching for changed bookmarks\n"))
489
500
490 r = compare(repo, repo._bookmarks, other.listkeys('bookmarks'),
501 r = compare(repo, repo._bookmarks, other.listkeys('bookmarks'),
491 srchex=hex)
502 srchex=hex)
492 addsrc, adddst, advsrc, advdst, diverge, differ, invalid, same = r
503 addsrc, adddst, advsrc, advdst, diverge, differ, invalid, same = r
493
504
494 outgoings = []
505 outgoings = []
495 if ui.debugflag:
506 if ui.debugflag:
496 getid = lambda id: id
507 getid = lambda id: id
497 else:
508 else:
498 getid = lambda id: id[:12]
509 getid = lambda id: id[:12]
499 if ui.verbose:
510 if ui.verbose:
500 def add(b, id, st):
511 def add(b, id, st):
501 outgoings.append(" %-25s %s %s\n" % (b, getid(id), st))
512 outgoings.append(" %-25s %s %s\n" % (b, getid(id), st))
502 else:
513 else:
503 def add(b, id, st):
514 def add(b, id, st):
504 outgoings.append(" %-25s %s\n" % (b, getid(id)))
515 outgoings.append(" %-25s %s\n" % (b, getid(id)))
505 for b, scid, dcid in addsrc:
516 for b, scid, dcid in addsrc:
506 # i18n: "added refers to a bookmark
517 # i18n: "added refers to a bookmark
507 add(b, scid, _('added'))
518 add(b, scid, _('added'))
508 for b, scid, dcid in adddst:
519 for b, scid, dcid in adddst:
509 # i18n: "deleted" refers to a bookmark
520 # i18n: "deleted" refers to a bookmark
510 add(b, ' ' * 40, _('deleted'))
521 add(b, ' ' * 40, _('deleted'))
511 for b, scid, dcid in advsrc:
522 for b, scid, dcid in advsrc:
512 # i18n: "advanced" refers to a bookmark
523 # i18n: "advanced" refers to a bookmark
513 add(b, scid, _('advanced'))
524 add(b, scid, _('advanced'))
514 for b, scid, dcid in diverge:
525 for b, scid, dcid in diverge:
515 # i18n: "diverged" refers to a bookmark
526 # i18n: "diverged" refers to a bookmark
516 add(b, scid, _('diverged'))
527 add(b, scid, _('diverged'))
517 for b, scid, dcid in differ:
528 for b, scid, dcid in differ:
518 # i18n: "changed" refers to a bookmark
529 # i18n: "changed" refers to a bookmark
519 add(b, scid, _('changed'))
530 add(b, scid, _('changed'))
520
531
521 if not outgoings:
532 if not outgoings:
522 ui.status(_("no changed bookmarks found\n"))
533 ui.status(_("no changed bookmarks found\n"))
523 return 1
534 return 1
524
535
525 for s in sorted(outgoings):
536 for s in sorted(outgoings):
526 ui.write(s)
537 ui.write(s)
527
538
528 return 0
539 return 0
529
540
530 def summary(repo, other):
541 def summary(repo, other):
531 '''Compare bookmarks between repo and other for "hg summary" output
542 '''Compare bookmarks between repo and other for "hg summary" output
532
543
533 This returns "(# of incoming, # of outgoing)" tuple.
544 This returns "(# of incoming, # of outgoing)" tuple.
534 '''
545 '''
535 r = compare(repo, other.listkeys('bookmarks'), repo._bookmarks,
546 r = compare(repo, other.listkeys('bookmarks'), repo._bookmarks,
536 dsthex=hex)
547 dsthex=hex)
537 addsrc, adddst, advsrc, advdst, diverge, differ, invalid, same = r
548 addsrc, adddst, advsrc, advdst, diverge, differ, invalid, same = r
538 return (len(addsrc), len(adddst))
549 return (len(addsrc), len(adddst))
539
550
540 def validdest(repo, old, new):
551 def validdest(repo, old, new):
541 """Is the new bookmark destination a valid update from the old one"""
552 """Is the new bookmark destination a valid update from the old one"""
542 repo = repo.unfiltered()
553 repo = repo.unfiltered()
543 if old == new:
554 if old == new:
544 # Old == new -> nothing to update.
555 # Old == new -> nothing to update.
545 return False
556 return False
546 elif not old:
557 elif not old:
547 # old is nullrev, anything is valid.
558 # old is nullrev, anything is valid.
548 # (new != nullrev has been excluded by the previous check)
559 # (new != nullrev has been excluded by the previous check)
549 return True
560 return True
550 elif repo.obsstore:
561 elif repo.obsstore:
551 return new.node() in obsolete.foreground(repo, [old.node()])
562 return new.node() in obsolete.foreground(repo, [old.node()])
552 else:
563 else:
553 # still an independent clause as it is lazier (and therefore faster)
564 # still an independent clause as it is lazier (and therefore faster)
554 return old.descendant(new)
565 return old.descendant(new)
General Comments 0
You need to be logged in to leave comments. Login now