##// END OF EJS Templates
bookmarks: forbid \0 \r \n : in bookmark names (BC)...
David Soria Parra -
r13425:0fe36c34 default
parent child Browse files
Show More
@@ -1,160 +1,176 b''
1 1 # Mercurial bookmark support code
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 of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from mercurial.i18n import _
9 9 from mercurial.node import nullid, nullrev, bin, hex, short
10 from mercurial import encoding
10 from mercurial import encoding, util
11 11 import os
12 12
13 def valid(mark):
14 for c in (':', '\0', '\n', '\r'):
15 if c in mark:
16 return False
17 return True
18
13 19 def read(repo):
14 20 '''Parse .hg/bookmarks file and return a dictionary
15 21
16 22 Bookmarks are stored as {HASH}\\s{NAME}\\n (localtags format) values
17 23 in the .hg/bookmarks file.
18 24 Read the file and return a (name=>nodeid) dictionary
19 25 '''
20 26 try:
21 27 bookmarks = {}
22 28 for line in repo.opener('bookmarks'):
23 29 sha, refspec = line.strip().split(' ', 1)
24 30 refspec = encoding.tolocal(refspec)
25 31 bookmarks[refspec] = repo.changelog.lookup(sha)
26 32 except:
27 33 pass
28 34 return bookmarks
29 35
30 36 def readcurrent(repo):
31 37 '''Get the current bookmark
32 38
33 39 If we use gittishsh branches we have a current bookmark that
34 40 we are on. This function returns the name of the bookmark. It
35 41 is stored in .hg/bookmarks.current
36 42 '''
37 43 mark = None
38 44 if os.path.exists(repo.join('bookmarks.current')):
39 45 file = repo.opener('bookmarks.current')
40 46 # No readline() in posixfile_nt, reading everything is cheap
41 47 mark = encoding.tolocal((file.readlines() or [''])[0])
42 48 if mark == '':
43 49 mark = None
44 50 file.close()
45 51 return mark
46 52
47 53 def write(repo):
48 54 '''Write bookmarks
49 55
50 56 Write the given bookmark => hash dictionary to the .hg/bookmarks file
51 57 in a format equal to those of localtags.
52 58
53 59 We also store a backup of the previous state in undo.bookmarks that
54 60 can be copied back on rollback.
55 61 '''
56 62 refs = repo._bookmarks
57 63
58 64 try:
59 65 bms = repo.opener('bookmarks').read()
60 66 except IOError:
61 67 bms = ''
62 68 repo.opener('undo.bookmarks', 'w').write(bms)
63 69
64 70 if repo._bookmarkcurrent not in refs:
65 71 setcurrent(repo, None)
72 for mark in refs.keys():
73 if not valid(mark):
74 raise util.Abort(_("bookmark '%s' contains illegal "
75 "character" % mark))
76
66 77 wlock = repo.wlock()
67 78 try:
79
68 80 file = repo.opener('bookmarks', 'w', atomictemp=True)
69 81 for refspec, node in refs.iteritems():
70 82 file.write("%s %s\n" % (hex(node), encoding.fromlocal(refspec)))
71 83 file.rename()
72 84
73 85 # touch 00changelog.i so hgweb reloads bookmarks (no lock needed)
74 86 try:
75 87 os.utime(repo.sjoin('00changelog.i'), None)
76 88 except OSError:
77 89 pass
78 90
79 91 finally:
80 92 wlock.release()
81 93
82 94 def setcurrent(repo, mark):
83 95 '''Set the name of the bookmark that we are currently on
84 96
85 97 Set the name of the bookmark that we are on (hg update <bookmark>).
86 98 The name is recorded in .hg/bookmarks.current
87 99 '''
88 100 current = repo._bookmarkcurrent
89 101 if current == mark:
90 102 return
91 103
92 104 refs = repo._bookmarks
93 105
94 106 # do not update if we do update to a rev equal to the current bookmark
95 107 if (mark and mark not in refs and
96 108 current and refs[current] == repo.changectx('.').node()):
97 109 return
98 110 if mark not in refs:
99 111 mark = ''
112 if not valid(mark):
113 raise util.Abort(_("bookmark '%s' contains illegal "
114 "character" % mark))
115
100 116 wlock = repo.wlock()
101 117 try:
102 118 file = repo.opener('bookmarks.current', 'w', atomictemp=True)
103 119 file.write(mark)
104 120 file.rename()
105 121 finally:
106 122 wlock.release()
107 123 repo._bookmarkcurrent = mark
108 124
109 125 def update(repo, parents, node):
110 126 marks = repo._bookmarks
111 127 update = False
112 128 mark = repo._bookmarkcurrent
113 129 if mark and marks[mark] in parents:
114 130 marks[mark] = node
115 131 update = True
116 132 if update:
117 133 write(repo)
118 134
119 135 def listbookmarks(repo):
120 136 # We may try to list bookmarks on a repo type that does not
121 137 # support it (e.g., statichttprepository).
122 138 if not hasattr(repo, '_bookmarks'):
123 139 return {}
124 140
125 141 d = {}
126 142 for k, v in repo._bookmarks.iteritems():
127 143 d[k] = hex(v)
128 144 return d
129 145
130 146 def pushbookmark(repo, key, old, new):
131 147 w = repo.wlock()
132 148 try:
133 149 marks = repo._bookmarks
134 150 if hex(marks.get(key, '')) != old:
135 151 return False
136 152 if new == '':
137 153 del marks[key]
138 154 else:
139 155 if new not in repo:
140 156 return False
141 157 marks[key] = repo[new].node()
142 158 write(repo)
143 159 return True
144 160 finally:
145 161 w.release()
146 162
147 163 def diff(ui, repo, remote):
148 164 ui.status(_("searching for changed bookmarks\n"))
149 165
150 166 lmarks = repo.listkeys('bookmarks')
151 167 rmarks = remote.listkeys('bookmarks')
152 168
153 169 diff = sorted(set(rmarks) - set(lmarks))
154 170 for k in diff:
155 171 ui.write(" %-25s %s\n" % (k, rmarks[k][:12]))
156 172
157 173 if len(diff) <= 0:
158 174 ui.status(_("no changed bookmarks found\n"))
159 175 return 1
160 176 return 0
@@ -1,214 +1,221 b''
1 1 $ echo "[extensions]" >> $HGRCPATH
2 2 $ echo "bookmarks=" >> $HGRCPATH
3 3
4 4 $ hg init
5 5
6 6 no bookmarks
7 7
8 8 $ hg bookmarks
9 9 no bookmarks set
10 10
11 11 bookmark rev -1
12 12
13 13 $ hg bookmark X
14 14
15 15 list bookmarks
16 16
17 17 $ hg bookmarks
18 18 * X -1:000000000000
19 19
20 20 list bookmarks with color
21 21
22 22 $ hg --config extensions.color= --config color.mode=ansi \
23 23 > bookmarks --color=always
24 24 \x1b[0;32m * X -1:000000000000\x1b[0m (esc)
25 25
26 26 $ echo a > a
27 27 $ hg add a
28 28 $ hg commit -m 0
29 29
30 30 bookmark X moved to rev 0
31 31
32 32 $ hg bookmarks
33 33 * X 0:f7b1eb17ad24
34 34
35 35 look up bookmark
36 36
37 37 $ hg log -r X
38 38 changeset: 0:f7b1eb17ad24
39 39 bookmark: X
40 40 tag: tip
41 41 user: test
42 42 date: Thu Jan 01 00:00:00 1970 +0000
43 43 summary: 0
44 44
45 45
46 46 second bookmark for rev 0
47 47
48 48 $ hg bookmark X2
49 49
50 50 bookmark rev -1 again
51 51
52 52 $ hg bookmark -r null Y
53 53
54 54 list bookmarks
55 55
56 56 $ hg bookmarks
57 57 X 0:f7b1eb17ad24
58 58 X2 0:f7b1eb17ad24
59 59 Y -1:000000000000
60 60
61 61 $ echo b > b
62 62 $ hg add b
63 63 $ hg commit -m 1
64 64
65 65 bookmarks revset
66 66
67 67 $ hg log -r 'bookmark()'
68 68 changeset: 0:f7b1eb17ad24
69 69 bookmark: X
70 70 bookmark: X2
71 71 user: test
72 72 date: Thu Jan 01 00:00:00 1970 +0000
73 73 summary: 0
74 74
75 75 $ hg log -r 'bookmark(Y)'
76 76 $ hg log -r 'bookmark(X2)'
77 77 changeset: 0:f7b1eb17ad24
78 78 bookmark: X
79 79 bookmark: X2
80 80 user: test
81 81 date: Thu Jan 01 00:00:00 1970 +0000
82 82 summary: 0
83 83
84 84 $ hg help revsets | grep 'bookmark('
85 85 "bookmark([name])"
86 86
87 87 bookmarks X and X2 moved to rev 1, Y at rev -1
88 88
89 89 $ hg bookmarks
90 90 X 0:f7b1eb17ad24
91 91 X2 0:f7b1eb17ad24
92 92 Y -1:000000000000
93 93
94 94 bookmark rev 0 again
95 95
96 96 $ hg bookmark -r 0 Z
97 97
98 98 $ hg update X
99 99 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
100 100 $ echo c > c
101 101 $ hg add c
102 102 $ hg commit -m 2
103 103 created new head
104 104
105 105 bookmarks X moved to rev 2, Y at rev -1, Z at rev 0
106 106
107 107 $ hg bookmarks
108 108 * X 2:db815d6d32e6
109 109 X2 0:f7b1eb17ad24
110 110 Y -1:000000000000
111 111 Z 0:f7b1eb17ad24
112 112
113 113 rename nonexistent bookmark
114 114
115 115 $ hg bookmark -m A B
116 116 abort: a bookmark of this name does not exist
117 117 [255]
118 118
119 119 rename to existent bookmark
120 120
121 121 $ hg bookmark -m X Y
122 122 abort: a bookmark of the same name already exists
123 123 [255]
124 124
125 125 force rename to existent bookmark
126 126
127 127 $ hg bookmark -f -m X Y
128 128
129 129 list bookmarks
130 130
131 131 $ hg bookmark
132 132 X2 0:f7b1eb17ad24
133 133 * Y 2:db815d6d32e6
134 134 Z 0:f7b1eb17ad24
135 135
136 136 rename without new name
137 137
138 138 $ hg bookmark -m Y
139 139 abort: new bookmark name required
140 140 [255]
141 141
142 142 delete without name
143 143
144 144 $ hg bookmark -d
145 145 abort: bookmark name required
146 146 [255]
147 147
148 148 delete nonexistent bookmark
149 149
150 150 $ hg bookmark -d A
151 151 abort: a bookmark of this name does not exist
152 152 [255]
153 153
154 154 bookmark name with spaces should be stripped
155 155
156 156 $ hg bookmark ' x y '
157 157
158 158 list bookmarks
159 159
160 160 $ hg bookmarks
161 161 X2 0:f7b1eb17ad24
162 162 Y 2:db815d6d32e6
163 163 Z 0:f7b1eb17ad24
164 164 * x y 2:db815d6d32e6
165 165
166 166 look up stripped bookmark name
167 167
168 168 $ hg log -r '"x y"'
169 169 changeset: 2:db815d6d32e6
170 170 bookmark: Y
171 171 bookmark: x y
172 172 tag: tip
173 173 parent: 0:f7b1eb17ad24
174 174 user: test
175 175 date: Thu Jan 01 00:00:00 1970 +0000
176 176 summary: 2
177 177
178 178
179 179 reject bookmark name with newline
180 180
181 181 $ hg bookmark '
182 182 > '
183 183 abort: bookmark name cannot contain newlines
184 184 [255]
185 185
186 186 bookmark with existing name
187 187
188 188 $ hg bookmark Z
189 189 abort: a bookmark of the same name already exists
190 190 [255]
191 191
192 192 force bookmark with existing name
193 193
194 194 $ hg bookmark -f Z
195 195
196 196 list bookmarks
197 197
198 198 $ hg bookmark
199 199 X2 0:f7b1eb17ad24
200 200 Y 2:db815d6d32e6
201 201 * Z 2:db815d6d32e6
202 202 x y 2:db815d6d32e6
203 203
204 204 revision but no bookmark name
205 205
206 206 $ hg bookmark -r .
207 207 abort: bookmark name required
208 208 [255]
209 209
210 210 bookmark name with whitespace only
211 211
212 212 $ hg bookmark ' '
213 213 abort: bookmark names cannot consist entirely of whitespace
214 214 [255]
215
216 invalid bookmark
217
218 $ hg bookmark 'foo:bar'
219 abort: bookmark 'foo:bar' contains illegal character
220 [255]
221
General Comments 0
You need to be logged in to leave comments. Login now