##// END OF EJS Templates
bookmarks: accept explicit -r 'wdir()' when adding new bookmarks (issue6218)...
Yuya Nishihara -
r43999:a80d5dde default
parent child Browse files
Show More
@@ -1,1046 +1,1051 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 __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import errno
10 import errno
11 import struct
11 import struct
12
12
13 from .i18n import _
13 from .i18n import _
14 from .node import (
14 from .node import (
15 bin,
15 bin,
16 hex,
16 hex,
17 short,
17 short,
18 wdirid,
18 wdirid,
19 )
19 )
20 from .pycompat import getattr
20 from .pycompat import getattr
21 from . import (
21 from . import (
22 encoding,
22 encoding,
23 error,
23 error,
24 obsutil,
24 obsutil,
25 pycompat,
25 pycompat,
26 scmutil,
26 scmutil,
27 txnutil,
27 txnutil,
28 util,
28 util,
29 )
29 )
30
30
31 # label constants
31 # label constants
32 # until 3.5, bookmarks.current was the advertised name, not
32 # until 3.5, bookmarks.current was the advertised name, not
33 # bookmarks.active, so we must use both to avoid breaking old
33 # bookmarks.active, so we must use both to avoid breaking old
34 # custom styles
34 # custom styles
35 activebookmarklabel = b'bookmarks.active bookmarks.current'
35 activebookmarklabel = b'bookmarks.active bookmarks.current'
36
36
37 BOOKMARKS_IN_STORE_REQUIREMENT = b'bookmarksinstore'
37 BOOKMARKS_IN_STORE_REQUIREMENT = b'bookmarksinstore'
38
38
39
39
40 def bookmarksinstore(repo):
40 def bookmarksinstore(repo):
41 return BOOKMARKS_IN_STORE_REQUIREMENT in repo.requirements
41 return BOOKMARKS_IN_STORE_REQUIREMENT in repo.requirements
42
42
43
43
44 def bookmarksvfs(repo):
44 def bookmarksvfs(repo):
45 return repo.svfs if bookmarksinstore(repo) else repo.vfs
45 return repo.svfs if bookmarksinstore(repo) else repo.vfs
46
46
47
47
48 def _getbkfile(repo):
48 def _getbkfile(repo):
49 """Hook so that extensions that mess with the store can hook bm storage.
49 """Hook so that extensions that mess with the store can hook bm storage.
50
50
51 For core, this just handles wether we should see pending
51 For core, this just handles wether we should see pending
52 bookmarks or the committed ones. Other extensions (like share)
52 bookmarks or the committed ones. Other extensions (like share)
53 may need to tweak this behavior further.
53 may need to tweak this behavior further.
54 """
54 """
55 fp, pending = txnutil.trypending(
55 fp, pending = txnutil.trypending(
56 repo.root, bookmarksvfs(repo), b'bookmarks'
56 repo.root, bookmarksvfs(repo), b'bookmarks'
57 )
57 )
58 return fp
58 return fp
59
59
60
60
61 class bmstore(object):
61 class bmstore(object):
62 r"""Storage for bookmarks.
62 r"""Storage for bookmarks.
63
63
64 This object should do all bookmark-related reads and writes, so
64 This object should do all bookmark-related reads and writes, so
65 that it's fairly simple to replace the storage underlying
65 that it's fairly simple to replace the storage underlying
66 bookmarks without having to clone the logic surrounding
66 bookmarks without having to clone the logic surrounding
67 bookmarks. This type also should manage the active bookmark, if
67 bookmarks. This type also should manage the active bookmark, if
68 any.
68 any.
69
69
70 This particular bmstore implementation stores bookmarks as
70 This particular bmstore implementation stores bookmarks as
71 {hash}\s{name}\n (the same format as localtags) in
71 {hash}\s{name}\n (the same format as localtags) in
72 .hg/bookmarks. The mapping is stored as {name: nodeid}.
72 .hg/bookmarks. The mapping is stored as {name: nodeid}.
73 """
73 """
74
74
75 def __init__(self, repo):
75 def __init__(self, repo):
76 self._repo = repo
76 self._repo = repo
77 self._refmap = refmap = {} # refspec: node
77 self._refmap = refmap = {} # refspec: node
78 self._nodemap = nodemap = {} # node: sorted([refspec, ...])
78 self._nodemap = nodemap = {} # node: sorted([refspec, ...])
79 self._clean = True
79 self._clean = True
80 self._aclean = True
80 self._aclean = True
81 has_node = repo.changelog.index.has_node
81 has_node = repo.changelog.index.has_node
82 tonode = bin # force local lookup
82 tonode = bin # force local lookup
83 try:
83 try:
84 with _getbkfile(repo) as bkfile:
84 with _getbkfile(repo) as bkfile:
85 for line in bkfile:
85 for line in bkfile:
86 line = line.strip()
86 line = line.strip()
87 if not line:
87 if not line:
88 continue
88 continue
89 try:
89 try:
90 sha, refspec = line.split(b' ', 1)
90 sha, refspec = line.split(b' ', 1)
91 node = tonode(sha)
91 node = tonode(sha)
92 if has_node(node):
92 if has_node(node):
93 refspec = encoding.tolocal(refspec)
93 refspec = encoding.tolocal(refspec)
94 refmap[refspec] = node
94 refmap[refspec] = node
95 nrefs = nodemap.get(node)
95 nrefs = nodemap.get(node)
96 if nrefs is None:
96 if nrefs is None:
97 nodemap[node] = [refspec]
97 nodemap[node] = [refspec]
98 else:
98 else:
99 nrefs.append(refspec)
99 nrefs.append(refspec)
100 if nrefs[-2] > refspec:
100 if nrefs[-2] > refspec:
101 # bookmarks weren't sorted before 4.5
101 # bookmarks weren't sorted before 4.5
102 nrefs.sort()
102 nrefs.sort()
103 except (TypeError, ValueError):
103 except (TypeError, ValueError):
104 # TypeError:
104 # TypeError:
105 # - bin(...)
105 # - bin(...)
106 # ValueError:
106 # ValueError:
107 # - node in nm, for non-20-bytes entry
107 # - node in nm, for non-20-bytes entry
108 # - split(...), for string without ' '
108 # - split(...), for string without ' '
109 bookmarkspath = b'.hg/bookmarks'
109 bookmarkspath = b'.hg/bookmarks'
110 if bookmarksinstore(repo):
110 if bookmarksinstore(repo):
111 bookmarkspath = b'.hg/store/bookmarks'
111 bookmarkspath = b'.hg/store/bookmarks'
112 repo.ui.warn(
112 repo.ui.warn(
113 _(b'malformed line in %s: %r\n')
113 _(b'malformed line in %s: %r\n')
114 % (bookmarkspath, pycompat.bytestr(line))
114 % (bookmarkspath, pycompat.bytestr(line))
115 )
115 )
116 except IOError as inst:
116 except IOError as inst:
117 if inst.errno != errno.ENOENT:
117 if inst.errno != errno.ENOENT:
118 raise
118 raise
119 self._active = _readactive(repo, self)
119 self._active = _readactive(repo, self)
120
120
121 @property
121 @property
122 def active(self):
122 def active(self):
123 return self._active
123 return self._active
124
124
125 @active.setter
125 @active.setter
126 def active(self, mark):
126 def active(self, mark):
127 if mark is not None and mark not in self._refmap:
127 if mark is not None and mark not in self._refmap:
128 raise AssertionError(b'bookmark %s does not exist!' % mark)
128 raise AssertionError(b'bookmark %s does not exist!' % mark)
129
129
130 self._active = mark
130 self._active = mark
131 self._aclean = False
131 self._aclean = False
132
132
133 def __len__(self):
133 def __len__(self):
134 return len(self._refmap)
134 return len(self._refmap)
135
135
136 def __iter__(self):
136 def __iter__(self):
137 return iter(self._refmap)
137 return iter(self._refmap)
138
138
139 def iteritems(self):
139 def iteritems(self):
140 return pycompat.iteritems(self._refmap)
140 return pycompat.iteritems(self._refmap)
141
141
142 def items(self):
142 def items(self):
143 return self._refmap.items()
143 return self._refmap.items()
144
144
145 # TODO: maybe rename to allnames()?
145 # TODO: maybe rename to allnames()?
146 def keys(self):
146 def keys(self):
147 return self._refmap.keys()
147 return self._refmap.keys()
148
148
149 # TODO: maybe rename to allnodes()? but nodes would have to be deduplicated
149 # TODO: maybe rename to allnodes()? but nodes would have to be deduplicated
150 # could be self._nodemap.keys()
150 # could be self._nodemap.keys()
151 def values(self):
151 def values(self):
152 return self._refmap.values()
152 return self._refmap.values()
153
153
154 def __contains__(self, mark):
154 def __contains__(self, mark):
155 return mark in self._refmap
155 return mark in self._refmap
156
156
157 def __getitem__(self, mark):
157 def __getitem__(self, mark):
158 return self._refmap[mark]
158 return self._refmap[mark]
159
159
160 def get(self, mark, default=None):
160 def get(self, mark, default=None):
161 return self._refmap.get(mark, default)
161 return self._refmap.get(mark, default)
162
162
163 def _set(self, mark, node):
163 def _set(self, mark, node):
164 self._clean = False
164 self._clean = False
165 if mark in self._refmap:
165 if mark in self._refmap:
166 self._del(mark)
166 self._del(mark)
167 self._refmap[mark] = node
167 self._refmap[mark] = node
168 nrefs = self._nodemap.get(node)
168 nrefs = self._nodemap.get(node)
169 if nrefs is None:
169 if nrefs is None:
170 self._nodemap[node] = [mark]
170 self._nodemap[node] = [mark]
171 else:
171 else:
172 nrefs.append(mark)
172 nrefs.append(mark)
173 nrefs.sort()
173 nrefs.sort()
174
174
175 def _del(self, mark):
175 def _del(self, mark):
176 self._clean = False
176 self._clean = False
177 node = self._refmap.pop(mark)
177 node = self._refmap.pop(mark)
178 nrefs = self._nodemap[node]
178 nrefs = self._nodemap[node]
179 if len(nrefs) == 1:
179 if len(nrefs) == 1:
180 assert nrefs[0] == mark
180 assert nrefs[0] == mark
181 del self._nodemap[node]
181 del self._nodemap[node]
182 else:
182 else:
183 nrefs.remove(mark)
183 nrefs.remove(mark)
184
184
185 def names(self, node):
185 def names(self, node):
186 """Return a sorted list of bookmarks pointing to the specified node"""
186 """Return a sorted list of bookmarks pointing to the specified node"""
187 return self._nodemap.get(node, [])
187 return self._nodemap.get(node, [])
188
188
189 def applychanges(self, repo, tr, changes):
189 def applychanges(self, repo, tr, changes):
190 """Apply a list of changes to bookmarks
190 """Apply a list of changes to bookmarks
191 """
191 """
192 bmchanges = tr.changes.get(b'bookmarks')
192 bmchanges = tr.changes.get(b'bookmarks')
193 for name, node in changes:
193 for name, node in changes:
194 old = self._refmap.get(name)
194 old = self._refmap.get(name)
195 if node is None:
195 if node is None:
196 self._del(name)
196 self._del(name)
197 else:
197 else:
198 self._set(name, node)
198 self._set(name, node)
199 if bmchanges is not None:
199 if bmchanges is not None:
200 # if a previous value exist preserve the "initial" value
200 # if a previous value exist preserve the "initial" value
201 previous = bmchanges.get(name)
201 previous = bmchanges.get(name)
202 if previous is not None:
202 if previous is not None:
203 old = previous[0]
203 old = previous[0]
204 bmchanges[name] = (old, node)
204 bmchanges[name] = (old, node)
205 self._recordchange(tr)
205 self._recordchange(tr)
206
206
207 def _recordchange(self, tr):
207 def _recordchange(self, tr):
208 """record that bookmarks have been changed in a transaction
208 """record that bookmarks have been changed in a transaction
209
209
210 The transaction is then responsible for updating the file content."""
210 The transaction is then responsible for updating the file content."""
211 location = b'' if bookmarksinstore(self._repo) else b'plain'
211 location = b'' if bookmarksinstore(self._repo) else b'plain'
212 tr.addfilegenerator(
212 tr.addfilegenerator(
213 b'bookmarks', (b'bookmarks',), self._write, location=location
213 b'bookmarks', (b'bookmarks',), self._write, location=location
214 )
214 )
215 tr.hookargs[b'bookmark_moved'] = b'1'
215 tr.hookargs[b'bookmark_moved'] = b'1'
216
216
217 def _writerepo(self, repo):
217 def _writerepo(self, repo):
218 """Factored out for extensibility"""
218 """Factored out for extensibility"""
219 rbm = repo._bookmarks
219 rbm = repo._bookmarks
220 if rbm.active not in self._refmap:
220 if rbm.active not in self._refmap:
221 rbm.active = None
221 rbm.active = None
222 rbm._writeactive()
222 rbm._writeactive()
223
223
224 if bookmarksinstore(repo):
224 if bookmarksinstore(repo):
225 vfs = repo.svfs
225 vfs = repo.svfs
226 lock = repo.lock()
226 lock = repo.lock()
227 else:
227 else:
228 vfs = repo.vfs
228 vfs = repo.vfs
229 lock = repo.wlock()
229 lock = repo.wlock()
230 with lock:
230 with lock:
231 with vfs(b'bookmarks', b'w', atomictemp=True, checkambig=True) as f:
231 with vfs(b'bookmarks', b'w', atomictemp=True, checkambig=True) as f:
232 self._write(f)
232 self._write(f)
233
233
234 def _writeactive(self):
234 def _writeactive(self):
235 if self._aclean:
235 if self._aclean:
236 return
236 return
237 with self._repo.wlock():
237 with self._repo.wlock():
238 if self._active is not None:
238 if self._active is not None:
239 with self._repo.vfs(
239 with self._repo.vfs(
240 b'bookmarks.current', b'w', atomictemp=True, checkambig=True
240 b'bookmarks.current', b'w', atomictemp=True, checkambig=True
241 ) as f:
241 ) as f:
242 f.write(encoding.fromlocal(self._active))
242 f.write(encoding.fromlocal(self._active))
243 else:
243 else:
244 self._repo.vfs.tryunlink(b'bookmarks.current')
244 self._repo.vfs.tryunlink(b'bookmarks.current')
245 self._aclean = True
245 self._aclean = True
246
246
247 def _write(self, fp):
247 def _write(self, fp):
248 for name, node in sorted(pycompat.iteritems(self._refmap)):
248 for name, node in sorted(pycompat.iteritems(self._refmap)):
249 fp.write(b"%s %s\n" % (hex(node), encoding.fromlocal(name)))
249 fp.write(b"%s %s\n" % (hex(node), encoding.fromlocal(name)))
250 self._clean = True
250 self._clean = True
251 self._repo.invalidatevolatilesets()
251 self._repo.invalidatevolatilesets()
252
252
253 def expandname(self, bname):
253 def expandname(self, bname):
254 if bname == b'.':
254 if bname == b'.':
255 if self.active:
255 if self.active:
256 return self.active
256 return self.active
257 else:
257 else:
258 raise error.RepoLookupError(_(b"no active bookmark"))
258 raise error.RepoLookupError(_(b"no active bookmark"))
259 return bname
259 return bname
260
260
261 def checkconflict(self, mark, force=False, target=None):
261 def checkconflict(self, mark, force=False, target=None):
262 """check repo for a potential clash of mark with an existing bookmark,
262 """check repo for a potential clash of mark with an existing bookmark,
263 branch, or hash
263 branch, or hash
264
264
265 If target is supplied, then check that we are moving the bookmark
265 If target is supplied, then check that we are moving the bookmark
266 forward.
266 forward.
267
267
268 If force is supplied, then forcibly move the bookmark to a new commit
268 If force is supplied, then forcibly move the bookmark to a new commit
269 regardless if it is a move forward.
269 regardless if it is a move forward.
270
270
271 If divergent bookmark are to be deleted, they will be returned as list.
271 If divergent bookmark are to be deleted, they will be returned as list.
272 """
272 """
273 cur = self._repo[b'.'].node()
273 cur = self._repo[b'.'].node()
274 if mark in self._refmap and not force:
274 if mark in self._refmap and not force:
275 if target:
275 if target:
276 if self._refmap[mark] == target and target == cur:
276 if self._refmap[mark] == target and target == cur:
277 # re-activating a bookmark
277 # re-activating a bookmark
278 return []
278 return []
279 rev = self._repo[target].rev()
279 rev = self._repo[target].rev()
280 anc = self._repo.changelog.ancestors([rev])
280 anc = self._repo.changelog.ancestors([rev])
281 bmctx = self._repo[self[mark]]
281 bmctx = self._repo[self[mark]]
282 divs = [
282 divs = [
283 self._refmap[b]
283 self._refmap[b]
284 for b in self._refmap
284 for b in self._refmap
285 if b.split(b'@', 1)[0] == mark.split(b'@', 1)[0]
285 if b.split(b'@', 1)[0] == mark.split(b'@', 1)[0]
286 ]
286 ]
287
287
288 # allow resolving a single divergent bookmark even if moving
288 # allow resolving a single divergent bookmark even if moving
289 # the bookmark across branches when a revision is specified
289 # the bookmark across branches when a revision is specified
290 # that contains a divergent bookmark
290 # that contains a divergent bookmark
291 if bmctx.rev() not in anc and target in divs:
291 if bmctx.rev() not in anc and target in divs:
292 return divergent2delete(self._repo, [target], mark)
292 return divergent2delete(self._repo, [target], mark)
293
293
294 deletefrom = [
294 deletefrom = [
295 b for b in divs if self._repo[b].rev() in anc or b == target
295 b for b in divs if self._repo[b].rev() in anc or b == target
296 ]
296 ]
297 delbms = divergent2delete(self._repo, deletefrom, mark)
297 delbms = divergent2delete(self._repo, deletefrom, mark)
298 if validdest(self._repo, bmctx, self._repo[target]):
298 if validdest(self._repo, bmctx, self._repo[target]):
299 self._repo.ui.status(
299 self._repo.ui.status(
300 _(b"moving bookmark '%s' forward from %s\n")
300 _(b"moving bookmark '%s' forward from %s\n")
301 % (mark, short(bmctx.node()))
301 % (mark, short(bmctx.node()))
302 )
302 )
303 return delbms
303 return delbms
304 raise error.Abort(
304 raise error.Abort(
305 _(b"bookmark '%s' already exists (use -f to force)") % mark
305 _(b"bookmark '%s' already exists (use -f to force)") % mark
306 )
306 )
307 if (
307 if (
308 mark in self._repo.branchmap()
308 mark in self._repo.branchmap()
309 or mark == self._repo.dirstate.branch()
309 or mark == self._repo.dirstate.branch()
310 ) and not force:
310 ) and not force:
311 raise error.Abort(
311 raise error.Abort(
312 _(b"a bookmark cannot have the name of an existing branch")
312 _(b"a bookmark cannot have the name of an existing branch")
313 )
313 )
314 if len(mark) > 3 and not force:
314 if len(mark) > 3 and not force:
315 try:
315 try:
316 shadowhash = scmutil.isrevsymbol(self._repo, mark)
316 shadowhash = scmutil.isrevsymbol(self._repo, mark)
317 except error.LookupError: # ambiguous identifier
317 except error.LookupError: # ambiguous identifier
318 shadowhash = False
318 shadowhash = False
319 if shadowhash:
319 if shadowhash:
320 self._repo.ui.warn(
320 self._repo.ui.warn(
321 _(
321 _(
322 b"bookmark %s matches a changeset hash\n"
322 b"bookmark %s matches a changeset hash\n"
323 b"(did you leave a -r out of an 'hg bookmark' "
323 b"(did you leave a -r out of an 'hg bookmark' "
324 b"command?)\n"
324 b"command?)\n"
325 )
325 )
326 % mark
326 % mark
327 )
327 )
328 return []
328 return []
329
329
330
330
331 def _readactive(repo, marks):
331 def _readactive(repo, marks):
332 """
332 """
333 Get the active bookmark. We can have an active bookmark that updates
333 Get the active bookmark. We can have an active bookmark that updates
334 itself as we commit. This function returns the name of that bookmark.
334 itself as we commit. This function returns the name of that bookmark.
335 It is stored in .hg/bookmarks.current
335 It is stored in .hg/bookmarks.current
336 """
336 """
337 # No readline() in osutil.posixfile, reading everything is
337 # No readline() in osutil.posixfile, reading everything is
338 # cheap.
338 # cheap.
339 content = repo.vfs.tryread(b'bookmarks.current')
339 content = repo.vfs.tryread(b'bookmarks.current')
340 mark = encoding.tolocal((content.splitlines() or [b''])[0])
340 mark = encoding.tolocal((content.splitlines() or [b''])[0])
341 if mark == b'' or mark not in marks:
341 if mark == b'' or mark not in marks:
342 mark = None
342 mark = None
343 return mark
343 return mark
344
344
345
345
346 def activate(repo, mark):
346 def activate(repo, mark):
347 """
347 """
348 Set the given bookmark to be 'active', meaning that this bookmark will
348 Set the given bookmark to be 'active', meaning that this bookmark will
349 follow new commits that are made.
349 follow new commits that are made.
350 The name is recorded in .hg/bookmarks.current
350 The name is recorded in .hg/bookmarks.current
351 """
351 """
352 repo._bookmarks.active = mark
352 repo._bookmarks.active = mark
353 repo._bookmarks._writeactive()
353 repo._bookmarks._writeactive()
354
354
355
355
356 def deactivate(repo):
356 def deactivate(repo):
357 """
357 """
358 Unset the active bookmark in this repository.
358 Unset the active bookmark in this repository.
359 """
359 """
360 repo._bookmarks.active = None
360 repo._bookmarks.active = None
361 repo._bookmarks._writeactive()
361 repo._bookmarks._writeactive()
362
362
363
363
364 def isactivewdirparent(repo):
364 def isactivewdirparent(repo):
365 """
365 """
366 Tell whether the 'active' bookmark (the one that follows new commits)
366 Tell whether the 'active' bookmark (the one that follows new commits)
367 points to one of the parents of the current working directory (wdir).
367 points to one of the parents of the current working directory (wdir).
368
368
369 While this is normally the case, it can on occasion be false; for example,
369 While this is normally the case, it can on occasion be false; for example,
370 immediately after a pull, the active bookmark can be moved to point
370 immediately after a pull, the active bookmark can be moved to point
371 to a place different than the wdir. This is solved by running `hg update`.
371 to a place different than the wdir. This is solved by running `hg update`.
372 """
372 """
373 mark = repo._activebookmark
373 mark = repo._activebookmark
374 marks = repo._bookmarks
374 marks = repo._bookmarks
375 parents = [p.node() for p in repo[None].parents()]
375 parents = [p.node() for p in repo[None].parents()]
376 return mark in marks and marks[mark] in parents
376 return mark in marks and marks[mark] in parents
377
377
378
378
379 def divergent2delete(repo, deletefrom, bm):
379 def divergent2delete(repo, deletefrom, bm):
380 """find divergent versions of bm on nodes in deletefrom.
380 """find divergent versions of bm on nodes in deletefrom.
381
381
382 the list of bookmark to delete."""
382 the list of bookmark to delete."""
383 todelete = []
383 todelete = []
384 marks = repo._bookmarks
384 marks = repo._bookmarks
385 divergent = [
385 divergent = [
386 b for b in marks if b.split(b'@', 1)[0] == bm.split(b'@', 1)[0]
386 b for b in marks if b.split(b'@', 1)[0] == bm.split(b'@', 1)[0]
387 ]
387 ]
388 for mark in divergent:
388 for mark in divergent:
389 if mark == b'@' or b'@' not in mark:
389 if mark == b'@' or b'@' not in mark:
390 # can't be divergent by definition
390 # can't be divergent by definition
391 continue
391 continue
392 if mark and marks[mark] in deletefrom:
392 if mark and marks[mark] in deletefrom:
393 if mark != bm:
393 if mark != bm:
394 todelete.append(mark)
394 todelete.append(mark)
395 return todelete
395 return todelete
396
396
397
397
398 def headsforactive(repo):
398 def headsforactive(repo):
399 """Given a repo with an active bookmark, return divergent bookmark nodes.
399 """Given a repo with an active bookmark, return divergent bookmark nodes.
400
400
401 Args:
401 Args:
402 repo: A repository with an active bookmark.
402 repo: A repository with an active bookmark.
403
403
404 Returns:
404 Returns:
405 A list of binary node ids that is the full list of other
405 A list of binary node ids that is the full list of other
406 revisions with bookmarks divergent from the active bookmark. If
406 revisions with bookmarks divergent from the active bookmark. If
407 there were no divergent bookmarks, then this list will contain
407 there were no divergent bookmarks, then this list will contain
408 only one entry.
408 only one entry.
409 """
409 """
410 if not repo._activebookmark:
410 if not repo._activebookmark:
411 raise ValueError(
411 raise ValueError(
412 b'headsforactive() only makes sense with an active bookmark'
412 b'headsforactive() only makes sense with an active bookmark'
413 )
413 )
414 name = repo._activebookmark.split(b'@', 1)[0]
414 name = repo._activebookmark.split(b'@', 1)[0]
415 heads = []
415 heads = []
416 for mark, n in pycompat.iteritems(repo._bookmarks):
416 for mark, n in pycompat.iteritems(repo._bookmarks):
417 if mark.split(b'@', 1)[0] == name:
417 if mark.split(b'@', 1)[0] == name:
418 heads.append(n)
418 heads.append(n)
419 return heads
419 return heads
420
420
421
421
422 def calculateupdate(ui, repo):
422 def calculateupdate(ui, repo):
423 '''Return a tuple (activemark, movemarkfrom) indicating the active bookmark
423 '''Return a tuple (activemark, movemarkfrom) indicating the active bookmark
424 and where to move the active bookmark from, if needed.'''
424 and where to move the active bookmark from, if needed.'''
425 checkout, movemarkfrom = None, None
425 checkout, movemarkfrom = None, None
426 activemark = repo._activebookmark
426 activemark = repo._activebookmark
427 if isactivewdirparent(repo):
427 if isactivewdirparent(repo):
428 movemarkfrom = repo[b'.'].node()
428 movemarkfrom = repo[b'.'].node()
429 elif activemark:
429 elif activemark:
430 ui.status(_(b"updating to active bookmark %s\n") % activemark)
430 ui.status(_(b"updating to active bookmark %s\n") % activemark)
431 checkout = activemark
431 checkout = activemark
432 return (checkout, movemarkfrom)
432 return (checkout, movemarkfrom)
433
433
434
434
435 def update(repo, parents, node):
435 def update(repo, parents, node):
436 deletefrom = parents
436 deletefrom = parents
437 marks = repo._bookmarks
437 marks = repo._bookmarks
438 active = marks.active
438 active = marks.active
439 if not active:
439 if not active:
440 return False
440 return False
441
441
442 bmchanges = []
442 bmchanges = []
443 if marks[active] in parents:
443 if marks[active] in parents:
444 new = repo[node]
444 new = repo[node]
445 divs = [
445 divs = [
446 repo[marks[b]]
446 repo[marks[b]]
447 for b in marks
447 for b in marks
448 if b.split(b'@', 1)[0] == active.split(b'@', 1)[0]
448 if b.split(b'@', 1)[0] == active.split(b'@', 1)[0]
449 ]
449 ]
450 anc = repo.changelog.ancestors([new.rev()])
450 anc = repo.changelog.ancestors([new.rev()])
451 deletefrom = [b.node() for b in divs if b.rev() in anc or b == new]
451 deletefrom = [b.node() for b in divs if b.rev() in anc or b == new]
452 if validdest(repo, repo[marks[active]], new):
452 if validdest(repo, repo[marks[active]], new):
453 bmchanges.append((active, new.node()))
453 bmchanges.append((active, new.node()))
454
454
455 for bm in divergent2delete(repo, deletefrom, active):
455 for bm in divergent2delete(repo, deletefrom, active):
456 bmchanges.append((bm, None))
456 bmchanges.append((bm, None))
457
457
458 if bmchanges:
458 if bmchanges:
459 with repo.lock(), repo.transaction(b'bookmark') as tr:
459 with repo.lock(), repo.transaction(b'bookmark') as tr:
460 marks.applychanges(repo, tr, bmchanges)
460 marks.applychanges(repo, tr, bmchanges)
461 return bool(bmchanges)
461 return bool(bmchanges)
462
462
463
463
464 def listbinbookmarks(repo):
464 def listbinbookmarks(repo):
465 # We may try to list bookmarks on a repo type that does not
465 # We may try to list bookmarks on a repo type that does not
466 # support it (e.g., statichttprepository).
466 # support it (e.g., statichttprepository).
467 marks = getattr(repo, '_bookmarks', {})
467 marks = getattr(repo, '_bookmarks', {})
468
468
469 hasnode = repo.changelog.hasnode
469 hasnode = repo.changelog.hasnode
470 for k, v in pycompat.iteritems(marks):
470 for k, v in pycompat.iteritems(marks):
471 # don't expose local divergent bookmarks
471 # don't expose local divergent bookmarks
472 if hasnode(v) and (b'@' not in k or k.endswith(b'@')):
472 if hasnode(v) and (b'@' not in k or k.endswith(b'@')):
473 yield k, v
473 yield k, v
474
474
475
475
476 def listbookmarks(repo):
476 def listbookmarks(repo):
477 d = {}
477 d = {}
478 for book, node in listbinbookmarks(repo):
478 for book, node in listbinbookmarks(repo):
479 d[book] = hex(node)
479 d[book] = hex(node)
480 return d
480 return d
481
481
482
482
483 def pushbookmark(repo, key, old, new):
483 def pushbookmark(repo, key, old, new):
484 if bookmarksinstore(repo):
484 if bookmarksinstore(repo):
485 wlock = util.nullcontextmanager()
485 wlock = util.nullcontextmanager()
486 else:
486 else:
487 wlock = repo.wlock()
487 wlock = repo.wlock()
488 with wlock, repo.lock(), repo.transaction(b'bookmarks') as tr:
488 with wlock, repo.lock(), repo.transaction(b'bookmarks') as tr:
489 marks = repo._bookmarks
489 marks = repo._bookmarks
490 existing = hex(marks.get(key, b''))
490 existing = hex(marks.get(key, b''))
491 if existing != old and existing != new:
491 if existing != old and existing != new:
492 return False
492 return False
493 if new == b'':
493 if new == b'':
494 changes = [(key, None)]
494 changes = [(key, None)]
495 else:
495 else:
496 if new not in repo:
496 if new not in repo:
497 return False
497 return False
498 changes = [(key, repo[new].node())]
498 changes = [(key, repo[new].node())]
499 marks.applychanges(repo, tr, changes)
499 marks.applychanges(repo, tr, changes)
500 return True
500 return True
501
501
502
502
503 def comparebookmarks(repo, srcmarks, dstmarks, targets=None):
503 def comparebookmarks(repo, srcmarks, dstmarks, targets=None):
504 '''Compare bookmarks between srcmarks and dstmarks
504 '''Compare bookmarks between srcmarks and dstmarks
505
505
506 This returns tuple "(addsrc, adddst, advsrc, advdst, diverge,
506 This returns tuple "(addsrc, adddst, advsrc, advdst, diverge,
507 differ, invalid)", each are list of bookmarks below:
507 differ, invalid)", each are list of bookmarks below:
508
508
509 :addsrc: added on src side (removed on dst side, perhaps)
509 :addsrc: added on src side (removed on dst side, perhaps)
510 :adddst: added on dst side (removed on src side, perhaps)
510 :adddst: added on dst side (removed on src side, perhaps)
511 :advsrc: advanced on src side
511 :advsrc: advanced on src side
512 :advdst: advanced on dst side
512 :advdst: advanced on dst side
513 :diverge: diverge
513 :diverge: diverge
514 :differ: changed, but changeset referred on src is unknown on dst
514 :differ: changed, but changeset referred on src is unknown on dst
515 :invalid: unknown on both side
515 :invalid: unknown on both side
516 :same: same on both side
516 :same: same on both side
517
517
518 Each elements of lists in result tuple is tuple "(bookmark name,
518 Each elements of lists in result tuple is tuple "(bookmark name,
519 changeset ID on source side, changeset ID on destination
519 changeset ID on source side, changeset ID on destination
520 side)". Each changeset ID is a binary node or None.
520 side)". Each changeset ID is a binary node or None.
521
521
522 Changeset IDs of tuples in "addsrc", "adddst", "differ" or
522 Changeset IDs of tuples in "addsrc", "adddst", "differ" or
523 "invalid" list may be unknown for repo.
523 "invalid" list may be unknown for repo.
524
524
525 If "targets" is specified, only bookmarks listed in it are
525 If "targets" is specified, only bookmarks listed in it are
526 examined.
526 examined.
527 '''
527 '''
528
528
529 if targets:
529 if targets:
530 bset = set(targets)
530 bset = set(targets)
531 else:
531 else:
532 srcmarkset = set(srcmarks)
532 srcmarkset = set(srcmarks)
533 dstmarkset = set(dstmarks)
533 dstmarkset = set(dstmarks)
534 bset = srcmarkset | dstmarkset
534 bset = srcmarkset | dstmarkset
535
535
536 results = ([], [], [], [], [], [], [], [])
536 results = ([], [], [], [], [], [], [], [])
537 addsrc = results[0].append
537 addsrc = results[0].append
538 adddst = results[1].append
538 adddst = results[1].append
539 advsrc = results[2].append
539 advsrc = results[2].append
540 advdst = results[3].append
540 advdst = results[3].append
541 diverge = results[4].append
541 diverge = results[4].append
542 differ = results[5].append
542 differ = results[5].append
543 invalid = results[6].append
543 invalid = results[6].append
544 same = results[7].append
544 same = results[7].append
545
545
546 for b in sorted(bset):
546 for b in sorted(bset):
547 if b not in srcmarks:
547 if b not in srcmarks:
548 if b in dstmarks:
548 if b in dstmarks:
549 adddst((b, None, dstmarks[b]))
549 adddst((b, None, dstmarks[b]))
550 else:
550 else:
551 invalid((b, None, None))
551 invalid((b, None, None))
552 elif b not in dstmarks:
552 elif b not in dstmarks:
553 addsrc((b, srcmarks[b], None))
553 addsrc((b, srcmarks[b], None))
554 else:
554 else:
555 scid = srcmarks[b]
555 scid = srcmarks[b]
556 dcid = dstmarks[b]
556 dcid = dstmarks[b]
557 if scid == dcid:
557 if scid == dcid:
558 same((b, scid, dcid))
558 same((b, scid, dcid))
559 elif scid in repo and dcid in repo:
559 elif scid in repo and dcid in repo:
560 sctx = repo[scid]
560 sctx = repo[scid]
561 dctx = repo[dcid]
561 dctx = repo[dcid]
562 if sctx.rev() < dctx.rev():
562 if sctx.rev() < dctx.rev():
563 if validdest(repo, sctx, dctx):
563 if validdest(repo, sctx, dctx):
564 advdst((b, scid, dcid))
564 advdst((b, scid, dcid))
565 else:
565 else:
566 diverge((b, scid, dcid))
566 diverge((b, scid, dcid))
567 else:
567 else:
568 if validdest(repo, dctx, sctx):
568 if validdest(repo, dctx, sctx):
569 advsrc((b, scid, dcid))
569 advsrc((b, scid, dcid))
570 else:
570 else:
571 diverge((b, scid, dcid))
571 diverge((b, scid, dcid))
572 else:
572 else:
573 # it is too expensive to examine in detail, in this case
573 # it is too expensive to examine in detail, in this case
574 differ((b, scid, dcid))
574 differ((b, scid, dcid))
575
575
576 return results
576 return results
577
577
578
578
579 def _diverge(ui, b, path, localmarks, remotenode):
579 def _diverge(ui, b, path, localmarks, remotenode):
580 '''Return appropriate diverged bookmark for specified ``path``
580 '''Return appropriate diverged bookmark for specified ``path``
581
581
582 This returns None, if it is failed to assign any divergent
582 This returns None, if it is failed to assign any divergent
583 bookmark name.
583 bookmark name.
584
584
585 This reuses already existing one with "@number" suffix, if it
585 This reuses already existing one with "@number" suffix, if it
586 refers ``remotenode``.
586 refers ``remotenode``.
587 '''
587 '''
588 if b == b'@':
588 if b == b'@':
589 b = b''
589 b = b''
590 # try to use an @pathalias suffix
590 # try to use an @pathalias suffix
591 # if an @pathalias already exists, we overwrite (update) it
591 # if an @pathalias already exists, we overwrite (update) it
592 if path.startswith(b"file:"):
592 if path.startswith(b"file:"):
593 path = util.url(path).path
593 path = util.url(path).path
594 for p, u in ui.configitems(b"paths"):
594 for p, u in ui.configitems(b"paths"):
595 if u.startswith(b"file:"):
595 if u.startswith(b"file:"):
596 u = util.url(u).path
596 u = util.url(u).path
597 if path == u:
597 if path == u:
598 return b'%s@%s' % (b, p)
598 return b'%s@%s' % (b, p)
599
599
600 # assign a unique "@number" suffix newly
600 # assign a unique "@number" suffix newly
601 for x in range(1, 100):
601 for x in range(1, 100):
602 n = b'%s@%d' % (b, x)
602 n = b'%s@%d' % (b, x)
603 if n not in localmarks or localmarks[n] == remotenode:
603 if n not in localmarks or localmarks[n] == remotenode:
604 return n
604 return n
605
605
606 return None
606 return None
607
607
608
608
609 def unhexlifybookmarks(marks):
609 def unhexlifybookmarks(marks):
610 binremotemarks = {}
610 binremotemarks = {}
611 for name, node in marks.items():
611 for name, node in marks.items():
612 binremotemarks[name] = bin(node)
612 binremotemarks[name] = bin(node)
613 return binremotemarks
613 return binremotemarks
614
614
615
615
616 _binaryentry = struct.Struct(b'>20sH')
616 _binaryentry = struct.Struct(b'>20sH')
617
617
618
618
619 def binaryencode(bookmarks):
619 def binaryencode(bookmarks):
620 """encode a '(bookmark, node)' iterable into a binary stream
620 """encode a '(bookmark, node)' iterable into a binary stream
621
621
622 the binary format is:
622 the binary format is:
623
623
624 <node><bookmark-length><bookmark-name>
624 <node><bookmark-length><bookmark-name>
625
625
626 :node: is a 20 bytes binary node,
626 :node: is a 20 bytes binary node,
627 :bookmark-length: an unsigned short,
627 :bookmark-length: an unsigned short,
628 :bookmark-name: the name of the bookmark (of length <bookmark-length>)
628 :bookmark-name: the name of the bookmark (of length <bookmark-length>)
629
629
630 wdirid (all bits set) will be used as a special value for "missing"
630 wdirid (all bits set) will be used as a special value for "missing"
631 """
631 """
632 binarydata = []
632 binarydata = []
633 for book, node in bookmarks:
633 for book, node in bookmarks:
634 if not node: # None or ''
634 if not node: # None or ''
635 node = wdirid
635 node = wdirid
636 binarydata.append(_binaryentry.pack(node, len(book)))
636 binarydata.append(_binaryentry.pack(node, len(book)))
637 binarydata.append(book)
637 binarydata.append(book)
638 return b''.join(binarydata)
638 return b''.join(binarydata)
639
639
640
640
641 def binarydecode(stream):
641 def binarydecode(stream):
642 """decode a binary stream into an '(bookmark, node)' iterable
642 """decode a binary stream into an '(bookmark, node)' iterable
643
643
644 the binary format is:
644 the binary format is:
645
645
646 <node><bookmark-length><bookmark-name>
646 <node><bookmark-length><bookmark-name>
647
647
648 :node: is a 20 bytes binary node,
648 :node: is a 20 bytes binary node,
649 :bookmark-length: an unsigned short,
649 :bookmark-length: an unsigned short,
650 :bookmark-name: the name of the bookmark (of length <bookmark-length>))
650 :bookmark-name: the name of the bookmark (of length <bookmark-length>))
651
651
652 wdirid (all bits set) will be used as a special value for "missing"
652 wdirid (all bits set) will be used as a special value for "missing"
653 """
653 """
654 entrysize = _binaryentry.size
654 entrysize = _binaryentry.size
655 books = []
655 books = []
656 while True:
656 while True:
657 entry = stream.read(entrysize)
657 entry = stream.read(entrysize)
658 if len(entry) < entrysize:
658 if len(entry) < entrysize:
659 if entry:
659 if entry:
660 raise error.Abort(_(b'bad bookmark stream'))
660 raise error.Abort(_(b'bad bookmark stream'))
661 break
661 break
662 node, length = _binaryentry.unpack(entry)
662 node, length = _binaryentry.unpack(entry)
663 bookmark = stream.read(length)
663 bookmark = stream.read(length)
664 if len(bookmark) < length:
664 if len(bookmark) < length:
665 if entry:
665 if entry:
666 raise error.Abort(_(b'bad bookmark stream'))
666 raise error.Abort(_(b'bad bookmark stream'))
667 if node == wdirid:
667 if node == wdirid:
668 node = None
668 node = None
669 books.append((bookmark, node))
669 books.append((bookmark, node))
670 return books
670 return books
671
671
672
672
673 def updatefromremote(ui, repo, remotemarks, path, trfunc, explicit=()):
673 def updatefromremote(ui, repo, remotemarks, path, trfunc, explicit=()):
674 ui.debug(b"checking for updated bookmarks\n")
674 ui.debug(b"checking for updated bookmarks\n")
675 localmarks = repo._bookmarks
675 localmarks = repo._bookmarks
676 (
676 (
677 addsrc,
677 addsrc,
678 adddst,
678 adddst,
679 advsrc,
679 advsrc,
680 advdst,
680 advdst,
681 diverge,
681 diverge,
682 differ,
682 differ,
683 invalid,
683 invalid,
684 same,
684 same,
685 ) = comparebookmarks(repo, remotemarks, localmarks)
685 ) = comparebookmarks(repo, remotemarks, localmarks)
686
686
687 status = ui.status
687 status = ui.status
688 warn = ui.warn
688 warn = ui.warn
689 if ui.configbool(b'ui', b'quietbookmarkmove'):
689 if ui.configbool(b'ui', b'quietbookmarkmove'):
690 status = warn = ui.debug
690 status = warn = ui.debug
691
691
692 explicit = set(explicit)
692 explicit = set(explicit)
693 changed = []
693 changed = []
694 for b, scid, dcid in addsrc:
694 for b, scid, dcid in addsrc:
695 if scid in repo: # add remote bookmarks for changes we already have
695 if scid in repo: # add remote bookmarks for changes we already have
696 changed.append(
696 changed.append(
697 (b, scid, status, _(b"adding remote bookmark %s\n") % b)
697 (b, scid, status, _(b"adding remote bookmark %s\n") % b)
698 )
698 )
699 elif b in explicit:
699 elif b in explicit:
700 explicit.remove(b)
700 explicit.remove(b)
701 ui.warn(
701 ui.warn(
702 _(b"remote bookmark %s points to locally missing %s\n")
702 _(b"remote bookmark %s points to locally missing %s\n")
703 % (b, hex(scid)[:12])
703 % (b, hex(scid)[:12])
704 )
704 )
705
705
706 for b, scid, dcid in advsrc:
706 for b, scid, dcid in advsrc:
707 changed.append((b, scid, status, _(b"updating bookmark %s\n") % b))
707 changed.append((b, scid, status, _(b"updating bookmark %s\n") % b))
708 # remove normal movement from explicit set
708 # remove normal movement from explicit set
709 explicit.difference_update(d[0] for d in changed)
709 explicit.difference_update(d[0] for d in changed)
710
710
711 for b, scid, dcid in diverge:
711 for b, scid, dcid in diverge:
712 if b in explicit:
712 if b in explicit:
713 explicit.discard(b)
713 explicit.discard(b)
714 changed.append((b, scid, status, _(b"importing bookmark %s\n") % b))
714 changed.append((b, scid, status, _(b"importing bookmark %s\n") % b))
715 else:
715 else:
716 db = _diverge(ui, b, path, localmarks, scid)
716 db = _diverge(ui, b, path, localmarks, scid)
717 if db:
717 if db:
718 changed.append(
718 changed.append(
719 (
719 (
720 db,
720 db,
721 scid,
721 scid,
722 warn,
722 warn,
723 _(b"divergent bookmark %s stored as %s\n") % (b, db),
723 _(b"divergent bookmark %s stored as %s\n") % (b, db),
724 )
724 )
725 )
725 )
726 else:
726 else:
727 warn(
727 warn(
728 _(
728 _(
729 b"warning: failed to assign numbered name "
729 b"warning: failed to assign numbered name "
730 b"to divergent bookmark %s\n"
730 b"to divergent bookmark %s\n"
731 )
731 )
732 % b
732 % b
733 )
733 )
734 for b, scid, dcid in adddst + advdst:
734 for b, scid, dcid in adddst + advdst:
735 if b in explicit:
735 if b in explicit:
736 explicit.discard(b)
736 explicit.discard(b)
737 changed.append((b, scid, status, _(b"importing bookmark %s\n") % b))
737 changed.append((b, scid, status, _(b"importing bookmark %s\n") % b))
738 for b, scid, dcid in differ:
738 for b, scid, dcid in differ:
739 if b in explicit:
739 if b in explicit:
740 explicit.remove(b)
740 explicit.remove(b)
741 ui.warn(
741 ui.warn(
742 _(b"remote bookmark %s points to locally missing %s\n")
742 _(b"remote bookmark %s points to locally missing %s\n")
743 % (b, hex(scid)[:12])
743 % (b, hex(scid)[:12])
744 )
744 )
745
745
746 if changed:
746 if changed:
747 tr = trfunc()
747 tr = trfunc()
748 changes = []
748 changes = []
749 for b, node, writer, msg in sorted(changed):
749 for b, node, writer, msg in sorted(changed):
750 changes.append((b, node))
750 changes.append((b, node))
751 writer(msg)
751 writer(msg)
752 localmarks.applychanges(repo, tr, changes)
752 localmarks.applychanges(repo, tr, changes)
753
753
754
754
755 def incoming(ui, repo, peer):
755 def incoming(ui, repo, peer):
756 '''Show bookmarks incoming from other to repo
756 '''Show bookmarks incoming from other to repo
757 '''
757 '''
758 ui.status(_(b"searching for changed bookmarks\n"))
758 ui.status(_(b"searching for changed bookmarks\n"))
759
759
760 with peer.commandexecutor() as e:
760 with peer.commandexecutor() as e:
761 remotemarks = unhexlifybookmarks(
761 remotemarks = unhexlifybookmarks(
762 e.callcommand(b'listkeys', {b'namespace': b'bookmarks',}).result()
762 e.callcommand(b'listkeys', {b'namespace': b'bookmarks',}).result()
763 )
763 )
764
764
765 r = comparebookmarks(repo, remotemarks, repo._bookmarks)
765 r = comparebookmarks(repo, remotemarks, repo._bookmarks)
766 addsrc, adddst, advsrc, advdst, diverge, differ, invalid, same = r
766 addsrc, adddst, advsrc, advdst, diverge, differ, invalid, same = r
767
767
768 incomings = []
768 incomings = []
769 if ui.debugflag:
769 if ui.debugflag:
770 getid = lambda id: id
770 getid = lambda id: id
771 else:
771 else:
772 getid = lambda id: id[:12]
772 getid = lambda id: id[:12]
773 if ui.verbose:
773 if ui.verbose:
774
774
775 def add(b, id, st):
775 def add(b, id, st):
776 incomings.append(b" %-25s %s %s\n" % (b, getid(id), st))
776 incomings.append(b" %-25s %s %s\n" % (b, getid(id), st))
777
777
778 else:
778 else:
779
779
780 def add(b, id, st):
780 def add(b, id, st):
781 incomings.append(b" %-25s %s\n" % (b, getid(id)))
781 incomings.append(b" %-25s %s\n" % (b, getid(id)))
782
782
783 for b, scid, dcid in addsrc:
783 for b, scid, dcid in addsrc:
784 # i18n: "added" refers to a bookmark
784 # i18n: "added" refers to a bookmark
785 add(b, hex(scid), _(b'added'))
785 add(b, hex(scid), _(b'added'))
786 for b, scid, dcid in advsrc:
786 for b, scid, dcid in advsrc:
787 # i18n: "advanced" refers to a bookmark
787 # i18n: "advanced" refers to a bookmark
788 add(b, hex(scid), _(b'advanced'))
788 add(b, hex(scid), _(b'advanced'))
789 for b, scid, dcid in diverge:
789 for b, scid, dcid in diverge:
790 # i18n: "diverged" refers to a bookmark
790 # i18n: "diverged" refers to a bookmark
791 add(b, hex(scid), _(b'diverged'))
791 add(b, hex(scid), _(b'diverged'))
792 for b, scid, dcid in differ:
792 for b, scid, dcid in differ:
793 # i18n: "changed" refers to a bookmark
793 # i18n: "changed" refers to a bookmark
794 add(b, hex(scid), _(b'changed'))
794 add(b, hex(scid), _(b'changed'))
795
795
796 if not incomings:
796 if not incomings:
797 ui.status(_(b"no changed bookmarks found\n"))
797 ui.status(_(b"no changed bookmarks found\n"))
798 return 1
798 return 1
799
799
800 for s in sorted(incomings):
800 for s in sorted(incomings):
801 ui.write(s)
801 ui.write(s)
802
802
803 return 0
803 return 0
804
804
805
805
806 def outgoing(ui, repo, other):
806 def outgoing(ui, repo, other):
807 '''Show bookmarks outgoing from repo to other
807 '''Show bookmarks outgoing from repo to other
808 '''
808 '''
809 ui.status(_(b"searching for changed bookmarks\n"))
809 ui.status(_(b"searching for changed bookmarks\n"))
810
810
811 remotemarks = unhexlifybookmarks(other.listkeys(b'bookmarks'))
811 remotemarks = unhexlifybookmarks(other.listkeys(b'bookmarks'))
812 r = comparebookmarks(repo, repo._bookmarks, remotemarks)
812 r = comparebookmarks(repo, repo._bookmarks, remotemarks)
813 addsrc, adddst, advsrc, advdst, diverge, differ, invalid, same = r
813 addsrc, adddst, advsrc, advdst, diverge, differ, invalid, same = r
814
814
815 outgoings = []
815 outgoings = []
816 if ui.debugflag:
816 if ui.debugflag:
817 getid = lambda id: id
817 getid = lambda id: id
818 else:
818 else:
819 getid = lambda id: id[:12]
819 getid = lambda id: id[:12]
820 if ui.verbose:
820 if ui.verbose:
821
821
822 def add(b, id, st):
822 def add(b, id, st):
823 outgoings.append(b" %-25s %s %s\n" % (b, getid(id), st))
823 outgoings.append(b" %-25s %s %s\n" % (b, getid(id), st))
824
824
825 else:
825 else:
826
826
827 def add(b, id, st):
827 def add(b, id, st):
828 outgoings.append(b" %-25s %s\n" % (b, getid(id)))
828 outgoings.append(b" %-25s %s\n" % (b, getid(id)))
829
829
830 for b, scid, dcid in addsrc:
830 for b, scid, dcid in addsrc:
831 # i18n: "added refers to a bookmark
831 # i18n: "added refers to a bookmark
832 add(b, hex(scid), _(b'added'))
832 add(b, hex(scid), _(b'added'))
833 for b, scid, dcid in adddst:
833 for b, scid, dcid in adddst:
834 # i18n: "deleted" refers to a bookmark
834 # i18n: "deleted" refers to a bookmark
835 add(b, b' ' * 40, _(b'deleted'))
835 add(b, b' ' * 40, _(b'deleted'))
836 for b, scid, dcid in advsrc:
836 for b, scid, dcid in advsrc:
837 # i18n: "advanced" refers to a bookmark
837 # i18n: "advanced" refers to a bookmark
838 add(b, hex(scid), _(b'advanced'))
838 add(b, hex(scid), _(b'advanced'))
839 for b, scid, dcid in diverge:
839 for b, scid, dcid in diverge:
840 # i18n: "diverged" refers to a bookmark
840 # i18n: "diverged" refers to a bookmark
841 add(b, hex(scid), _(b'diverged'))
841 add(b, hex(scid), _(b'diverged'))
842 for b, scid, dcid in differ:
842 for b, scid, dcid in differ:
843 # i18n: "changed" refers to a bookmark
843 # i18n: "changed" refers to a bookmark
844 add(b, hex(scid), _(b'changed'))
844 add(b, hex(scid), _(b'changed'))
845
845
846 if not outgoings:
846 if not outgoings:
847 ui.status(_(b"no changed bookmarks found\n"))
847 ui.status(_(b"no changed bookmarks found\n"))
848 return 1
848 return 1
849
849
850 for s in sorted(outgoings):
850 for s in sorted(outgoings):
851 ui.write(s)
851 ui.write(s)
852
852
853 return 0
853 return 0
854
854
855
855
856 def summary(repo, peer):
856 def summary(repo, peer):
857 '''Compare bookmarks between repo and other for "hg summary" output
857 '''Compare bookmarks between repo and other for "hg summary" output
858
858
859 This returns "(# of incoming, # of outgoing)" tuple.
859 This returns "(# of incoming, # of outgoing)" tuple.
860 '''
860 '''
861 with peer.commandexecutor() as e:
861 with peer.commandexecutor() as e:
862 remotemarks = unhexlifybookmarks(
862 remotemarks = unhexlifybookmarks(
863 e.callcommand(b'listkeys', {b'namespace': b'bookmarks',}).result()
863 e.callcommand(b'listkeys', {b'namespace': b'bookmarks',}).result()
864 )
864 )
865
865
866 r = comparebookmarks(repo, remotemarks, repo._bookmarks)
866 r = comparebookmarks(repo, remotemarks, repo._bookmarks)
867 addsrc, adddst, advsrc, advdst, diverge, differ, invalid, same = r
867 addsrc, adddst, advsrc, advdst, diverge, differ, invalid, same = r
868 return (len(addsrc), len(adddst))
868 return (len(addsrc), len(adddst))
869
869
870
870
871 def validdest(repo, old, new):
871 def validdest(repo, old, new):
872 """Is the new bookmark destination a valid update from the old one"""
872 """Is the new bookmark destination a valid update from the old one"""
873 repo = repo.unfiltered()
873 repo = repo.unfiltered()
874 if old == new:
874 if old == new:
875 # Old == new -> nothing to update.
875 # Old == new -> nothing to update.
876 return False
876 return False
877 elif not old:
877 elif not old:
878 # old is nullrev, anything is valid.
878 # old is nullrev, anything is valid.
879 # (new != nullrev has been excluded by the previous check)
879 # (new != nullrev has been excluded by the previous check)
880 return True
880 return True
881 elif repo.obsstore:
881 elif repo.obsstore:
882 return new.node() in obsutil.foreground(repo, [old.node()])
882 return new.node() in obsutil.foreground(repo, [old.node()])
883 else:
883 else:
884 # still an independent clause as it is lazier (and therefore faster)
884 # still an independent clause as it is lazier (and therefore faster)
885 return old.isancestorof(new)
885 return old.isancestorof(new)
886
886
887
887
888 def checkformat(repo, mark):
888 def checkformat(repo, mark):
889 """return a valid version of a potential bookmark name
889 """return a valid version of a potential bookmark name
890
890
891 Raises an abort error if the bookmark name is not valid.
891 Raises an abort error if the bookmark name is not valid.
892 """
892 """
893 mark = mark.strip()
893 mark = mark.strip()
894 if not mark:
894 if not mark:
895 raise error.Abort(
895 raise error.Abort(
896 _(b"bookmark names cannot consist entirely of whitespace")
896 _(b"bookmark names cannot consist entirely of whitespace")
897 )
897 )
898 scmutil.checknewlabel(repo, mark, b'bookmark')
898 scmutil.checknewlabel(repo, mark, b'bookmark')
899 return mark
899 return mark
900
900
901
901
902 def delete(repo, tr, names):
902 def delete(repo, tr, names):
903 """remove a mark from the bookmark store
903 """remove a mark from the bookmark store
904
904
905 Raises an abort error if mark does not exist.
905 Raises an abort error if mark does not exist.
906 """
906 """
907 marks = repo._bookmarks
907 marks = repo._bookmarks
908 changes = []
908 changes = []
909 for mark in names:
909 for mark in names:
910 if mark not in marks:
910 if mark not in marks:
911 raise error.Abort(_(b"bookmark '%s' does not exist") % mark)
911 raise error.Abort(_(b"bookmark '%s' does not exist") % mark)
912 if mark == repo._activebookmark:
912 if mark == repo._activebookmark:
913 deactivate(repo)
913 deactivate(repo)
914 changes.append((mark, None))
914 changes.append((mark, None))
915 marks.applychanges(repo, tr, changes)
915 marks.applychanges(repo, tr, changes)
916
916
917
917
918 def rename(repo, tr, old, new, force=False, inactive=False):
918 def rename(repo, tr, old, new, force=False, inactive=False):
919 """rename a bookmark from old to new
919 """rename a bookmark from old to new
920
920
921 If force is specified, then the new name can overwrite an existing
921 If force is specified, then the new name can overwrite an existing
922 bookmark.
922 bookmark.
923
923
924 If inactive is specified, then do not activate the new bookmark.
924 If inactive is specified, then do not activate the new bookmark.
925
925
926 Raises an abort error if old is not in the bookmark store.
926 Raises an abort error if old is not in the bookmark store.
927 """
927 """
928 marks = repo._bookmarks
928 marks = repo._bookmarks
929 mark = checkformat(repo, new)
929 mark = checkformat(repo, new)
930 if old not in marks:
930 if old not in marks:
931 raise error.Abort(_(b"bookmark '%s' does not exist") % old)
931 raise error.Abort(_(b"bookmark '%s' does not exist") % old)
932 changes = []
932 changes = []
933 for bm in marks.checkconflict(mark, force):
933 for bm in marks.checkconflict(mark, force):
934 changes.append((bm, None))
934 changes.append((bm, None))
935 changes.extend([(mark, marks[old]), (old, None)])
935 changes.extend([(mark, marks[old]), (old, None)])
936 marks.applychanges(repo, tr, changes)
936 marks.applychanges(repo, tr, changes)
937 if repo._activebookmark == old and not inactive:
937 if repo._activebookmark == old and not inactive:
938 activate(repo, mark)
938 activate(repo, mark)
939
939
940
940
941 def addbookmarks(repo, tr, names, rev=None, force=False, inactive=False):
941 def addbookmarks(repo, tr, names, rev=None, force=False, inactive=False):
942 """add a list of bookmarks
942 """add a list of bookmarks
943
943
944 If force is specified, then the new name can overwrite an existing
944 If force is specified, then the new name can overwrite an existing
945 bookmark.
945 bookmark.
946
946
947 If inactive is specified, then do not activate any bookmark. Otherwise, the
947 If inactive is specified, then do not activate any bookmark. Otherwise, the
948 first bookmark is activated.
948 first bookmark is activated.
949
949
950 Raises an abort error if old is not in the bookmark store.
950 Raises an abort error if old is not in the bookmark store.
951 """
951 """
952 marks = repo._bookmarks
952 marks = repo._bookmarks
953 cur = repo[b'.'].node()
953 cur = repo[b'.'].node()
954 newact = None
954 newact = None
955 changes = []
955 changes = []
956
956
957 # unhide revs if any
957 # unhide revs if any
958 if rev:
958 if rev:
959 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
959 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
960
960
961 ctx = scmutil.revsingle(repo, rev)
961 ctx = scmutil.revsingle(repo, rev, None)
962 # bookmarking wdir means creating a bookmark on p1 and activating it
963 activatenew = not inactive and ctx.rev() is None
964 if ctx.node() is None:
965 ctx = ctx.p1()
962 tgt = ctx.node()
966 tgt = ctx.node()
967 assert tgt
963
968
964 for mark in names:
969 for mark in names:
965 mark = checkformat(repo, mark)
970 mark = checkformat(repo, mark)
966 if newact is None:
971 if newact is None:
967 newact = mark
972 newact = mark
968 if inactive and mark == repo._activebookmark:
973 if inactive and mark == repo._activebookmark:
969 deactivate(repo)
974 deactivate(repo)
970 continue
975 continue
971 for bm in marks.checkconflict(mark, force, tgt):
976 for bm in marks.checkconflict(mark, force, tgt):
972 changes.append((bm, None))
977 changes.append((bm, None))
973 changes.append((mark, tgt))
978 changes.append((mark, tgt))
974
979
975 # nothing changed but for the one deactivated above
980 # nothing changed but for the one deactivated above
976 if not changes:
981 if not changes:
977 return
982 return
978
983
979 if ctx.hidden():
984 if ctx.hidden():
980 repo.ui.warn(_(b"bookmarking hidden changeset %s\n") % ctx.hex()[:12])
985 repo.ui.warn(_(b"bookmarking hidden changeset %s\n") % ctx.hex()[:12])
981
986
982 if ctx.obsolete():
987 if ctx.obsolete():
983 msg = obsutil._getfilteredreason(repo, ctx.hex()[:12], ctx)
988 msg = obsutil._getfilteredreason(repo, ctx.hex()[:12], ctx)
984 repo.ui.warn(b"(%s)\n" % msg)
989 repo.ui.warn(b"(%s)\n" % msg)
985
990
986 marks.applychanges(repo, tr, changes)
991 marks.applychanges(repo, tr, changes)
987 if not inactive and cur == marks[newact] and not rev:
992 if activatenew and cur == marks[newact]:
988 activate(repo, newact)
993 activate(repo, newact)
989 elif cur != tgt and newact == repo._activebookmark:
994 elif cur != tgt and newact == repo._activebookmark:
990 deactivate(repo)
995 deactivate(repo)
991
996
992
997
993 def _printbookmarks(ui, repo, fm, bmarks):
998 def _printbookmarks(ui, repo, fm, bmarks):
994 """private method to print bookmarks
999 """private method to print bookmarks
995
1000
996 Provides a way for extensions to control how bookmarks are printed (e.g.
1001 Provides a way for extensions to control how bookmarks are printed (e.g.
997 prepend or postpend names)
1002 prepend or postpend names)
998 """
1003 """
999 hexfn = fm.hexfunc
1004 hexfn = fm.hexfunc
1000 if len(bmarks) == 0 and fm.isplain():
1005 if len(bmarks) == 0 and fm.isplain():
1001 ui.status(_(b"no bookmarks set\n"))
1006 ui.status(_(b"no bookmarks set\n"))
1002 for bmark, (n, prefix, label) in sorted(pycompat.iteritems(bmarks)):
1007 for bmark, (n, prefix, label) in sorted(pycompat.iteritems(bmarks)):
1003 fm.startitem()
1008 fm.startitem()
1004 fm.context(repo=repo)
1009 fm.context(repo=repo)
1005 if not ui.quiet:
1010 if not ui.quiet:
1006 fm.plain(b' %s ' % prefix, label=label)
1011 fm.plain(b' %s ' % prefix, label=label)
1007 fm.write(b'bookmark', b'%s', bmark, label=label)
1012 fm.write(b'bookmark', b'%s', bmark, label=label)
1008 pad = b" " * (25 - encoding.colwidth(bmark))
1013 pad = b" " * (25 - encoding.colwidth(bmark))
1009 fm.condwrite(
1014 fm.condwrite(
1010 not ui.quiet,
1015 not ui.quiet,
1011 b'rev node',
1016 b'rev node',
1012 pad + b' %d:%s',
1017 pad + b' %d:%s',
1013 repo.changelog.rev(n),
1018 repo.changelog.rev(n),
1014 hexfn(n),
1019 hexfn(n),
1015 label=label,
1020 label=label,
1016 )
1021 )
1017 fm.data(active=(activebookmarklabel in label))
1022 fm.data(active=(activebookmarklabel in label))
1018 fm.plain(b'\n')
1023 fm.plain(b'\n')
1019
1024
1020
1025
1021 def printbookmarks(ui, repo, fm, names=None):
1026 def printbookmarks(ui, repo, fm, names=None):
1022 """print bookmarks by the given formatter
1027 """print bookmarks by the given formatter
1023
1028
1024 Provides a way for extensions to control how bookmarks are printed.
1029 Provides a way for extensions to control how bookmarks are printed.
1025 """
1030 """
1026 marks = repo._bookmarks
1031 marks = repo._bookmarks
1027 bmarks = {}
1032 bmarks = {}
1028 for bmark in names or marks:
1033 for bmark in names or marks:
1029 if bmark not in marks:
1034 if bmark not in marks:
1030 raise error.Abort(_(b"bookmark '%s' does not exist") % bmark)
1035 raise error.Abort(_(b"bookmark '%s' does not exist") % bmark)
1031 active = repo._activebookmark
1036 active = repo._activebookmark
1032 if bmark == active:
1037 if bmark == active:
1033 prefix, label = b'*', activebookmarklabel
1038 prefix, label = b'*', activebookmarklabel
1034 else:
1039 else:
1035 prefix, label = b' ', b''
1040 prefix, label = b' ', b''
1036
1041
1037 bmarks[bmark] = (marks[bmark], prefix, label)
1042 bmarks[bmark] = (marks[bmark], prefix, label)
1038 _printbookmarks(ui, repo, fm, bmarks)
1043 _printbookmarks(ui, repo, fm, bmarks)
1039
1044
1040
1045
1041 def preparehookargs(name, old, new):
1046 def preparehookargs(name, old, new):
1042 if new is None:
1047 if new is None:
1043 new = b''
1048 new = b''
1044 if old is None:
1049 if old is None:
1045 old = b''
1050 old = b''
1046 return {b'bookmark': name, b'node': hex(new), b'oldnode': hex(old)}
1051 return {b'bookmark': name, b'node': hex(new), b'oldnode': hex(old)}
@@ -1,1247 +1,1257 b''
1
1
2 $ hg init repo
2 $ hg init repo
3 $ cd repo
3 $ cd repo
4
4
5 $ cat > $TESTTMP/hook.sh <<'EOF'
5 $ cat > $TESTTMP/hook.sh <<'EOF'
6 > echo "test-hook-bookmark: $HG_BOOKMARK: $HG_OLDNODE -> $HG_NODE"
6 > echo "test-hook-bookmark: $HG_BOOKMARK: $HG_OLDNODE -> $HG_NODE"
7 > EOF
7 > EOF
8 $ TESTHOOK="hooks.txnclose-bookmark.test=sh $TESTTMP/hook.sh"
8 $ TESTHOOK="hooks.txnclose-bookmark.test=sh $TESTTMP/hook.sh"
9
9
10 no bookmarks
10 no bookmarks
11
11
12 $ hg bookmarks
12 $ hg bookmarks
13 no bookmarks set
13 no bookmarks set
14
14
15 $ hg bookmarks -Tjson
15 $ hg bookmarks -Tjson
16 [
16 [
17 ]
17 ]
18
18
19 bookmark rev -1
19 bookmark rev -1
20
20
21 $ hg bookmark X --config "$TESTHOOK"
21 $ hg bookmark X --config "$TESTHOOK"
22 test-hook-bookmark: X: -> 0000000000000000000000000000000000000000
22 test-hook-bookmark: X: -> 0000000000000000000000000000000000000000
23
23
24 list bookmarks
24 list bookmarks
25
25
26 $ hg bookmarks
26 $ hg bookmarks
27 * X -1:000000000000
27 * X -1:000000000000
28
28
29 list bookmarks with color
29 list bookmarks with color
30
30
31 $ hg --config extensions.color= --config color.mode=ansi \
31 $ hg --config extensions.color= --config color.mode=ansi \
32 > bookmarks --color=always
32 > bookmarks --color=always
33 \x1b[0;32m * \x1b[0m\x1b[0;32mX\x1b[0m\x1b[0;32m -1:000000000000\x1b[0m (esc)
33 \x1b[0;32m * \x1b[0m\x1b[0;32mX\x1b[0m\x1b[0;32m -1:000000000000\x1b[0m (esc)
34
34
35 $ echo a > a
35 $ echo a > a
36 $ hg add a
36 $ hg add a
37 $ hg commit -m 0 --config "$TESTHOOK"
37 $ hg commit -m 0 --config "$TESTHOOK"
38 test-hook-bookmark: X: 0000000000000000000000000000000000000000 -> f7b1eb17ad24730a1651fccd46c43826d1bbc2ac
38 test-hook-bookmark: X: 0000000000000000000000000000000000000000 -> f7b1eb17ad24730a1651fccd46c43826d1bbc2ac
39
39
40 bookmark X moved to rev 0
40 bookmark X moved to rev 0
41
41
42 $ hg bookmarks
42 $ hg bookmarks
43 * X 0:f7b1eb17ad24
43 * X 0:f7b1eb17ad24
44
44
45 look up bookmark
45 look up bookmark
46
46
47 $ hg log -r X
47 $ hg log -r X
48 changeset: 0:f7b1eb17ad24
48 changeset: 0:f7b1eb17ad24
49 bookmark: X
49 bookmark: X
50 tag: tip
50 tag: tip
51 user: test
51 user: test
52 date: Thu Jan 01 00:00:00 1970 +0000
52 date: Thu Jan 01 00:00:00 1970 +0000
53 summary: 0
53 summary: 0
54
54
55
55
56 second bookmark for rev 0, command should work even with ui.strict on
56 second bookmark for rev 0, command should work even with ui.strict on
57
57
58 $ hg --config ui.strict=1 bookmark X2 --config "$TESTHOOK"
58 $ hg --config ui.strict=1 bookmark X2 --config "$TESTHOOK"
59 test-hook-bookmark: X2: -> f7b1eb17ad24730a1651fccd46c43826d1bbc2ac
59 test-hook-bookmark: X2: -> f7b1eb17ad24730a1651fccd46c43826d1bbc2ac
60
60
61 bookmark rev -1 again
61 bookmark rev -1 again
62
62
63 $ hg bookmark -r null Y
63 $ hg bookmark -r null Y
64
64
65 list bookmarks
65 list bookmarks
66
66
67 $ hg bookmarks
67 $ hg bookmarks
68 X 0:f7b1eb17ad24
68 X 0:f7b1eb17ad24
69 * X2 0:f7b1eb17ad24
69 * X2 0:f7b1eb17ad24
70 Y -1:000000000000
70 Y -1:000000000000
71 $ hg bookmarks -l
71 $ hg bookmarks -l
72 X 0:f7b1eb17ad24
72 X 0:f7b1eb17ad24
73 * X2 0:f7b1eb17ad24
73 * X2 0:f7b1eb17ad24
74 Y -1:000000000000
74 Y -1:000000000000
75 $ hg bookmarks -l X Y
75 $ hg bookmarks -l X Y
76 X 0:f7b1eb17ad24
76 X 0:f7b1eb17ad24
77 Y -1:000000000000
77 Y -1:000000000000
78 $ hg bookmarks -l .
78 $ hg bookmarks -l .
79 * X2 0:f7b1eb17ad24
79 * X2 0:f7b1eb17ad24
80 $ hg bookmarks -l X A Y
80 $ hg bookmarks -l X A Y
81 abort: bookmark 'A' does not exist
81 abort: bookmark 'A' does not exist
82 [255]
82 [255]
83 $ hg bookmarks -l -r0
83 $ hg bookmarks -l -r0
84 abort: --rev is incompatible with --list
84 abort: --rev is incompatible with --list
85 [255]
85 [255]
86 $ hg bookmarks -l --inactive
86 $ hg bookmarks -l --inactive
87 abort: --inactive is incompatible with --list
87 abort: --inactive is incompatible with --list
88 [255]
88 [255]
89
89
90 $ hg log -T '{bookmarks % "{rev} {bookmark}\n"}'
90 $ hg log -T '{bookmarks % "{rev} {bookmark}\n"}'
91 0 X
91 0 X
92 0 X2
92 0 X2
93
93
94 $ echo b > b
94 $ echo b > b
95 $ hg add b
95 $ hg add b
96 $ hg commit -m 1 --config "$TESTHOOK"
96 $ hg commit -m 1 --config "$TESTHOOK"
97 test-hook-bookmark: X2: f7b1eb17ad24730a1651fccd46c43826d1bbc2ac -> 925d80f479bb026b0fb3deb27503780b13f74123
97 test-hook-bookmark: X2: f7b1eb17ad24730a1651fccd46c43826d1bbc2ac -> 925d80f479bb026b0fb3deb27503780b13f74123
98
98
99 $ hg bookmarks -T '{rev}:{node|shortest} {bookmark} {desc|firstline}\n'
99 $ hg bookmarks -T '{rev}:{node|shortest} {bookmark} {desc|firstline}\n'
100 0:f7b1 X 0
100 0:f7b1 X 0
101 1:925d X2 1
101 1:925d X2 1
102 -1:0000 Y
102 -1:0000 Y
103
103
104 $ hg bookmarks -Tjson
104 $ hg bookmarks -Tjson
105 [
105 [
106 {
106 {
107 "active": false,
107 "active": false,
108 "bookmark": "X",
108 "bookmark": "X",
109 "node": "f7b1eb17ad24730a1651fccd46c43826d1bbc2ac",
109 "node": "f7b1eb17ad24730a1651fccd46c43826d1bbc2ac",
110 "rev": 0
110 "rev": 0
111 },
111 },
112 {
112 {
113 "active": true,
113 "active": true,
114 "bookmark": "X2",
114 "bookmark": "X2",
115 "node": "925d80f479bb026b0fb3deb27503780b13f74123",
115 "node": "925d80f479bb026b0fb3deb27503780b13f74123",
116 "rev": 1
116 "rev": 1
117 },
117 },
118 {
118 {
119 "active": false,
119 "active": false,
120 "bookmark": "Y",
120 "bookmark": "Y",
121 "node": "0000000000000000000000000000000000000000",
121 "node": "0000000000000000000000000000000000000000",
122 "rev": -1
122 "rev": -1
123 }
123 }
124 ]
124 ]
125
125
126 bookmarks revset
126 bookmarks revset
127
127
128 $ hg log -r 'bookmark()'
128 $ hg log -r 'bookmark()'
129 changeset: 0:f7b1eb17ad24
129 changeset: 0:f7b1eb17ad24
130 bookmark: X
130 bookmark: X
131 user: test
131 user: test
132 date: Thu Jan 01 00:00:00 1970 +0000
132 date: Thu Jan 01 00:00:00 1970 +0000
133 summary: 0
133 summary: 0
134
134
135 changeset: 1:925d80f479bb
135 changeset: 1:925d80f479bb
136 bookmark: X2
136 bookmark: X2
137 tag: tip
137 tag: tip
138 user: test
138 user: test
139 date: Thu Jan 01 00:00:00 1970 +0000
139 date: Thu Jan 01 00:00:00 1970 +0000
140 summary: 1
140 summary: 1
141
141
142 $ hg log -r 'bookmark(Y)'
142 $ hg log -r 'bookmark(Y)'
143 $ hg log -r 'bookmark(X2)'
143 $ hg log -r 'bookmark(X2)'
144 changeset: 1:925d80f479bb
144 changeset: 1:925d80f479bb
145 bookmark: X2
145 bookmark: X2
146 tag: tip
146 tag: tip
147 user: test
147 user: test
148 date: Thu Jan 01 00:00:00 1970 +0000
148 date: Thu Jan 01 00:00:00 1970 +0000
149 summary: 1
149 summary: 1
150
150
151 $ hg log -r 'bookmark("re:X")'
151 $ hg log -r 'bookmark("re:X")'
152 changeset: 0:f7b1eb17ad24
152 changeset: 0:f7b1eb17ad24
153 bookmark: X
153 bookmark: X
154 user: test
154 user: test
155 date: Thu Jan 01 00:00:00 1970 +0000
155 date: Thu Jan 01 00:00:00 1970 +0000
156 summary: 0
156 summary: 0
157
157
158 changeset: 1:925d80f479bb
158 changeset: 1:925d80f479bb
159 bookmark: X2
159 bookmark: X2
160 tag: tip
160 tag: tip
161 user: test
161 user: test
162 date: Thu Jan 01 00:00:00 1970 +0000
162 date: Thu Jan 01 00:00:00 1970 +0000
163 summary: 1
163 summary: 1
164
164
165 $ hg log -r 'bookmark("literal:X")'
165 $ hg log -r 'bookmark("literal:X")'
166 changeset: 0:f7b1eb17ad24
166 changeset: 0:f7b1eb17ad24
167 bookmark: X
167 bookmark: X
168 user: test
168 user: test
169 date: Thu Jan 01 00:00:00 1970 +0000
169 date: Thu Jan 01 00:00:00 1970 +0000
170 summary: 0
170 summary: 0
171
171
172
172
173 "." is expanded to the active bookmark:
173 "." is expanded to the active bookmark:
174
174
175 $ hg log -r 'bookmark(.)'
175 $ hg log -r 'bookmark(.)'
176 changeset: 1:925d80f479bb
176 changeset: 1:925d80f479bb
177 bookmark: X2
177 bookmark: X2
178 tag: tip
178 tag: tip
179 user: test
179 user: test
180 date: Thu Jan 01 00:00:00 1970 +0000
180 date: Thu Jan 01 00:00:00 1970 +0000
181 summary: 1
181 summary: 1
182
182
183
183
184 but "literal:." is not since "." seems not a literal bookmark:
184 but "literal:." is not since "." seems not a literal bookmark:
185
185
186 $ hg log -r 'bookmark("literal:.")'
186 $ hg log -r 'bookmark("literal:.")'
187 abort: bookmark '.' does not exist!
187 abort: bookmark '.' does not exist!
188 [255]
188 [255]
189
189
190 "." should fail if there's no active bookmark:
190 "." should fail if there's no active bookmark:
191
191
192 $ hg bookmark --inactive
192 $ hg bookmark --inactive
193 $ hg log -r 'bookmark(.)'
193 $ hg log -r 'bookmark(.)'
194 abort: no active bookmark!
194 abort: no active bookmark!
195 [255]
195 [255]
196 $ hg log -r 'present(bookmark(.))'
196 $ hg log -r 'present(bookmark(.))'
197
197
198 $ hg log -r 'bookmark(unknown)'
198 $ hg log -r 'bookmark(unknown)'
199 abort: bookmark 'unknown' does not exist!
199 abort: bookmark 'unknown' does not exist!
200 [255]
200 [255]
201 $ hg log -r 'bookmark("literal:unknown")'
201 $ hg log -r 'bookmark("literal:unknown")'
202 abort: bookmark 'unknown' does not exist!
202 abort: bookmark 'unknown' does not exist!
203 [255]
203 [255]
204 $ hg log -r 'bookmark("re:unknown")'
204 $ hg log -r 'bookmark("re:unknown")'
205 $ hg log -r 'present(bookmark("literal:unknown"))'
205 $ hg log -r 'present(bookmark("literal:unknown"))'
206 $ hg log -r 'present(bookmark("re:unknown"))'
206 $ hg log -r 'present(bookmark("re:unknown"))'
207
207
208 $ hg help revsets | grep 'bookmark('
208 $ hg help revsets | grep 'bookmark('
209 "bookmark([name])"
209 "bookmark([name])"
210
210
211 reactivate "X2"
211 reactivate "X2"
212
212
213 $ hg update X2
213 $ hg update X2
214 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
214 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
215 (activating bookmark X2)
215 (activating bookmark X2)
216
216
217 bookmarks X and X2 moved to rev 1, Y at rev -1
217 bookmarks X and X2 moved to rev 1, Y at rev -1
218
218
219 $ hg bookmarks
219 $ hg bookmarks
220 X 0:f7b1eb17ad24
220 X 0:f7b1eb17ad24
221 * X2 1:925d80f479bb
221 * X2 1:925d80f479bb
222 Y -1:000000000000
222 Y -1:000000000000
223
223
224 bookmark rev 0 again
224 bookmark rev 0 again
225
225
226 $ hg bookmark -r 0 Z
226 $ hg bookmark -r 0 Z
227
227
228 $ hg update X
228 $ hg update X
229 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
229 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
230 (activating bookmark X)
230 (activating bookmark X)
231 $ echo c > c
231 $ echo c > c
232 $ hg add c
232 $ hg add c
233 $ hg commit -m 2
233 $ hg commit -m 2
234 created new head
234 created new head
235
235
236 bookmarks X moved to rev 2, Y at rev -1, Z at rev 0
236 bookmarks X moved to rev 2, Y at rev -1, Z at rev 0
237
237
238 $ hg bookmarks
238 $ hg bookmarks
239 * X 2:db815d6d32e6
239 * X 2:db815d6d32e6
240 X2 1:925d80f479bb
240 X2 1:925d80f479bb
241 Y -1:000000000000
241 Y -1:000000000000
242 Z 0:f7b1eb17ad24
242 Z 0:f7b1eb17ad24
243
243
244 rename nonexistent bookmark
244 rename nonexistent bookmark
245
245
246 $ hg bookmark -m A B
246 $ hg bookmark -m A B
247 abort: bookmark 'A' does not exist
247 abort: bookmark 'A' does not exist
248 [255]
248 [255]
249
249
250 rename to existent bookmark
250 rename to existent bookmark
251
251
252 $ hg bookmark -m X Y
252 $ hg bookmark -m X Y
253 abort: bookmark 'Y' already exists (use -f to force)
253 abort: bookmark 'Y' already exists (use -f to force)
254 [255]
254 [255]
255
255
256 force rename to existent bookmark
256 force rename to existent bookmark
257
257
258 $ hg bookmark -f -m X Y
258 $ hg bookmark -f -m X Y
259
259
260 rename bookmark using .
260 rename bookmark using .
261
261
262 $ hg book rename-me
262 $ hg book rename-me
263 $ hg book -m . renamed --config "$TESTHOOK"
263 $ hg book -m . renamed --config "$TESTHOOK"
264 test-hook-bookmark: rename-me: db815d6d32e69058eadefc8cffbad37675707975 ->
264 test-hook-bookmark: rename-me: db815d6d32e69058eadefc8cffbad37675707975 ->
265 test-hook-bookmark: renamed: -> db815d6d32e69058eadefc8cffbad37675707975
265 test-hook-bookmark: renamed: -> db815d6d32e69058eadefc8cffbad37675707975
266 $ hg bookmark
266 $ hg bookmark
267 X2 1:925d80f479bb
267 X2 1:925d80f479bb
268 Y 2:db815d6d32e6
268 Y 2:db815d6d32e6
269 Z 0:f7b1eb17ad24
269 Z 0:f7b1eb17ad24
270 * renamed 2:db815d6d32e6
270 * renamed 2:db815d6d32e6
271 $ hg up -q Y
271 $ hg up -q Y
272 $ hg book -d renamed --config "$TESTHOOK"
272 $ hg book -d renamed --config "$TESTHOOK"
273 test-hook-bookmark: renamed: db815d6d32e69058eadefc8cffbad37675707975 ->
273 test-hook-bookmark: renamed: db815d6d32e69058eadefc8cffbad37675707975 ->
274
274
275 rename bookmark using . with no active bookmark
275 rename bookmark using . with no active bookmark
276
276
277 $ hg book rename-me
277 $ hg book rename-me
278 $ hg book -i rename-me
278 $ hg book -i rename-me
279 $ hg book -m . renamed
279 $ hg book -m . renamed
280 abort: no active bookmark!
280 abort: no active bookmark!
281 [255]
281 [255]
282 $ hg up -q Y
282 $ hg up -q Y
283 $ hg book -d rename-me
283 $ hg book -d rename-me
284
284
285 delete bookmark using .
285 delete bookmark using .
286
286
287 $ hg book delete-me
287 $ hg book delete-me
288 $ hg book -d .
288 $ hg book -d .
289 $ hg bookmark
289 $ hg bookmark
290 X2 1:925d80f479bb
290 X2 1:925d80f479bb
291 Y 2:db815d6d32e6
291 Y 2:db815d6d32e6
292 Z 0:f7b1eb17ad24
292 Z 0:f7b1eb17ad24
293 $ hg up -q Y
293 $ hg up -q Y
294
294
295 delete bookmark using . with no active bookmark
295 delete bookmark using . with no active bookmark
296
296
297 $ hg book delete-me
297 $ hg book delete-me
298 $ hg book -i delete-me
298 $ hg book -i delete-me
299 $ hg book -d .
299 $ hg book -d .
300 abort: no active bookmark!
300 abort: no active bookmark!
301 [255]
301 [255]
302 $ hg up -q Y
302 $ hg up -q Y
303 $ hg book -d delete-me
303 $ hg book -d delete-me
304
304
305 list bookmarks
305 list bookmarks
306
306
307 $ hg bookmark
307 $ hg bookmark
308 X2 1:925d80f479bb
308 X2 1:925d80f479bb
309 * Y 2:db815d6d32e6
309 * Y 2:db815d6d32e6
310 Z 0:f7b1eb17ad24
310 Z 0:f7b1eb17ad24
311
311
312 bookmarks from a revset
312 bookmarks from a revset
313 $ hg bookmark -r '.^1' REVSET
313 $ hg bookmark -r '.^1' REVSET
314 $ hg bookmark -r ':tip' TIP
314 $ hg bookmark -r ':tip' TIP
315 $ hg up -q TIP
315 $ hg up -q TIP
316 $ hg bookmarks
316 $ hg bookmarks
317 REVSET 0:f7b1eb17ad24
317 REVSET 0:f7b1eb17ad24
318 * TIP 2:db815d6d32e6
318 * TIP 2:db815d6d32e6
319 X2 1:925d80f479bb
319 X2 1:925d80f479bb
320 Y 2:db815d6d32e6
320 Y 2:db815d6d32e6
321 Z 0:f7b1eb17ad24
321 Z 0:f7b1eb17ad24
322
322
323 $ hg bookmark -d REVSET
323 $ hg bookmark -d REVSET
324 $ hg bookmark -d TIP
324 $ hg bookmark -d TIP
325
325
326 rename without new name or multiple names
326 rename without new name or multiple names
327
327
328 $ hg bookmark -m Y
328 $ hg bookmark -m Y
329 abort: new bookmark name required
329 abort: new bookmark name required
330 [255]
330 [255]
331 $ hg bookmark -m Y Y2 Y3
331 $ hg bookmark -m Y Y2 Y3
332 abort: only one new bookmark name allowed
332 abort: only one new bookmark name allowed
333 [255]
333 [255]
334
334
335 delete without name
335 delete without name
336
336
337 $ hg bookmark -d
337 $ hg bookmark -d
338 abort: bookmark name required
338 abort: bookmark name required
339 [255]
339 [255]
340
340
341 delete nonexistent bookmark
341 delete nonexistent bookmark
342
342
343 $ hg bookmark -d A
343 $ hg bookmark -d A
344 abort: bookmark 'A' does not exist
344 abort: bookmark 'A' does not exist
345 [255]
345 [255]
346
346
347 delete with --inactive
347 delete with --inactive
348
348
349 $ hg bookmark -d --inactive Y
349 $ hg bookmark -d --inactive Y
350 abort: --inactive is incompatible with --delete
350 abort: --inactive is incompatible with --delete
351 [255]
351 [255]
352
352
353 bookmark name with spaces should be stripped
353 bookmark name with spaces should be stripped
354
354
355 $ hg bookmark ' x y '
355 $ hg bookmark ' x y '
356
356
357 list bookmarks
357 list bookmarks
358
358
359 $ hg bookmarks
359 $ hg bookmarks
360 X2 1:925d80f479bb
360 X2 1:925d80f479bb
361 Y 2:db815d6d32e6
361 Y 2:db815d6d32e6
362 Z 0:f7b1eb17ad24
362 Z 0:f7b1eb17ad24
363 * x y 2:db815d6d32e6
363 * x y 2:db815d6d32e6
364 $ hg log -T '{bookmarks % "{rev} {bookmark}\n"}'
364 $ hg log -T '{bookmarks % "{rev} {bookmark}\n"}'
365 2 Y
365 2 Y
366 2 x y
366 2 x y
367 1 X2
367 1 X2
368 0 Z
368 0 Z
369
369
370 look up stripped bookmark name
370 look up stripped bookmark name
371
371
372 $ hg log -r '"x y"'
372 $ hg log -r '"x y"'
373 changeset: 2:db815d6d32e6
373 changeset: 2:db815d6d32e6
374 bookmark: Y
374 bookmark: Y
375 bookmark: x y
375 bookmark: x y
376 tag: tip
376 tag: tip
377 parent: 0:f7b1eb17ad24
377 parent: 0:f7b1eb17ad24
378 user: test
378 user: test
379 date: Thu Jan 01 00:00:00 1970 +0000
379 date: Thu Jan 01 00:00:00 1970 +0000
380 summary: 2
380 summary: 2
381
381
382
382
383 reject bookmark name with newline
383 reject bookmark name with newline
384
384
385 $ hg bookmark '
385 $ hg bookmark '
386 > '
386 > '
387 abort: bookmark names cannot consist entirely of whitespace
387 abort: bookmark names cannot consist entirely of whitespace
388 [255]
388 [255]
389
389
390 $ hg bookmark -m Z '
390 $ hg bookmark -m Z '
391 > '
391 > '
392 abort: bookmark names cannot consist entirely of whitespace
392 abort: bookmark names cannot consist entirely of whitespace
393 [255]
393 [255]
394
394
395 bookmark with reserved name
395 bookmark with reserved name
396
396
397 $ hg bookmark tip
397 $ hg bookmark tip
398 abort: the name 'tip' is reserved
398 abort: the name 'tip' is reserved
399 [255]
399 [255]
400
400
401 $ hg bookmark .
401 $ hg bookmark .
402 abort: the name '.' is reserved
402 abort: the name '.' is reserved
403 [255]
403 [255]
404
404
405 $ hg bookmark null
405 $ hg bookmark null
406 abort: the name 'null' is reserved
406 abort: the name 'null' is reserved
407 [255]
407 [255]
408
408
409
409
410 bookmark with existing name
410 bookmark with existing name
411
411
412 $ hg bookmark X2
412 $ hg bookmark X2
413 abort: bookmark 'X2' already exists (use -f to force)
413 abort: bookmark 'X2' already exists (use -f to force)
414 [255]
414 [255]
415
415
416 $ hg bookmark -m Y Z
416 $ hg bookmark -m Y Z
417 abort: bookmark 'Z' already exists (use -f to force)
417 abort: bookmark 'Z' already exists (use -f to force)
418 [255]
418 [255]
419
419
420 bookmark with name of branch
420 bookmark with name of branch
421
421
422 $ hg bookmark default
422 $ hg bookmark default
423 abort: a bookmark cannot have the name of an existing branch
423 abort: a bookmark cannot have the name of an existing branch
424 [255]
424 [255]
425
425
426 $ hg bookmark -m Y default
426 $ hg bookmark -m Y default
427 abort: a bookmark cannot have the name of an existing branch
427 abort: a bookmark cannot have the name of an existing branch
428 [255]
428 [255]
429
429
430 bookmark with integer name
430 bookmark with integer name
431
431
432 $ hg bookmark 10
432 $ hg bookmark 10
433 abort: cannot use an integer as a name
433 abort: cannot use an integer as a name
434 [255]
434 [255]
435
435
436 bookmark with a name that matches a node id
436 bookmark with a name that matches a node id
437 $ hg bookmark 925d80f479bb db815d6d32e6 --config "$TESTHOOK"
437 $ hg bookmark 925d80f479bb db815d6d32e6 --config "$TESTHOOK"
438 bookmark 925d80f479bb matches a changeset hash
438 bookmark 925d80f479bb matches a changeset hash
439 (did you leave a -r out of an 'hg bookmark' command?)
439 (did you leave a -r out of an 'hg bookmark' command?)
440 bookmark db815d6d32e6 matches a changeset hash
440 bookmark db815d6d32e6 matches a changeset hash
441 (did you leave a -r out of an 'hg bookmark' command?)
441 (did you leave a -r out of an 'hg bookmark' command?)
442 test-hook-bookmark: 925d80f479bb: -> db815d6d32e69058eadefc8cffbad37675707975
442 test-hook-bookmark: 925d80f479bb: -> db815d6d32e69058eadefc8cffbad37675707975
443 test-hook-bookmark: db815d6d32e6: -> db815d6d32e69058eadefc8cffbad37675707975
443 test-hook-bookmark: db815d6d32e6: -> db815d6d32e69058eadefc8cffbad37675707975
444 $ hg bookmark -d 925d80f479bb
444 $ hg bookmark -d 925d80f479bb
445 $ hg bookmark -d db815d6d32e6
445 $ hg bookmark -d db815d6d32e6
446
446
447 $ cd ..
447 $ cd ..
448
448
449 bookmark with a name that matches an ambiguous node id
449 bookmark with a name that matches an ambiguous node id
450
450
451 $ hg init ambiguous
451 $ hg init ambiguous
452 $ cd ambiguous
452 $ cd ambiguous
453 $ echo 0 > a
453 $ echo 0 > a
454 $ hg ci -qAm 0
454 $ hg ci -qAm 0
455 $ for i in 1057 2857 4025; do
455 $ for i in 1057 2857 4025; do
456 > hg up -q 0
456 > hg up -q 0
457 > echo $i > a
457 > echo $i > a
458 > hg ci -qm $i
458 > hg ci -qm $i
459 > done
459 > done
460 $ hg up -q null
460 $ hg up -q null
461 $ hg log -r0: -T '{rev}:{node}\n'
461 $ hg log -r0: -T '{rev}:{node}\n'
462 0:b4e73ffab476aa0ee32ed81ca51e07169844bc6a
462 0:b4e73ffab476aa0ee32ed81ca51e07169844bc6a
463 1:c56256a09cd28e5764f32e8e2810d0f01e2e357a
463 1:c56256a09cd28e5764f32e8e2810d0f01e2e357a
464 2:c5623987d205cd6d9d8389bfc40fff9dbb670b48
464 2:c5623987d205cd6d9d8389bfc40fff9dbb670b48
465 3:c562ddd9c94164376c20b86b0b4991636a3bf84f
465 3:c562ddd9c94164376c20b86b0b4991636a3bf84f
466
466
467 $ hg bookmark -r0 c562
467 $ hg bookmark -r0 c562
468 $ hg bookmarks
468 $ hg bookmarks
469 c562 0:b4e73ffab476
469 c562 0:b4e73ffab476
470
470
471 $ cd ..
471 $ cd ..
472
472
473 incompatible options
473 incompatible options
474
474
475 $ cd repo
475 $ cd repo
476
476
477 $ hg bookmark -m Y -d Z
477 $ hg bookmark -m Y -d Z
478 abort: --delete and --rename are incompatible
478 abort: --delete and --rename are incompatible
479 [255]
479 [255]
480
480
481 $ hg bookmark -r 1 -d Z
481 $ hg bookmark -r 1 -d Z
482 abort: --rev is incompatible with --delete
482 abort: --rev is incompatible with --delete
483 [255]
483 [255]
484
484
485 $ hg bookmark -r 1 -m Z Y
485 $ hg bookmark -r 1 -m Z Y
486 abort: --rev is incompatible with --rename
486 abort: --rev is incompatible with --rename
487 [255]
487 [255]
488
488
489 force bookmark with existing name
489 force bookmark with existing name
490
490
491 $ hg bookmark -f X2 --config "$TESTHOOK"
491 $ hg bookmark -f X2 --config "$TESTHOOK"
492 test-hook-bookmark: X2: 925d80f479bb026b0fb3deb27503780b13f74123 -> db815d6d32e69058eadefc8cffbad37675707975
492 test-hook-bookmark: X2: 925d80f479bb026b0fb3deb27503780b13f74123 -> db815d6d32e69058eadefc8cffbad37675707975
493
493
494 force bookmark back to where it was, should deactivate it
494 force bookmark back to where it was, should deactivate it
495
495
496 $ hg bookmark -fr1 X2
496 $ hg bookmark -fr1 X2
497 $ hg bookmarks
497 $ hg bookmarks
498 X2 1:925d80f479bb
498 X2 1:925d80f479bb
499 Y 2:db815d6d32e6
499 Y 2:db815d6d32e6
500 Z 0:f7b1eb17ad24
500 Z 0:f7b1eb17ad24
501 x y 2:db815d6d32e6
501 x y 2:db815d6d32e6
502
502
503 forward bookmark to descendant without --force
503 forward bookmark to descendant without --force
504
504
505 $ hg bookmark Z
505 $ hg bookmark Z
506 moving bookmark 'Z' forward from f7b1eb17ad24
506 moving bookmark 'Z' forward from f7b1eb17ad24
507
507
508 list bookmarks
508 list bookmarks
509
509
510 $ hg bookmark
510 $ hg bookmark
511 X2 1:925d80f479bb
511 X2 1:925d80f479bb
512 Y 2:db815d6d32e6
512 Y 2:db815d6d32e6
513 * Z 2:db815d6d32e6
513 * Z 2:db815d6d32e6
514 x y 2:db815d6d32e6
514 x y 2:db815d6d32e6
515 $ hg log -T '{bookmarks % "{rev} {bookmark}\n"}'
515 $ hg log -T '{bookmarks % "{rev} {bookmark}\n"}'
516 2 Y
516 2 Y
517 2 Z
517 2 Z
518 2 x y
518 2 x y
519 1 X2
519 1 X2
520
520
521 revision but no bookmark name
521 revision but no bookmark name
522
522
523 $ hg bookmark -r .
523 $ hg bookmark -r .
524 abort: bookmark name required
524 abort: bookmark name required
525 [255]
525 [255]
526
526
527 bookmark name with whitespace only
527 bookmark name with whitespace only
528
528
529 $ hg bookmark ' '
529 $ hg bookmark ' '
530 abort: bookmark names cannot consist entirely of whitespace
530 abort: bookmark names cannot consist entirely of whitespace
531 [255]
531 [255]
532
532
533 $ hg bookmark -m Y ' '
533 $ hg bookmark -m Y ' '
534 abort: bookmark names cannot consist entirely of whitespace
534 abort: bookmark names cannot consist entirely of whitespace
535 [255]
535 [255]
536
536
537 invalid bookmark
537 invalid bookmark
538
538
539 $ hg bookmark 'foo:bar'
539 $ hg bookmark 'foo:bar'
540 abort: ':' cannot be used in a name
540 abort: ':' cannot be used in a name
541 [255]
541 [255]
542
542
543 $ hg bookmark 'foo
543 $ hg bookmark 'foo
544 > bar'
544 > bar'
545 abort: '\n' cannot be used in a name
545 abort: '\n' cannot be used in a name
546 [255]
546 [255]
547
547
548 the bookmark extension should be ignored now that it is part of core
548 the bookmark extension should be ignored now that it is part of core
549
549
550 $ echo "[extensions]" >> $HGRCPATH
550 $ echo "[extensions]" >> $HGRCPATH
551 $ echo "bookmarks=" >> $HGRCPATH
551 $ echo "bookmarks=" >> $HGRCPATH
552 $ hg bookmarks
552 $ hg bookmarks
553 X2 1:925d80f479bb
553 X2 1:925d80f479bb
554 Y 2:db815d6d32e6
554 Y 2:db815d6d32e6
555 * Z 2:db815d6d32e6
555 * Z 2:db815d6d32e6
556 x y 2:db815d6d32e6
556 x y 2:db815d6d32e6
557
557
558 test summary
558 test summary
559
559
560 $ hg summary
560 $ hg summary
561 parent: 2:db815d6d32e6 tip
561 parent: 2:db815d6d32e6 tip
562 2
562 2
563 branch: default
563 branch: default
564 bookmarks: *Z Y x y
564 bookmarks: *Z Y x y
565 commit: (clean)
565 commit: (clean)
566 update: 1 new changesets, 2 branch heads (merge)
566 update: 1 new changesets, 2 branch heads (merge)
567 phases: 3 draft
567 phases: 3 draft
568
568
569 test id
569 test id
570
570
571 $ hg id
571 $ hg id
572 db815d6d32e6 tip Y/Z/x y
572 db815d6d32e6 tip Y/Z/x y
573
573
574 test rollback
574 test rollback
575
575
576 $ echo foo > f1
576 $ echo foo > f1
577 $ hg bookmark tmp-rollback
577 $ hg bookmark tmp-rollback
578 $ hg ci -Amr
578 $ hg ci -Amr
579 adding f1
579 adding f1
580 $ hg bookmarks
580 $ hg bookmarks
581 X2 1:925d80f479bb
581 X2 1:925d80f479bb
582 Y 2:db815d6d32e6
582 Y 2:db815d6d32e6
583 Z 2:db815d6d32e6
583 Z 2:db815d6d32e6
584 * tmp-rollback 3:2bf5cfec5864
584 * tmp-rollback 3:2bf5cfec5864
585 x y 2:db815d6d32e6
585 x y 2:db815d6d32e6
586 $ hg rollback
586 $ hg rollback
587 repository tip rolled back to revision 2 (undo commit)
587 repository tip rolled back to revision 2 (undo commit)
588 working directory now based on revision 2
588 working directory now based on revision 2
589 $ hg bookmarks
589 $ hg bookmarks
590 X2 1:925d80f479bb
590 X2 1:925d80f479bb
591 Y 2:db815d6d32e6
591 Y 2:db815d6d32e6
592 Z 2:db815d6d32e6
592 Z 2:db815d6d32e6
593 * tmp-rollback 2:db815d6d32e6
593 * tmp-rollback 2:db815d6d32e6
594 x y 2:db815d6d32e6
594 x y 2:db815d6d32e6
595 $ hg bookmark -f Z -r 1
595 $ hg bookmark -f Z -r 1
596 $ hg rollback
596 $ hg rollback
597 repository tip rolled back to revision 2 (undo bookmark)
597 repository tip rolled back to revision 2 (undo bookmark)
598 $ hg bookmarks
598 $ hg bookmarks
599 X2 1:925d80f479bb
599 X2 1:925d80f479bb
600 Y 2:db815d6d32e6
600 Y 2:db815d6d32e6
601 Z 2:db815d6d32e6
601 Z 2:db815d6d32e6
602 * tmp-rollback 2:db815d6d32e6
602 * tmp-rollback 2:db815d6d32e6
603 x y 2:db815d6d32e6
603 x y 2:db815d6d32e6
604 $ hg bookmark -d tmp-rollback
604 $ hg bookmark -d tmp-rollback
605
605
606 activate bookmark on working dir parent without --force
606 activate bookmark on working dir parent without --force
607
607
608 $ hg bookmark --inactive Z
608 $ hg bookmark --inactive Z
609 $ hg bookmark Z
609 $ hg bookmark Z
610
610
611 deactivate current 'Z', but also add 'Y'
611 deactivate current 'Z', but also add 'Y'
612
612
613 $ hg bookmark -d Y
613 $ hg bookmark -d Y
614 $ hg bookmark --inactive Z Y
614 $ hg bookmark --inactive Z Y
615 $ hg bookmark -l
615 $ hg bookmark -l
616 X2 1:925d80f479bb
616 X2 1:925d80f479bb
617 Y 2:db815d6d32e6
617 Y 2:db815d6d32e6
618 Z 2:db815d6d32e6
618 Z 2:db815d6d32e6
619 x y 2:db815d6d32e6
619 x y 2:db815d6d32e6
620 $ hg bookmark Z
620 $ hg bookmark Z
621
621
622 bookmark wdir to activate it (issue6218)
623
624 $ hg bookmark -d Z
625 $ hg bookmark -r 'wdir()' Z
626 $ hg bookmark -l
627 X2 1:925d80f479bb
628 Y 2:db815d6d32e6
629 * Z 2:db815d6d32e6
630 x y 2:db815d6d32e6
631
622 test clone
632 test clone
623
633
624 $ hg bookmark -r 2 -i @
634 $ hg bookmark -r 2 -i @
625 $ hg bookmark -r 2 -i a@
635 $ hg bookmark -r 2 -i a@
626 $ hg bookmarks
636 $ hg bookmarks
627 @ 2:db815d6d32e6
637 @ 2:db815d6d32e6
628 X2 1:925d80f479bb
638 X2 1:925d80f479bb
629 Y 2:db815d6d32e6
639 Y 2:db815d6d32e6
630 * Z 2:db815d6d32e6
640 * Z 2:db815d6d32e6
631 a@ 2:db815d6d32e6
641 a@ 2:db815d6d32e6
632 x y 2:db815d6d32e6
642 x y 2:db815d6d32e6
633 $ hg clone . cloned-bookmarks
643 $ hg clone . cloned-bookmarks
634 updating to bookmark @
644 updating to bookmark @
635 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
645 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
636 $ hg -R cloned-bookmarks bookmarks
646 $ hg -R cloned-bookmarks bookmarks
637 * @ 2:db815d6d32e6
647 * @ 2:db815d6d32e6
638 X2 1:925d80f479bb
648 X2 1:925d80f479bb
639 Y 2:db815d6d32e6
649 Y 2:db815d6d32e6
640 Z 2:db815d6d32e6
650 Z 2:db815d6d32e6
641 a@ 2:db815d6d32e6
651 a@ 2:db815d6d32e6
642 x y 2:db815d6d32e6
652 x y 2:db815d6d32e6
643
653
644 test clone with pull protocol
654 test clone with pull protocol
645
655
646 $ hg clone --pull . cloned-bookmarks-pull
656 $ hg clone --pull . cloned-bookmarks-pull
647 requesting all changes
657 requesting all changes
648 adding changesets
658 adding changesets
649 adding manifests
659 adding manifests
650 adding file changes
660 adding file changes
651 added 3 changesets with 3 changes to 3 files (+1 heads)
661 added 3 changesets with 3 changes to 3 files (+1 heads)
652 new changesets f7b1eb17ad24:db815d6d32e6
662 new changesets f7b1eb17ad24:db815d6d32e6
653 updating to bookmark @
663 updating to bookmark @
654 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
664 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
655 $ hg -R cloned-bookmarks-pull bookmarks
665 $ hg -R cloned-bookmarks-pull bookmarks
656 * @ 2:db815d6d32e6
666 * @ 2:db815d6d32e6
657 X2 1:925d80f479bb
667 X2 1:925d80f479bb
658 Y 2:db815d6d32e6
668 Y 2:db815d6d32e6
659 Z 2:db815d6d32e6
669 Z 2:db815d6d32e6
660 a@ 2:db815d6d32e6
670 a@ 2:db815d6d32e6
661 x y 2:db815d6d32e6
671 x y 2:db815d6d32e6
662
672
663 delete multiple bookmarks at once
673 delete multiple bookmarks at once
664
674
665 $ hg bookmark -d @ a@
675 $ hg bookmark -d @ a@
666
676
667 test clone with a bookmark named "default" (issue3677)
677 test clone with a bookmark named "default" (issue3677)
668
678
669 $ hg bookmark -r 1 -f -i default
679 $ hg bookmark -r 1 -f -i default
670 $ hg clone . cloned-bookmark-default
680 $ hg clone . cloned-bookmark-default
671 updating to branch default
681 updating to branch default
672 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
682 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
673 $ hg -R cloned-bookmark-default bookmarks
683 $ hg -R cloned-bookmark-default bookmarks
674 X2 1:925d80f479bb
684 X2 1:925d80f479bb
675 Y 2:db815d6d32e6
685 Y 2:db815d6d32e6
676 Z 2:db815d6d32e6
686 Z 2:db815d6d32e6
677 default 1:925d80f479bb
687 default 1:925d80f479bb
678 x y 2:db815d6d32e6
688 x y 2:db815d6d32e6
679 $ hg -R cloned-bookmark-default parents -q
689 $ hg -R cloned-bookmark-default parents -q
680 2:db815d6d32e6
690 2:db815d6d32e6
681 $ hg bookmark -d default
691 $ hg bookmark -d default
682
692
683 test clone with a specific revision
693 test clone with a specific revision
684
694
685 $ hg clone -r 925d80 . cloned-bookmarks-rev
695 $ hg clone -r 925d80 . cloned-bookmarks-rev
686 adding changesets
696 adding changesets
687 adding manifests
697 adding manifests
688 adding file changes
698 adding file changes
689 added 2 changesets with 2 changes to 2 files
699 added 2 changesets with 2 changes to 2 files
690 new changesets f7b1eb17ad24:925d80f479bb
700 new changesets f7b1eb17ad24:925d80f479bb
691 updating to branch default
701 updating to branch default
692 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
702 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
693 $ hg -R cloned-bookmarks-rev bookmarks
703 $ hg -R cloned-bookmarks-rev bookmarks
694 X2 1:925d80f479bb
704 X2 1:925d80f479bb
695
705
696 test clone with update to a bookmark
706 test clone with update to a bookmark
697
707
698 $ hg clone -u Z . ../cloned-bookmarks-update
708 $ hg clone -u Z . ../cloned-bookmarks-update
699 updating to branch default
709 updating to branch default
700 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
710 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
701 $ hg -R ../cloned-bookmarks-update bookmarks
711 $ hg -R ../cloned-bookmarks-update bookmarks
702 X2 1:925d80f479bb
712 X2 1:925d80f479bb
703 Y 2:db815d6d32e6
713 Y 2:db815d6d32e6
704 * Z 2:db815d6d32e6
714 * Z 2:db815d6d32e6
705 x y 2:db815d6d32e6
715 x y 2:db815d6d32e6
706
716
707 create bundle with two heads
717 create bundle with two heads
708
718
709 $ hg clone . tobundle
719 $ hg clone . tobundle
710 updating to branch default
720 updating to branch default
711 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
721 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
712 $ echo x > tobundle/x
722 $ echo x > tobundle/x
713 $ hg -R tobundle add tobundle/x
723 $ hg -R tobundle add tobundle/x
714 $ hg -R tobundle commit -m'x'
724 $ hg -R tobundle commit -m'x'
715 $ hg -R tobundle update -r -2
725 $ hg -R tobundle update -r -2
716 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
726 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
717 $ echo y > tobundle/y
727 $ echo y > tobundle/y
718 $ hg -R tobundle branch test
728 $ hg -R tobundle branch test
719 marked working directory as branch test
729 marked working directory as branch test
720 (branches are permanent and global, did you want a bookmark?)
730 (branches are permanent and global, did you want a bookmark?)
721 $ hg -R tobundle add tobundle/y
731 $ hg -R tobundle add tobundle/y
722 $ hg -R tobundle commit -m'y'
732 $ hg -R tobundle commit -m'y'
723 $ hg -R tobundle bundle tobundle.hg
733 $ hg -R tobundle bundle tobundle.hg
724 searching for changes
734 searching for changes
725 2 changesets found
735 2 changesets found
726 $ hg unbundle tobundle.hg
736 $ hg unbundle tobundle.hg
727 adding changesets
737 adding changesets
728 adding manifests
738 adding manifests
729 adding file changes
739 adding file changes
730 added 2 changesets with 2 changes to 2 files (+1 heads)
740 added 2 changesets with 2 changes to 2 files (+1 heads)
731 new changesets 125c9a1d6df6:9ba5f110a0b3 (2 drafts)
741 new changesets 125c9a1d6df6:9ba5f110a0b3 (2 drafts)
732 (run 'hg heads' to see heads, 'hg merge' to merge)
742 (run 'hg heads' to see heads, 'hg merge' to merge)
733
743
734 update to active bookmark if it's not the parent
744 update to active bookmark if it's not the parent
735
745
736 (it is known issue that fsmonitor can't handle nested repositories. In
746 (it is known issue that fsmonitor can't handle nested repositories. In
737 this test scenario, cloned-bookmark-default and tobundle exist in the
747 this test scenario, cloned-bookmark-default and tobundle exist in the
738 working directory of current repository)
748 working directory of current repository)
739
749
740 $ hg summary
750 $ hg summary
741 parent: 2:db815d6d32e6
751 parent: 2:db815d6d32e6
742 2
752 2
743 branch: default
753 branch: default
744 bookmarks: *Z Y x y
754 bookmarks: *Z Y x y
745 commit: 1 added, 1 unknown (new branch head) (no-fsmonitor !)
755 commit: 1 added, 1 unknown (new branch head) (no-fsmonitor !)
746 commit: 1 added, * unknown (new branch head) (glob) (fsmonitor !)
756 commit: 1 added, * unknown (new branch head) (glob) (fsmonitor !)
747 update: 2 new changesets (update)
757 update: 2 new changesets (update)
748 phases: 5 draft
758 phases: 5 draft
749 $ hg update
759 $ hg update
750 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
760 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
751 updating bookmark Z
761 updating bookmark Z
752 $ hg bookmarks
762 $ hg bookmarks
753 X2 1:925d80f479bb
763 X2 1:925d80f479bb
754 Y 2:db815d6d32e6
764 Y 2:db815d6d32e6
755 * Z 3:125c9a1d6df6
765 * Z 3:125c9a1d6df6
756 x y 2:db815d6d32e6
766 x y 2:db815d6d32e6
757
767
758 pull --update works the same as pull && update
768 pull --update works the same as pull && update
759
769
760 $ hg bookmark -r3 Y
770 $ hg bookmark -r3 Y
761 moving bookmark 'Y' forward from db815d6d32e6
771 moving bookmark 'Y' forward from db815d6d32e6
762 $ cp -R ../cloned-bookmarks-update ../cloned-bookmarks-manual-update
772 $ cp -R ../cloned-bookmarks-update ../cloned-bookmarks-manual-update
763 $ cp -R ../cloned-bookmarks-update ../cloned-bookmarks-manual-update-with-divergence
773 $ cp -R ../cloned-bookmarks-update ../cloned-bookmarks-manual-update-with-divergence
764
774
765 (manual version)
775 (manual version)
766
776
767 $ hg -R ../cloned-bookmarks-manual-update update Y
777 $ hg -R ../cloned-bookmarks-manual-update update Y
768 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
778 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
769 (activating bookmark Y)
779 (activating bookmark Y)
770 $ hg -R ../cloned-bookmarks-manual-update pull .
780 $ hg -R ../cloned-bookmarks-manual-update pull .
771 pulling from .
781 pulling from .
772 searching for changes
782 searching for changes
773 adding changesets
783 adding changesets
774 adding manifests
784 adding manifests
775 adding file changes
785 adding file changes
776 updating bookmark Y
786 updating bookmark Y
777 updating bookmark Z
787 updating bookmark Z
778 added 2 changesets with 2 changes to 2 files (+1 heads)
788 added 2 changesets with 2 changes to 2 files (+1 heads)
779 new changesets 125c9a1d6df6:9ba5f110a0b3
789 new changesets 125c9a1d6df6:9ba5f110a0b3
780 (run 'hg heads' to see heads, 'hg merge' to merge)
790 (run 'hg heads' to see heads, 'hg merge' to merge)
781
791
782 (# tests strange but with --date crashing when bookmark have to move)
792 (# tests strange but with --date crashing when bookmark have to move)
783
793
784 $ hg -R ../cloned-bookmarks-manual-update update -d 1986
794 $ hg -R ../cloned-bookmarks-manual-update update -d 1986
785 abort: revision matching date not found
795 abort: revision matching date not found
786 [255]
796 [255]
787 $ hg -R ../cloned-bookmarks-manual-update update
797 $ hg -R ../cloned-bookmarks-manual-update update
788 updating to active bookmark Y
798 updating to active bookmark Y
789 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
799 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
790
800
791 (all in one version)
801 (all in one version)
792
802
793 $ hg -R ../cloned-bookmarks-update update Y
803 $ hg -R ../cloned-bookmarks-update update Y
794 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
804 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
795 (activating bookmark Y)
805 (activating bookmark Y)
796 $ hg -R ../cloned-bookmarks-update pull --update .
806 $ hg -R ../cloned-bookmarks-update pull --update .
797 pulling from .
807 pulling from .
798 searching for changes
808 searching for changes
799 adding changesets
809 adding changesets
800 adding manifests
810 adding manifests
801 adding file changes
811 adding file changes
802 updating bookmark Y
812 updating bookmark Y
803 updating bookmark Z
813 updating bookmark Z
804 added 2 changesets with 2 changes to 2 files (+1 heads)
814 added 2 changesets with 2 changes to 2 files (+1 heads)
805 new changesets 125c9a1d6df6:9ba5f110a0b3
815 new changesets 125c9a1d6df6:9ba5f110a0b3
806 updating to active bookmark Y
816 updating to active bookmark Y
807 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
817 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
808
818
809 We warn about divergent during bare update to the active bookmark
819 We warn about divergent during bare update to the active bookmark
810
820
811 $ hg -R ../cloned-bookmarks-manual-update-with-divergence update Y
821 $ hg -R ../cloned-bookmarks-manual-update-with-divergence update Y
812 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
822 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
813 (activating bookmark Y)
823 (activating bookmark Y)
814 $ hg -R ../cloned-bookmarks-manual-update-with-divergence bookmarks -r X2 Y@1
824 $ hg -R ../cloned-bookmarks-manual-update-with-divergence bookmarks -r X2 Y@1
815 $ hg -R ../cloned-bookmarks-manual-update-with-divergence bookmarks
825 $ hg -R ../cloned-bookmarks-manual-update-with-divergence bookmarks
816 X2 1:925d80f479bb
826 X2 1:925d80f479bb
817 * Y 2:db815d6d32e6
827 * Y 2:db815d6d32e6
818 Y@1 1:925d80f479bb
828 Y@1 1:925d80f479bb
819 Z 2:db815d6d32e6
829 Z 2:db815d6d32e6
820 x y 2:db815d6d32e6
830 x y 2:db815d6d32e6
821 $ hg -R ../cloned-bookmarks-manual-update-with-divergence pull
831 $ hg -R ../cloned-bookmarks-manual-update-with-divergence pull
822 pulling from $TESTTMP/repo
832 pulling from $TESTTMP/repo
823 searching for changes
833 searching for changes
824 adding changesets
834 adding changesets
825 adding manifests
835 adding manifests
826 adding file changes
836 adding file changes
827 updating bookmark Y
837 updating bookmark Y
828 updating bookmark Z
838 updating bookmark Z
829 added 2 changesets with 2 changes to 2 files (+1 heads)
839 added 2 changesets with 2 changes to 2 files (+1 heads)
830 new changesets 125c9a1d6df6:9ba5f110a0b3
840 new changesets 125c9a1d6df6:9ba5f110a0b3
831 (run 'hg heads' to see heads, 'hg merge' to merge)
841 (run 'hg heads' to see heads, 'hg merge' to merge)
832 $ hg -R ../cloned-bookmarks-manual-update-with-divergence update
842 $ hg -R ../cloned-bookmarks-manual-update-with-divergence update
833 updating to active bookmark Y
843 updating to active bookmark Y
834 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
844 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
835 1 other divergent bookmarks for "Y"
845 1 other divergent bookmarks for "Y"
836
846
837 test wrongly formated bookmark
847 test wrongly formated bookmark
838
848
839 $ echo '' >> .hg/bookmarks
849 $ echo '' >> .hg/bookmarks
840 $ hg bookmarks
850 $ hg bookmarks
841 X2 1:925d80f479bb
851 X2 1:925d80f479bb
842 Y 3:125c9a1d6df6
852 Y 3:125c9a1d6df6
843 * Z 3:125c9a1d6df6
853 * Z 3:125c9a1d6df6
844 x y 2:db815d6d32e6
854 x y 2:db815d6d32e6
845 $ echo "Ican'thasformatedlines" >> .hg/bookmarks
855 $ echo "Ican'thasformatedlines" >> .hg/bookmarks
846 $ hg bookmarks
856 $ hg bookmarks
847 malformed line in .hg/bookmarks: "Ican'thasformatedlines"
857 malformed line in .hg/bookmarks: "Ican'thasformatedlines"
848 X2 1:925d80f479bb
858 X2 1:925d80f479bb
849 Y 3:125c9a1d6df6
859 Y 3:125c9a1d6df6
850 * Z 3:125c9a1d6df6
860 * Z 3:125c9a1d6df6
851 x y 2:db815d6d32e6
861 x y 2:db815d6d32e6
852
862
853 test missing revisions
863 test missing revisions
854
864
855 $ echo "925d80f479b925d80f479bc925d80f479bccabab z" > .hg/bookmarks
865 $ echo "925d80f479b925d80f479bc925d80f479bccabab z" > .hg/bookmarks
856 $ hg book
866 $ hg book
857 no bookmarks set
867 no bookmarks set
858
868
859 test stripping a non-checked-out but bookmarked revision
869 test stripping a non-checked-out but bookmarked revision
860
870
861 $ hg log --graph
871 $ hg log --graph
862 o changeset: 4:9ba5f110a0b3
872 o changeset: 4:9ba5f110a0b3
863 | branch: test
873 | branch: test
864 | tag: tip
874 | tag: tip
865 | parent: 2:db815d6d32e6
875 | parent: 2:db815d6d32e6
866 | user: test
876 | user: test
867 | date: Thu Jan 01 00:00:00 1970 +0000
877 | date: Thu Jan 01 00:00:00 1970 +0000
868 | summary: y
878 | summary: y
869 |
879 |
870 | @ changeset: 3:125c9a1d6df6
880 | @ changeset: 3:125c9a1d6df6
871 |/ user: test
881 |/ user: test
872 | date: Thu Jan 01 00:00:00 1970 +0000
882 | date: Thu Jan 01 00:00:00 1970 +0000
873 | summary: x
883 | summary: x
874 |
884 |
875 o changeset: 2:db815d6d32e6
885 o changeset: 2:db815d6d32e6
876 | parent: 0:f7b1eb17ad24
886 | parent: 0:f7b1eb17ad24
877 | user: test
887 | user: test
878 | date: Thu Jan 01 00:00:00 1970 +0000
888 | date: Thu Jan 01 00:00:00 1970 +0000
879 | summary: 2
889 | summary: 2
880 |
890 |
881 | o changeset: 1:925d80f479bb
891 | o changeset: 1:925d80f479bb
882 |/ user: test
892 |/ user: test
883 | date: Thu Jan 01 00:00:00 1970 +0000
893 | date: Thu Jan 01 00:00:00 1970 +0000
884 | summary: 1
894 | summary: 1
885 |
895 |
886 o changeset: 0:f7b1eb17ad24
896 o changeset: 0:f7b1eb17ad24
887 user: test
897 user: test
888 date: Thu Jan 01 00:00:00 1970 +0000
898 date: Thu Jan 01 00:00:00 1970 +0000
889 summary: 0
899 summary: 0
890
900
891 $ hg book should-end-on-two
901 $ hg book should-end-on-two
892 $ hg co --clean 4
902 $ hg co --clean 4
893 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
903 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
894 (leaving bookmark should-end-on-two)
904 (leaving bookmark should-end-on-two)
895 $ hg book four
905 $ hg book four
896 $ hg --config extensions.mq= strip 3
906 $ hg --config extensions.mq= strip 3
897 saved backup bundle to * (glob)
907 saved backup bundle to * (glob)
898 should-end-on-two should end up pointing to revision 2, as that's the
908 should-end-on-two should end up pointing to revision 2, as that's the
899 tipmost surviving ancestor of the stripped revision.
909 tipmost surviving ancestor of the stripped revision.
900 $ hg log --graph
910 $ hg log --graph
901 @ changeset: 3:9ba5f110a0b3
911 @ changeset: 3:9ba5f110a0b3
902 | branch: test
912 | branch: test
903 | bookmark: four
913 | bookmark: four
904 | tag: tip
914 | tag: tip
905 | user: test
915 | user: test
906 | date: Thu Jan 01 00:00:00 1970 +0000
916 | date: Thu Jan 01 00:00:00 1970 +0000
907 | summary: y
917 | summary: y
908 |
918 |
909 o changeset: 2:db815d6d32e6
919 o changeset: 2:db815d6d32e6
910 | bookmark: should-end-on-two
920 | bookmark: should-end-on-two
911 | parent: 0:f7b1eb17ad24
921 | parent: 0:f7b1eb17ad24
912 | user: test
922 | user: test
913 | date: Thu Jan 01 00:00:00 1970 +0000
923 | date: Thu Jan 01 00:00:00 1970 +0000
914 | summary: 2
924 | summary: 2
915 |
925 |
916 | o changeset: 1:925d80f479bb
926 | o changeset: 1:925d80f479bb
917 |/ user: test
927 |/ user: test
918 | date: Thu Jan 01 00:00:00 1970 +0000
928 | date: Thu Jan 01 00:00:00 1970 +0000
919 | summary: 1
929 | summary: 1
920 |
930 |
921 o changeset: 0:f7b1eb17ad24
931 o changeset: 0:f7b1eb17ad24
922 user: test
932 user: test
923 date: Thu Jan 01 00:00:00 1970 +0000
933 date: Thu Jan 01 00:00:00 1970 +0000
924 summary: 0
934 summary: 0
925
935
926
936
927 no-op update doesn't deactivate bookmarks
937 no-op update doesn't deactivate bookmarks
928
938
929 (it is known issue that fsmonitor can't handle nested repositories. In
939 (it is known issue that fsmonitor can't handle nested repositories. In
930 this test scenario, cloned-bookmark-default and tobundle exist in the
940 this test scenario, cloned-bookmark-default and tobundle exist in the
931 working directory of current repository)
941 working directory of current repository)
932
942
933 $ hg bookmarks
943 $ hg bookmarks
934 * four 3:9ba5f110a0b3
944 * four 3:9ba5f110a0b3
935 should-end-on-two 2:db815d6d32e6
945 should-end-on-two 2:db815d6d32e6
936 $ hg up four
946 $ hg up four
937 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
947 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
938 $ hg up
948 $ hg up
939 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
949 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
940 $ hg sum
950 $ hg sum
941 parent: 3:9ba5f110a0b3 tip
951 parent: 3:9ba5f110a0b3 tip
942 y
952 y
943 branch: test
953 branch: test
944 bookmarks: *four
954 bookmarks: *four
945 commit: 2 unknown (clean) (no-fsmonitor !)
955 commit: 2 unknown (clean) (no-fsmonitor !)
946 commit: * unknown (clean) (glob) (fsmonitor !)
956 commit: * unknown (clean) (glob) (fsmonitor !)
947 update: (current)
957 update: (current)
948 phases: 4 draft
958 phases: 4 draft
949
959
950 test clearing divergent bookmarks of linear ancestors
960 test clearing divergent bookmarks of linear ancestors
951
961
952 $ hg bookmark Z -r 0
962 $ hg bookmark Z -r 0
953 $ hg bookmark Z@1 -r 1
963 $ hg bookmark Z@1 -r 1
954 $ hg bookmark Z@2 -r 2
964 $ hg bookmark Z@2 -r 2
955 $ hg bookmark Z@3 -r 3
965 $ hg bookmark Z@3 -r 3
956 $ hg book
966 $ hg book
957 Z 0:f7b1eb17ad24
967 Z 0:f7b1eb17ad24
958 Z@1 1:925d80f479bb
968 Z@1 1:925d80f479bb
959 Z@2 2:db815d6d32e6
969 Z@2 2:db815d6d32e6
960 Z@3 3:9ba5f110a0b3
970 Z@3 3:9ba5f110a0b3
961 * four 3:9ba5f110a0b3
971 * four 3:9ba5f110a0b3
962 should-end-on-two 2:db815d6d32e6
972 should-end-on-two 2:db815d6d32e6
963 $ hg bookmark Z
973 $ hg bookmark Z
964 moving bookmark 'Z' forward from f7b1eb17ad24
974 moving bookmark 'Z' forward from f7b1eb17ad24
965 $ hg book
975 $ hg book
966 * Z 3:9ba5f110a0b3
976 * Z 3:9ba5f110a0b3
967 Z@1 1:925d80f479bb
977 Z@1 1:925d80f479bb
968 four 3:9ba5f110a0b3
978 four 3:9ba5f110a0b3
969 should-end-on-two 2:db815d6d32e6
979 should-end-on-two 2:db815d6d32e6
970
980
971 test clearing only a single divergent bookmark across branches
981 test clearing only a single divergent bookmark across branches
972
982
973 $ hg book foo -r 1
983 $ hg book foo -r 1
974 $ hg book foo@1 -r 0
984 $ hg book foo@1 -r 0
975 $ hg book foo@2 -r 2
985 $ hg book foo@2 -r 2
976 $ hg book foo@3 -r 3
986 $ hg book foo@3 -r 3
977 $ hg book foo -r foo@3
987 $ hg book foo -r foo@3
978 $ hg book
988 $ hg book
979 * Z 3:9ba5f110a0b3
989 * Z 3:9ba5f110a0b3
980 Z@1 1:925d80f479bb
990 Z@1 1:925d80f479bb
981 foo 3:9ba5f110a0b3
991 foo 3:9ba5f110a0b3
982 foo@1 0:f7b1eb17ad24
992 foo@1 0:f7b1eb17ad24
983 foo@2 2:db815d6d32e6
993 foo@2 2:db815d6d32e6
984 four 3:9ba5f110a0b3
994 four 3:9ba5f110a0b3
985 should-end-on-two 2:db815d6d32e6
995 should-end-on-two 2:db815d6d32e6
986
996
987 pull --update works the same as pull && update (case #2)
997 pull --update works the same as pull && update (case #2)
988
998
989 It is assumed that "hg pull" itself doesn't update current active
999 It is assumed that "hg pull" itself doesn't update current active
990 bookmark ('Y' in tests below).
1000 bookmark ('Y' in tests below).
991
1001
992 $ hg pull -q ../cloned-bookmarks-update
1002 $ hg pull -q ../cloned-bookmarks-update
993 divergent bookmark Z stored as Z@2
1003 divergent bookmark Z stored as Z@2
994
1004
995 (pulling revision on another named branch with --update updates
1005 (pulling revision on another named branch with --update updates
996 neither the working directory nor current active bookmark: "no-op"
1006 neither the working directory nor current active bookmark: "no-op"
997 case)
1007 case)
998
1008
999 $ echo yy >> y
1009 $ echo yy >> y
1000 $ hg commit -m yy
1010 $ hg commit -m yy
1001
1011
1002 $ hg -R ../cloned-bookmarks-update bookmarks | grep ' Y '
1012 $ hg -R ../cloned-bookmarks-update bookmarks | grep ' Y '
1003 * Y 3:125c9a1d6df6
1013 * Y 3:125c9a1d6df6
1004 $ hg -R ../cloned-bookmarks-update pull . --update
1014 $ hg -R ../cloned-bookmarks-update pull . --update
1005 pulling from .
1015 pulling from .
1006 searching for changes
1016 searching for changes
1007 adding changesets
1017 adding changesets
1008 adding manifests
1018 adding manifests
1009 adding file changes
1019 adding file changes
1010 divergent bookmark Z stored as Z@default
1020 divergent bookmark Z stored as Z@default
1011 adding remote bookmark foo
1021 adding remote bookmark foo
1012 adding remote bookmark four
1022 adding remote bookmark four
1013 adding remote bookmark should-end-on-two
1023 adding remote bookmark should-end-on-two
1014 added 1 changesets with 1 changes to 1 files
1024 added 1 changesets with 1 changes to 1 files
1015 new changesets 5fb12f0f2d51
1025 new changesets 5fb12f0f2d51
1016 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1026 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1017 $ hg -R ../cloned-bookmarks-update parents -T "{rev}:{node|short}\n"
1027 $ hg -R ../cloned-bookmarks-update parents -T "{rev}:{node|short}\n"
1018 3:125c9a1d6df6
1028 3:125c9a1d6df6
1019 $ hg -R ../cloned-bookmarks-update bookmarks | grep ' Y '
1029 $ hg -R ../cloned-bookmarks-update bookmarks | grep ' Y '
1020 * Y 3:125c9a1d6df6
1030 * Y 3:125c9a1d6df6
1021
1031
1022 (pulling revision on current named/topological branch with --update
1032 (pulling revision on current named/topological branch with --update
1023 updates the working directory and current active bookmark)
1033 updates the working directory and current active bookmark)
1024
1034
1025 $ hg update -C -q 125c9a1d6df6
1035 $ hg update -C -q 125c9a1d6df6
1026 $ echo xx >> x
1036 $ echo xx >> x
1027 $ hg commit -m xx
1037 $ hg commit -m xx
1028
1038
1029 $ hg -R ../cloned-bookmarks-update bookmarks | grep ' Y '
1039 $ hg -R ../cloned-bookmarks-update bookmarks | grep ' Y '
1030 * Y 3:125c9a1d6df6
1040 * Y 3:125c9a1d6df6
1031 $ hg -R ../cloned-bookmarks-update pull . --update
1041 $ hg -R ../cloned-bookmarks-update pull . --update
1032 pulling from .
1042 pulling from .
1033 searching for changes
1043 searching for changes
1034 adding changesets
1044 adding changesets
1035 adding manifests
1045 adding manifests
1036 adding file changes
1046 adding file changes
1037 divergent bookmark Z stored as Z@default
1047 divergent bookmark Z stored as Z@default
1038 added 1 changesets with 1 changes to 1 files
1048 added 1 changesets with 1 changes to 1 files
1039 new changesets 81dcce76aa0b
1049 new changesets 81dcce76aa0b
1040 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1050 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1041 updating bookmark Y
1051 updating bookmark Y
1042 $ hg -R ../cloned-bookmarks-update parents -T "{rev}:{node|short}\n"
1052 $ hg -R ../cloned-bookmarks-update parents -T "{rev}:{node|short}\n"
1043 6:81dcce76aa0b
1053 6:81dcce76aa0b
1044 $ hg -R ../cloned-bookmarks-update bookmarks | grep ' Y '
1054 $ hg -R ../cloned-bookmarks-update bookmarks | grep ' Y '
1045 * Y 6:81dcce76aa0b
1055 * Y 6:81dcce76aa0b
1046
1056
1047 $ cd ..
1057 $ cd ..
1048
1058
1049 ensure changelog is written before bookmarks
1059 ensure changelog is written before bookmarks
1050 $ hg init orderrepo
1060 $ hg init orderrepo
1051 $ cd orderrepo
1061 $ cd orderrepo
1052 $ touch a
1062 $ touch a
1053 $ hg commit -Aqm one
1063 $ hg commit -Aqm one
1054 $ hg book mybook
1064 $ hg book mybook
1055 $ echo a > a
1065 $ echo a > a
1056
1066
1057 $ cat > $TESTTMP/pausefinalize.py <<EOF
1067 $ cat > $TESTTMP/pausefinalize.py <<EOF
1058 > from __future__ import absolute_import
1068 > from __future__ import absolute_import
1059 > import os
1069 > import os
1060 > import time
1070 > import time
1061 > from mercurial import extensions, localrepo
1071 > from mercurial import extensions, localrepo
1062 > def transaction(orig, self, desc, report=None):
1072 > def transaction(orig, self, desc, report=None):
1063 > tr = orig(self, desc, report)
1073 > tr = orig(self, desc, report)
1064 > def sleep(*args, **kwargs):
1074 > def sleep(*args, **kwargs):
1065 > retry = 20
1075 > retry = 20
1066 > while retry > 0 and not os.path.exists(b"$TESTTMP/unpause"):
1076 > while retry > 0 and not os.path.exists(b"$TESTTMP/unpause"):
1067 > retry -= 1
1077 > retry -= 1
1068 > time.sleep(0.5)
1078 > time.sleep(0.5)
1069 > if os.path.exists(b"$TESTTMP/unpause"):
1079 > if os.path.exists(b"$TESTTMP/unpause"):
1070 > os.remove(b"$TESTTMP/unpause")
1080 > os.remove(b"$TESTTMP/unpause")
1071 > # It is important that this finalizer start with 'a', so it runs before
1081 > # It is important that this finalizer start with 'a', so it runs before
1072 > # the changelog finalizer appends to the changelog.
1082 > # the changelog finalizer appends to the changelog.
1073 > tr.addfinalize(b'a-sleep', sleep)
1083 > tr.addfinalize(b'a-sleep', sleep)
1074 > return tr
1084 > return tr
1075 >
1085 >
1076 > def extsetup(ui):
1086 > def extsetup(ui):
1077 > # This extension inserts an artifical pause during the transaction
1087 > # This extension inserts an artifical pause during the transaction
1078 > # finalizer, so we can run commands mid-transaction-close.
1088 > # finalizer, so we can run commands mid-transaction-close.
1079 > extensions.wrapfunction(localrepo.localrepository, 'transaction',
1089 > extensions.wrapfunction(localrepo.localrepository, 'transaction',
1080 > transaction)
1090 > transaction)
1081 > EOF
1091 > EOF
1082 $ hg commit -qm two --config extensions.pausefinalize=$TESTTMP/pausefinalize.py &
1092 $ hg commit -qm two --config extensions.pausefinalize=$TESTTMP/pausefinalize.py &
1083 $ sleep 2
1093 $ sleep 2
1084 $ hg log -r .
1094 $ hg log -r .
1085 changeset: 0:867bc5792c8c
1095 changeset: 0:867bc5792c8c
1086 bookmark: mybook
1096 bookmark: mybook
1087 tag: tip
1097 tag: tip
1088 user: test
1098 user: test
1089 date: Thu Jan 01 00:00:00 1970 +0000
1099 date: Thu Jan 01 00:00:00 1970 +0000
1090 summary: one
1100 summary: one
1091
1101
1092 $ hg bookmarks
1102 $ hg bookmarks
1093 * mybook 0:867bc5792c8c
1103 * mybook 0:867bc5792c8c
1094 $ touch $TESTTMP/unpause
1104 $ touch $TESTTMP/unpause
1095
1105
1096 $ cd ..
1106 $ cd ..
1097
1107
1098 check whether HG_PENDING makes pending changes only in related
1108 check whether HG_PENDING makes pending changes only in related
1099 repositories visible to an external hook.
1109 repositories visible to an external hook.
1100
1110
1101 (emulate a transaction running concurrently by copied
1111 (emulate a transaction running concurrently by copied
1102 .hg/bookmarks.pending in subsequent test)
1112 .hg/bookmarks.pending in subsequent test)
1103
1113
1104 $ cat > $TESTTMP/savepending.sh <<EOF
1114 $ cat > $TESTTMP/savepending.sh <<EOF
1105 > cp .hg/bookmarks.pending .hg/bookmarks.pending.saved
1115 > cp .hg/bookmarks.pending .hg/bookmarks.pending.saved
1106 > exit 1 # to avoid adding new bookmark for subsequent tests
1116 > exit 1 # to avoid adding new bookmark for subsequent tests
1107 > EOF
1117 > EOF
1108
1118
1109 $ hg init unrelated
1119 $ hg init unrelated
1110 $ cd unrelated
1120 $ cd unrelated
1111 $ echo a > a
1121 $ echo a > a
1112 $ hg add a
1122 $ hg add a
1113 $ hg commit -m '#0'
1123 $ hg commit -m '#0'
1114 $ hg --config hooks.pretxnclose="sh $TESTTMP/savepending.sh" bookmarks INVISIBLE
1124 $ hg --config hooks.pretxnclose="sh $TESTTMP/savepending.sh" bookmarks INVISIBLE
1115 transaction abort!
1125 transaction abort!
1116 rollback completed
1126 rollback completed
1117 abort: pretxnclose hook exited with status 1
1127 abort: pretxnclose hook exited with status 1
1118 [255]
1128 [255]
1119 $ cp .hg/bookmarks.pending.saved .hg/bookmarks.pending
1129 $ cp .hg/bookmarks.pending.saved .hg/bookmarks.pending
1120
1130
1121 (check visible bookmarks while transaction running in repo)
1131 (check visible bookmarks while transaction running in repo)
1122
1132
1123 $ cat > $TESTTMP/checkpending.sh <<EOF
1133 $ cat > $TESTTMP/checkpending.sh <<EOF
1124 > echo "@repo"
1134 > echo "@repo"
1125 > hg -R "$TESTTMP/repo" bookmarks
1135 > hg -R "$TESTTMP/repo" bookmarks
1126 > echo "@unrelated"
1136 > echo "@unrelated"
1127 > hg -R "$TESTTMP/unrelated" bookmarks
1137 > hg -R "$TESTTMP/unrelated" bookmarks
1128 > exit 1 # to avoid adding new bookmark for subsequent tests
1138 > exit 1 # to avoid adding new bookmark for subsequent tests
1129 > EOF
1139 > EOF
1130
1140
1131 $ cd ../repo
1141 $ cd ../repo
1132 $ hg --config hooks.pretxnclose="sh $TESTTMP/checkpending.sh" bookmarks NEW
1142 $ hg --config hooks.pretxnclose="sh $TESTTMP/checkpending.sh" bookmarks NEW
1133 @repo
1143 @repo
1134 * NEW 6:81dcce76aa0b
1144 * NEW 6:81dcce76aa0b
1135 X2 1:925d80f479bb
1145 X2 1:925d80f479bb
1136 Y 4:125c9a1d6df6
1146 Y 4:125c9a1d6df6
1137 Z 5:5fb12f0f2d51
1147 Z 5:5fb12f0f2d51
1138 Z@1 1:925d80f479bb
1148 Z@1 1:925d80f479bb
1139 Z@2 4:125c9a1d6df6
1149 Z@2 4:125c9a1d6df6
1140 foo 3:9ba5f110a0b3
1150 foo 3:9ba5f110a0b3
1141 foo@1 0:f7b1eb17ad24
1151 foo@1 0:f7b1eb17ad24
1142 foo@2 2:db815d6d32e6
1152 foo@2 2:db815d6d32e6
1143 four 3:9ba5f110a0b3
1153 four 3:9ba5f110a0b3
1144 should-end-on-two 2:db815d6d32e6
1154 should-end-on-two 2:db815d6d32e6
1145 x y 2:db815d6d32e6
1155 x y 2:db815d6d32e6
1146 @unrelated
1156 @unrelated
1147 no bookmarks set
1157 no bookmarks set
1148 transaction abort!
1158 transaction abort!
1149 rollback completed
1159 rollback completed
1150 abort: pretxnclose hook exited with status 1
1160 abort: pretxnclose hook exited with status 1
1151 [255]
1161 [255]
1152
1162
1153 Check pretxnclose-bookmark can abort a transaction
1163 Check pretxnclose-bookmark can abort a transaction
1154 --------------------------------------------------
1164 --------------------------------------------------
1155
1165
1156 add hooks:
1166 add hooks:
1157
1167
1158 * to prevent NEW bookmark on a non-public changeset
1168 * to prevent NEW bookmark on a non-public changeset
1159 * to prevent non-forward move of NEW bookmark
1169 * to prevent non-forward move of NEW bookmark
1160
1170
1161 $ cat << EOF >> .hg/hgrc
1171 $ cat << EOF >> .hg/hgrc
1162 > [hooks]
1172 > [hooks]
1163 > pretxnclose-bookmark.force-public = sh -c "(echo \$HG_BOOKMARK| grep -v NEW > /dev/null) || [ -z \"\$HG_NODE\" ] || (hg log -r \"\$HG_NODE\" -T '{phase}' | grep public > /dev/null)"
1173 > pretxnclose-bookmark.force-public = sh -c "(echo \$HG_BOOKMARK| grep -v NEW > /dev/null) || [ -z \"\$HG_NODE\" ] || (hg log -r \"\$HG_NODE\" -T '{phase}' | grep public > /dev/null)"
1164 > pretxnclose-bookmark.force-forward = sh -c "(echo \$HG_BOOKMARK| grep -v NEW > /dev/null) || [ -z \"\$HG_NODE\" ] || (hg log -r \"max(\$HG_OLDNODE::\$HG_NODE)\" -T 'MATCH' | grep MATCH > /dev/null)"
1174 > pretxnclose-bookmark.force-forward = sh -c "(echo \$HG_BOOKMARK| grep -v NEW > /dev/null) || [ -z \"\$HG_NODE\" ] || (hg log -r \"max(\$HG_OLDNODE::\$HG_NODE)\" -T 'MATCH' | grep MATCH > /dev/null)"
1165 > EOF
1175 > EOF
1166
1176
1167 $ hg log -G -T phases
1177 $ hg log -G -T phases
1168 @ changeset: 6:81dcce76aa0b
1178 @ changeset: 6:81dcce76aa0b
1169 | tag: tip
1179 | tag: tip
1170 | phase: draft
1180 | phase: draft
1171 | parent: 4:125c9a1d6df6
1181 | parent: 4:125c9a1d6df6
1172 | user: test
1182 | user: test
1173 | date: Thu Jan 01 00:00:00 1970 +0000
1183 | date: Thu Jan 01 00:00:00 1970 +0000
1174 | summary: xx
1184 | summary: xx
1175 |
1185 |
1176 | o changeset: 5:5fb12f0f2d51
1186 | o changeset: 5:5fb12f0f2d51
1177 | | branch: test
1187 | | branch: test
1178 | | bookmark: Z
1188 | | bookmark: Z
1179 | | phase: draft
1189 | | phase: draft
1180 | | parent: 3:9ba5f110a0b3
1190 | | parent: 3:9ba5f110a0b3
1181 | | user: test
1191 | | user: test
1182 | | date: Thu Jan 01 00:00:00 1970 +0000
1192 | | date: Thu Jan 01 00:00:00 1970 +0000
1183 | | summary: yy
1193 | | summary: yy
1184 | |
1194 | |
1185 o | changeset: 4:125c9a1d6df6
1195 o | changeset: 4:125c9a1d6df6
1186 | | bookmark: Y
1196 | | bookmark: Y
1187 | | bookmark: Z@2
1197 | | bookmark: Z@2
1188 | | phase: public
1198 | | phase: public
1189 | | parent: 2:db815d6d32e6
1199 | | parent: 2:db815d6d32e6
1190 | | user: test
1200 | | user: test
1191 | | date: Thu Jan 01 00:00:00 1970 +0000
1201 | | date: Thu Jan 01 00:00:00 1970 +0000
1192 | | summary: x
1202 | | summary: x
1193 | |
1203 | |
1194 | o changeset: 3:9ba5f110a0b3
1204 | o changeset: 3:9ba5f110a0b3
1195 |/ branch: test
1205 |/ branch: test
1196 | bookmark: foo
1206 | bookmark: foo
1197 | bookmark: four
1207 | bookmark: four
1198 | phase: public
1208 | phase: public
1199 | user: test
1209 | user: test
1200 | date: Thu Jan 01 00:00:00 1970 +0000
1210 | date: Thu Jan 01 00:00:00 1970 +0000
1201 | summary: y
1211 | summary: y
1202 |
1212 |
1203 o changeset: 2:db815d6d32e6
1213 o changeset: 2:db815d6d32e6
1204 | bookmark: foo@2
1214 | bookmark: foo@2
1205 | bookmark: should-end-on-two
1215 | bookmark: should-end-on-two
1206 | bookmark: x y
1216 | bookmark: x y
1207 | phase: public
1217 | phase: public
1208 | parent: 0:f7b1eb17ad24
1218 | parent: 0:f7b1eb17ad24
1209 | user: test
1219 | user: test
1210 | date: Thu Jan 01 00:00:00 1970 +0000
1220 | date: Thu Jan 01 00:00:00 1970 +0000
1211 | summary: 2
1221 | summary: 2
1212 |
1222 |
1213 | o changeset: 1:925d80f479bb
1223 | o changeset: 1:925d80f479bb
1214 |/ bookmark: X2
1224 |/ bookmark: X2
1215 | bookmark: Z@1
1225 | bookmark: Z@1
1216 | phase: public
1226 | phase: public
1217 | user: test
1227 | user: test
1218 | date: Thu Jan 01 00:00:00 1970 +0000
1228 | date: Thu Jan 01 00:00:00 1970 +0000
1219 | summary: 1
1229 | summary: 1
1220 |
1230 |
1221 o changeset: 0:f7b1eb17ad24
1231 o changeset: 0:f7b1eb17ad24
1222 bookmark: foo@1
1232 bookmark: foo@1
1223 phase: public
1233 phase: public
1224 user: test
1234 user: test
1225 date: Thu Jan 01 00:00:00 1970 +0000
1235 date: Thu Jan 01 00:00:00 1970 +0000
1226 summary: 0
1236 summary: 0
1227
1237
1228
1238
1229 attempt to create on a default changeset
1239 attempt to create on a default changeset
1230
1240
1231 $ hg bookmark -r 81dcce76aa0b NEW
1241 $ hg bookmark -r 81dcce76aa0b NEW
1232 transaction abort!
1242 transaction abort!
1233 rollback completed
1243 rollback completed
1234 abort: pretxnclose-bookmark.force-public hook exited with status 1
1244 abort: pretxnclose-bookmark.force-public hook exited with status 1
1235 [255]
1245 [255]
1236
1246
1237 create on a public changeset
1247 create on a public changeset
1238
1248
1239 $ hg bookmark -r 9ba5f110a0b3 NEW
1249 $ hg bookmark -r 9ba5f110a0b3 NEW
1240
1250
1241 move to the other branch
1251 move to the other branch
1242
1252
1243 $ hg bookmark -f -r 125c9a1d6df6 NEW
1253 $ hg bookmark -f -r 125c9a1d6df6 NEW
1244 transaction abort!
1254 transaction abort!
1245 rollback completed
1255 rollback completed
1246 abort: pretxnclose-bookmark.force-forward hook exited with status 1
1256 abort: pretxnclose-bookmark.force-forward hook exited with status 1
1247 [255]
1257 [255]
General Comments 0
You need to be logged in to leave comments. Login now