##// END OF EJS Templates
bookmarks: Only save undo.bookmarks if bookmarks exist...
Joel Rosdahl -
r7253:8b81d1e2 default
parent child Browse files
Show More
@@ -1,224 +1,225 b''
1 1 # Mercurial extension to provide the 'hg bookmark' command
2 2 #
3 3 # Copyright 2008 David Soria Parra <dsp@php.net>
4 4 #
5 5 # This software may be used and distributed according to the terms
6 6 # of the GNU General Public License, incorporated herein by reference.
7 7
8 8 '''mercurial bookmarks
9 9
10 10 Mercurial bookmarks are local moveable pointers to changesets. Every
11 11 bookmark points to a changeset identified by its hash. If you commit a
12 12 changeset that is based on a changeset that has a bookmark on it, the
13 13 bookmark is forwarded to the new changeset.
14 14
15 15 It is possible to use bookmark names in every revision lookup (e.g. hg
16 16 merge, hg update).
17 17 '''
18 18
19 19 from mercurial.commands import templateopts, hex, short
20 20 from mercurial.i18n import _
21 21 from mercurial import cmdutil, util, commands, changelog
22 22 from mercurial.node import nullrev
23 23 from mercurial.repo import RepoError
24 24 import mercurial, mercurial.localrepo, mercurial.repair, os
25 25
26 26 def parse(repo):
27 27 '''Parse .hg/bookmarks file and return a dictionary
28 28
29 29 Bookmarks are stored as {HASH}\s{NAME}\n (localtags format) values
30 30 in the .hg/bookmarks file. They are read by the parse() method and
31 31 returned as a dictionary with name => hash values.
32 32
33 33 The parsed dictionary is cached until a write() operation is done.
34 34 '''
35 35 try:
36 36 if repo._bookmarks:
37 37 return repo._bookmarks
38 38 repo._bookmarks = {}
39 39 for line in repo.opener('bookmarks'):
40 40 sha, refspec = line.strip().split(' ', 1)
41 41 repo._bookmarks[refspec] = repo.lookup(sha)
42 42 except:
43 43 pass
44 44 return repo._bookmarks
45 45
46 46 def write(repo, refs):
47 47 '''Write bookmarks
48 48
49 49 Write the given bookmark => hash dictionary to the .hg/bookmarks file
50 50 in a format equal to those of localtags.
51 51
52 52 We also store a backup of the previous state in undo.bookmarks that
53 53 can be copied back on rollback.
54 54 '''
55 util.copyfile(repo.join('bookmarks'), repo.join('undo.bookmarks'))
55 if os.path.exists(repo.join('bookmarks')):
56 util.copyfile(repo.join('bookmarks'), repo.join('undo.bookmarks'))
56 57 file = repo.opener('bookmarks', 'w+')
57 58 for refspec, node in refs.items():
58 59 file.write("%s %s\n" % (hex(node), refspec))
59 60 file.close()
60 61
61 62 def bookmark(ui, repo, mark=None, rev=None, force=False, delete=False, move=None):
62 63 '''mercurial bookmarks
63 64
64 65 Bookmarks are pointers to certain commits that move when
65 66 commiting. Bookmarks are local. They can be renamed, copied and
66 67 deleted. It is possible to use bookmark names in 'hg merge' and 'hg
67 68 update' to update to a given bookmark.
68 69
69 70 You can use 'hg bookmark [NAME]' to set a bookmark on the current tip
70 71 with the given name. If you specify a second [NAME] the bookmark is
71 72 set to the bookmark that has that name. You can also pass revision
72 73 identifiers to set bookmarks too.
73 74 '''
74 75 hexfn = ui.debugflag and hex or short
75 76 marks = parse(repo)
76 77 cur = repo.changectx('.').node()
77 78
78 79 if move:
79 80 if move not in marks:
80 81 raise util.Abort(_("a bookmark of this name does not exist"))
81 82 if mark in marks and not force:
82 83 raise util.Abort(_("a bookmark of the same name already exists"))
83 84 marks[mark] = marks[move]
84 85 del marks[move]
85 86 write(repo, marks)
86 87 return
87 88
88 89 if delete:
89 90 if mark == None:
90 91 raise util.Abort(_("bookmark name required"))
91 92 if mark not in marks:
92 93 raise util.Abort(_("a bookmark of this name does not exist"))
93 94 del marks[mark]
94 95 write(repo, marks)
95 96 return
96 97
97 98 if mark != None:
98 99 if mark.strip().count("\n") > 0:
99 100 raise Exception("bookmark cannot contain newlines")
100 101 if mark in marks and not force:
101 102 raise util.Abort(_("a bookmark of the same name already exists"))
102 103 if ((mark in repo.branchtags() or mark == repo.dirstate.branch())
103 104 and not force):
104 105 raise util.Abort(
105 106 _("a bookmark cannot have the name of an existing branch"))
106 107 if rev:
107 108 marks[mark] = repo.lookup(rev)
108 109 else:
109 110 marks[mark] = repo.changectx('.').node()
110 111 write(repo, marks)
111 112 return
112 113
113 114 if mark == None:
114 115 if len(marks) == 0:
115 116 ui.status("no bookmarks set\n")
116 117 else:
117 118 for bmark, n in marks.iteritems():
118 119 prefix = (n == cur) and '*' or ' '
119 120 ui.write(" %s %-25s %d:%s\n" % (
120 121 prefix, bmark, repo.changelog.rev(n), hexfn(n)))
121 122 return
122 123
123 124 def _revstostrip(changelog, node):
124 125 srev = changelog.rev(node)
125 126 tostrip = [srev]
126 127 saveheads = []
127 128 for r in xrange(srev, changelog.rev(changelog.tip()) + 1):
128 129 parents = changelog.parentrevs(r)
129 130 if parents[0] in tostrip or parents[1] in tostrip:
130 131 tostrip.append(r)
131 132 if parents[1] != nullrev:
132 133 for p in parents:
133 134 if p not in tostrip and p > striprev:
134 135 saveheads.append(p)
135 136 return [r for r in tostrip if r not in saveheads]
136 137
137 138 def strip(ui, repo, node, backup="all"):
138 139 """Strip bookmarks if revisions are stripped using
139 140 the mercurial.strip method. This usually happens during
140 141 qpush and qpop"""
141 142 revisions = _revstostrip(repo.changelog, node)
142 143 marks = parse(repo)
143 144 update = []
144 145 for mark, n in marks.items():
145 146 if repo.changelog.rev(n) in revisions:
146 147 update.append(mark)
147 148 result = oldstrip(ui, repo, node, backup)
148 149 if len(update) > 0:
149 150 for m in update:
150 151 marks[m] = repo.changectx('.').node()
151 152 write(repo, marks)
152 153
153 154 oldstrip = mercurial.repair.strip
154 155 mercurial.repair.strip = strip
155 156
156 157 def reposetup(ui, repo):
157 158 if not isinstance(repo, mercurial.localrepo.localrepository):
158 159 return
159 160
160 161 # init a bookmark cache as otherwise we would get a infinite reading
161 162 # in lookup()
162 163 repo._bookmarks = None
163 164
164 165 class bookmark_repo(repo.__class__):
165 166 def rollback(self):
166 167 if os.path.exists(self.join('undo.bookmarks')):
167 168 util.rename(self.join('undo.bookmarks'), self.join('bookmarks'))
168 169 return super(bookmark_repo, self).rollback()
169 170
170 171 def lookup(self, key):
171 172 if self._bookmarks is None:
172 173 self._bookmarks = parse(self)
173 174 if key in self._bookmarks:
174 175 key = self._bookmarks[key]
175 176 return super(bookmark_repo, self).lookup(key)
176 177
177 178 def commit(self, *k, **kw):
178 179 """Add a revision to the repository and
179 180 move the bookmark"""
180 181 node = super(bookmark_repo, self).commit(*k, **kw)
181 182 parents = repo.changelog.parents(node)
182 183 marks = parse(repo)
183 184 update = False
184 185 for mark, n in marks.items():
185 186 if n in parents:
186 187 marks[mark] = node
187 188 update = True
188 189 if update:
189 190 write(repo, marks)
190 191 return node
191 192
192 193 def addchangegroup(self, source, srctype, url, emptyok=False):
193 194 try:
194 195 onode = repo.changectx('.').node()
195 196 except RepoError, inst:
196 197 pass
197 198
198 199 result = super(bookmark_repo, self).addchangegroup(
199 200 source, srctype, url, emptyok)
200 201 if result > 1:
201 202 # We have more heads than before
202 203 return result
203 204 node = repo.changelog.tip()
204 205 marks = parse(repo)
205 206 update = False
206 207 for mark, n in marks.items():
207 208 if n == onode:
208 209 marks[mark] = node
209 210 update = True
210 211 if update:
211 212 write(repo, marks)
212 213 return result
213 214
214 215 repo.__class__ = bookmark_repo
215 216
216 217 cmdtable = {
217 218 "bookmarks":
218 219 (bookmark,
219 220 [('f', 'force', False, _('force')),
220 221 ('r', 'rev', '', _('revision')),
221 222 ('d', 'delete', False, _('delete a given bookmark')),
222 223 ('m', 'move', '', _('move a given bookmark'))],
223 224 _('hg bookmarks [-d] [-m NAME] [-r NAME] [NAME]')),
224 225 }
General Comments 0
You need to be logged in to leave comments. Login now