##// END OF EJS Templates
bookmarks: introduce a bmstore to manage bookmark persistence...
Augie Fackler -
r17922:7f5dab94 default
parent child Browse files
Show More
@@ -219,9 +219,10 b' class mercurial_sink(converter_sink):'
219 219 return
220 220
221 221 self.ui.status(_("updating bookmarks\n"))
222 destmarks = self.repo._bookmarks
222 223 for bookmark in updatedbookmark:
223 self.repo._bookmarks[bookmark] = bin(updatedbookmark[bookmark])
224 bookmarks.write(self.repo)
224 destmarks[bookmark] = bin(updatedbookmark[bookmark])
225 destmarks.write()
225 226
226 227 def hascommit(self, rev):
227 228 if rev not in self.repo and self.clonebranches:
@@ -144,7 +144,6 b' except ImportError:'
144 144 import pickle
145 145 import os
146 146
147 from mercurial import bookmarks
148 147 from mercurial import cmdutil
149 148 from mercurial import discovery
150 149 from mercurial import error
@@ -740,12 +739,13 b' def movebookmarks(ui, repo, mapping, old'
740 739 # nothing to move
741 740 moves.append((bk, new[-1]))
742 741 if moves:
742 marks = repo._bookmarks
743 743 for mark, new in moves:
744 old = repo._bookmarks[mark]
744 old = marks[mark]
745 745 ui.note(_('histedit: moving bookmarks %s from %s to %s\n')
746 746 % (mark, node.short(old), node.short(new)))
747 repo._bookmarks[mark] = new
748 bookmarks.write(repo)
747 marks[mark] = new
748 marks.write()
749 749
750 750 def cleanupnode(ui, repo, name, nodes):
751 751 """strip a group of nodes from the repository
@@ -63,7 +63,7 b' from mercurial.i18n import _'
63 63 from mercurial.node import bin, hex, short, nullid, nullrev
64 64 from mercurial.lock import release
65 65 from mercurial import commands, cmdutil, hg, scmutil, util, revset
66 from mercurial import repair, extensions, error, phases, bookmarks
66 from mercurial import repair, extensions, error, phases
67 67 from mercurial import patch as patchmod
68 68 import os, re, errno, shutil
69 69
@@ -1675,9 +1675,10 b' class queue(object):'
1675 1675 patchf.write(chunk)
1676 1676 patchf.close()
1677 1677
1678 marks = repo._bookmarks
1678 1679 for bm in bmlist:
1679 repo._bookmarks[bm] = n
1680 bookmarks.write(repo)
1680 marks[bm] = n
1681 marks.write()
1681 1682
1682 1683 self.applied.append(statusentry(n, patchfn))
1683 1684 except: # re-raises
@@ -2999,7 +3000,7 b' def strip(ui, repo, *revs, **opts):'
2999 3000 revs.update(set(rsrevs))
3000 3001 if not revs:
3001 3002 del marks[mark]
3002 repo._writebookmarks(mark)
3003 marks.write()
3003 3004 ui.write(_("bookmark '%s' deleted\n") % mark)
3004 3005
3005 3006 if not revs:
@@ -3049,7 +3050,7 b' def strip(ui, repo, *revs, **opts):'
3049 3050
3050 3051 if opts.get('bookmark'):
3051 3052 del marks[mark]
3052 repo._writebookmarks(marks)
3053 marks.write()
3053 3054 ui.write(_("bookmark '%s' deleted\n") % mark)
3054 3055
3055 3056 repo.mq.strip(repo, revs, backup=backup, update=update,
@@ -479,13 +479,14 b' def updatemq(repo, state, skipped, **opt'
479 479
480 480 def updatebookmarks(repo, nstate, originalbookmarks, **opts):
481 481 'Move bookmarks to their correct changesets'
482 marks = repo._bookmarks
482 483 for k, v in originalbookmarks.iteritems():
483 484 if v in nstate:
484 485 if nstate[v] != nullmerge:
485 486 # update the bookmarks for revs that have moved
486 repo._bookmarks[k] = nstate[v]
487 marks[k] = nstate[v]
487 488
488 bookmarks.write(repo)
489 marks.write()
489 490
490 491 def storestatus(repo, originalwd, target, state, collapse, keep, keepbranches,
491 492 external):
@@ -10,32 +10,72 b' from mercurial.node import hex'
10 10 from mercurial import encoding, error, util, obsolete
11 11 import errno, os
12 12
13 def read(repo):
14 '''Parse .hg/bookmarks file and return a dictionary
13 class bmstore(dict):
14 """Storage for bookmarks.
15
16 This object should do all bookmark reads and writes, so that it's
17 fairly simple to replace the storage underlying bookmarks without
18 having to clone the logic surrounding bookmarks.
19
20 This particular bmstore implementation stores bookmarks as
21 {hash}\s{name}\n (the same format as localtags) in
22 .hg/bookmarks. The mapping is stored as {name: nodeid}.
23
24 This class does NOT handle the "current" bookmark state at this
25 time.
26 """
15 27
16 Bookmarks are stored as {HASH}\\s{NAME}\\n (localtags format) values
17 in the .hg/bookmarks file.
18 Read the file and return a (name=>nodeid) dictionary
19 '''
20 bookmarks = {}
21 try:
22 for line in repo.opener('bookmarks'):
23 line = line.strip()
24 if not line:
25 continue
26 if ' ' not in line:
27 repo.ui.warn(_('malformed line in .hg/bookmarks: %r\n') % line)
28 continue
29 sha, refspec = line.split(' ', 1)
30 refspec = encoding.tolocal(refspec)
28 def __init__(self, repo):
29 dict.__init__(self)
30 self._repo = repo
31 try:
32 for line in repo.vfs('bookmarks'):
33 line = line.strip()
34 if not line:
35 continue
36 if ' ' not in line:
37 repo.ui.warn(_('malformed line in .hg/bookmarks: %r\n')
38 % line)
39 continue
40 sha, refspec = line.split(' ', 1)
41 refspec = encoding.tolocal(refspec)
42 try:
43 self[refspec] = repo.changelog.lookup(sha)
44 except LookupError:
45 pass
46 except IOError, inst:
47 if inst.errno != errno.ENOENT:
48 raise
49
50 def write(self):
51 '''Write bookmarks
52
53 Write the given bookmark => hash dictionary to the .hg/bookmarks file
54 in a format equal to those of localtags.
55
56 We also store a backup of the previous state in undo.bookmarks that
57 can be copied back on rollback.
58 '''
59 repo = self._repo
60 if repo._bookmarkcurrent not in self:
61 setcurrent(repo, None)
62
63 wlock = repo.wlock()
64 try:
65
66 file = repo.vfs('bookmarks', 'w', atomictemp=True)
67 for name, node in self.iteritems():
68 file.write("%s %s\n" % (hex(node), encoding.fromlocal(name)))
69 file.close()
70
71 # touch 00changelog.i so hgweb reloads bookmarks (no lock needed)
31 72 try:
32 bookmarks[refspec] = repo.changelog.lookup(sha)
33 except LookupError:
73 os.utime(repo.sjoin('00changelog.i'), None)
74 except OSError:
34 75 pass
35 except IOError, inst:
36 if inst.errno != errno.ENOENT:
37 raise
38 return bookmarks
76
77 finally:
78 wlock.release()
39 79
40 80 def readcurrent(repo):
41 81 '''Get the current bookmark
@@ -60,37 +100,6 b' def readcurrent(repo):'
60 100 file.close()
61 101 return mark
62 102
63 def write(repo):
64 '''Write bookmarks
65
66 Write the given bookmark => hash dictionary to the .hg/bookmarks file
67 in a format equal to those of localtags.
68
69 We also store a backup of the previous state in undo.bookmarks that
70 can be copied back on rollback.
71 '''
72 refs = repo._bookmarks
73
74 if repo._bookmarkcurrent not in refs:
75 setcurrent(repo, None)
76
77 wlock = repo.wlock()
78 try:
79
80 file = repo.opener('bookmarks', 'w', atomictemp=True)
81 for refspec, node in refs.iteritems():
82 file.write("%s %s\n" % (hex(node), encoding.fromlocal(refspec)))
83 file.close()
84
85 # touch 00changelog.i so hgweb reloads bookmarks (no lock needed)
86 try:
87 os.utime(repo.sjoin('00changelog.i'), None)
88 except OSError:
89 pass
90
91 finally:
92 wlock.release()
93
94 103 def setcurrent(repo, mark):
95 104 '''Set the name of the bookmark that we are currently on
96 105
@@ -152,7 +161,7 b' def update(repo, parents, node):'
152 161 if mark != cur:
153 162 del marks[mark]
154 163 if update:
155 repo._writebookmarks(marks)
164 marks.write()
156 165 return update
157 166
158 167 def listbookmarks(repo):
@@ -179,7 +188,7 b' def pushbookmark(repo, key, old, new):'
179 188 if new not in repo:
180 189 return False
181 190 marks[key] = repo[new].node()
182 write(repo)
191 marks.write()
183 192 return True
184 193 finally:
185 194 w.release()
@@ -188,16 +197,17 b' def updatefromremote(ui, repo, remote, p'
188 197 ui.debug("checking for updated bookmarks\n")
189 198 rb = remote.listkeys('bookmarks')
190 199 changed = False
200 localmarks = repo._bookmarks
191 201 for k in rb.keys():
192 if k in repo._bookmarks:
193 nr, nl = rb[k], repo._bookmarks[k]
202 if k in localmarks:
203 nr, nl = rb[k], localmarks[k]
194 204 if nr in repo:
195 205 cr = repo[nr]
196 206 cl = repo[nl]
197 207 if cl.rev() >= cr.rev():
198 208 continue
199 209 if validdest(repo, cl, cr):
200 repo._bookmarks[k] = cr.node()
210 localmarks[k] = cr.node()
201 211 changed = True
202 212 ui.status(_("updating bookmark %s\n") % k)
203 213 else:
@@ -208,7 +218,7 b' def updatefromremote(ui, repo, remote, p'
208 218 # find a unique @ suffix
209 219 for x in range(1, 100):
210 220 n = '%s@%d' % (kd, x)
211 if n not in repo._bookmarks:
221 if n not in localmarks:
212 222 break
213 223 # try to use an @pathalias suffix
214 224 # if an @pathalias already exists, we overwrite (update) it
@@ -216,17 +226,17 b' def updatefromremote(ui, repo, remote, p'
216 226 if path == u:
217 227 n = '%s@%s' % (kd, p)
218 228
219 repo._bookmarks[n] = cr.node()
229 localmarks[n] = cr.node()
220 230 changed = True
221 231 ui.warn(_("divergent bookmark %s stored as %s\n") % (k, n))
222 232 elif rb[k] in repo:
223 233 # add remote bookmarks for changes we already have
224 repo._bookmarks[k] = repo[rb[k]].node()
234 localmarks[k] = repo[rb[k]].node()
225 235 changed = True
226 236 ui.status(_("adding remote bookmark %s\n") % k)
227 237
228 238 if changed:
229 write(repo)
239 localmarks.write()
230 240
231 241 def diff(ui, dst, src):
232 242 ui.status(_("searching for changed bookmarks\n"))
@@ -10,7 +10,7 b' from i18n import _'
10 10 import os, sys, errno, re, tempfile
11 11 import util, scmutil, templater, patch, error, templatekw, revlog, copies
12 12 import match as matchmod
13 import subrepo, context, repair, bookmarks, graphmod, revset, phases, obsolete
13 import subrepo, context, repair, graphmod, revset, phases, obsolete
14 14 import changelog
15 15 import lock as lockmod
16 16
@@ -1756,9 +1756,10 b' def amend(ui, repo, commitfunc, old, ext'
1756 1756 # Move bookmarks from old parent to amend commit
1757 1757 bms = repo.nodebookmarks(old.node())
1758 1758 if bms:
1759 marks = repo._bookmarks
1759 1760 for bm in bms:
1760 repo._bookmarks[bm] = newid
1761 bookmarks.write(repo)
1761 marks[bm] = newid
1762 marks.write()
1762 1763 #commit the whole amend process
1763 1764 if obsolete._enabled and newid != old.node():
1764 1765 # mark the new changeset as successor of the rewritten one
@@ -821,7 +821,7 b' def bookmark(ui, repo, mark=None, rev=No'
821 821 if mark == repo._bookmarkcurrent:
822 822 bookmarks.setcurrent(repo, None)
823 823 del marks[mark]
824 bookmarks.write(repo)
824 marks.write()
825 825
826 826 elif rename:
827 827 if mark is None:
@@ -834,7 +834,7 b' def bookmark(ui, repo, mark=None, rev=No'
834 834 if repo._bookmarkcurrent == rename and not inactive:
835 835 bookmarks.setcurrent(repo, mark)
836 836 del marks[rename]
837 bookmarks.write(repo)
837 marks.write()
838 838
839 839 elif mark is not None:
840 840 mark = checkformat(mark)
@@ -848,7 +848,7 b' def bookmark(ui, repo, mark=None, rev=No'
848 848 marks[mark] = cur
849 849 if not inactive and cur == marks[mark]:
850 850 bookmarks.setcurrent(repo, mark)
851 bookmarks.write(repo)
851 marks.write()
852 852
853 853 # Same message whether trying to deactivate the current bookmark (-i
854 854 # with no NAME) or listing bookmarks
@@ -1321,11 +1321,12 b' def commit(ui, repo, *pats, **opts):'
1321 1321 elif marks:
1322 1322 ui.debug('moving bookmarks %r from %s to %s\n' %
1323 1323 (marks, old.hex(), hex(node)))
1324 newmarks = repo._bookmarks
1324 1325 for bm in marks:
1325 repo._bookmarks[bm] = node
1326 newmarks[bm] = node
1326 1327 if bm == current:
1327 1328 bookmarks.setcurrent(repo, bm)
1328 bookmarks.write(repo)
1329 newmarks.write()
1329 1330 else:
1330 1331 e = cmdutil.commiteditor
1331 1332 if opts.get('force_editor'):
@@ -4673,11 +4674,12 b' def pull(ui, repo, source="default", **o'
4673 4674
4674 4675 # update specified bookmarks
4675 4676 if opts.get('bookmark'):
4677 marks = repo._bookmarks
4676 4678 for b in opts['bookmark']:
4677 4679 # explicit pull overrides local bookmark if any
4678 4680 ui.status(_("importing bookmark %s\n") % b)
4679 repo._bookmarks[b] = repo[rb[b]].node()
4680 bookmarks.write(repo)
4681 marks[b] = repo[rb[b]].node()
4682 marks.write()
4681 4683
4682 4684 return ret
4683 4685
@@ -391,14 +391,15 b' def clone(ui, peeropts, source, dest=Non'
391 391 destrepo = destpeer.local()
392 392 if destrepo and srcpeer.capable("pushkey"):
393 393 rb = srcpeer.listkeys('bookmarks')
394 marks = destrepo._bookmarks
394 395 for k, n in rb.iteritems():
395 396 try:
396 397 m = destrepo.lookup(n)
397 destrepo._bookmarks[k] = m
398 marks[k] = m
398 399 except error.RepoLookupError:
399 400 pass
400 401 if rb:
401 bookmarks.write(destrepo)
402 marks.write()
402 403 elif srcrepo and destpeer.capable("pushkey"):
403 404 for k, n in srcrepo._bookmarks.iteritems():
404 405 destpeer.pushkey('bookmarks', k, '', hex(n))
@@ -265,15 +265,12 b' class localrepository(object):'
265 265
266 266 @filecache('bookmarks')
267 267 def _bookmarks(self):
268 return bookmarks.read(self)
268 return bookmarks.bmstore(self)
269 269
270 270 @filecache('bookmarks.current')
271 271 def _bookmarkcurrent(self):
272 272 return bookmarks.readcurrent(self)
273 273
274 def _writebookmarks(self, marks):
275 bookmarks.write(self)
276
277 274 def bookmarkheads(self, bookmark):
278 275 name = bookmark.split('@', 1)[0]
279 276 heads = []
@@ -6,7 +6,7 b''
6 6 # This software may be used and distributed according to the terms of the
7 7 # GNU General Public License version 2 or any later version.
8 8
9 from mercurial import changegroup, bookmarks
9 from mercurial import changegroup
10 10 from mercurial.node import short
11 11 from mercurial.i18n import _
12 12 import os
@@ -181,7 +181,7 b' def strip(ui, repo, nodelist, backup="al'
181 181
182 182 for m in updatebm:
183 183 bm[m] = repo[newbmtarget].node()
184 bookmarks.write(repo)
184 bm.write()
185 185 except: # re-raises
186 186 if backupfile:
187 187 ui.warn(_("strip failed, full bundle stored in '%s'\n")
General Comments 0
You need to be logged in to leave comments. Login now