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