# HG changeset patch # User Benoit Boissinot # Date 1225111975 -3600 # Node ID 7e2e3aacc5dcebc33b1038a3577a25510dae3186 # Parent 671b3e1eac2ec3f75c7991fb6fbaf0f7a97b469f# Parent 3cbadcf28a4c194dc9605ddddff227489ec82a2b merge with dsop diff -r 671b3e1eac2e -r 7e2e3aacc5dc hgext/bookmarks.py --- a/hgext/bookmarks.py Mon Oct 27 13:22:45 2008 +0100 +++ b/hgext/bookmarks.py Mon Oct 27 13:52:55 2008 +0100 @@ -4,31 +4,32 @@ # # This software may be used and distributed according to the terms # of the GNU General Public License, incorporated herein by reference. + '''mercurial bookmarks -Mercurial bookmarks are local moveable pointers to changesets. Every bookmark -points to a changesets identified by it's hash. If you commit a changeset -that is based on a changeset that has a bookmark on it, the bookmark is forwarded -to the new changeset. +Mercurial bookmarks are local moveable pointers to changesets. Every +bookmark points to a changeset identified by its hash. If you commit a +changeset that is based on a changeset that has a bookmark on it, the +bookmark is forwarded to the new changeset. -It is possible to use bookmark names in every revision lookup (e.g. hg merge, hg update) +It is possible to use bookmark names in every revision lookup (e.g. hg +merge, hg update). ''' + from mercurial.commands import templateopts, hex, short from mercurial.i18n import _ from mercurial import cmdutil, util, commands, changelog -from mercurial.node import nullrev +from mercurial.node import nullid, nullrev from mercurial.repo import RepoError import mercurial, mercurial.localrepo, mercurial.repair, os - def parse(repo): '''Parse .hg/bookmarks file and return a dictionary - - Bookmarks are stored as {HASH}\s{NAME}\n (localtags format) - values in the .hg/bookmarks file. - They are read by the parse() method and returned as a dictionary with - name => hash values. - + + Bookmarks are stored as {HASH}\s{NAME}\n (localtags format) values + in the .hg/bookmarks file. They are read by the parse() method and + returned as a dictionary with name => hash values. + The parsed dictionary is cached until a write() operation is done. ''' try: @@ -44,63 +45,68 @@ def write(repo, refs): '''Write bookmarks - + Write the given bookmark => hash dictionary to the .hg/bookmarks file in a format equal to those of localtags. We also store a backup of the previous state in undo.bookmarks that can be copied back on rollback. ''' - util.copyfile(repo.join('bookmarks'), repo.join('undo.bookmarks')) + if os.path.exists(repo.join('bookmarks')): + util.copyfile(repo.join('bookmarks'), repo.join('undo.bookmarks')) file = repo.opener('bookmarks', 'w+') for refspec, node in refs.items(): file.write("%s %s\n" % (hex(node), refspec)) file.close() -def bookmark(ui, repo, mark=None, rev=None, force=False, delete=False, move=None): +def bookmark(ui, repo, mark=None, rev=None, force=False, delete=False, rename=None): '''mercurial bookmarks - - Bookmarks are pointer to certain commits that move when commiting. - Bookmarks are local. They can be renamed, copied and deleted. - It is possible to use bookmark names in 'hg merge' and 'hg update' to - update to a given bookmark. - - You can use 'hg bookmark [NAME]' to set a bookmark on the current tip - with the given name. If you specify a second [NAME] the bookmark is - set to the bookmark that has that name. You can also pass revision - identifiers to set bookmarks too. + + Bookmarks are pointers to certain commits that move when + commiting. Bookmarks are local. They can be renamed, copied and + deleted. It is possible to use bookmark names in 'hg merge' and 'hg + update' to update to a given bookmark. + + You can use 'hg bookmark NAME' to set a bookmark on the current + tip with the given name. If you specify a revision using -r REV + (where REV may be an existing bookmark), the bookmark is set to + that revision. ''' hexfn = ui.debugflag and hex or short marks = parse(repo) cur = repo.changectx('.').node() - if move: - if move not in marks: - raise util.Abort(_("a bookmark of this name doesnot exists")) + if rename: + if rename not in marks: + raise util.Abort(_("a bookmark of this name does not exist")) if mark in marks and not force: raise util.Abort(_("a bookmark of the same name already exists")) - marks[mark] = marks[move] - del marks[move] + if mark is None: + raise util.Abort(_("new bookmark name required")) + marks[mark] = marks[rename] + del marks[rename] write(repo, marks) return - + if delete: if mark == None: raise util.Abort(_("bookmark name required")) if mark not in marks: - raise util.Abort(_("a bookmark of this name does not exists")) + raise util.Abort(_("a bookmark of this name does not exist")) del marks[mark] write(repo, marks) return if mark != None: - if mark.strip().count("\n") > 0: - raise Exception("bookmark cannot contain newlines") + if "\n" in mark: + raise util.Abort(_("bookmark name cannot contain newlines")) + mark = mark.strip() if mark in marks and not force: raise util.Abort(_("a bookmark of the same name already exists")) - if ((mark in repo.branchtags() or mark == repo.dirstate.branch()) + if ((mark in repo.branchtags() or mark == repo.dirstate.branch()) and not force): - raise util.Abort(_("a bookmark cannot have the name of an existing branch")) + raise util.Abort( + _("a bookmark cannot have the name of an existing branch")) if rev: marks[mark] = repo.lookup(rev) else: @@ -109,12 +115,15 @@ return if mark == None: + if rev: + raise util.Abort(_("bookmark name required")) if len(marks) == 0: ui.status("no bookmarks set\n") else: for bmark, n in marks.iteritems(): prefix = (n == cur) and '*' or ' ' - ui.write(" %s %-25s %d:%s\n" % (prefix, bmark, repo.changelog.rev(n), hexfn(n))) + ui.write(" %s %-25s %d:%s\n" % ( + prefix, bmark, repo.changelog.rev(n), hexfn(n))) return def _revstostrip(changelog, node): @@ -162,7 +171,7 @@ def rollback(self): if os.path.exists(self.join('undo.bookmarks')): util.rename(self.join('undo.bookmarks'), self.join('bookmarks')) - return super(bookmark_repo, self).rollback() + return super(bookmark_repo, self).rollback() def lookup(self, key): if self._bookmarks is None: @@ -175,7 +184,11 @@ """Add a revision to the repository and move the bookmark""" node = super(bookmark_repo, self).commit(*k, **kw) + if node == None: + return None parents = repo.changelog.parents(node) + if parents[1] == nullid: + parents = (parents[0],) marks = parse(repo) update = False for mark, n in marks.items(): @@ -192,7 +205,8 @@ except RepoError, inst: pass - result = super(bookmark_repo, self).addchangegroup(source, srctype, url, emptyok) + result = super(bookmark_repo, self).addchangegroup( + source, srctype, url, emptyok) if result > 1: # We have more heads than before return result @@ -215,6 +229,6 @@ [('f', 'force', False, _('force')), ('r', 'rev', '', _('revision')), ('d', 'delete', False, _('delete a given bookmark')), - ('m', 'move', '', _('move a given bookmark'))], + ('m', 'rename', '', _('rename a given bookmark'))], _('hg bookmarks [-d] [-m NAME] [-r NAME] [NAME]')), } diff -r 671b3e1eac2e -r 7e2e3aacc5dc tests/test-bookmarks --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-bookmarks Mon Oct 27 13:52:55 2008 +0100 @@ -0,0 +1,99 @@ +#!/bin/sh + +echo "[extensions]" >> $HGRCPATH +echo "bookmarks=" >> $HGRCPATH + +hg init + +echo % no bookmarks +hg bookmarks + +echo % bookmark rev -1 +hg bookmark X + +echo % list bookmarks +hg bookmarks + +echo a > a +hg add a +hg commit -m 0 + +echo % bookmark X moved to rev 0 +hg bookmarks + +echo % look up bookmark +hg log -r X + +echo % second bookmark for rev 0 +hg bookmark X2 + +echo % bookmark rev -1 again +hg bookmark -r null Y + +echo % list bookmarks +hg bookmarks + +echo b > b +hg add b +hg commit -m 1 + +echo % bookmarks X and X2 moved to rev 1, Y at rev -1 +hg bookmarks + +echo % bookmark rev 0 again +hg bookmark -r 0 Z + +echo c > c +hg add c +hg commit -m 2 + +echo % bookmarks X and X2 moved to rev 2, Y at rev -1, Z at rev 0 +hg bookmarks + +echo % rename nonexistent bookmark +hg bookmark -m A B + +echo % rename to existent bookmark +hg bookmark -m X Y + +echo % force rename to existent bookmark +hg bookmark -f -m X Y + +echo % list bookmarks +hg bookmark + +echo % rename without new name +hg bookmark -m Y + +echo % delete without name +hg bookmark -d + +echo % delete nonexistent bookmark +hg bookmark -d A + +echo % bookmark name with spaces should be stripped +hg bookmark ' x y ' + +echo % list bookmarks +hg bookmarks + +echo % look up stripped bookmark name +hg log -r 'x y' + +echo % reject bookmark name with newline +hg bookmark ' +' + +echo % bookmark with existing name +hg bookmark Z + +echo % force bookmark with existing name +hg bookmark -f Z + +echo % list bookmarks +hg bookmark + +echo % revision but no bookmark name +hg bookmark -r . + +true diff -r 671b3e1eac2e -r 7e2e3aacc5dc tests/test-bookmarks.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-bookmarks.out Mon Oct 27 13:52:55 2008 +0100 @@ -0,0 +1,70 @@ +% no bookmarks +no bookmarks set +% bookmark rev -1 +% list bookmarks + * X -1:000000000000 +% bookmark X moved to rev 0 + * X 0:f7b1eb17ad24 +% look up bookmark +changeset: 0:f7b1eb17ad24 +tag: tip +user: test +date: Thu Jan 01 00:00:00 1970 +0000 +summary: 0 + +% second bookmark for rev 0 +% bookmark rev -1 again +% list bookmarks + * X2 0:f7b1eb17ad24 + * X 0:f7b1eb17ad24 + Y -1:000000000000 +% bookmarks X and X2 moved to rev 1, Y at rev -1 + * X2 1:925d80f479bb + * X 1:925d80f479bb + Y -1:000000000000 +% bookmark rev 0 again +% bookmarks X and X2 moved to rev 2, Y at rev -1, Z at rev 0 + * X2 2:0316ce92851d + * X 2:0316ce92851d + Z 0:f7b1eb17ad24 + Y -1:000000000000 +% rename nonexistent bookmark +abort: a bookmark of this name does not exist +% rename to existent bookmark +abort: a bookmark of the same name already exists +% force rename to existent bookmark +% list bookmarks + * X2 2:0316ce92851d + * Y 2:0316ce92851d + Z 0:f7b1eb17ad24 +% rename without new name +abort: new bookmark name required +% delete without name +abort: bookmark name required +% delete nonexistent bookmark +abort: a bookmark of this name does not exist +% bookmark name with spaces should be stripped +% list bookmarks + * X2 2:0316ce92851d + * Y 2:0316ce92851d + Z 0:f7b1eb17ad24 + * x y 2:0316ce92851d +% look up stripped bookmark name +changeset: 2:0316ce92851d +tag: tip +user: test +date: Thu Jan 01 00:00:00 1970 +0000 +summary: 2 + +% reject bookmark name with newline +abort: bookmark name cannot contain newlines +% bookmark with existing name +abort: a bookmark of the same name already exists +% force bookmark with existing name +% list bookmarks + * X2 2:0316ce92851d + * Y 2:0316ce92851d + * Z 2:0316ce92851d + * x y 2:0316ce92851d +% revision but no bookmark name +abort: bookmark name required