##// END OF EJS Templates
bookmarks: fixes bug where a deleted bookmark may still be treated as current when track.current option is set
Alex Unden -
r7817:cb516e78 default
parent child Browse files
Show More
@@ -1,319 +1,321 b''
1 # Mercurial extension to provide the 'hg bookmark' command
1 # Mercurial extension to provide the 'hg bookmark' command
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
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 '''mercurial bookmarks
8 '''mercurial bookmarks
9
9
10 Mercurial bookmarks are local moveable pointers to changesets. Every
10 Mercurial bookmarks are local moveable pointers to changesets. Every
11 bookmark points to a changeset identified by its hash. If you commit a
11 bookmark points to a changeset identified by its hash. If you commit a
12 changeset that is based on a changeset that has a bookmark on it, the
12 changeset that is based on a changeset that has a bookmark on it, the
13 bookmark is forwarded to the new changeset.
13 bookmark is forwarded to the new changeset.
14
14
15 It is possible to use bookmark names in every revision lookup (e.g. hg
15 It is possible to use bookmark names in every revision lookup (e.g. hg
16 merge, hg update).
16 merge, hg update).
17
17
18 The bookmark extension offers the possiblity to have a more git-like experience
18 The bookmark extension offers the possiblity to have a more git-like experience
19 by adding the following configuration option to your .hgrc:
19 by adding the following configuration option to your .hgrc:
20
20
21 [bookmarks]
21 [bookmarks]
22 track.current = True
22 track.current = True
23
23
24 This will cause bookmarks to track the bookmark that you are currently on, and
24 This will cause bookmarks to track the bookmark that you are currently on, and
25 just updates it. This is similar to git's approach of branching.
25 just updates it. This is similar to git's approach of branching.
26 '''
26 '''
27
27
28 from mercurial.i18n import _
28 from mercurial.i18n import _
29 from mercurial.node import nullid, nullrev, hex, short
29 from mercurial.node import nullid, nullrev, hex, short
30 from mercurial import util, commands, localrepo, repair, extensions
30 from mercurial import util, commands, localrepo, repair, extensions
31 import os
31 import os
32
32
33 def parse(repo):
33 def parse(repo):
34 '''Parse .hg/bookmarks file and return a dictionary
34 '''Parse .hg/bookmarks file and return a dictionary
35
35
36 Bookmarks are stored as {HASH}\\s{NAME}\\n (localtags format) values
36 Bookmarks are stored as {HASH}\\s{NAME}\\n (localtags format) values
37 in the .hg/bookmarks file. They are read by the parse() method and
37 in the .hg/bookmarks file. They are read by the parse() method and
38 returned as a dictionary with name => hash values.
38 returned as a dictionary with name => hash values.
39
39
40 The parsed dictionary is cached until a write() operation is done.
40 The parsed dictionary is cached until a write() operation is done.
41 '''
41 '''
42 try:
42 try:
43 if repo._bookmarks:
43 if repo._bookmarks:
44 return repo._bookmarks
44 return repo._bookmarks
45 repo._bookmarks = {}
45 repo._bookmarks = {}
46 for line in repo.opener('bookmarks'):
46 for line in repo.opener('bookmarks'):
47 sha, refspec = line.strip().split(' ', 1)
47 sha, refspec = line.strip().split(' ', 1)
48 repo._bookmarks[refspec] = repo.lookup(sha)
48 repo._bookmarks[refspec] = repo.lookup(sha)
49 except:
49 except:
50 pass
50 pass
51 return repo._bookmarks
51 return repo._bookmarks
52
52
53 def write(repo, refs):
53 def write(repo, refs):
54 '''Write bookmarks
54 '''Write bookmarks
55
55
56 Write the given bookmark => hash dictionary to the .hg/bookmarks file
56 Write the given bookmark => hash dictionary to the .hg/bookmarks file
57 in a format equal to those of localtags.
57 in a format equal to those of localtags.
58
58
59 We also store a backup of the previous state in undo.bookmarks that
59 We also store a backup of the previous state in undo.bookmarks that
60 can be copied back on rollback.
60 can be copied back on rollback.
61 '''
61 '''
62 if os.path.exists(repo.join('bookmarks')):
62 if os.path.exists(repo.join('bookmarks')):
63 util.copyfile(repo.join('bookmarks'), repo.join('undo.bookmarks'))
63 util.copyfile(repo.join('bookmarks'), repo.join('undo.bookmarks'))
64 if current(repo) not in refs:
64 if current(repo) not in refs:
65 setcurrent(repo, None)
65 setcurrent(repo, None)
66 file = repo.opener('bookmarks', 'w+')
66 file = repo.opener('bookmarks', 'w+')
67 for refspec, node in refs.iteritems():
67 for refspec, node in refs.iteritems():
68 file.write("%s %s\n" % (hex(node), refspec))
68 file.write("%s %s\n" % (hex(node), refspec))
69 file.close()
69 file.close()
70
70
71 def current(repo):
71 def current(repo):
72 '''Get the current bookmark
72 '''Get the current bookmark
73
73
74 If we use gittishsh branches we have a current bookmark that
74 If we use gittishsh branches we have a current bookmark that
75 we are on. This function returns the name of the bookmark. It
75 we are on. This function returns the name of the bookmark. It
76 is stored in .hg/bookmarks.current
76 is stored in .hg/bookmarks.current
77 '''
77 '''
78 if repo._bookmarkcurrent:
78 if repo._bookmarkcurrent:
79 return repo._bookmarkcurrent
79 return repo._bookmarkcurrent
80 mark = None
80 mark = None
81 if os.path.exists(repo.join('bookmarks.current')):
81 if os.path.exists(repo.join('bookmarks.current')):
82 file = repo.opener('bookmarks.current')
82 file = repo.opener('bookmarks.current')
83 # No readline() in posixfile_nt, reading everything is cheap
83 # No readline() in posixfile_nt, reading everything is cheap
84 mark = (file.readlines() or [''])[0]
84 mark = (file.readlines() or [''])[0]
85 if mark == '':
85 if mark == '':
86 mark = None
86 mark = None
87 file.close()
87 file.close()
88 repo._bookmarkcurrent = mark
88 repo._bookmarkcurrent = mark
89 return mark
89 return mark
90
90
91 def setcurrent(repo, mark):
91 def setcurrent(repo, mark):
92 '''Set the name of the bookmark that we are currently on
92 '''Set the name of the bookmark that we are currently on
93
93
94 Set the name of the bookmark that we are on (hg update <bookmark>).
94 Set the name of the bookmark that we are on (hg update <bookmark>).
95 The name is recoreded in .hg/bookmarks.current
95 The name is recoreded in .hg/bookmarks.current
96 '''
96 '''
97 if current(repo) == mark:
97 if current(repo) == mark:
98 return
98 return
99
99
100 refs = parse(repo)
100 refs = parse(repo)
101
101
102 # do not update if we do update to a rev equal to the current bookmark
102 # do not update if we do update to a rev equal to the current bookmark
103 if (mark not in refs and
103 if (mark and mark not in refs and
104 current(repo) and refs[current(repo)] == repo.changectx('.').node()):
104 current(repo) and refs[current(repo)] == repo.changectx('.').node()):
105 return
105 return
106 if mark not in refs:
106 if mark not in refs:
107 mark = ''
107 mark = ''
108 file = repo.opener('bookmarks.current', 'w+')
108 file = repo.opener('bookmarks.current', 'w+')
109 file.write(mark)
109 file.write(mark)
110 file.close()
110 file.close()
111 repo._bookmarkcurrent = mark
111 repo._bookmarkcurrent = mark
112
112
113 def bookmark(ui, repo, mark=None, rev=None, force=False, delete=False, rename=None):
113 def bookmark(ui, repo, mark=None, rev=None, force=False, delete=False, rename=None):
114 '''mercurial bookmarks
114 '''mercurial bookmarks
115
115
116 Bookmarks are pointers to certain commits that move when
116 Bookmarks are pointers to certain commits that move when
117 commiting. Bookmarks are local. They can be renamed, copied and
117 commiting. Bookmarks are local. They can be renamed, copied and
118 deleted. It is possible to use bookmark names in 'hg merge' and 'hg
118 deleted. It is possible to use bookmark names in 'hg merge' and 'hg
119 update' to update to a given bookmark.
119 update' to update to a given bookmark.
120
120
121 You can use 'hg bookmark NAME' to set a bookmark on the current
121 You can use 'hg bookmark NAME' to set a bookmark on the current
122 tip with the given name. If you specify a revision using -r REV
122 tip with the given name. If you specify a revision using -r REV
123 (where REV may be an existing bookmark), the bookmark is set to
123 (where REV may be an existing bookmark), the bookmark is set to
124 that revision.
124 that revision.
125 '''
125 '''
126 hexfn = ui.debugflag and hex or short
126 hexfn = ui.debugflag and hex or short
127 marks = parse(repo)
127 marks = parse(repo)
128 cur = repo.changectx('.').node()
128 cur = repo.changectx('.').node()
129
129
130 if rename:
130 if rename:
131 if rename not in marks:
131 if rename not in marks:
132 raise util.Abort(_("a bookmark of this name does not exist"))
132 raise util.Abort(_("a bookmark of this name does not exist"))
133 if mark in marks and not force:
133 if mark in marks and not force:
134 raise util.Abort(_("a bookmark of the same name already exists"))
134 raise util.Abort(_("a bookmark of the same name already exists"))
135 if mark is None:
135 if mark is None:
136 raise util.Abort(_("new bookmark name required"))
136 raise util.Abort(_("new bookmark name required"))
137 marks[mark] = marks[rename]
137 marks[mark] = marks[rename]
138 del marks[rename]
138 del marks[rename]
139 if current(repo) == rename:
139 if current(repo) == rename:
140 setcurrent(repo, mark)
140 setcurrent(repo, mark)
141 write(repo, marks)
141 write(repo, marks)
142 return
142 return
143
143
144 if delete:
144 if delete:
145 if mark == None:
145 if mark == None:
146 raise util.Abort(_("bookmark name required"))
146 raise util.Abort(_("bookmark name required"))
147 if mark not in marks:
147 if mark not in marks:
148 raise util.Abort(_("a bookmark of this name does not exist"))
148 raise util.Abort(_("a bookmark of this name does not exist"))
149 if mark == current(repo):
150 setcurrent(repo, None)
149 del marks[mark]
151 del marks[mark]
150 write(repo, marks)
152 write(repo, marks)
151 return
153 return
152
154
153 if mark != None:
155 if mark != None:
154 if "\n" in mark:
156 if "\n" in mark:
155 raise util.Abort(_("bookmark name cannot contain newlines"))
157 raise util.Abort(_("bookmark name cannot contain newlines"))
156 mark = mark.strip()
158 mark = mark.strip()
157 if mark in marks and not force:
159 if mark in marks and not force:
158 raise util.Abort(_("a bookmark of the same name already exists"))
160 raise util.Abort(_("a bookmark of the same name already exists"))
159 if ((mark in repo.branchtags() or mark == repo.dirstate.branch())
161 if ((mark in repo.branchtags() or mark == repo.dirstate.branch())
160 and not force):
162 and not force):
161 raise util.Abort(
163 raise util.Abort(
162 _("a bookmark cannot have the name of an existing branch"))
164 _("a bookmark cannot have the name of an existing branch"))
163 if rev:
165 if rev:
164 marks[mark] = repo.lookup(rev)
166 marks[mark] = repo.lookup(rev)
165 else:
167 else:
166 marks[mark] = repo.changectx('.').node()
168 marks[mark] = repo.changectx('.').node()
167 setcurrent(repo, mark)
169 setcurrent(repo, mark)
168 write(repo, marks)
170 write(repo, marks)
169 return
171 return
170
172
171 if mark == None:
173 if mark == None:
172 if rev:
174 if rev:
173 raise util.Abort(_("bookmark name required"))
175 raise util.Abort(_("bookmark name required"))
174 if len(marks) == 0:
176 if len(marks) == 0:
175 ui.status("no bookmarks set\n")
177 ui.status("no bookmarks set\n")
176 else:
178 else:
177 for bmark, n in marks.iteritems():
179 for bmark, n in marks.iteritems():
178 if ui.configbool('bookmarks', 'track.current'):
180 if ui.configbool('bookmarks', 'track.current'):
179 prefix = (bmark == current(repo) and n == cur) and '*' or ' '
181 prefix = (bmark == current(repo) and n == cur) and '*' or ' '
180 else:
182 else:
181 prefix = (n == cur) and '*' or ' '
183 prefix = (n == cur) and '*' or ' '
182
184
183 ui.write(" %s %-25s %d:%s\n" % (
185 ui.write(" %s %-25s %d:%s\n" % (
184 prefix, bmark, repo.changelog.rev(n), hexfn(n)))
186 prefix, bmark, repo.changelog.rev(n), hexfn(n)))
185 return
187 return
186
188
187 def _revstostrip(changelog, node):
189 def _revstostrip(changelog, node):
188 srev = changelog.rev(node)
190 srev = changelog.rev(node)
189 tostrip = [srev]
191 tostrip = [srev]
190 saveheads = []
192 saveheads = []
191 for r in xrange(srev, len(changelog)):
193 for r in xrange(srev, len(changelog)):
192 parents = changelog.parentrevs(r)
194 parents = changelog.parentrevs(r)
193 if parents[0] in tostrip or parents[1] in tostrip:
195 if parents[0] in tostrip or parents[1] in tostrip:
194 tostrip.append(r)
196 tostrip.append(r)
195 if parents[1] != nullrev:
197 if parents[1] != nullrev:
196 for p in parents:
198 for p in parents:
197 if p not in tostrip and p > srev:
199 if p not in tostrip and p > srev:
198 saveheads.append(p)
200 saveheads.append(p)
199 return [r for r in tostrip if r not in saveheads]
201 return [r for r in tostrip if r not in saveheads]
200
202
201 def strip(oldstrip, ui, repo, node, backup="all"):
203 def strip(oldstrip, ui, repo, node, backup="all"):
202 """Strip bookmarks if revisions are stripped using
204 """Strip bookmarks if revisions are stripped using
203 the mercurial.strip method. This usually happens during
205 the mercurial.strip method. This usually happens during
204 qpush and qpop"""
206 qpush and qpop"""
205 revisions = _revstostrip(repo.changelog, node)
207 revisions = _revstostrip(repo.changelog, node)
206 marks = parse(repo)
208 marks = parse(repo)
207 update = []
209 update = []
208 for mark, n in marks.iteritems():
210 for mark, n in marks.iteritems():
209 if repo.changelog.rev(n) in revisions:
211 if repo.changelog.rev(n) in revisions:
210 update.append(mark)
212 update.append(mark)
211 oldstrip(ui, repo, node, backup)
213 oldstrip(ui, repo, node, backup)
212 if len(update) > 0:
214 if len(update) > 0:
213 for m in update:
215 for m in update:
214 marks[m] = repo.changectx('.').node()
216 marks[m] = repo.changectx('.').node()
215 write(repo, marks)
217 write(repo, marks)
216
218
217 def reposetup(ui, repo):
219 def reposetup(ui, repo):
218 if not isinstance(repo, localrepo.localrepository):
220 if not isinstance(repo, localrepo.localrepository):
219 return
221 return
220
222
221 # init a bookmark cache as otherwise we would get a infinite reading
223 # init a bookmark cache as otherwise we would get a infinite reading
222 # in lookup()
224 # in lookup()
223 repo._bookmarks = None
225 repo._bookmarks = None
224 repo._bookmarkcurrent = None
226 repo._bookmarkcurrent = None
225
227
226 class bookmark_repo(repo.__class__):
228 class bookmark_repo(repo.__class__):
227 def rollback(self):
229 def rollback(self):
228 if os.path.exists(self.join('undo.bookmarks')):
230 if os.path.exists(self.join('undo.bookmarks')):
229 util.rename(self.join('undo.bookmarks'), self.join('bookmarks'))
231 util.rename(self.join('undo.bookmarks'), self.join('bookmarks'))
230 return super(bookmark_repo, self).rollback()
232 return super(bookmark_repo, self).rollback()
231
233
232 def lookup(self, key):
234 def lookup(self, key):
233 if self._bookmarks is None:
235 if self._bookmarks is None:
234 self._bookmarks = parse(self)
236 self._bookmarks = parse(self)
235 if key in self._bookmarks:
237 if key in self._bookmarks:
236 key = self._bookmarks[key]
238 key = self._bookmarks[key]
237 return super(bookmark_repo, self).lookup(key)
239 return super(bookmark_repo, self).lookup(key)
238
240
239 def commit(self, *k, **kw):
241 def commit(self, *k, **kw):
240 """Add a revision to the repository and
242 """Add a revision to the repository and
241 move the bookmark"""
243 move the bookmark"""
242 node = super(bookmark_repo, self).commit(*k, **kw)
244 node = super(bookmark_repo, self).commit(*k, **kw)
243 if node == None:
245 if node == None:
244 return None
246 return None
245 parents = repo.changelog.parents(node)
247 parents = repo.changelog.parents(node)
246 if parents[1] == nullid:
248 if parents[1] == nullid:
247 parents = (parents[0],)
249 parents = (parents[0],)
248 marks = parse(repo)
250 marks = parse(repo)
249 update = False
251 update = False
250 for mark, n in marks.items():
252 for mark, n in marks.items():
251 if ui.configbool('bookmarks', 'track.current'):
253 if ui.configbool('bookmarks', 'track.current'):
252 if mark == current(repo) and n in parents:
254 if mark == current(repo) and n in parents:
253 marks[mark] = node
255 marks[mark] = node
254 update = True
256 update = True
255 else:
257 else:
256 if n in parents:
258 if n in parents:
257 marks[mark] = node
259 marks[mark] = node
258 update = True
260 update = True
259 if update:
261 if update:
260 write(repo, marks)
262 write(repo, marks)
261 return node
263 return node
262
264
263 def addchangegroup(self, source, srctype, url, emptyok=False):
265 def addchangegroup(self, source, srctype, url, emptyok=False):
264 parents = repo.dirstate.parents()
266 parents = repo.dirstate.parents()
265
267
266 result = super(bookmark_repo, self).addchangegroup(
268 result = super(bookmark_repo, self).addchangegroup(
267 source, srctype, url, emptyok)
269 source, srctype, url, emptyok)
268 if result > 1:
270 if result > 1:
269 # We have more heads than before
271 # We have more heads than before
270 return result
272 return result
271 node = repo.changelog.tip()
273 node = repo.changelog.tip()
272 marks = parse(repo)
274 marks = parse(repo)
273 update = False
275 update = False
274 for mark, n in marks.items():
276 for mark, n in marks.items():
275 if n in parents:
277 if n in parents:
276 marks[mark] = node
278 marks[mark] = node
277 update = True
279 update = True
278 if update:
280 if update:
279 write(repo, marks)
281 write(repo, marks)
280 return result
282 return result
281
283
282 def tags(self):
284 def tags(self):
283 """Merge bookmarks with normal tags"""
285 """Merge bookmarks with normal tags"""
284 if self.tagscache:
286 if self.tagscache:
285 return self.tagscache
287 return self.tagscache
286
288
287 tagscache = super(bookmark_repo, self).tags()
289 tagscache = super(bookmark_repo, self).tags()
288 tagscache.update(parse(repo))
290 tagscache.update(parse(repo))
289 return tagscache
291 return tagscache
290
292
291 repo.__class__ = bookmark_repo
293 repo.__class__ = bookmark_repo
292
294
293 def uisetup(ui):
295 def uisetup(ui):
294 extensions.wrapfunction(repair, "strip", strip)
296 extensions.wrapfunction(repair, "strip", strip)
295 if ui.configbool('bookmarks', 'track.current'):
297 if ui.configbool('bookmarks', 'track.current'):
296 extensions.wrapcommand(commands.table, 'update', updatecurbookmark)
298 extensions.wrapcommand(commands.table, 'update', updatecurbookmark)
297
299
298 def updatecurbookmark(orig, ui, repo, *args, **opts):
300 def updatecurbookmark(orig, ui, repo, *args, **opts):
299 '''Set the current bookmark
301 '''Set the current bookmark
300
302
301 If the user updates to a bookmark we update the .hg/bookmarks.current
303 If the user updates to a bookmark we update the .hg/bookmarks.current
302 file.
304 file.
303 '''
305 '''
304 res = orig(ui, repo, *args, **opts)
306 res = orig(ui, repo, *args, **opts)
305 rev = opts['rev']
307 rev = opts['rev']
306 if not rev and len(args) > 0:
308 if not rev and len(args) > 0:
307 rev = args[0]
309 rev = args[0]
308 setcurrent(repo, rev)
310 setcurrent(repo, rev)
309 return res
311 return res
310
312
311 cmdtable = {
313 cmdtable = {
312 "bookmarks":
314 "bookmarks":
313 (bookmark,
315 (bookmark,
314 [('f', 'force', False, _('force')),
316 [('f', 'force', False, _('force')),
315 ('r', 'rev', '', _('revision')),
317 ('r', 'rev', '', _('revision')),
316 ('d', 'delete', False, _('delete a given bookmark')),
318 ('d', 'delete', False, _('delete a given bookmark')),
317 ('m', 'rename', '', _('rename a given bookmark'))],
319 ('m', 'rename', '', _('rename a given bookmark'))],
318 _('hg bookmarks [-f] [-d] [-m NAME] [-r NAME] [NAME]')),
320 _('hg bookmarks [-f] [-d] [-m NAME] [-r NAME] [NAME]')),
319 }
321 }
@@ -1,44 +1,55 b''
1 #!/bin/sh
1 #!/bin/sh
2
2
3 echo "[extensions]" >> $HGRCPATH
3 echo "[extensions]" >> $HGRCPATH
4 echo "bookmarks=" >> $HGRCPATH
4 echo "bookmarks=" >> $HGRCPATH
5
5
6 echo "[bookmarks]" >> $HGRCPATH
6 echo "[bookmarks]" >> $HGRCPATH
7 echo "track.current = True" >> $HGRCPATH
7 echo "track.current = True" >> $HGRCPATH
8
8
9 hg init
9 hg init
10
10
11 echo % no bookmarks
11 echo % no bookmarks
12 hg bookmarks
12 hg bookmarks
13
13
14 echo % set bookmark X
14 echo % set bookmark X
15 hg bookmark X
15 hg bookmark X
16
16
17 echo % list bookmarks
17 echo % list bookmarks
18 hg bookmark
18 hg bookmark
19
19
20 echo % update to bookmark X
20 echo % update to bookmark X
21 hg update X
21 hg update X
22
22
23 echo % list bookmarks
23 echo % list bookmarks
24 hg bookmarks
24 hg bookmarks
25
25
26 echo % rename
26 echo % rename
27 hg bookmark -m X Z
27 hg bookmark -m X Z
28
28
29 echo % list bookmarks
29 echo % list bookmarks
30 hg bookmarks
30 hg bookmarks
31
31
32 echo % new bookmark Y
32 echo % new bookmark Y
33 hg bookmark Y
33 hg bookmark Y
34
34
35 echo % list bookmarks
35 echo % list bookmarks
36 hg bookmark
36 hg bookmark
37
37
38 echo % commit
38 echo % commit
39 echo 'b' > b
39 echo 'b' > b
40 hg add b
40 hg add b
41 hg commit -m'test'
41 hg commit -m'test'
42
42
43 echo % list bookmarks
43 echo % list bookmarks
44 hg bookmark
44 hg bookmark
45
46 echo % delete bookmarks
47 hg bookmark -d Y
48 hg bookmark -d Z
49
50 echo % list bookmarks
51 hg bookmark
52
53 echo % update to tip
54 hg update tip
55
@@ -1,20 +1,25 b''
1 % no bookmarks
1 % no bookmarks
2 no bookmarks set
2 no bookmarks set
3 % set bookmark X
3 % set bookmark X
4 % list bookmarks
4 % list bookmarks
5 * X -1:000000000000
5 * X -1:000000000000
6 % update to bookmark X
6 % update to bookmark X
7 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
7 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
8 % list bookmarks
8 % list bookmarks
9 * X -1:000000000000
9 * X -1:000000000000
10 % rename
10 % rename
11 % list bookmarks
11 % list bookmarks
12 * Z -1:000000000000
12 * Z -1:000000000000
13 % new bookmark Y
13 % new bookmark Y
14 % list bookmarks
14 % list bookmarks
15 * Y -1:000000000000
15 * Y -1:000000000000
16 Z -1:000000000000
16 Z -1:000000000000
17 % commit
17 % commit
18 % list bookmarks
18 % list bookmarks
19 * Y 0:719295282060
19 * Y 0:719295282060
20 Z -1:000000000000
20 Z -1:000000000000
21 % delete bookmarks
22 % list bookmarks
23 no bookmarks set
24 % update to tip
25 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
General Comments 0
You need to be logged in to leave comments. Login now