##// END OF EJS Templates
bookmarks: disallow bookmarks named 'tip', '.', or 'null'...
Kevin Bullock -
r17816:19388ba7 default
parent child Browse files
Show More
@@ -1,284 +1,286 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 hex
9 from mercurial.node import hex
10 from mercurial import encoding, error, util, obsolete, phases
10 from mercurial import encoding, error, util, obsolete, phases
11 import errno, os
11 import errno, os
12
12
13 def checkvalid(mark):
13 def checkvalid(mark):
14 for c in (':', '\0', '\n', '\r'):
14 for c in (':', '\0', '\n', '\r'):
15 if c in mark:
15 if c in mark:
16 raise util.Abort(_("bookmark '%s' contains illegal "
16 raise util.Abort(_("bookmark '%s' contains illegal "
17 "character" % mark))
17 "character" % mark))
18 if mark in ['tip', '.', 'null']:
19 raise util.Abort(_('the name \'%s\' is reserved') % mark)
18
20
19 def read(repo):
21 def read(repo):
20 '''Parse .hg/bookmarks file and return a dictionary
22 '''Parse .hg/bookmarks file and return a dictionary
21
23
22 Bookmarks are stored as {HASH}\\s{NAME}\\n (localtags format) values
24 Bookmarks are stored as {HASH}\\s{NAME}\\n (localtags format) values
23 in the .hg/bookmarks file.
25 in the .hg/bookmarks file.
24 Read the file and return a (name=>nodeid) dictionary
26 Read the file and return a (name=>nodeid) dictionary
25 '''
27 '''
26 bookmarks = {}
28 bookmarks = {}
27 try:
29 try:
28 for line in repo.opener('bookmarks'):
30 for line in repo.opener('bookmarks'):
29 line = line.strip()
31 line = line.strip()
30 if not line:
32 if not line:
31 continue
33 continue
32 if ' ' not in line:
34 if ' ' not in line:
33 repo.ui.warn(_('malformed line in .hg/bookmarks: %r\n') % line)
35 repo.ui.warn(_('malformed line in .hg/bookmarks: %r\n') % line)
34 continue
36 continue
35 sha, refspec = line.split(' ', 1)
37 sha, refspec = line.split(' ', 1)
36 refspec = encoding.tolocal(refspec)
38 refspec = encoding.tolocal(refspec)
37 try:
39 try:
38 bookmarks[refspec] = repo.changelog.lookup(sha)
40 bookmarks[refspec] = repo.changelog.lookup(sha)
39 except LookupError:
41 except LookupError:
40 pass
42 pass
41 except IOError, inst:
43 except IOError, inst:
42 if inst.errno != errno.ENOENT:
44 if inst.errno != errno.ENOENT:
43 raise
45 raise
44 return bookmarks
46 return bookmarks
45
47
46 def readcurrent(repo):
48 def readcurrent(repo):
47 '''Get the current bookmark
49 '''Get the current bookmark
48
50
49 If we use gittishsh branches we have a current bookmark that
51 If we use gittishsh branches we have a current bookmark that
50 we are on. This function returns the name of the bookmark. It
52 we are on. This function returns the name of the bookmark. It
51 is stored in .hg/bookmarks.current
53 is stored in .hg/bookmarks.current
52 '''
54 '''
53 mark = None
55 mark = None
54 try:
56 try:
55 file = repo.opener('bookmarks.current')
57 file = repo.opener('bookmarks.current')
56 except IOError, inst:
58 except IOError, inst:
57 if inst.errno != errno.ENOENT:
59 if inst.errno != errno.ENOENT:
58 raise
60 raise
59 return None
61 return None
60 try:
62 try:
61 # No readline() in osutil.posixfile, reading everything is cheap
63 # No readline() in osutil.posixfile, reading everything is cheap
62 mark = encoding.tolocal((file.readlines() or [''])[0])
64 mark = encoding.tolocal((file.readlines() or [''])[0])
63 if mark == '' or mark not in repo._bookmarks:
65 if mark == '' or mark not in repo._bookmarks:
64 mark = None
66 mark = None
65 finally:
67 finally:
66 file.close()
68 file.close()
67 return mark
69 return mark
68
70
69 def write(repo):
71 def write(repo):
70 '''Write bookmarks
72 '''Write bookmarks
71
73
72 Write the given bookmark => hash dictionary to the .hg/bookmarks file
74 Write the given bookmark => hash dictionary to the .hg/bookmarks file
73 in a format equal to those of localtags.
75 in a format equal to those of localtags.
74
76
75 We also store a backup of the previous state in undo.bookmarks that
77 We also store a backup of the previous state in undo.bookmarks that
76 can be copied back on rollback.
78 can be copied back on rollback.
77 '''
79 '''
78 refs = repo._bookmarks
80 refs = repo._bookmarks
79
81
80 if repo._bookmarkcurrent not in refs:
82 if repo._bookmarkcurrent not in refs:
81 setcurrent(repo, None)
83 setcurrent(repo, None)
82 for mark in refs.keys():
84 for mark in refs.keys():
83 checkvalid(mark)
85 checkvalid(mark)
84
86
85 wlock = repo.wlock()
87 wlock = repo.wlock()
86 try:
88 try:
87
89
88 file = repo.opener('bookmarks', 'w', atomictemp=True)
90 file = repo.opener('bookmarks', 'w', atomictemp=True)
89 for refspec, node in refs.iteritems():
91 for refspec, node in refs.iteritems():
90 file.write("%s %s\n" % (hex(node), encoding.fromlocal(refspec)))
92 file.write("%s %s\n" % (hex(node), encoding.fromlocal(refspec)))
91 file.close()
93 file.close()
92
94
93 # touch 00changelog.i so hgweb reloads bookmarks (no lock needed)
95 # touch 00changelog.i so hgweb reloads bookmarks (no lock needed)
94 try:
96 try:
95 os.utime(repo.sjoin('00changelog.i'), None)
97 os.utime(repo.sjoin('00changelog.i'), None)
96 except OSError:
98 except OSError:
97 pass
99 pass
98
100
99 finally:
101 finally:
100 wlock.release()
102 wlock.release()
101
103
102 def setcurrent(repo, mark):
104 def setcurrent(repo, mark):
103 '''Set the name of the bookmark that we are currently on
105 '''Set the name of the bookmark that we are currently on
104
106
105 Set the name of the bookmark that we are on (hg update <bookmark>).
107 Set the name of the bookmark that we are on (hg update <bookmark>).
106 The name is recorded in .hg/bookmarks.current
108 The name is recorded in .hg/bookmarks.current
107 '''
109 '''
108 current = repo._bookmarkcurrent
110 current = repo._bookmarkcurrent
109 if current == mark:
111 if current == mark:
110 return
112 return
111
113
112 if mark not in repo._bookmarks:
114 if mark not in repo._bookmarks:
113 mark = ''
115 mark = ''
114 checkvalid(mark)
116 checkvalid(mark)
115
117
116 wlock = repo.wlock()
118 wlock = repo.wlock()
117 try:
119 try:
118 file = repo.opener('bookmarks.current', 'w', atomictemp=True)
120 file = repo.opener('bookmarks.current', 'w', atomictemp=True)
119 file.write(encoding.fromlocal(mark))
121 file.write(encoding.fromlocal(mark))
120 file.close()
122 file.close()
121 finally:
123 finally:
122 wlock.release()
124 wlock.release()
123 repo._bookmarkcurrent = mark
125 repo._bookmarkcurrent = mark
124
126
125 def unsetcurrent(repo):
127 def unsetcurrent(repo):
126 wlock = repo.wlock()
128 wlock = repo.wlock()
127 try:
129 try:
128 try:
130 try:
129 util.unlink(repo.join('bookmarks.current'))
131 util.unlink(repo.join('bookmarks.current'))
130 repo._bookmarkcurrent = None
132 repo._bookmarkcurrent = None
131 except OSError, inst:
133 except OSError, inst:
132 if inst.errno != errno.ENOENT:
134 if inst.errno != errno.ENOENT:
133 raise
135 raise
134 finally:
136 finally:
135 wlock.release()
137 wlock.release()
136
138
137 def updatecurrentbookmark(repo, oldnode, curbranch):
139 def updatecurrentbookmark(repo, oldnode, curbranch):
138 try:
140 try:
139 return update(repo, oldnode, repo.branchtip(curbranch))
141 return update(repo, oldnode, repo.branchtip(curbranch))
140 except error.RepoLookupError:
142 except error.RepoLookupError:
141 if curbranch == "default": # no default branch!
143 if curbranch == "default": # no default branch!
142 return update(repo, oldnode, repo.lookup("tip"))
144 return update(repo, oldnode, repo.lookup("tip"))
143 else:
145 else:
144 raise util.Abort(_("branch %s not found") % curbranch)
146 raise util.Abort(_("branch %s not found") % curbranch)
145
147
146 def update(repo, parents, node):
148 def update(repo, parents, node):
147 marks = repo._bookmarks
149 marks = repo._bookmarks
148 update = False
150 update = False
149 cur = repo._bookmarkcurrent
151 cur = repo._bookmarkcurrent
150 if not cur:
152 if not cur:
151 return False
153 return False
152
154
153 toupdate = [b for b in marks if b.split('@', 1)[0] == cur.split('@', 1)[0]]
155 toupdate = [b for b in marks if b.split('@', 1)[0] == cur.split('@', 1)[0]]
154 for mark in toupdate:
156 for mark in toupdate:
155 if mark and marks[mark] in parents:
157 if mark and marks[mark] in parents:
156 old = repo[marks[mark]]
158 old = repo[marks[mark]]
157 new = repo[node]
159 new = repo[node]
158 if old.descendant(new) and mark == cur:
160 if old.descendant(new) and mark == cur:
159 marks[cur] = new.node()
161 marks[cur] = new.node()
160 update = True
162 update = True
161 if mark != cur:
163 if mark != cur:
162 del marks[mark]
164 del marks[mark]
163 if update:
165 if update:
164 repo._writebookmarks(marks)
166 repo._writebookmarks(marks)
165 return update
167 return update
166
168
167 def listbookmarks(repo):
169 def listbookmarks(repo):
168 # We may try to list bookmarks on a repo type that does not
170 # We may try to list bookmarks on a repo type that does not
169 # support it (e.g., statichttprepository).
171 # support it (e.g., statichttprepository).
170 marks = getattr(repo, '_bookmarks', {})
172 marks = getattr(repo, '_bookmarks', {})
171
173
172 d = {}
174 d = {}
173 for k, v in marks.iteritems():
175 for k, v in marks.iteritems():
174 # don't expose local divergent bookmarks
176 # don't expose local divergent bookmarks
175 if '@' not in k or k.endswith('@'):
177 if '@' not in k or k.endswith('@'):
176 d[k] = hex(v)
178 d[k] = hex(v)
177 return d
179 return d
178
180
179 def pushbookmark(repo, key, old, new):
181 def pushbookmark(repo, key, old, new):
180 w = repo.wlock()
182 w = repo.wlock()
181 try:
183 try:
182 marks = repo._bookmarks
184 marks = repo._bookmarks
183 if hex(marks.get(key, '')) != old:
185 if hex(marks.get(key, '')) != old:
184 return False
186 return False
185 if new == '':
187 if new == '':
186 del marks[key]
188 del marks[key]
187 else:
189 else:
188 if new not in repo:
190 if new not in repo:
189 return False
191 return False
190 marks[key] = repo[new].node()
192 marks[key] = repo[new].node()
191 write(repo)
193 write(repo)
192 return True
194 return True
193 finally:
195 finally:
194 w.release()
196 w.release()
195
197
196 def updatefromremote(ui, repo, remote, path):
198 def updatefromremote(ui, repo, remote, path):
197 ui.debug("checking for updated bookmarks\n")
199 ui.debug("checking for updated bookmarks\n")
198 rb = remote.listkeys('bookmarks')
200 rb = remote.listkeys('bookmarks')
199 changed = False
201 changed = False
200 for k in rb.keys():
202 for k in rb.keys():
201 if k in repo._bookmarks:
203 if k in repo._bookmarks:
202 nr, nl = rb[k], repo._bookmarks[k]
204 nr, nl = rb[k], repo._bookmarks[k]
203 if nr in repo:
205 if nr in repo:
204 cr = repo[nr]
206 cr = repo[nr]
205 cl = repo[nl]
207 cl = repo[nl]
206 if cl.rev() >= cr.rev():
208 if cl.rev() >= cr.rev():
207 continue
209 continue
208 if validdest(repo, cl, cr):
210 if validdest(repo, cl, cr):
209 repo._bookmarks[k] = cr.node()
211 repo._bookmarks[k] = cr.node()
210 changed = True
212 changed = True
211 ui.status(_("updating bookmark %s\n") % k)
213 ui.status(_("updating bookmark %s\n") % k)
212 else:
214 else:
213 if k == '@':
215 if k == '@':
214 kd = ''
216 kd = ''
215 else:
217 else:
216 kd = k
218 kd = k
217 # find a unique @ suffix
219 # find a unique @ suffix
218 for x in range(1, 100):
220 for x in range(1, 100):
219 n = '%s@%d' % (kd, x)
221 n = '%s@%d' % (kd, x)
220 if n not in repo._bookmarks:
222 if n not in repo._bookmarks:
221 break
223 break
222 # try to use an @pathalias suffix
224 # try to use an @pathalias suffix
223 # if an @pathalias already exists, we overwrite (update) it
225 # if an @pathalias already exists, we overwrite (update) it
224 for p, u in ui.configitems("paths"):
226 for p, u in ui.configitems("paths"):
225 if path == u:
227 if path == u:
226 n = '%s@%s' % (kd, p)
228 n = '%s@%s' % (kd, p)
227
229
228 repo._bookmarks[n] = cr.node()
230 repo._bookmarks[n] = cr.node()
229 changed = True
231 changed = True
230 ui.warn(_("divergent bookmark %s stored as %s\n") % (k, n))
232 ui.warn(_("divergent bookmark %s stored as %s\n") % (k, n))
231 elif rb[k] in repo:
233 elif rb[k] in repo:
232 # add remote bookmarks for changes we already have
234 # add remote bookmarks for changes we already have
233 repo._bookmarks[k] = repo[rb[k]].node()
235 repo._bookmarks[k] = repo[rb[k]].node()
234 changed = True
236 changed = True
235 ui.status(_("adding remote bookmark %s\n") % k)
237 ui.status(_("adding remote bookmark %s\n") % k)
236
238
237 if changed:
239 if changed:
238 write(repo)
240 write(repo)
239
241
240 def diff(ui, dst, src):
242 def diff(ui, dst, src):
241 ui.status(_("searching for changed bookmarks\n"))
243 ui.status(_("searching for changed bookmarks\n"))
242
244
243 smarks = src.listkeys('bookmarks')
245 smarks = src.listkeys('bookmarks')
244 dmarks = dst.listkeys('bookmarks')
246 dmarks = dst.listkeys('bookmarks')
245
247
246 diff = sorted(set(smarks) - set(dmarks))
248 diff = sorted(set(smarks) - set(dmarks))
247 for k in diff:
249 for k in diff:
248 mark = ui.debugflag and smarks[k] or smarks[k][:12]
250 mark = ui.debugflag and smarks[k] or smarks[k][:12]
249 ui.write(" %-25s %s\n" % (k, mark))
251 ui.write(" %-25s %s\n" % (k, mark))
250
252
251 if len(diff) <= 0:
253 if len(diff) <= 0:
252 ui.status(_("no changed bookmarks found\n"))
254 ui.status(_("no changed bookmarks found\n"))
253 return 1
255 return 1
254 return 0
256 return 0
255
257
256 def validdest(repo, old, new):
258 def validdest(repo, old, new):
257 """Is the new bookmark destination a valid update from the old one"""
259 """Is the new bookmark destination a valid update from the old one"""
258 if old == new:
260 if old == new:
259 # Old == new -> nothing to update.
261 # Old == new -> nothing to update.
260 return False
262 return False
261 elif not old:
263 elif not old:
262 # old is nullrev, anything is valid.
264 # old is nullrev, anything is valid.
263 # (new != nullrev has been excluded by the previous check)
265 # (new != nullrev has been excluded by the previous check)
264 return True
266 return True
265 elif repo.obsstore:
267 elif repo.obsstore:
266 # We only need this complicated logic if there is obsolescence
268 # We only need this complicated logic if there is obsolescence
267 # XXX will probably deserve an optimised revset.
269 # XXX will probably deserve an optimised revset.
268
270
269 validdests = set([old])
271 validdests = set([old])
270 plen = -1
272 plen = -1
271 # compute the whole set of successors or descendants
273 # compute the whole set of successors or descendants
272 while len(validdests) != plen:
274 while len(validdests) != plen:
273 plen = len(validdests)
275 plen = len(validdests)
274 succs = set(c.node() for c in validdests)
276 succs = set(c.node() for c in validdests)
275 for c in validdests:
277 for c in validdests:
276 if c.phase() > phases.public:
278 if c.phase() > phases.public:
277 # obsolescence marker does not apply to public changeset
279 # obsolescence marker does not apply to public changeset
278 succs.update(obsolete.anysuccessors(repo.obsstore,
280 succs.update(obsolete.anysuccessors(repo.obsstore,
279 c.node()))
281 c.node()))
280 validdests = set(repo.set('%ln::', succs))
282 validdests = set(repo.set('%ln::', succs))
281 validdests.remove(old)
283 validdests.remove(old)
282 return new in validdests
284 return new in validdests
283 else:
285 else:
284 return old.descendant(new)
286 return old.descendant(new)
@@ -1,520 +1,535 b''
1 $ hg init
1 $ hg init
2
2
3 no bookmarks
3 no bookmarks
4
4
5 $ hg bookmarks
5 $ hg bookmarks
6 no bookmarks set
6 no bookmarks set
7
7
8 bookmark rev -1
8 bookmark rev -1
9
9
10 $ hg bookmark X
10 $ hg bookmark X
11
11
12 list bookmarks
12 list bookmarks
13
13
14 $ hg bookmarks
14 $ hg bookmarks
15 * X -1:000000000000
15 * X -1:000000000000
16
16
17 list bookmarks with color
17 list bookmarks with color
18
18
19 $ hg --config extensions.color= --config color.mode=ansi \
19 $ hg --config extensions.color= --config color.mode=ansi \
20 > bookmarks --color=always
20 > bookmarks --color=always
21 \x1b[0;32m * X -1:000000000000\x1b[0m (esc)
21 \x1b[0;32m * X -1:000000000000\x1b[0m (esc)
22
22
23 $ echo a > a
23 $ echo a > a
24 $ hg add a
24 $ hg add a
25 $ hg commit -m 0
25 $ hg commit -m 0
26
26
27 bookmark X moved to rev 0
27 bookmark X moved to rev 0
28
28
29 $ hg bookmarks
29 $ hg bookmarks
30 * X 0:f7b1eb17ad24
30 * X 0:f7b1eb17ad24
31
31
32 look up bookmark
32 look up bookmark
33
33
34 $ hg log -r X
34 $ hg log -r X
35 changeset: 0:f7b1eb17ad24
35 changeset: 0:f7b1eb17ad24
36 bookmark: X
36 bookmark: X
37 tag: tip
37 tag: tip
38 user: test
38 user: test
39 date: Thu Jan 01 00:00:00 1970 +0000
39 date: Thu Jan 01 00:00:00 1970 +0000
40 summary: 0
40 summary: 0
41
41
42
42
43 second bookmark for rev 0
43 second bookmark for rev 0
44
44
45 $ hg bookmark X2
45 $ hg bookmark X2
46
46
47 bookmark rev -1 again
47 bookmark rev -1 again
48
48
49 $ hg bookmark -r null Y
49 $ hg bookmark -r null Y
50
50
51 list bookmarks
51 list bookmarks
52
52
53 $ hg bookmarks
53 $ hg bookmarks
54 X 0:f7b1eb17ad24
54 X 0:f7b1eb17ad24
55 * X2 0:f7b1eb17ad24
55 * X2 0:f7b1eb17ad24
56 Y -1:000000000000
56 Y -1:000000000000
57
57
58 $ echo b > b
58 $ echo b > b
59 $ hg add b
59 $ hg add b
60 $ hg commit -m 1
60 $ hg commit -m 1
61
61
62 bookmarks revset
62 bookmarks revset
63
63
64 $ hg log -r 'bookmark()'
64 $ hg log -r 'bookmark()'
65 changeset: 0:f7b1eb17ad24
65 changeset: 0:f7b1eb17ad24
66 bookmark: X
66 bookmark: X
67 user: test
67 user: test
68 date: Thu Jan 01 00:00:00 1970 +0000
68 date: Thu Jan 01 00:00:00 1970 +0000
69 summary: 0
69 summary: 0
70
70
71 changeset: 1:925d80f479bb
71 changeset: 1:925d80f479bb
72 bookmark: X2
72 bookmark: X2
73 tag: tip
73 tag: tip
74 user: test
74 user: test
75 date: Thu Jan 01 00:00:00 1970 +0000
75 date: Thu Jan 01 00:00:00 1970 +0000
76 summary: 1
76 summary: 1
77
77
78 $ hg log -r 'bookmark(Y)'
78 $ hg log -r 'bookmark(Y)'
79 $ hg log -r 'bookmark(X2)'
79 $ hg log -r 'bookmark(X2)'
80 changeset: 1:925d80f479bb
80 changeset: 1:925d80f479bb
81 bookmark: X2
81 bookmark: X2
82 tag: tip
82 tag: tip
83 user: test
83 user: test
84 date: Thu Jan 01 00:00:00 1970 +0000
84 date: Thu Jan 01 00:00:00 1970 +0000
85 summary: 1
85 summary: 1
86
86
87 $ hg log -r 'bookmark("re:X")'
87 $ hg log -r 'bookmark("re:X")'
88 changeset: 0:f7b1eb17ad24
88 changeset: 0:f7b1eb17ad24
89 bookmark: X
89 bookmark: X
90 user: test
90 user: test
91 date: Thu Jan 01 00:00:00 1970 +0000
91 date: Thu Jan 01 00:00:00 1970 +0000
92 summary: 0
92 summary: 0
93
93
94 changeset: 1:925d80f479bb
94 changeset: 1:925d80f479bb
95 bookmark: X2
95 bookmark: X2
96 tag: tip
96 tag: tip
97 user: test
97 user: test
98 date: Thu Jan 01 00:00:00 1970 +0000
98 date: Thu Jan 01 00:00:00 1970 +0000
99 summary: 1
99 summary: 1
100
100
101 $ hg log -r 'bookmark(unknown)'
101 $ hg log -r 'bookmark(unknown)'
102 abort: bookmark 'unknown' does not exist
102 abort: bookmark 'unknown' does not exist
103 [255]
103 [255]
104
104
105 $ hg help revsets | grep 'bookmark('
105 $ hg help revsets | grep 'bookmark('
106 "bookmark([name])"
106 "bookmark([name])"
107
107
108 bookmarks X and X2 moved to rev 1, Y at rev -1
108 bookmarks X and X2 moved to rev 1, Y at rev -1
109
109
110 $ hg bookmarks
110 $ hg bookmarks
111 X 0:f7b1eb17ad24
111 X 0:f7b1eb17ad24
112 * X2 1:925d80f479bb
112 * X2 1:925d80f479bb
113 Y -1:000000000000
113 Y -1:000000000000
114
114
115 bookmark rev 0 again
115 bookmark rev 0 again
116
116
117 $ hg bookmark -r 0 Z
117 $ hg bookmark -r 0 Z
118
118
119 $ hg update X
119 $ hg update X
120 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
120 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
121 $ echo c > c
121 $ echo c > c
122 $ hg add c
122 $ hg add c
123 $ hg commit -m 2
123 $ hg commit -m 2
124 created new head
124 created new head
125
125
126 bookmarks X moved to rev 2, Y at rev -1, Z at rev 0
126 bookmarks X moved to rev 2, Y at rev -1, Z at rev 0
127
127
128 $ hg bookmarks
128 $ hg bookmarks
129 * X 2:db815d6d32e6
129 * X 2:db815d6d32e6
130 X2 1:925d80f479bb
130 X2 1:925d80f479bb
131 Y -1:000000000000
131 Y -1:000000000000
132 Z 0:f7b1eb17ad24
132 Z 0:f7b1eb17ad24
133
133
134 rename nonexistent bookmark
134 rename nonexistent bookmark
135
135
136 $ hg bookmark -m A B
136 $ hg bookmark -m A B
137 abort: bookmark 'A' does not exist
137 abort: bookmark 'A' does not exist
138 [255]
138 [255]
139
139
140 rename to existent bookmark
140 rename to existent bookmark
141
141
142 $ hg bookmark -m X Y
142 $ hg bookmark -m X Y
143 abort: bookmark 'Y' already exists (use -f to force)
143 abort: bookmark 'Y' already exists (use -f to force)
144 [255]
144 [255]
145
145
146 force rename to existent bookmark
146 force rename to existent bookmark
147
147
148 $ hg bookmark -f -m X Y
148 $ hg bookmark -f -m X Y
149
149
150 list bookmarks
150 list bookmarks
151
151
152 $ hg bookmark
152 $ hg bookmark
153 X2 1:925d80f479bb
153 X2 1:925d80f479bb
154 * Y 2:db815d6d32e6
154 * Y 2:db815d6d32e6
155 Z 0:f7b1eb17ad24
155 Z 0:f7b1eb17ad24
156
156
157 bookmarks from a revset
157 bookmarks from a revset
158 $ hg bookmark -r '.^1' REVSET
158 $ hg bookmark -r '.^1' REVSET
159 $ hg bookmark -r ':tip' TIP
159 $ hg bookmark -r ':tip' TIP
160 $ hg bookmarks
160 $ hg bookmarks
161 REVSET 0:f7b1eb17ad24
161 REVSET 0:f7b1eb17ad24
162 * TIP 2:db815d6d32e6
162 * TIP 2:db815d6d32e6
163 X2 1:925d80f479bb
163 X2 1:925d80f479bb
164 Y 2:db815d6d32e6
164 Y 2:db815d6d32e6
165 Z 0:f7b1eb17ad24
165 Z 0:f7b1eb17ad24
166
166
167 $ hg bookmark -d REVSET
167 $ hg bookmark -d REVSET
168 $ hg bookmark -d TIP
168 $ hg bookmark -d TIP
169
169
170 rename without new name
170 rename without new name
171
171
172 $ hg bookmark -m Y
172 $ hg bookmark -m Y
173 abort: new bookmark name required
173 abort: new bookmark name required
174 [255]
174 [255]
175
175
176 delete without name
176 delete without name
177
177
178 $ hg bookmark -d
178 $ hg bookmark -d
179 abort: bookmark name required
179 abort: bookmark name required
180 [255]
180 [255]
181
181
182 delete nonexistent bookmark
182 delete nonexistent bookmark
183
183
184 $ hg bookmark -d A
184 $ hg bookmark -d A
185 abort: bookmark 'A' does not exist
185 abort: bookmark 'A' does not exist
186 [255]
186 [255]
187
187
188 bookmark name with spaces should be stripped
188 bookmark name with spaces should be stripped
189
189
190 $ hg bookmark ' x y '
190 $ hg bookmark ' x y '
191
191
192 list bookmarks
192 list bookmarks
193
193
194 $ hg bookmarks
194 $ hg bookmarks
195 X2 1:925d80f479bb
195 X2 1:925d80f479bb
196 Y 2:db815d6d32e6
196 Y 2:db815d6d32e6
197 Z 0:f7b1eb17ad24
197 Z 0:f7b1eb17ad24
198 * x y 2:db815d6d32e6
198 * x y 2:db815d6d32e6
199
199
200 look up stripped bookmark name
200 look up stripped bookmark name
201
201
202 $ hg log -r '"x y"'
202 $ hg log -r '"x y"'
203 changeset: 2:db815d6d32e6
203 changeset: 2:db815d6d32e6
204 bookmark: Y
204 bookmark: Y
205 bookmark: x y
205 bookmark: x y
206 tag: tip
206 tag: tip
207 parent: 0:f7b1eb17ad24
207 parent: 0:f7b1eb17ad24
208 user: test
208 user: test
209 date: Thu Jan 01 00:00:00 1970 +0000
209 date: Thu Jan 01 00:00:00 1970 +0000
210 summary: 2
210 summary: 2
211
211
212
212
213 reject bookmark name with newline
213 reject bookmark name with newline
214
214
215 $ hg bookmark '
215 $ hg bookmark '
216 > '
216 > '
217 abort: bookmark names cannot consist entirely of whitespace
217 abort: bookmark names cannot consist entirely of whitespace
218 [255]
218 [255]
219
219
220 $ hg bookmark -m Z '
220 $ hg bookmark -m Z '
221 > '
221 > '
222 abort: bookmark names cannot consist entirely of whitespace
222 abort: bookmark names cannot consist entirely of whitespace
223 [255]
223 [255]
224
224
225 bookmark with reserved name
226
227 $ hg bookmark tip
228 abort: the name 'tip' is reserved
229 [255]
230
231 $ hg bookmark .
232 abort: the name '.' is reserved
233 [255]
234
235 $ hg bookmark null
236 abort: the name 'null' is reserved
237 [255]
238
239
225 bookmark with existing name
240 bookmark with existing name
226
241
227 $ hg bookmark Z
242 $ hg bookmark Z
228 abort: bookmark 'Z' already exists (use -f to force)
243 abort: bookmark 'Z' already exists (use -f to force)
229 [255]
244 [255]
230
245
231 $ hg bookmark -m Y Z
246 $ hg bookmark -m Y Z
232 abort: bookmark 'Z' already exists (use -f to force)
247 abort: bookmark 'Z' already exists (use -f to force)
233 [255]
248 [255]
234
249
235 bookmark with name of branch
250 bookmark with name of branch
236
251
237 $ hg bookmark default
252 $ hg bookmark default
238 abort: a bookmark cannot have the name of an existing branch
253 abort: a bookmark cannot have the name of an existing branch
239 [255]
254 [255]
240
255
241 $ hg bookmark -m Y default
256 $ hg bookmark -m Y default
242 abort: a bookmark cannot have the name of an existing branch
257 abort: a bookmark cannot have the name of an existing branch
243 [255]
258 [255]
244
259
245 incompatible options
260 incompatible options
246
261
247 $ hg bookmark -m Y -d Z
262 $ hg bookmark -m Y -d Z
248 abort: --delete and --rename are incompatible
263 abort: --delete and --rename are incompatible
249 [255]
264 [255]
250
265
251 $ hg bookmark -r 1 -d Z
266 $ hg bookmark -r 1 -d Z
252 abort: --rev is incompatible with --delete
267 abort: --rev is incompatible with --delete
253 [255]
268 [255]
254
269
255 $ hg bookmark -r 1 -m Z Y
270 $ hg bookmark -r 1 -m Z Y
256 abort: --rev is incompatible with --rename
271 abort: --rev is incompatible with --rename
257 [255]
272 [255]
258
273
259 force bookmark with existing name
274 force bookmark with existing name
260
275
261 $ hg bookmark -f Z
276 $ hg bookmark -f Z
262
277
263 list bookmarks
278 list bookmarks
264
279
265 $ hg bookmark
280 $ hg bookmark
266 X2 1:925d80f479bb
281 X2 1:925d80f479bb
267 Y 2:db815d6d32e6
282 Y 2:db815d6d32e6
268 * Z 2:db815d6d32e6
283 * Z 2:db815d6d32e6
269 x y 2:db815d6d32e6
284 x y 2:db815d6d32e6
270
285
271 revision but no bookmark name
286 revision but no bookmark name
272
287
273 $ hg bookmark -r .
288 $ hg bookmark -r .
274 abort: bookmark name required
289 abort: bookmark name required
275 [255]
290 [255]
276
291
277 bookmark name with whitespace only
292 bookmark name with whitespace only
278
293
279 $ hg bookmark ' '
294 $ hg bookmark ' '
280 abort: bookmark names cannot consist entirely of whitespace
295 abort: bookmark names cannot consist entirely of whitespace
281 [255]
296 [255]
282
297
283 $ hg bookmark -m Y ' '
298 $ hg bookmark -m Y ' '
284 abort: bookmark names cannot consist entirely of whitespace
299 abort: bookmark names cannot consist entirely of whitespace
285 [255]
300 [255]
286
301
287 invalid bookmark
302 invalid bookmark
288
303
289 $ hg bookmark 'foo:bar'
304 $ hg bookmark 'foo:bar'
290 abort: bookmark 'foo:bar' contains illegal character
305 abort: bookmark 'foo:bar' contains illegal character
291 [255]
306 [255]
292
307
293 the bookmark extension should be ignored now that it is part of core
308 the bookmark extension should be ignored now that it is part of core
294
309
295 $ echo "[extensions]" >> $HGRCPATH
310 $ echo "[extensions]" >> $HGRCPATH
296 $ echo "bookmarks=" >> $HGRCPATH
311 $ echo "bookmarks=" >> $HGRCPATH
297 $ hg bookmarks
312 $ hg bookmarks
298 X2 1:925d80f479bb
313 X2 1:925d80f479bb
299 Y 2:db815d6d32e6
314 Y 2:db815d6d32e6
300 * Z 2:db815d6d32e6
315 * Z 2:db815d6d32e6
301 x y 2:db815d6d32e6
316 x y 2:db815d6d32e6
302
317
303 test summary
318 test summary
304
319
305 $ hg summary
320 $ hg summary
306 parent: 2:db815d6d32e6 tip
321 parent: 2:db815d6d32e6 tip
307 2
322 2
308 branch: default
323 branch: default
309 bookmarks: *Z Y x y
324 bookmarks: *Z Y x y
310 commit: (clean)
325 commit: (clean)
311 update: 1 new changesets, 2 branch heads (merge)
326 update: 1 new changesets, 2 branch heads (merge)
312
327
313 test id
328 test id
314
329
315 $ hg id
330 $ hg id
316 db815d6d32e6 tip Y/Z/x y
331 db815d6d32e6 tip Y/Z/x y
317
332
318 test rollback
333 test rollback
319
334
320 $ echo foo > f1
335 $ echo foo > f1
321 $ hg ci -Amr
336 $ hg ci -Amr
322 adding f1
337 adding f1
323 $ hg bookmark -f Y -r 1
338 $ hg bookmark -f Y -r 1
324 $ hg bookmark -f Z -r 1
339 $ hg bookmark -f Z -r 1
325 $ hg rollback
340 $ hg rollback
326 repository tip rolled back to revision 2 (undo commit)
341 repository tip rolled back to revision 2 (undo commit)
327 working directory now based on revision 2
342 working directory now based on revision 2
328 $ hg bookmarks
343 $ hg bookmarks
329 X2 1:925d80f479bb
344 X2 1:925d80f479bb
330 Y 2:db815d6d32e6
345 Y 2:db815d6d32e6
331 * Z 2:db815d6d32e6
346 * Z 2:db815d6d32e6
332 x y 2:db815d6d32e6
347 x y 2:db815d6d32e6
333
348
334 test clone
349 test clone
335
350
336 $ hg bookmark -r 2 -i @
351 $ hg bookmark -r 2 -i @
337 $ hg bookmark -r 2 -i a@
352 $ hg bookmark -r 2 -i a@
338 $ hg bookmarks
353 $ hg bookmarks
339 @ 2:db815d6d32e6
354 @ 2:db815d6d32e6
340 X2 1:925d80f479bb
355 X2 1:925d80f479bb
341 Y 2:db815d6d32e6
356 Y 2:db815d6d32e6
342 * Z 2:db815d6d32e6
357 * Z 2:db815d6d32e6
343 a@ 2:db815d6d32e6
358 a@ 2:db815d6d32e6
344 x y 2:db815d6d32e6
359 x y 2:db815d6d32e6
345 $ hg clone . cloned-bookmarks
360 $ hg clone . cloned-bookmarks
346 updating to branch default
361 updating to branch default
347 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
362 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
348 $ hg -R cloned-bookmarks bookmarks
363 $ hg -R cloned-bookmarks bookmarks
349 @ 2:db815d6d32e6
364 @ 2:db815d6d32e6
350 X2 1:925d80f479bb
365 X2 1:925d80f479bb
351 Y 2:db815d6d32e6
366 Y 2:db815d6d32e6
352 Z 2:db815d6d32e6
367 Z 2:db815d6d32e6
353 a@ 2:db815d6d32e6
368 a@ 2:db815d6d32e6
354 x y 2:db815d6d32e6
369 x y 2:db815d6d32e6
355
370
356 test clone with pull protocol
371 test clone with pull protocol
357
372
358 $ hg clone --pull . cloned-bookmarks-pull
373 $ hg clone --pull . cloned-bookmarks-pull
359 requesting all changes
374 requesting all changes
360 adding changesets
375 adding changesets
361 adding manifests
376 adding manifests
362 adding file changes
377 adding file changes
363 added 3 changesets with 3 changes to 3 files (+1 heads)
378 added 3 changesets with 3 changes to 3 files (+1 heads)
364 updating to branch default
379 updating to branch default
365 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
380 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
366 $ hg -R cloned-bookmarks-pull bookmarks
381 $ hg -R cloned-bookmarks-pull bookmarks
367 @ 2:db815d6d32e6
382 @ 2:db815d6d32e6
368 X2 1:925d80f479bb
383 X2 1:925d80f479bb
369 Y 2:db815d6d32e6
384 Y 2:db815d6d32e6
370 Z 2:db815d6d32e6
385 Z 2:db815d6d32e6
371 a@ 2:db815d6d32e6
386 a@ 2:db815d6d32e6
372 x y 2:db815d6d32e6
387 x y 2:db815d6d32e6
373
388
374 $ hg bookmark -d @
389 $ hg bookmark -d @
375 $ hg bookmark -d a@
390 $ hg bookmark -d a@
376
391
377 test clone with a specific revision
392 test clone with a specific revision
378
393
379 $ hg clone -r 925d80 . cloned-bookmarks-rev
394 $ hg clone -r 925d80 . cloned-bookmarks-rev
380 adding changesets
395 adding changesets
381 adding manifests
396 adding manifests
382 adding file changes
397 adding file changes
383 added 2 changesets with 2 changes to 2 files
398 added 2 changesets with 2 changes to 2 files
384 updating to branch default
399 updating to branch default
385 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
400 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
386 $ hg -R cloned-bookmarks-rev bookmarks
401 $ hg -R cloned-bookmarks-rev bookmarks
387 X2 1:925d80f479bb
402 X2 1:925d80f479bb
388
403
389 test clone with update to a bookmark
404 test clone with update to a bookmark
390
405
391 $ hg clone -u Z . cloned-bookmarks-update
406 $ hg clone -u Z . cloned-bookmarks-update
392 updating to branch default
407 updating to branch default
393 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
408 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
394 $ hg -R cloned-bookmarks-update bookmarks
409 $ hg -R cloned-bookmarks-update bookmarks
395 X2 1:925d80f479bb
410 X2 1:925d80f479bb
396 Y 2:db815d6d32e6
411 Y 2:db815d6d32e6
397 * Z 2:db815d6d32e6
412 * Z 2:db815d6d32e6
398 x y 2:db815d6d32e6
413 x y 2:db815d6d32e6
399
414
400 create bundle with two heads
415 create bundle with two heads
401
416
402 $ hg clone . tobundle
417 $ hg clone . tobundle
403 updating to branch default
418 updating to branch default
404 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
419 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
405 $ echo x > tobundle/x
420 $ echo x > tobundle/x
406 $ hg -R tobundle add tobundle/x
421 $ hg -R tobundle add tobundle/x
407 $ hg -R tobundle commit -m'x'
422 $ hg -R tobundle commit -m'x'
408 $ hg -R tobundle update -r -2
423 $ hg -R tobundle update -r -2
409 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
424 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
410 $ echo y > tobundle/y
425 $ echo y > tobundle/y
411 $ hg -R tobundle branch test
426 $ hg -R tobundle branch test
412 marked working directory as branch test
427 marked working directory as branch test
413 (branches are permanent and global, did you want a bookmark?)
428 (branches are permanent and global, did you want a bookmark?)
414 $ hg -R tobundle add tobundle/y
429 $ hg -R tobundle add tobundle/y
415 $ hg -R tobundle commit -m'y'
430 $ hg -R tobundle commit -m'y'
416 $ hg -R tobundle bundle tobundle.hg
431 $ hg -R tobundle bundle tobundle.hg
417 searching for changes
432 searching for changes
418 2 changesets found
433 2 changesets found
419 $ hg unbundle tobundle.hg
434 $ hg unbundle tobundle.hg
420 adding changesets
435 adding changesets
421 adding manifests
436 adding manifests
422 adding file changes
437 adding file changes
423 added 2 changesets with 2 changes to 2 files (+1 heads)
438 added 2 changesets with 2 changes to 2 files (+1 heads)
424 (run 'hg heads' to see heads, 'hg merge' to merge)
439 (run 'hg heads' to see heads, 'hg merge' to merge)
425 $ hg update
440 $ hg update
426 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
441 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
427 $ hg bookmarks
442 $ hg bookmarks
428 X2 1:925d80f479bb
443 X2 1:925d80f479bb
429 Y 2:db815d6d32e6
444 Y 2:db815d6d32e6
430 * Z 3:125c9a1d6df6
445 * Z 3:125c9a1d6df6
431 x y 2:db815d6d32e6
446 x y 2:db815d6d32e6
432
447
433 test wrongly formated bookmark
448 test wrongly formated bookmark
434
449
435 $ echo '' >> .hg/bookmarks
450 $ echo '' >> .hg/bookmarks
436 $ hg bookmarks
451 $ hg bookmarks
437 X2 1:925d80f479bb
452 X2 1:925d80f479bb
438 Y 2:db815d6d32e6
453 Y 2:db815d6d32e6
439 * Z 3:125c9a1d6df6
454 * Z 3:125c9a1d6df6
440 x y 2:db815d6d32e6
455 x y 2:db815d6d32e6
441 $ echo "Ican'thasformatedlines" >> .hg/bookmarks
456 $ echo "Ican'thasformatedlines" >> .hg/bookmarks
442 $ hg bookmarks
457 $ hg bookmarks
443 malformed line in .hg/bookmarks: "Ican'thasformatedlines"
458 malformed line in .hg/bookmarks: "Ican'thasformatedlines"
444 X2 1:925d80f479bb
459 X2 1:925d80f479bb
445 Y 2:db815d6d32e6
460 Y 2:db815d6d32e6
446 * Z 3:125c9a1d6df6
461 * Z 3:125c9a1d6df6
447 x y 2:db815d6d32e6
462 x y 2:db815d6d32e6
448
463
449 test missing revisions
464 test missing revisions
450
465
451 $ echo "925d80f479bc z" > .hg/bookmarks
466 $ echo "925d80f479bc z" > .hg/bookmarks
452 $ hg book
467 $ hg book
453 no bookmarks set
468 no bookmarks set
454
469
455 test stripping a non-checked-out but bookmarked revision
470 test stripping a non-checked-out but bookmarked revision
456
471
457 $ hg --config extensions.graphlog= log --graph
472 $ hg --config extensions.graphlog= log --graph
458 o changeset: 4:9ba5f110a0b3
473 o changeset: 4:9ba5f110a0b3
459 | branch: test
474 | branch: test
460 | tag: tip
475 | tag: tip
461 | parent: 2:db815d6d32e6
476 | parent: 2:db815d6d32e6
462 | user: test
477 | user: test
463 | date: Thu Jan 01 00:00:00 1970 +0000
478 | date: Thu Jan 01 00:00:00 1970 +0000
464 | summary: y
479 | summary: y
465 |
480 |
466 | @ changeset: 3:125c9a1d6df6
481 | @ changeset: 3:125c9a1d6df6
467 |/ user: test
482 |/ user: test
468 | date: Thu Jan 01 00:00:00 1970 +0000
483 | date: Thu Jan 01 00:00:00 1970 +0000
469 | summary: x
484 | summary: x
470 |
485 |
471 o changeset: 2:db815d6d32e6
486 o changeset: 2:db815d6d32e6
472 | parent: 0:f7b1eb17ad24
487 | parent: 0:f7b1eb17ad24
473 | user: test
488 | user: test
474 | date: Thu Jan 01 00:00:00 1970 +0000
489 | date: Thu Jan 01 00:00:00 1970 +0000
475 | summary: 2
490 | summary: 2
476 |
491 |
477 | o changeset: 1:925d80f479bb
492 | o changeset: 1:925d80f479bb
478 |/ user: test
493 |/ user: test
479 | date: Thu Jan 01 00:00:00 1970 +0000
494 | date: Thu Jan 01 00:00:00 1970 +0000
480 | summary: 1
495 | summary: 1
481 |
496 |
482 o changeset: 0:f7b1eb17ad24
497 o changeset: 0:f7b1eb17ad24
483 user: test
498 user: test
484 date: Thu Jan 01 00:00:00 1970 +0000
499 date: Thu Jan 01 00:00:00 1970 +0000
485 summary: 0
500 summary: 0
486
501
487 $ hg book should-end-on-two
502 $ hg book should-end-on-two
488 $ hg co --clean 4
503 $ hg co --clean 4
489 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
504 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
490 $ hg book four
505 $ hg book four
491 $ hg --config extensions.mq= strip 3
506 $ hg --config extensions.mq= strip 3
492 saved backup bundle to * (glob)
507 saved backup bundle to * (glob)
493 should-end-on-two should end up pointing to revision 2, as that's the
508 should-end-on-two should end up pointing to revision 2, as that's the
494 tipmost surviving ancestor of the stripped revision.
509 tipmost surviving ancestor of the stripped revision.
495 $ hg --config extensions.graphlog= log --graph
510 $ hg --config extensions.graphlog= log --graph
496 @ changeset: 3:9ba5f110a0b3
511 @ changeset: 3:9ba5f110a0b3
497 | branch: test
512 | branch: test
498 | bookmark: four
513 | bookmark: four
499 | tag: tip
514 | tag: tip
500 | user: test
515 | user: test
501 | date: Thu Jan 01 00:00:00 1970 +0000
516 | date: Thu Jan 01 00:00:00 1970 +0000
502 | summary: y
517 | summary: y
503 |
518 |
504 o changeset: 2:db815d6d32e6
519 o changeset: 2:db815d6d32e6
505 | bookmark: should-end-on-two
520 | bookmark: should-end-on-two
506 | parent: 0:f7b1eb17ad24
521 | parent: 0:f7b1eb17ad24
507 | user: test
522 | user: test
508 | date: Thu Jan 01 00:00:00 1970 +0000
523 | date: Thu Jan 01 00:00:00 1970 +0000
509 | summary: 2
524 | summary: 2
510 |
525 |
511 | o changeset: 1:925d80f479bb
526 | o changeset: 1:925d80f479bb
512 |/ user: test
527 |/ user: test
513 | date: Thu Jan 01 00:00:00 1970 +0000
528 | date: Thu Jan 01 00:00:00 1970 +0000
514 | summary: 1
529 | summary: 1
515 |
530 |
516 o changeset: 0:f7b1eb17ad24
531 o changeset: 0:f7b1eb17ad24
517 user: test
532 user: test
518 date: Thu Jan 01 00:00:00 1970 +0000
533 date: Thu Jan 01 00:00:00 1970 +0000
519 summary: 0
534 summary: 0
520
535
General Comments 0
You need to be logged in to leave comments. Login now