##// END OF EJS Templates
bookmarks: move basic io to core
Matt Mackall -
r13350:a7376b92 default
parent child Browse files
Show More
@@ -0,0 +1,73
1 # Mercurial bookmark support code
2 #
3 # Copyright 2008 David Soria Parra <dsp@php.net>
4 #
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.
7
8 from mercurial.i18n import _
9 from mercurial.node import nullid, nullrev, bin, hex, short
10 from mercurial import encoding
11 import os
12
13 def write(repo):
14 '''Write bookmarks
15
16 Write the given bookmark => hash dictionary to the .hg/bookmarks file
17 in a format equal to those of localtags.
18
19 We also store a backup of the previous state in undo.bookmarks that
20 can be copied back on rollback.
21 '''
22 refs = repo._bookmarks
23
24 try:
25 bms = repo.opener('bookmarks').read()
26 except IOError:
27 bms = ''
28 repo.opener('undo.bookmarks', 'w').write(bms)
29
30 if repo._bookmarkcurrent not in refs:
31 setcurrent(repo, None)
32 wlock = repo.wlock()
33 try:
34 file = repo.opener('bookmarks', 'w', atomictemp=True)
35 for refspec, node in refs.iteritems():
36 file.write("%s %s\n" % (hex(node), encoding.fromlocal(refspec)))
37 file.rename()
38
39 # touch 00changelog.i so hgweb reloads bookmarks (no lock needed)
40 try:
41 os.utime(repo.sjoin('00changelog.i'), None)
42 except OSError:
43 pass
44
45 finally:
46 wlock.release()
47
48 def setcurrent(repo, mark):
49 '''Set the name of the bookmark that we are currently on
50
51 Set the name of the bookmark that we are on (hg update <bookmark>).
52 The name is recorded in .hg/bookmarks.current
53 '''
54 current = repo._bookmarkcurrent
55 if current == mark:
56 return
57
58 refs = repo._bookmarks
59
60 # do not update if we do update to a rev equal to the current bookmark
61 if (mark and mark not in refs and
62 current and refs[current] == repo.changectx('.').node()):
63 return
64 if mark not in refs:
65 mark = ''
66 wlock = repo.wlock()
67 try:
68 file = repo.opener('bookmarks.current', 'w', atomictemp=True)
69 file.write(mark)
70 file.rename()
71 finally:
72 wlock.release()
73 repo._bookmarkcurrent = mark
@@ -1,581 +1,520
1 1 # Mercurial extension to provide the 'hg bookmark' command
2 2 #
3 3 # Copyright 2008 David Soria Parra <dsp@php.net>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 '''track a line of development with movable markers
9 9
10 10 Bookmarks are local movable markers to changesets. Every bookmark
11 11 points to a changeset identified by its hash. If you commit a
12 12 changeset that is based on a changeset that has a bookmark on it, the
13 13 bookmark shifts to the new changeset.
14 14
15 15 It is possible to use bookmark names in every revision lookup (e.g.
16 16 :hg:`merge`, :hg:`update`).
17 17
18 18 By default, when several bookmarks point to the same changeset, they
19 19 will all move forward together. It is possible to obtain a more
20 20 git-like experience by adding the following configuration option to
21 21 your configuration file::
22 22
23 23 [bookmarks]
24 24 track.current = True
25 25
26 26 This will cause Mercurial to track the bookmark that you are currently
27 27 using, and only update it. This is similar to git's approach to
28 28 branching.
29 29 '''
30 30
31 31 from mercurial.i18n import _
32 32 from mercurial.node import nullid, nullrev, bin, hex, short
33 33 from mercurial import util, commands, repair, extensions, pushkey, hg, url
34 34 from mercurial import revset, encoding
35 from mercurial import bookmarks
35 36 import os
36 37
37 def write(repo):
38 '''Write bookmarks
39
40 Write the given bookmark => hash dictionary to the .hg/bookmarks file
41 in a format equal to those of localtags.
42
43 We also store a backup of the previous state in undo.bookmarks that
44 can be copied back on rollback.
45 '''
46 refs = repo._bookmarks
47
48 try:
49 bms = repo.opener('bookmarks').read()
50 except IOError:
51 bms = ''
52 repo.opener('undo.bookmarks', 'w').write(bms)
53
54 if repo._bookmarkcurrent not in refs:
55 setcurrent(repo, None)
56 wlock = repo.wlock()
57 try:
58 file = repo.opener('bookmarks', 'w', atomictemp=True)
59 for refspec, node in refs.iteritems():
60 file.write("%s %s\n" % (hex(node), encoding.fromlocal(refspec)))
61 file.rename()
62
63 # touch 00changelog.i so hgweb reloads bookmarks (no lock needed)
64 try:
65 os.utime(repo.sjoin('00changelog.i'), None)
66 except OSError:
67 pass
68
69 finally:
70 wlock.release()
71
72 def setcurrent(repo, mark):
73 '''Set the name of the bookmark that we are currently on
74
75 Set the name of the bookmark that we are on (hg update <bookmark>).
76 The name is recorded in .hg/bookmarks.current
77 '''
78 current = repo._bookmarkcurrent
79 if current == mark:
80 return
81
82 refs = repo._bookmarks
83
84 # do not update if we do update to a rev equal to the current bookmark
85 if (mark and mark not in refs and
86 current and refs[current] == repo.changectx('.').node()):
87 return
88 if mark not in refs:
89 mark = ''
90 wlock = repo.wlock()
91 try:
92 file = repo.opener('bookmarks.current', 'w', atomictemp=True)
93 file.write(mark)
94 file.rename()
95 finally:
96 wlock.release()
97 repo._bookmarkcurrent = mark
98
99 38 def bookmark(ui, repo, mark=None, rev=None, force=False, delete=False, rename=None):
100 39 '''track a line of development with movable markers
101 40
102 41 Bookmarks are pointers to certain commits that move when
103 42 committing. Bookmarks are local. They can be renamed, copied and
104 43 deleted. It is possible to use bookmark names in :hg:`merge` and
105 44 :hg:`update` to merge and update respectively to a given bookmark.
106 45
107 46 You can use :hg:`bookmark NAME` to set a bookmark on the working
108 47 directory's parent revision with the given name. If you specify
109 48 a revision using -r REV (where REV may be an existing bookmark),
110 49 the bookmark is assigned to that revision.
111 50
112 51 Bookmarks can be pushed and pulled between repositories (see :hg:`help
113 52 push` and :hg:`help pull`). This requires the bookmark extension to be
114 53 enabled for both the local and remote repositories.
115 54 '''
116 55 hexfn = ui.debugflag and hex or short
117 56 marks = repo._bookmarks
118 57 cur = repo.changectx('.').node()
119 58
120 59 if rename:
121 60 if rename not in marks:
122 61 raise util.Abort(_("a bookmark of this name does not exist"))
123 62 if mark in marks and not force:
124 63 raise util.Abort(_("a bookmark of the same name already exists"))
125 64 if mark is None:
126 65 raise util.Abort(_("new bookmark name required"))
127 66 marks[mark] = marks[rename]
128 67 del marks[rename]
129 68 if repo._bookmarkcurrent == rename:
130 setcurrent(repo, mark)
131 write(repo)
69 bookmarks.setcurrent(repo, mark)
70 bookmarks.write(repo)
132 71 return
133 72
134 73 if delete:
135 74 if mark is None:
136 75 raise util.Abort(_("bookmark name required"))
137 76 if mark not in marks:
138 77 raise util.Abort(_("a bookmark of this name does not exist"))
139 78 if mark == repo._bookmarkcurrent:
140 setcurrent(repo, None)
79 bookmarks.setcurrent(repo, None)
141 80 del marks[mark]
142 write(repo)
81 bookmarks.write(repo)
143 82 return
144 83
145 84 if mark is not None:
146 85 if "\n" in mark:
147 86 raise util.Abort(_("bookmark name cannot contain newlines"))
148 87 mark = mark.strip()
149 88 if not mark:
150 89 raise util.Abort(_("bookmark names cannot consist entirely of "
151 90 "whitespace"))
152 91 if mark in marks and not force:
153 92 raise util.Abort(_("a bookmark of the same name already exists"))
154 93 if ((mark in repo.branchtags() or mark == repo.dirstate.branch())
155 94 and not force):
156 95 raise util.Abort(
157 96 _("a bookmark cannot have the name of an existing branch"))
158 97 if rev:
159 98 marks[mark] = repo.lookup(rev)
160 99 else:
161 100 marks[mark] = repo.changectx('.').node()
162 setcurrent(repo, mark)
163 write(repo)
101 bookmarks.setcurrent(repo, mark)
102 bookmarks.write(repo)
164 103 return
165 104
166 105 if mark is None:
167 106 if rev:
168 107 raise util.Abort(_("bookmark name required"))
169 108 if len(marks) == 0:
170 109 ui.status(_("no bookmarks set\n"))
171 110 else:
172 111 for bmark, n in marks.iteritems():
173 112 if ui.configbool('bookmarks', 'track.current'):
174 113 current = repo._bookmarkcurrent
175 114 if bmark == current and n == cur:
176 115 prefix, label = '*', 'bookmarks.current'
177 116 else:
178 117 prefix, label = ' ', ''
179 118 else:
180 119 if n == cur:
181 120 prefix, label = '*', 'bookmarks.current'
182 121 else:
183 122 prefix, label = ' ', ''
184 123
185 124 if ui.quiet:
186 125 ui.write("%s\n" % bmark, label=label)
187 126 else:
188 127 ui.write(" %s %-25s %d:%s\n" % (
189 128 prefix, bmark, repo.changelog.rev(n), hexfn(n)),
190 129 label=label)
191 130 return
192 131
193 132 def _revstostrip(changelog, node):
194 133 srev = changelog.rev(node)
195 134 tostrip = [srev]
196 135 saveheads = []
197 136 for r in xrange(srev, len(changelog)):
198 137 parents = changelog.parentrevs(r)
199 138 if parents[0] in tostrip or parents[1] in tostrip:
200 139 tostrip.append(r)
201 140 if parents[1] != nullrev:
202 141 for p in parents:
203 142 if p not in tostrip and p > srev:
204 143 saveheads.append(p)
205 144 return [r for r in tostrip if r not in saveheads]
206 145
207 146 def strip(oldstrip, ui, repo, node, backup="all"):
208 147 """Strip bookmarks if revisions are stripped using
209 148 the mercurial.strip method. This usually happens during
210 149 qpush and qpop"""
211 150 revisions = _revstostrip(repo.changelog, node)
212 151 marks = repo._bookmarks
213 152 update = []
214 153 for mark, n in marks.iteritems():
215 154 if repo.changelog.rev(n) in revisions:
216 155 update.append(mark)
217 156 oldstrip(ui, repo, node, backup)
218 157 if len(update) > 0:
219 158 for m in update:
220 159 marks[m] = repo.changectx('.').node()
221 write(repo)
160 bookmarks.write(repo)
222 161
223 162 def reposetup(ui, repo):
224 163 if not repo.local():
225 164 return
226 165
227 166 class bookmark_repo(repo.__class__):
228 167
229 168 @util.propertycache
230 169 def _bookmarks(self):
231 170 '''Parse .hg/bookmarks file and return a dictionary
232 171
233 172 Bookmarks are stored as {HASH}\\s{NAME}\\n (localtags format) values
234 173 in the .hg/bookmarks file.
235 174 Read the file and return a (name=>nodeid) dictionary
236 175 '''
237 176 try:
238 177 bookmarks = {}
239 178 for line in self.opener('bookmarks'):
240 179 sha, refspec = line.strip().split(' ', 1)
241 180 refspec = encoding.tolocal(refspec)
242 181 bookmarks[refspec] = self.changelog.lookup(sha)
243 182 except:
244 183 pass
245 184 return bookmarks
246 185
247 186 @util.propertycache
248 187 def _bookmarkcurrent(self):
249 188 '''Get the current bookmark
250 189
251 190 If we use gittishsh branches we have a current bookmark that
252 191 we are on. This function returns the name of the bookmark. It
253 192 is stored in .hg/bookmarks.current
254 193 '''
255 194 mark = None
256 195 if os.path.exists(self.join('bookmarks.current')):
257 196 file = self.opener('bookmarks.current')
258 197 # No readline() in posixfile_nt, reading everything is cheap
259 198 mark = (file.readlines() or [''])[0]
260 199 if mark == '':
261 200 mark = None
262 201 file.close()
263 202 return mark
264 203
265 204 def rollback(self, dryrun=False):
266 205 if os.path.exists(self.join('undo.bookmarks')):
267 206 if not dryrun:
268 207 util.rename(self.join('undo.bookmarks'), self.join('bookmarks'))
269 208 elif not os.path.exists(self.sjoin("undo")):
270 209 # avoid "no rollback information available" message
271 210 return 0
272 211 return super(bookmark_repo, self).rollback(dryrun)
273 212
274 213 def lookup(self, key):
275 214 if key in self._bookmarks:
276 215 key = self._bookmarks[key]
277 216 return super(bookmark_repo, self).lookup(key)
278 217
279 218 def _bookmarksupdate(self, parents, node):
280 219 marks = self._bookmarks
281 220 update = False
282 221 if ui.configbool('bookmarks', 'track.current'):
283 222 mark = self._bookmarkcurrent
284 223 if mark and marks[mark] in parents:
285 224 marks[mark] = node
286 225 update = True
287 226 else:
288 227 for mark, n in marks.items():
289 228 if n in parents:
290 229 marks[mark] = node
291 230 update = True
292 231 if update:
293 write(self)
232 bookmarks.write(self)
294 233
295 234 def commitctx(self, ctx, error=False):
296 235 """Add a revision to the repository and
297 236 move the bookmark"""
298 237 wlock = self.wlock() # do both commit and bookmark with lock held
299 238 try:
300 239 node = super(bookmark_repo, self).commitctx(ctx, error)
301 240 if node is None:
302 241 return None
303 242 parents = self.changelog.parents(node)
304 243 if parents[1] == nullid:
305 244 parents = (parents[0],)
306 245
307 246 self._bookmarksupdate(parents, node)
308 247 return node
309 248 finally:
310 249 wlock.release()
311 250
312 251 def pull(self, remote, heads=None, force=False):
313 252 result = super(bookmark_repo, self).pull(remote, heads, force)
314 253
315 254 self.ui.debug("checking for updated bookmarks\n")
316 255 rb = remote.listkeys('bookmarks')
317 256 changed = False
318 257 for k in rb.keys():
319 258 if k in self._bookmarks:
320 259 nr, nl = rb[k], self._bookmarks[k]
321 260 if nr in self:
322 261 cr = self[nr]
323 262 cl = self[nl]
324 263 if cl.rev() >= cr.rev():
325 264 continue
326 265 if cr in cl.descendants():
327 266 self._bookmarks[k] = cr.node()
328 267 changed = True
329 268 self.ui.status(_("updating bookmark %s\n") % k)
330 269 else:
331 270 self.ui.warn(_("not updating divergent"
332 271 " bookmark %s\n") % k)
333 272 if changed:
334 write(repo)
273 bookmarks.write(repo)
335 274
336 275 return result
337 276
338 277 def push(self, remote, force=False, revs=None, newbranch=False):
339 278 result = super(bookmark_repo, self).push(remote, force, revs,
340 279 newbranch)
341 280
342 281 self.ui.debug("checking for updated bookmarks\n")
343 282 rb = remote.listkeys('bookmarks')
344 283 for k in rb.keys():
345 284 if k in self._bookmarks:
346 285 nr, nl = rb[k], hex(self._bookmarks[k])
347 286 if nr in self:
348 287 cr = self[nr]
349 288 cl = self[nl]
350 289 if cl in cr.descendants():
351 290 r = remote.pushkey('bookmarks', k, nr, nl)
352 291 if r:
353 292 self.ui.status(_("updating bookmark %s\n") % k)
354 293 else:
355 294 self.ui.warn(_('updating bookmark %s'
356 295 ' failed!\n') % k)
357 296
358 297 return result
359 298
360 299 def addchangegroup(self, *args, **kwargs):
361 300 result = super(bookmark_repo, self).addchangegroup(*args, **kwargs)
362 301 if result > 1:
363 302 # We have more heads than before
364 303 return result
365 304 node = self.changelog.tip()
366 305 parents = self.dirstate.parents()
367 306 self._bookmarksupdate(parents, node)
368 307 return result
369 308
370 309 def _findtags(self):
371 310 """Merge bookmarks with normal tags"""
372 311 (tags, tagtypes) = super(bookmark_repo, self)._findtags()
373 312 tags.update(self._bookmarks)
374 313 return (tags, tagtypes)
375 314
376 315 if hasattr(repo, 'invalidate'):
377 316 def invalidate(self):
378 317 super(bookmark_repo, self).invalidate()
379 318 for attr in ('_bookmarks', '_bookmarkcurrent'):
380 319 if attr in self.__dict__:
381 320 delattr(self, attr)
382 321
383 322 repo.__class__ = bookmark_repo
384 323
385 324 def listbookmarks(repo):
386 325 # We may try to list bookmarks on a repo type that does not
387 326 # support it (e.g., statichttprepository).
388 327 if not hasattr(repo, '_bookmarks'):
389 328 return {}
390 329
391 330 d = {}
392 331 for k, v in repo._bookmarks.iteritems():
393 332 d[k] = hex(v)
394 333 return d
395 334
396 335 def pushbookmark(repo, key, old, new):
397 336 w = repo.wlock()
398 337 try:
399 338 marks = repo._bookmarks
400 339 if hex(marks.get(key, '')) != old:
401 340 return False
402 341 if new == '':
403 342 del marks[key]
404 343 else:
405 344 if new not in repo:
406 345 return False
407 346 marks[key] = repo[new].node()
408 write(repo)
347 bookmarks.write(repo)
409 348 return True
410 349 finally:
411 350 w.release()
412 351
413 352 def pull(oldpull, ui, repo, source="default", **opts):
414 353 # translate bookmark args to rev args for actual pull
415 354 if opts.get('bookmark'):
416 355 # this is an unpleasant hack as pull will do this internally
417 356 source, branches = hg.parseurl(ui.expandpath(source),
418 357 opts.get('branch'))
419 358 other = hg.repository(hg.remoteui(repo, opts), source)
420 359 rb = other.listkeys('bookmarks')
421 360
422 361 for b in opts['bookmark']:
423 362 if b not in rb:
424 363 raise util.Abort(_('remote bookmark %s not found!') % b)
425 364 opts.setdefault('rev', []).append(b)
426 365
427 366 result = oldpull(ui, repo, source, **opts)
428 367
429 368 # update specified bookmarks
430 369 if opts.get('bookmark'):
431 370 for b in opts['bookmark']:
432 371 # explicit pull overrides local bookmark if any
433 372 ui.status(_("importing bookmark %s\n") % b)
434 373 repo._bookmarks[b] = repo[rb[b]].node()
435 write(repo)
374 bookmarks.write(repo)
436 375
437 376 return result
438 377
439 378 def push(oldpush, ui, repo, dest=None, **opts):
440 379 dopush = True
441 380 if opts.get('bookmark'):
442 381 dopush = False
443 382 for b in opts['bookmark']:
444 383 if b in repo._bookmarks:
445 384 dopush = True
446 385 opts.setdefault('rev', []).append(b)
447 386
448 387 result = 0
449 388 if dopush:
450 389 result = oldpush(ui, repo, dest, **opts)
451 390
452 391 if opts.get('bookmark'):
453 392 # this is an unpleasant hack as push will do this internally
454 393 dest = ui.expandpath(dest or 'default-push', dest or 'default')
455 394 dest, branches = hg.parseurl(dest, opts.get('branch'))
456 395 other = hg.repository(hg.remoteui(repo, opts), dest)
457 396 rb = other.listkeys('bookmarks')
458 397 for b in opts['bookmark']:
459 398 # explicit push overrides remote bookmark if any
460 399 if b in repo._bookmarks:
461 400 ui.status(_("exporting bookmark %s\n") % b)
462 401 new = repo[b].hex()
463 402 elif b in rb:
464 403 ui.status(_("deleting remote bookmark %s\n") % b)
465 404 new = '' # delete
466 405 else:
467 406 ui.warn(_('bookmark %s does not exist on the local '
468 407 'or remote repository!\n') % b)
469 408 return 2
470 409 old = rb.get(b, '')
471 410 r = other.pushkey('bookmarks', b, old, new)
472 411 if not r:
473 412 ui.warn(_('updating bookmark %s failed!\n') % b)
474 413 if not result:
475 414 result = 2
476 415
477 416 return result
478 417
479 418 def diffbookmarks(ui, repo, remote):
480 419 ui.status(_("searching for changed bookmarks\n"))
481 420
482 421 lmarks = repo.listkeys('bookmarks')
483 422 rmarks = remote.listkeys('bookmarks')
484 423
485 424 diff = sorted(set(rmarks) - set(lmarks))
486 425 for k in diff:
487 426 ui.write(" %-25s %s\n" % (k, rmarks[k][:12]))
488 427
489 428 if len(diff) <= 0:
490 429 ui.status(_("no changed bookmarks found\n"))
491 430 return 1
492 431 return 0
493 432
494 433 def incoming(oldincoming, ui, repo, source="default", **opts):
495 434 if opts.get('bookmarks'):
496 435 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
497 436 other = hg.repository(hg.remoteui(repo, opts), source)
498 437 ui.status(_('comparing with %s\n') % url.hidepassword(source))
499 438 return diffbookmarks(ui, repo, other)
500 439 else:
501 440 return oldincoming(ui, repo, source, **opts)
502 441
503 442 def outgoing(oldoutgoing, ui, repo, dest=None, **opts):
504 443 if opts.get('bookmarks'):
505 444 dest = ui.expandpath(dest or 'default-push', dest or 'default')
506 445 dest, branches = hg.parseurl(dest, opts.get('branch'))
507 446 other = hg.repository(hg.remoteui(repo, opts), dest)
508 447 ui.status(_('comparing with %s\n') % url.hidepassword(dest))
509 448 return diffbookmarks(ui, other, repo)
510 449 else:
511 450 return oldoutgoing(ui, repo, dest, **opts)
512 451
513 452 def uisetup(ui):
514 453 extensions.wrapfunction(repair, "strip", strip)
515 454 if ui.configbool('bookmarks', 'track.current'):
516 455 extensions.wrapcommand(commands.table, 'update', updatecurbookmark)
517 456
518 457 entry = extensions.wrapcommand(commands.table, 'pull', pull)
519 458 entry[1].append(('B', 'bookmark', [],
520 459 _("bookmark to import"),
521 460 _('BOOKMARK')))
522 461 entry = extensions.wrapcommand(commands.table, 'push', push)
523 462 entry[1].append(('B', 'bookmark', [],
524 463 _("bookmark to export"),
525 464 _('BOOKMARK')))
526 465 entry = extensions.wrapcommand(commands.table, 'incoming', incoming)
527 466 entry[1].append(('B', 'bookmarks', False,
528 467 _("compare bookmark")))
529 468 entry = extensions.wrapcommand(commands.table, 'outgoing', outgoing)
530 469 entry[1].append(('B', 'bookmarks', False,
531 470 _("compare bookmark")))
532 471
533 472 pushkey.register('bookmarks', pushbookmark, listbookmarks)
534 473
535 474 def updatecurbookmark(orig, ui, repo, *args, **opts):
536 475 '''Set the current bookmark
537 476
538 477 If the user updates to a bookmark we update the .hg/bookmarks.current
539 478 file.
540 479 '''
541 480 res = orig(ui, repo, *args, **opts)
542 481 rev = opts['rev']
543 482 if not rev and len(args) > 0:
544 483 rev = args[0]
545 setcurrent(repo, rev)
484 bookmarks.setcurrent(repo, rev)
546 485 return res
547 486
548 487 def bmrevset(repo, subset, x):
549 488 """``bookmark([name])``
550 489 The named bookmark or all bookmarks.
551 490 """
552 491 # i18n: "bookmark" is a keyword
553 492 args = revset.getargs(x, 0, 1, _('bookmark takes one or no arguments'))
554 493 if args:
555 494 bm = revset.getstring(args[0],
556 495 # i18n: "bookmark" is a keyword
557 496 _('the argument to bookmark must be a string'))
558 497 bmrev = listbookmarks(repo).get(bm, None)
559 498 if bmrev:
560 499 bmrev = repo.changelog.rev(bin(bmrev))
561 500 return [r for r in subset if r == bmrev]
562 501 bms = set([repo.changelog.rev(bin(r)) for r in listbookmarks(repo).values()])
563 502 return [r for r in subset if r in bms]
564 503
565 504 def extsetup(ui):
566 505 revset.symbols['bookmark'] = bmrevset
567 506
568 507 cmdtable = {
569 508 "bookmarks":
570 509 (bookmark,
571 510 [('f', 'force', False, _('force')),
572 511 ('r', 'rev', '', _('revision'), _('REV')),
573 512 ('d', 'delete', False, _('delete a given bookmark')),
574 513 ('m', 'rename', '', _('rename a given bookmark'), _('NAME'))],
575 514 _('hg bookmarks [-f] [-d] [-m NAME] [-r REV] [NAME]')),
576 515 }
577 516
578 517 colortable = {'bookmarks.current': 'green'}
579 518
580 519 # tell hggettext to extract docstrings from these functions:
581 520 i18nfunctions = [bmrevset]
General Comments 0
You need to be logged in to leave comments. Login now