##// END OF EJS Templates
bookmarks: Strip bookmark names of whitespace, just like tag names
Joel Rosdahl -
r7260:eb91b9ce default
parent child Browse files
Show More
@@ -1,231 +1,232 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 nullid, 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 55 if os.path.exists(repo.join('bookmarks')):
56 56 util.copyfile(repo.join('bookmarks'), repo.join('undo.bookmarks'))
57 57 file = repo.opener('bookmarks', 'w+')
58 58 for refspec, node in refs.items():
59 59 file.write("%s %s\n" % (hex(node), refspec))
60 60 file.close()
61 61
62 62 def bookmark(ui, repo, mark=None, rev=None, force=False, delete=False, rename=None):
63 63 '''mercurial bookmarks
64 64
65 65 Bookmarks are pointers to certain commits that move when
66 66 commiting. Bookmarks are local. They can be renamed, copied and
67 67 deleted. It is possible to use bookmark names in 'hg merge' and 'hg
68 68 update' to update to a given bookmark.
69 69
70 70 You can use 'hg bookmark NAME' to set a bookmark on the current
71 71 tip with the given name. If you specify a revision using -r REV
72 72 (where REV may be an existing bookmark), the bookmark is set to
73 73 that revision.
74 74 '''
75 75 hexfn = ui.debugflag and hex or short
76 76 marks = parse(repo)
77 77 cur = repo.changectx('.').node()
78 78
79 79 if rename:
80 80 if rename not in marks:
81 81 raise util.Abort(_("a bookmark of this name does not exist"))
82 82 if mark in marks and not force:
83 83 raise util.Abort(_("a bookmark of the same name already exists"))
84 84 if mark is None:
85 85 raise util.Abort(_("new bookmark name required"))
86 86 marks[mark] = marks[rename]
87 87 del marks[rename]
88 88 write(repo, marks)
89 89 return
90 90
91 91 if delete:
92 92 if mark == None:
93 93 raise util.Abort(_("bookmark name required"))
94 94 if mark not in marks:
95 95 raise util.Abort(_("a bookmark of this name does not exist"))
96 96 del marks[mark]
97 97 write(repo, marks)
98 98 return
99 99
100 100 if mark != None:
101 101 if "\n" in mark:
102 102 raise util.Abort(_("bookmark name cannot contain newlines"))
103 mark = mark.strip()
103 104 if mark in marks and not force:
104 105 raise util.Abort(_("a bookmark of the same name already exists"))
105 106 if ((mark in repo.branchtags() or mark == repo.dirstate.branch())
106 107 and not force):
107 108 raise util.Abort(
108 109 _("a bookmark cannot have the name of an existing branch"))
109 110 if rev:
110 111 marks[mark] = repo.lookup(rev)
111 112 else:
112 113 marks[mark] = repo.changectx('.').node()
113 114 write(repo, marks)
114 115 return
115 116
116 117 if mark == None:
117 118 if rev:
118 119 raise util.Abort(_("bookmark name required"))
119 120 if len(marks) == 0:
120 121 ui.status("no bookmarks set\n")
121 122 else:
122 123 for bmark, n in marks.iteritems():
123 124 prefix = (n == cur) and '*' or ' '
124 125 ui.write(" %s %-25s %d:%s\n" % (
125 126 prefix, bmark, repo.changelog.rev(n), hexfn(n)))
126 127 return
127 128
128 129 def _revstostrip(changelog, node):
129 130 srev = changelog.rev(node)
130 131 tostrip = [srev]
131 132 saveheads = []
132 133 for r in xrange(srev, changelog.rev(changelog.tip()) + 1):
133 134 parents = changelog.parentrevs(r)
134 135 if parents[0] in tostrip or parents[1] in tostrip:
135 136 tostrip.append(r)
136 137 if parents[1] != nullrev:
137 138 for p in parents:
138 139 if p not in tostrip and p > striprev:
139 140 saveheads.append(p)
140 141 return [r for r in tostrip if r not in saveheads]
141 142
142 143 def strip(ui, repo, node, backup="all"):
143 144 """Strip bookmarks if revisions are stripped using
144 145 the mercurial.strip method. This usually happens during
145 146 qpush and qpop"""
146 147 revisions = _revstostrip(repo.changelog, node)
147 148 marks = parse(repo)
148 149 update = []
149 150 for mark, n in marks.items():
150 151 if repo.changelog.rev(n) in revisions:
151 152 update.append(mark)
152 153 result = oldstrip(ui, repo, node, backup)
153 154 if len(update) > 0:
154 155 for m in update:
155 156 marks[m] = repo.changectx('.').node()
156 157 write(repo, marks)
157 158
158 159 oldstrip = mercurial.repair.strip
159 160 mercurial.repair.strip = strip
160 161
161 162 def reposetup(ui, repo):
162 163 if not isinstance(repo, mercurial.localrepo.localrepository):
163 164 return
164 165
165 166 # init a bookmark cache as otherwise we would get a infinite reading
166 167 # in lookup()
167 168 repo._bookmarks = None
168 169
169 170 class bookmark_repo(repo.__class__):
170 171 def rollback(self):
171 172 if os.path.exists(self.join('undo.bookmarks')):
172 173 util.rename(self.join('undo.bookmarks'), self.join('bookmarks'))
173 174 return super(bookmark_repo, self).rollback()
174 175
175 176 def lookup(self, key):
176 177 if self._bookmarks is None:
177 178 self._bookmarks = parse(self)
178 179 if key in self._bookmarks:
179 180 key = self._bookmarks[key]
180 181 return super(bookmark_repo, self).lookup(key)
181 182
182 183 def commit(self, *k, **kw):
183 184 """Add a revision to the repository and
184 185 move the bookmark"""
185 186 node = super(bookmark_repo, self).commit(*k, **kw)
186 187 parents = repo.changelog.parents(node)
187 188 if parents[1] == nullid:
188 189 parents = (parents[0],)
189 190 marks = parse(repo)
190 191 update = False
191 192 for mark, n in marks.items():
192 193 if n in parents:
193 194 marks[mark] = node
194 195 update = True
195 196 if update:
196 197 write(repo, marks)
197 198 return node
198 199
199 200 def addchangegroup(self, source, srctype, url, emptyok=False):
200 201 try:
201 202 onode = repo.changectx('.').node()
202 203 except RepoError, inst:
203 204 pass
204 205
205 206 result = super(bookmark_repo, self).addchangegroup(
206 207 source, srctype, url, emptyok)
207 208 if result > 1:
208 209 # We have more heads than before
209 210 return result
210 211 node = repo.changelog.tip()
211 212 marks = parse(repo)
212 213 update = False
213 214 for mark, n in marks.items():
214 215 if n == onode:
215 216 marks[mark] = node
216 217 update = True
217 218 if update:
218 219 write(repo, marks)
219 220 return result
220 221
221 222 repo.__class__ = bookmark_repo
222 223
223 224 cmdtable = {
224 225 "bookmarks":
225 226 (bookmark,
226 227 [('f', 'force', False, _('force')),
227 228 ('r', 'rev', '', _('revision')),
228 229 ('d', 'delete', False, _('delete a given bookmark')),
229 230 ('m', 'rename', '', _('rename a given bookmark'))],
230 231 _('hg bookmarks [-d] [-m NAME] [-r NAME] [NAME]')),
231 232 }
General Comments 0
You need to be logged in to leave comments. Login now