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