##// END OF EJS Templates
bookmarks: pull known bookmarks from server that are newer
Matt Mackall -
r11373:306fef84 default
parent child Browse files
Show More
@@ -1,367 +1,393 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 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 '''track a line of development with movable markers
8 '''track a line of development with movable markers
9
9
10 Bookmarks are local movable markers to changesets. Every bookmark
10 Bookmarks are local movable markers to changesets. Every bookmark
11 points to a changeset identified by its hash. If you commit a
11 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 shifts to the new changeset.
13 bookmark shifts to the new changeset.
14
14
15 It is possible to use bookmark names in every revision lookup (e.g.
15 It is possible to use bookmark names in every revision lookup (e.g.
16 :hg:`merge`, :hg:`update`).
16 :hg:`merge`, :hg:`update`).
17
17
18 By default, when several bookmarks point to the same changeset, they
18 By default, when several bookmarks point to the same changeset, they
19 will all move forward together. It is possible to obtain a more
19 will all move forward together. It is possible to obtain a more
20 git-like experience by adding the following configuration option to
20 git-like experience by adding the following configuration option to
21 your .hgrc::
21 your .hgrc::
22
22
23 [bookmarks]
23 [bookmarks]
24 track.current = True
24 track.current = True
25
25
26 This will cause Mercurial to track the bookmark that you are currently
26 This will cause Mercurial to track the bookmark that you are currently
27 using, and only update it. This is similar to git's approach to
27 using, and only update it. This is similar to git's approach to
28 branching.
28 branching.
29 '''
29 '''
30
30
31 from mercurial.i18n import _
31 from mercurial.i18n import _
32 from mercurial.node import nullid, nullrev, hex, short
32 from mercurial.node import nullid, nullrev, hex, short
33 from mercurial import util, commands, repair, extensions, pushkey
33 from mercurial import util, commands, repair, extensions, pushkey
34 import os
34 import os
35
35
36 def write(repo):
36 def write(repo):
37 '''Write bookmarks
37 '''Write bookmarks
38
38
39 Write the given bookmark => hash dictionary to the .hg/bookmarks file
39 Write the given bookmark => hash dictionary to the .hg/bookmarks file
40 in a format equal to those of localtags.
40 in a format equal to those of localtags.
41
41
42 We also store a backup of the previous state in undo.bookmarks that
42 We also store a backup of the previous state in undo.bookmarks that
43 can be copied back on rollback.
43 can be copied back on rollback.
44 '''
44 '''
45 refs = repo._bookmarks
45 refs = repo._bookmarks
46 if os.path.exists(repo.join('bookmarks')):
46 if os.path.exists(repo.join('bookmarks')):
47 util.copyfile(repo.join('bookmarks'), repo.join('undo.bookmarks'))
47 util.copyfile(repo.join('bookmarks'), repo.join('undo.bookmarks'))
48 if repo._bookmarkcurrent not in refs:
48 if repo._bookmarkcurrent not in refs:
49 setcurrent(repo, None)
49 setcurrent(repo, None)
50 wlock = repo.wlock()
50 wlock = repo.wlock()
51 try:
51 try:
52 file = repo.opener('bookmarks', 'w', atomictemp=True)
52 file = repo.opener('bookmarks', 'w', atomictemp=True)
53 for refspec, node in refs.iteritems():
53 for refspec, node in refs.iteritems():
54 file.write("%s %s\n" % (hex(node), refspec))
54 file.write("%s %s\n" % (hex(node), refspec))
55 file.rename()
55 file.rename()
56 finally:
56 finally:
57 wlock.release()
57 wlock.release()
58
58
59 def setcurrent(repo, mark):
59 def setcurrent(repo, mark):
60 '''Set the name of the bookmark that we are currently on
60 '''Set the name of the bookmark that we are currently on
61
61
62 Set the name of the bookmark that we are on (hg update <bookmark>).
62 Set the name of the bookmark that we are on (hg update <bookmark>).
63 The name is recorded in .hg/bookmarks.current
63 The name is recorded in .hg/bookmarks.current
64 '''
64 '''
65 current = repo._bookmarkcurrent
65 current = repo._bookmarkcurrent
66 if current == mark:
66 if current == mark:
67 return
67 return
68
68
69 refs = repo._bookmarks
69 refs = repo._bookmarks
70
70
71 # do not update if we do update to a rev equal to the current bookmark
71 # do not update if we do update to a rev equal to the current bookmark
72 if (mark and mark not in refs and
72 if (mark and mark not in refs and
73 current and refs[current] == repo.changectx('.').node()):
73 current and refs[current] == repo.changectx('.').node()):
74 return
74 return
75 if mark not in refs:
75 if mark not in refs:
76 mark = ''
76 mark = ''
77 wlock = repo.wlock()
77 wlock = repo.wlock()
78 try:
78 try:
79 file = repo.opener('bookmarks.current', 'w', atomictemp=True)
79 file = repo.opener('bookmarks.current', 'w', atomictemp=True)
80 file.write(mark)
80 file.write(mark)
81 file.rename()
81 file.rename()
82 finally:
82 finally:
83 wlock.release()
83 wlock.release()
84 repo._bookmarkcurrent = mark
84 repo._bookmarkcurrent = mark
85
85
86 def bookmark(ui, repo, mark=None, rev=None, force=False, delete=False, rename=None):
86 def bookmark(ui, repo, mark=None, rev=None, force=False, delete=False, rename=None):
87 '''track a line of development with movable markers
87 '''track a line of development with movable markers
88
88
89 Bookmarks are pointers to certain commits that move when
89 Bookmarks are pointers to certain commits that move when
90 committing. Bookmarks are local. They can be renamed, copied and
90 committing. Bookmarks are local. They can be renamed, copied and
91 deleted. It is possible to use bookmark names in :hg:`merge` and
91 deleted. It is possible to use bookmark names in :hg:`merge` and
92 :hg:`update` to merge and update respectively to a given bookmark.
92 :hg:`update` to merge and update respectively to a given bookmark.
93
93
94 You can use :hg:`bookmark NAME` to set a bookmark on the working
94 You can use :hg:`bookmark NAME` to set a bookmark on the working
95 directory's parent revision with the given name. If you specify
95 directory's parent revision with the given name. If you specify
96 a revision using -r REV (where REV may be an existing bookmark),
96 a revision using -r REV (where REV may be an existing bookmark),
97 the bookmark is assigned to that revision.
97 the bookmark is assigned to that revision.
98 '''
98 '''
99 hexfn = ui.debugflag and hex or short
99 hexfn = ui.debugflag and hex or short
100 marks = repo._bookmarks
100 marks = repo._bookmarks
101 cur = repo.changectx('.').node()
101 cur = repo.changectx('.').node()
102
102
103 if rename:
103 if rename:
104 if rename not in marks:
104 if rename not in marks:
105 raise util.Abort(_("a bookmark of this name does not exist"))
105 raise util.Abort(_("a bookmark of this name does not exist"))
106 if mark in marks and not force:
106 if mark in marks and not force:
107 raise util.Abort(_("a bookmark of the same name already exists"))
107 raise util.Abort(_("a bookmark of the same name already exists"))
108 if mark is None:
108 if mark is None:
109 raise util.Abort(_("new bookmark name required"))
109 raise util.Abort(_("new bookmark name required"))
110 marks[mark] = marks[rename]
110 marks[mark] = marks[rename]
111 del marks[rename]
111 del marks[rename]
112 if repo._bookmarkcurrent == rename:
112 if repo._bookmarkcurrent == rename:
113 setcurrent(repo, mark)
113 setcurrent(repo, mark)
114 write(repo)
114 write(repo)
115 return
115 return
116
116
117 if delete:
117 if delete:
118 if mark is None:
118 if mark is None:
119 raise util.Abort(_("bookmark name required"))
119 raise util.Abort(_("bookmark name required"))
120 if mark not in marks:
120 if mark not in marks:
121 raise util.Abort(_("a bookmark of this name does not exist"))
121 raise util.Abort(_("a bookmark of this name does not exist"))
122 if mark == repo._bookmarkcurrent:
122 if mark == repo._bookmarkcurrent:
123 setcurrent(repo, None)
123 setcurrent(repo, None)
124 del marks[mark]
124 del marks[mark]
125 write(repo)
125 write(repo)
126 return
126 return
127
127
128 if mark != None:
128 if mark != None:
129 if "\n" in mark:
129 if "\n" in mark:
130 raise util.Abort(_("bookmark name cannot contain newlines"))
130 raise util.Abort(_("bookmark name cannot contain newlines"))
131 mark = mark.strip()
131 mark = mark.strip()
132 if mark in marks and not force:
132 if mark in marks and not force:
133 raise util.Abort(_("a bookmark of the same name already exists"))
133 raise util.Abort(_("a bookmark of the same name already exists"))
134 if ((mark in repo.branchtags() or mark == repo.dirstate.branch())
134 if ((mark in repo.branchtags() or mark == repo.dirstate.branch())
135 and not force):
135 and not force):
136 raise util.Abort(
136 raise util.Abort(
137 _("a bookmark cannot have the name of an existing branch"))
137 _("a bookmark cannot have the name of an existing branch"))
138 if rev:
138 if rev:
139 marks[mark] = repo.lookup(rev)
139 marks[mark] = repo.lookup(rev)
140 else:
140 else:
141 marks[mark] = repo.changectx('.').node()
141 marks[mark] = repo.changectx('.').node()
142 setcurrent(repo, mark)
142 setcurrent(repo, mark)
143 write(repo)
143 write(repo)
144 return
144 return
145
145
146 if mark is None:
146 if mark is None:
147 if rev:
147 if rev:
148 raise util.Abort(_("bookmark name required"))
148 raise util.Abort(_("bookmark name required"))
149 if len(marks) == 0:
149 if len(marks) == 0:
150 ui.status(_("no bookmarks set\n"))
150 ui.status(_("no bookmarks set\n"))
151 else:
151 else:
152 for bmark, n in marks.iteritems():
152 for bmark, n in marks.iteritems():
153 if ui.configbool('bookmarks', 'track.current'):
153 if ui.configbool('bookmarks', 'track.current'):
154 current = repo._bookmarkcurrent
154 current = repo._bookmarkcurrent
155 if bmark == current and n == cur:
155 if bmark == current and n == cur:
156 prefix, label = '*', 'bookmarks.current'
156 prefix, label = '*', 'bookmarks.current'
157 else:
157 else:
158 prefix, label = ' ', ''
158 prefix, label = ' ', ''
159 else:
159 else:
160 if n == cur:
160 if n == cur:
161 prefix, label = '*', 'bookmarks.current'
161 prefix, label = '*', 'bookmarks.current'
162 else:
162 else:
163 prefix, label = ' ', ''
163 prefix, label = ' ', ''
164
164
165 if ui.quiet:
165 if ui.quiet:
166 ui.write("%s\n" % bmark, label=label)
166 ui.write("%s\n" % bmark, label=label)
167 else:
167 else:
168 ui.write(" %s %-25s %d:%s\n" % (
168 ui.write(" %s %-25s %d:%s\n" % (
169 prefix, bmark, repo.changelog.rev(n), hexfn(n)),
169 prefix, bmark, repo.changelog.rev(n), hexfn(n)),
170 label=label)
170 label=label)
171 return
171 return
172
172
173 def _revstostrip(changelog, node):
173 def _revstostrip(changelog, node):
174 srev = changelog.rev(node)
174 srev = changelog.rev(node)
175 tostrip = [srev]
175 tostrip = [srev]
176 saveheads = []
176 saveheads = []
177 for r in xrange(srev, len(changelog)):
177 for r in xrange(srev, len(changelog)):
178 parents = changelog.parentrevs(r)
178 parents = changelog.parentrevs(r)
179 if parents[0] in tostrip or parents[1] in tostrip:
179 if parents[0] in tostrip or parents[1] in tostrip:
180 tostrip.append(r)
180 tostrip.append(r)
181 if parents[1] != nullrev:
181 if parents[1] != nullrev:
182 for p in parents:
182 for p in parents:
183 if p not in tostrip and p > srev:
183 if p not in tostrip and p > srev:
184 saveheads.append(p)
184 saveheads.append(p)
185 return [r for r in tostrip if r not in saveheads]
185 return [r for r in tostrip if r not in saveheads]
186
186
187 def strip(oldstrip, ui, repo, node, backup="all"):
187 def strip(oldstrip, ui, repo, node, backup="all"):
188 """Strip bookmarks if revisions are stripped using
188 """Strip bookmarks if revisions are stripped using
189 the mercurial.strip method. This usually happens during
189 the mercurial.strip method. This usually happens during
190 qpush and qpop"""
190 qpush and qpop"""
191 revisions = _revstostrip(repo.changelog, node)
191 revisions = _revstostrip(repo.changelog, node)
192 marks = repo._bookmarks
192 marks = repo._bookmarks
193 update = []
193 update = []
194 for mark, n in marks.iteritems():
194 for mark, n in marks.iteritems():
195 if repo.changelog.rev(n) in revisions:
195 if repo.changelog.rev(n) in revisions:
196 update.append(mark)
196 update.append(mark)
197 oldstrip(ui, repo, node, backup)
197 oldstrip(ui, repo, node, backup)
198 if len(update) > 0:
198 if len(update) > 0:
199 for m in update:
199 for m in update:
200 marks[m] = repo.changectx('.').node()
200 marks[m] = repo.changectx('.').node()
201 write(repo)
201 write(repo)
202
202
203 def reposetup(ui, repo):
203 def reposetup(ui, repo):
204 if not repo.local():
204 if not repo.local():
205 return
205 return
206
206
207 class bookmark_repo(repo.__class__):
207 class bookmark_repo(repo.__class__):
208
208
209 @util.propertycache
209 @util.propertycache
210 def _bookmarks(self):
210 def _bookmarks(self):
211 '''Parse .hg/bookmarks file and return a dictionary
211 '''Parse .hg/bookmarks file and return a dictionary
212
212
213 Bookmarks are stored as {HASH}\\s{NAME}\\n (localtags format) values
213 Bookmarks are stored as {HASH}\\s{NAME}\\n (localtags format) values
214 in the .hg/bookmarks file. They are read returned as a dictionary
214 in the .hg/bookmarks file. They are read returned as a dictionary
215 with name => hash values.
215 with name => hash values.
216 '''
216 '''
217 try:
217 try:
218 bookmarks = {}
218 bookmarks = {}
219 for line in self.opener('bookmarks'):
219 for line in self.opener('bookmarks'):
220 sha, refspec = line.strip().split(' ', 1)
220 sha, refspec = line.strip().split(' ', 1)
221 bookmarks[refspec] = super(bookmark_repo, self).lookup(sha)
221 bookmarks[refspec] = super(bookmark_repo, self).lookup(sha)
222 except:
222 except:
223 pass
223 pass
224 return bookmarks
224 return bookmarks
225
225
226 @util.propertycache
226 @util.propertycache
227 def _bookmarkcurrent(self):
227 def _bookmarkcurrent(self):
228 '''Get the current bookmark
228 '''Get the current bookmark
229
229
230 If we use gittishsh branches we have a current bookmark that
230 If we use gittishsh branches we have a current bookmark that
231 we are on. This function returns the name of the bookmark. It
231 we are on. This function returns the name of the bookmark. It
232 is stored in .hg/bookmarks.current
232 is stored in .hg/bookmarks.current
233 '''
233 '''
234 mark = None
234 mark = None
235 if os.path.exists(self.join('bookmarks.current')):
235 if os.path.exists(self.join('bookmarks.current')):
236 file = self.opener('bookmarks.current')
236 file = self.opener('bookmarks.current')
237 # No readline() in posixfile_nt, reading everything is cheap
237 # No readline() in posixfile_nt, reading everything is cheap
238 mark = (file.readlines() or [''])[0]
238 mark = (file.readlines() or [''])[0]
239 if mark == '':
239 if mark == '':
240 mark = None
240 mark = None
241 file.close()
241 file.close()
242 return mark
242 return mark
243
243
244 def rollback(self, *args):
244 def rollback(self, *args):
245 if os.path.exists(self.join('undo.bookmarks')):
245 if os.path.exists(self.join('undo.bookmarks')):
246 util.rename(self.join('undo.bookmarks'), self.join('bookmarks'))
246 util.rename(self.join('undo.bookmarks'), self.join('bookmarks'))
247 return super(bookmark_repo, self).rollback(*args)
247 return super(bookmark_repo, self).rollback(*args)
248
248
249 def lookup(self, key):
249 def lookup(self, key):
250 if key in self._bookmarks:
250 if key in self._bookmarks:
251 key = self._bookmarks[key]
251 key = self._bookmarks[key]
252 return super(bookmark_repo, self).lookup(key)
252 return super(bookmark_repo, self).lookup(key)
253
253
254 def _bookmarksupdate(self, parents, node):
254 def _bookmarksupdate(self, parents, node):
255 marks = self._bookmarks
255 marks = self._bookmarks
256 update = False
256 update = False
257 if ui.configbool('bookmarks', 'track.current'):
257 if ui.configbool('bookmarks', 'track.current'):
258 mark = self._bookmarkcurrent
258 mark = self._bookmarkcurrent
259 if mark and marks[mark] in parents:
259 if mark and marks[mark] in parents:
260 marks[mark] = node
260 marks[mark] = node
261 update = True
261 update = True
262 else:
262 else:
263 for mark, n in marks.items():
263 for mark, n in marks.items():
264 if n in parents:
264 if n in parents:
265 marks[mark] = node
265 marks[mark] = node
266 update = True
266 update = True
267 if update:
267 if update:
268 write(self)
268 write(self)
269
269
270 def commitctx(self, ctx, error=False):
270 def commitctx(self, ctx, error=False):
271 """Add a revision to the repository and
271 """Add a revision to the repository and
272 move the bookmark"""
272 move the bookmark"""
273 wlock = self.wlock() # do both commit and bookmark with lock held
273 wlock = self.wlock() # do both commit and bookmark with lock held
274 try:
274 try:
275 node = super(bookmark_repo, self).commitctx(ctx, error)
275 node = super(bookmark_repo, self).commitctx(ctx, error)
276 if node is None:
276 if node is None:
277 return None
277 return None
278 parents = self.changelog.parents(node)
278 parents = self.changelog.parents(node)
279 if parents[1] == nullid:
279 if parents[1] == nullid:
280 parents = (parents[0],)
280 parents = (parents[0],)
281
281
282 self._bookmarksupdate(parents, node)
282 self._bookmarksupdate(parents, node)
283 return node
283 return node
284 finally:
284 finally:
285 wlock.release()
285 wlock.release()
286
286
287 def pull(self, remote, heads=None, force=False):
288 result = super(bookmark_repo, self).pull(remote, heads, force)
289
290 self.ui.debug("checking for updated bookmarks\n")
291 rb = remote.listkeys('bookmarks')
292 changes = 0
293 for k in rb.keys():
294 if k in self._bookmarks:
295 nr, nl = rb[k], self._bookmarks[k]
296 if nr in self:
297 cr = self[nr]
298 cl = self[nl]
299 if cl.rev() >= cr.rev():
300 continue
301 if cr in cl.descendants():
302 self._bookmarks[k] = cr.node()
303 changes += 1
304 self.ui.status(_("updating bookmark %s\n") % k)
305 else:
306 self.ui.warn(_("not updating divergent"
307 " bookmark %s\n") % k)
308 if changes:
309 write(repo)
310
311 return result
312
287 def addchangegroup(self, source, srctype, url, emptyok=False):
313 def addchangegroup(self, source, srctype, url, emptyok=False):
288 parents = self.dirstate.parents()
314 parents = self.dirstate.parents()
289
315
290 result = super(bookmark_repo, self).addchangegroup(
316 result = super(bookmark_repo, self).addchangegroup(
291 source, srctype, url, emptyok)
317 source, srctype, url, emptyok)
292 if result > 1:
318 if result > 1:
293 # We have more heads than before
319 # We have more heads than before
294 return result
320 return result
295 node = self.changelog.tip()
321 node = self.changelog.tip()
296
322
297 self._bookmarksupdate(parents, node)
323 self._bookmarksupdate(parents, node)
298 return result
324 return result
299
325
300 def _findtags(self):
326 def _findtags(self):
301 """Merge bookmarks with normal tags"""
327 """Merge bookmarks with normal tags"""
302 (tags, tagtypes) = super(bookmark_repo, self)._findtags()
328 (tags, tagtypes) = super(bookmark_repo, self)._findtags()
303 tags.update(self._bookmarks)
329 tags.update(self._bookmarks)
304 return (tags, tagtypes)
330 return (tags, tagtypes)
305
331
306 if hasattr(repo, 'invalidate'):
332 if hasattr(repo, 'invalidate'):
307 def invalidate(self):
333 def invalidate(self):
308 super(bookmark_repo, self).invalidate()
334 super(bookmark_repo, self).invalidate()
309 for attr in ('_bookmarks', '_bookmarkcurrent'):
335 for attr in ('_bookmarks', '_bookmarkcurrent'):
310 if attr in self.__dict__:
336 if attr in self.__dict__:
311 delattr(self, attr)
337 delattr(self, attr)
312
338
313 repo.__class__ = bookmark_repo
339 repo.__class__ = bookmark_repo
314
340
315 def listbookmarks(repo):
341 def listbookmarks(repo):
316 d = {}
342 d = {}
317 for k, v in repo._bookmarks.iteritems():
343 for k, v in repo._bookmarks.iteritems():
318 d[k] = hex(v)
344 d[k] = hex(v)
319 return d
345 return d
320
346
321 def pushbookmark(repo, key, old, new):
347 def pushbookmark(repo, key, old, new):
322 w = repo.wlock()
348 w = repo.wlock()
323 try:
349 try:
324 marks = repo._bookmarks
350 marks = repo._bookmarks
325 if hex(marks.get(key, '')) != old:
351 if hex(marks.get(key, '')) != old:
326 return False
352 return False
327 if new == '':
353 if new == '':
328 del marks[key]
354 del marks[key]
329 else:
355 else:
330 if new not in repo:
356 if new not in repo:
331 return False
357 return False
332 marks[key] = repo[new].node()
358 marks[key] = repo[new].node()
333 write(repo)
359 write(repo)
334 return True
360 return True
335 finally:
361 finally:
336 w.release()
362 w.release()
337
363
338 def uisetup(ui):
364 def uisetup(ui):
339 extensions.wrapfunction(repair, "strip", strip)
365 extensions.wrapfunction(repair, "strip", strip)
340 if ui.configbool('bookmarks', 'track.current'):
366 if ui.configbool('bookmarks', 'track.current'):
341 extensions.wrapcommand(commands.table, 'update', updatecurbookmark)
367 extensions.wrapcommand(commands.table, 'update', updatecurbookmark)
342 pushkey.register('bookmarks', pushbookmark, listbookmarks)
368 pushkey.register('bookmarks', pushbookmark, listbookmarks)
343
369
344 def updatecurbookmark(orig, ui, repo, *args, **opts):
370 def updatecurbookmark(orig, ui, repo, *args, **opts):
345 '''Set the current bookmark
371 '''Set the current bookmark
346
372
347 If the user updates to a bookmark we update the .hg/bookmarks.current
373 If the user updates to a bookmark we update the .hg/bookmarks.current
348 file.
374 file.
349 '''
375 '''
350 res = orig(ui, repo, *args, **opts)
376 res = orig(ui, repo, *args, **opts)
351 rev = opts['rev']
377 rev = opts['rev']
352 if not rev and len(args) > 0:
378 if not rev and len(args) > 0:
353 rev = args[0]
379 rev = args[0]
354 setcurrent(repo, rev)
380 setcurrent(repo, rev)
355 return res
381 return res
356
382
357 cmdtable = {
383 cmdtable = {
358 "bookmarks":
384 "bookmarks":
359 (bookmark,
385 (bookmark,
360 [('f', 'force', False, _('force')),
386 [('f', 'force', False, _('force')),
361 ('r', 'rev', '', _('revision'), _('REV')),
387 ('r', 'rev', '', _('revision'), _('REV')),
362 ('d', 'delete', False, _('delete a given bookmark')),
388 ('d', 'delete', False, _('delete a given bookmark')),
363 ('m', 'rename', '', _('rename a given bookmark'), _('NAME'))],
389 ('m', 'rename', '', _('rename a given bookmark'), _('NAME'))],
364 _('hg bookmarks [-f] [-d] [-m NAME] [-r REV] [NAME]')),
390 _('hg bookmarks [-f] [-d] [-m NAME] [-r REV] [NAME]')),
365 }
391 }
366
392
367 colortable = {'bookmarks.current': 'green'}
393 colortable = {'bookmarks.current': 'green'}
General Comments 0
You need to be logged in to leave comments. Login now