--- a/hgext/bookmarks.py Thu Feb 10 13:46:28 2011 -0600
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,216 +0,0 @@
-# Mercurial extension to provide the 'hg bookmark' command
-#
-# Copyright 2008 David Soria Parra <dsp@php.net>
-#
-# This software may be used and distributed according to the terms of the
-# GNU General Public License version 2 or any later version.
-
-'''track a line of development with movable markers
-
-Bookmarks are local movable markers 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 shifts to the new changeset.
-
-It is possible to use bookmark names in every revision lookup (e.g.
-:hg:`merge`, :hg:`update`).
-
-By default, when several bookmarks point to the same changeset, they
-will all move forward together. It is possible to obtain a more
-git-like experience by adding the following configuration option to
-your configuration file::
-
- [bookmarks]
- track.current = True
-
-This will cause Mercurial to track the bookmark that you are currently
-using, and only update it. This is similar to git's approach to
-branching.
-'''
-
-from mercurial.i18n import _
-from mercurial.node import nullid, nullrev, bin, hex, short
-from mercurial import util, commands, repair, extensions, pushkey, hg, url
-from mercurial import encoding
-from mercurial import bookmarks
-import os
-
-def bookmark(ui, repo, mark=None, rev=None, force=False, delete=False, rename=None):
- '''track a line of development with movable markers
-
- Bookmarks are pointers to certain commits that move when
- committing. Bookmarks are local. They can be renamed, copied and
- deleted. It is possible to use bookmark names in :hg:`merge` and
- :hg:`update` to merge and update respectively to a given bookmark.
-
- You can use :hg:`bookmark NAME` to set a bookmark on the working
- directory's parent revision with the given name. If you specify
- a revision using -r REV (where REV may be an existing bookmark),
- the bookmark is assigned to that revision.
-
- Bookmarks can be pushed and pulled between repositories (see :hg:`help
- push` and :hg:`help pull`). This requires the bookmark extension to be
- enabled for both the local and remote repositories.
- '''
- hexfn = ui.debugflag and hex or short
- marks = repo._bookmarks
- cur = repo.changectx('.').node()
-
- 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"))
- if mark is None:
- raise util.Abort(_("new bookmark name required"))
- marks[mark] = marks[rename]
- del marks[rename]
- if repo._bookmarkcurrent == rename:
- bookmarks.setcurrent(repo, mark)
- bookmarks.write(repo)
- return
-
- if delete:
- if mark is None:
- raise util.Abort(_("bookmark name required"))
- if mark not in marks:
- raise util.Abort(_("a bookmark of this name does not exist"))
- if mark == repo._bookmarkcurrent:
- bookmarks.setcurrent(repo, None)
- del marks[mark]
- bookmarks.write(repo)
- return
-
- if mark is not None:
- if "\n" in mark:
- raise util.Abort(_("bookmark name cannot contain newlines"))
- mark = mark.strip()
- if not mark:
- raise util.Abort(_("bookmark names cannot consist entirely of "
- "whitespace"))
- 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())
- and not force):
- raise util.Abort(
- _("a bookmark cannot have the name of an existing branch"))
- if rev:
- marks[mark] = repo.lookup(rev)
- else:
- marks[mark] = repo.changectx('.').node()
- bookmarks.setcurrent(repo, mark)
- bookmarks.write(repo)
- return
-
- if mark is 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():
- if ui.configbool('bookmarks', 'track.current'):
- current = repo._bookmarkcurrent
- if bmark == current and n == cur:
- prefix, label = '*', 'bookmarks.current'
- else:
- prefix, label = ' ', ''
- else:
- if n == cur:
- prefix, label = '*', 'bookmarks.current'
- else:
- prefix, label = ' ', ''
-
- if ui.quiet:
- ui.write("%s\n" % bmark, label=label)
- else:
- ui.write(" %s %-25s %d:%s\n" % (
- prefix, bmark, repo.changelog.rev(n), hexfn(n)),
- label=label)
- return
-
-def pull(oldpull, ui, repo, source="default", **opts):
- # translate bookmark args to rev args for actual pull
- if opts.get('bookmark'):
- # this is an unpleasant hack as pull will do this internally
- source, branches = hg.parseurl(ui.expandpath(source),
- opts.get('branch'))
- other = hg.repository(hg.remoteui(repo, opts), source)
- rb = other.listkeys('bookmarks')
-
- for b in opts['bookmark']:
- if b not in rb:
- raise util.Abort(_('remote bookmark %s not found!') % b)
- opts.setdefault('rev', []).append(b)
-
- result = oldpull(ui, repo, source, **opts)
-
- # update specified bookmarks
- if opts.get('bookmark'):
- for b in opts['bookmark']:
- # explicit pull overrides local bookmark if any
- ui.status(_("importing bookmark %s\n") % b)
- repo._bookmarks[b] = repo[rb[b]].node()
- bookmarks.write(repo)
-
- return result
-
-def push(oldpush, ui, repo, dest=None, **opts):
- dopush = True
- if opts.get('bookmark'):
- dopush = False
- for b in opts['bookmark']:
- if b in repo._bookmarks:
- dopush = True
- opts.setdefault('rev', []).append(b)
-
- result = 0
- if dopush:
- result = oldpush(ui, repo, dest, **opts)
-
- if opts.get('bookmark'):
- # this is an unpleasant hack as push will do this internally
- dest = ui.expandpath(dest or 'default-push', dest or 'default')
- dest, branches = hg.parseurl(dest, opts.get('branch'))
- other = hg.repository(hg.remoteui(repo, opts), dest)
- rb = other.listkeys('bookmarks')
- for b in opts['bookmark']:
- # explicit push overrides remote bookmark if any
- if b in repo._bookmarks:
- ui.status(_("exporting bookmark %s\n") % b)
- new = repo[b].hex()
- elif b in rb:
- ui.status(_("deleting remote bookmark %s\n") % b)
- new = '' # delete
- else:
- ui.warn(_('bookmark %s does not exist on the local '
- 'or remote repository!\n') % b)
- return 2
- old = rb.get(b, '')
- r = other.pushkey('bookmarks', b, old, new)
- if not r:
- ui.warn(_('updating bookmark %s failed!\n') % b)
- if not result:
- result = 2
-
- return result
-
-def uisetup(ui):
- entry = extensions.wrapcommand(commands.table, 'pull', pull)
- entry[1].append(('B', 'bookmark', [],
- _("bookmark to import"),
- _('BOOKMARK')))
- entry = extensions.wrapcommand(commands.table, 'push', push)
- entry[1].append(('B', 'bookmark', [],
- _("bookmark to export"),
- _('BOOKMARK')))
-
-cmdtable = {
- "bookmarks":
- (bookmark,
- [('f', 'force', False, _('force')),
- ('r', 'rev', '', _('revision'), _('REV')),
- ('d', 'delete', False, _('delete a given bookmark')),
- ('m', 'rename', '', _('rename a given bookmark'), _('NAME'))],
- _('hg bookmarks [-f] [-d] [-m NAME] [-r REV] [NAME]')),
-}
--- a/mercurial/commands.py Thu Feb 10 13:46:28 2011 -0600
+++ b/mercurial/commands.py Thu Feb 10 13:46:28 2011 -0600
@@ -457,6 +457,100 @@
cmdutil.bail_if_changed(repo)
return hg.clean(repo, node)
+def bookmark(ui, repo, mark=None, rev=None, force=False, delete=False, rename=None):
+ '''track a line of development with movable markers
+
+ Bookmarks are pointers to certain commits that move when
+ committing. Bookmarks are local. They can be renamed, copied and
+ deleted. It is possible to use bookmark names in :hg:`merge` and
+ :hg:`update` to merge and update respectively to a given bookmark.
+
+ You can use :hg:`bookmark NAME` to set a bookmark on the working
+ directory's parent revision with the given name. If you specify
+ a revision using -r REV (where REV may be an existing bookmark),
+ the bookmark is assigned to that revision.
+
+ Bookmarks can be pushed and pulled between repositories (see :hg:`help
+ push` and :hg:`help pull`). This requires the bookmark extension to be
+ enabled for both the local and remote repositories.
+ '''
+ hexfn = ui.debugflag and hex or short
+ marks = repo._bookmarks
+ cur = repo.changectx('.').node()
+
+ 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"))
+ if mark is None:
+ raise util.Abort(_("new bookmark name required"))
+ marks[mark] = marks[rename]
+ del marks[rename]
+ if repo._bookmarkcurrent == rename:
+ bookmarks.setcurrent(repo, mark)
+ bookmarks.write(repo)
+ return
+
+ if delete:
+ if mark is None:
+ raise util.Abort(_("bookmark name required"))
+ if mark not in marks:
+ raise util.Abort(_("a bookmark of this name does not exist"))
+ if mark == repo._bookmarkcurrent:
+ bookmarks.setcurrent(repo, None)
+ del marks[mark]
+ bookmarks.write(repo)
+ return
+
+ if mark is not None:
+ if "\n" in mark:
+ raise util.Abort(_("bookmark name cannot contain newlines"))
+ mark = mark.strip()
+ if not mark:
+ raise util.Abort(_("bookmark names cannot consist entirely of "
+ "whitespace"))
+ 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())
+ and not force):
+ raise util.Abort(
+ _("a bookmark cannot have the name of an existing branch"))
+ if rev:
+ marks[mark] = repo.lookup(rev)
+ else:
+ marks[mark] = repo.changectx('.').node()
+ bookmarks.setcurrent(repo, mark)
+ bookmarks.write(repo)
+ return
+
+ if mark is 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():
+ if ui.configbool('bookmarks', 'track.current'):
+ current = repo._bookmarkcurrent
+ if bmark == current and n == cur:
+ prefix, label = '*', 'bookmarks.current'
+ else:
+ prefix, label = ' ', ''
+ else:
+ if n == cur:
+ prefix, label = '*', 'bookmarks.current'
+ else:
+ prefix, label = ' ', ''
+
+ if ui.quiet:
+ ui.write("%s\n" % bmark, label=label)
+ else:
+ ui.write(" %s %-25s %d:%s\n" % (
+ prefix, bmark, repo.changelog.rev(n), hexfn(n)),
+ label=label)
+ return
+
def branch(ui, repo, label=None, **opts):
"""set or show the current branch name
@@ -2806,6 +2900,16 @@
other = hg.repository(hg.remoteui(repo, opts), source)
ui.status(_('pulling from %s\n') % url.hidepassword(source))
revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
+
+ if opts.get('bookmark'):
+ if not revs:
+ revs = []
+ rb = other.listkeys('bookmarks')
+ for b in opts['bookmark']:
+ if b not in rb:
+ raise util.Abort(_('remote bookmark %s not found!') % b)
+ revs.append(rb[b])
+
if revs:
try:
revs = [other.lookup(rev) for rev in revs]
@@ -2819,10 +2923,21 @@
checkout = str(repo.changelog.rev(other.lookup(checkout)))
repo._subtoppath = source
try:
- return postincoming(ui, repo, modheads, opts.get('update'), checkout)
+ ret = postincoming(ui, repo, modheads, opts.get('update'), checkout)
+
finally:
del repo._subtoppath
+ # update specified bookmarks
+ if opts.get('bookmark'):
+ for b in opts['bookmark']:
+ # explicit pull overrides local bookmark if any
+ ui.status(_("importing bookmark %s\n") % b)
+ repo._bookmarks[b] = repo[rb[b]].node()
+ bookmarks.write(repo)
+
+ return ret
+
def push(ui, repo, dest=None, **opts):
"""push changes to the specified destination
@@ -2852,6 +2967,17 @@
Returns 0 if push was successful, 1 if nothing to push.
"""
+
+ if opts.get('bookmark'):
+ for b in opts['bookmark']:
+ # translate -B options to -r so changesets get pushed
+ if b in repo._bookmarks:
+ opts.setdefault('rev', []).append(b)
+ else:
+ # if we try to push a deleted bookmark, translate it to null
+ # this lets simultaneous -r, -b options continue working
+ opts.setdefault('rev', []).append("null")
+
dest = ui.expandpath(dest or 'default-push', dest or 'default')
dest, branches = hg.parseurl(dest, opts.get('branch'))
revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
@@ -2870,9 +2996,33 @@
return False
finally:
del repo._subtoppath
- r = repo.push(other, opts.get('force'), revs=revs,
- newbranch=opts.get('new_branch'))
- return r == 0
+ result = repo.push(other, opts.get('force'), revs=revs,
+ newbranch=opts.get('new_branch'))
+
+ result = (result == 0)
+
+ if opts.get('bookmark'):
+ rb = other.listkeys('bookmarks')
+ for b in opts['bookmark']:
+ # explicit push overrides remote bookmark if any
+ if b in repo._bookmarks:
+ ui.status(_("exporting bookmark %s\n") % b)
+ new = repo[b].hex()
+ elif b in rb:
+ ui.status(_("deleting remote bookmark %s\n") % b)
+ new = '' # delete
+ else:
+ ui.warn(_('bookmark %s does not exist on the local '
+ 'or remote repository!\n') % b)
+ return 2
+ old = rb.get(b, '')
+ r = other.pushkey('bookmarks', b, old, new)
+ if not r:
+ ui.warn(_('updating bookmark %s failed!\n') % b)
+ if not result:
+ result = 2
+
+ return result
def recover(ui, repo):
"""roll back an interrupted transaction
@@ -4091,6 +4241,13 @@
_('use command to check changeset state'), _('CMD')),
('U', 'noupdate', False, _('do not update to target'))],
_("[-gbsr] [-U] [-c CMD] [REV]")),
+ "bookmarks":
+ (bookmark,
+ [('f', 'force', False, _('force')),
+ ('r', 'rev', '', _('revision'), _('REV')),
+ ('d', 'delete', False, _('delete a given bookmark')),
+ ('m', 'rename', '', _('rename a given bookmark'), _('NAME'))],
+ _('hg bookmarks [-f] [-d] [-m NAME] [-r REV] [NAME]')),
"branch":
(branch,
[('f', 'force', None,
@@ -4396,6 +4553,7 @@
_('run even when remote repository is unrelated')),
('r', 'rev', [],
_('a remote changeset intended to be added'), _('REV')),
+ ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
('b', 'branch', [],
_('a specific branch you would like to pull'), _('BRANCH')),
] + remoteopts,
@@ -4406,6 +4564,7 @@
('r', 'rev', [],
_('a changeset intended to be included in the destination'),
_('REV')),
+ ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
('b', 'branch', [],
_('a specific branch you would like to push'), _('BRANCH')),
('', 'new-branch', False, _('allow pushing a new branch')),
--- a/mercurial/extensions.py Thu Feb 10 13:46:28 2011 -0600
+++ b/mercurial/extensions.py Thu Feb 10 13:46:28 2011 -0600
@@ -11,7 +11,7 @@
_extensions = {}
_order = []
-_ignore = ['hbisect']
+_ignore = ['hbisect', 'bookmarks']
def extensions():
for name in _order:
--- a/tests/test-bookmarks-pushpull.t Thu Feb 10 13:46:28 2011 -0600
+++ b/tests/test-bookmarks-pushpull.t Thu Feb 10 13:46:28 2011 -0600
@@ -71,14 +71,21 @@
$ hg book -d W
$ hg push -B W ../a
+ pushing to ../a
+ searching for changes
+ no changes found
deleting remote bookmark W
push/pull name that doesn't exist
$ hg push -B badname ../a
+ pushing to ../a
+ searching for changes
+ no changes found
bookmark badname does not exist on the local or remote repository!
[2]
$ hg pull -B anotherbadname ../a
+ pulling from ../a
abort: remote bookmark anotherbadname not found!
[255]
--- a/tests/test-debugcomplete.t Thu Feb 10 13:46:28 2011 -0600
+++ b/tests/test-debugcomplete.t Thu Feb 10 13:46:28 2011 -0600
@@ -6,6 +6,7 @@
archive
backout
bisect
+ bookmarks
branch
branches
bundle
@@ -187,8 +188,8 @@
init: ssh, remotecmd, insecure
log: follow, follow-first, date, copies, keyword, rev, removed, only-merges, user, only-branch, branch, prune, patch, git, limit, no-merges, stat, style, template, include, exclude
merge: force, tool, rev, preview
- pull: update, force, rev, branch, ssh, remotecmd, insecure
- push: force, rev, branch, new-branch, ssh, remotecmd, insecure
+ pull: update, force, rev, bookmark, branch, ssh, remotecmd, insecure
+ push: force, rev, bookmark, branch, new-branch, ssh, remotecmd, insecure
remove: after, force, include, exclude
serve: accesslog, daemon, daemon-pipefds, errorlog, port, address, prefix, name, web-conf, webdir-conf, pid-file, stdio, templates, style, ipv6, certificate
status: all, modified, added, removed, deleted, clean, unknown, ignored, no-status, copies, print0, rev, change, include, exclude, subrepos
@@ -198,6 +199,7 @@
archive: no-decode, prefix, rev, type, subrepos, include, exclude
backout: merge, parent, tool, rev, include, exclude, message, logfile, date, user
bisect: reset, good, bad, skip, command, noupdate
+ bookmarks: force, rev, delete, rename
branch: force, clean
branches: active, closed
bundle: force, rev, branch, base, all, type, ssh, remotecmd, insecure
--- a/tests/test-globalopts.t Thu Feb 10 13:46:28 2011 -0600
+++ b/tests/test-globalopts.t Thu Feb 10 13:46:28 2011 -0600
@@ -284,6 +284,7 @@
archive create an unversioned archive of a repository revision
backout reverse effect of earlier changeset
bisect subdivision search of changesets
+ bookmarks track a line of development with movable markers
branch set or show the current branch name
branches list repository named branches
bundle create a changegroup file
@@ -360,6 +361,7 @@
archive create an unversioned archive of a repository revision
backout reverse effect of earlier changeset
bisect subdivision search of changesets
+ bookmarks track a line of development with movable markers
branch set or show the current branch name
branches list repository named branches
bundle create a changegroup file
--- a/tests/test-help.t Thu Feb 10 13:46:28 2011 -0600
+++ b/tests/test-help.t Thu Feb 10 13:46:28 2011 -0600
@@ -55,6 +55,7 @@
archive create an unversioned archive of a repository revision
backout reverse effect of earlier changeset
bisect subdivision search of changesets
+ bookmarks track a line of development with movable markers
branch set or show the current branch name
branches list repository named branches
bundle create a changegroup file
@@ -127,6 +128,7 @@
archive create an unversioned archive of a repository revision
backout reverse effect of earlier changeset
bisect subdivision search of changesets
+ bookmarks track a line of development with movable markers
branch set or show the current branch name
branches list repository named branches
bundle create a changegroup file
@@ -649,6 +651,7 @@
archive create an unversioned archive of a repository revision
backout reverse effect of earlier changeset
bisect subdivision search of changesets
+ bookmarks track a line of development with movable markers
branch set or show the current branch name
branches list repository named branches
bundle create a changegroup file
--- a/tests/test-ssh.t Thu Feb 10 13:46:28 2011 -0600
+++ b/tests/test-ssh.t Thu Feb 10 13:46:28 2011 -0600
@@ -233,6 +233,9 @@
importing bookmark foo
$ hg book -d foo
$ hg push -B foo
+ pushing to ssh://user@dummy/remote
+ searching for changes
+ no changes found
deleting remote bookmark foo
a bad, evil hook that prints to stdout
@@ -287,5 +290,3 @@
Got arguments 1:user@dummy 2:hg -R remote serve --stdio
Got arguments 1:user@dummy 2:hg -R remote serve --stdio
Got arguments 1:user@dummy 2:hg -R remote serve --stdio
- Got arguments 1:user@dummy 2:hg -R remote serve --stdio
- Got arguments 1:user@dummy 2:hg -R remote serve --stdio