hgext/bookflow.py
changeset 40834 9cec7a36bab8
child 41031 b53c5651fcf6
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hgext/bookflow.py	Mon Dec 03 14:17:38 2018 -0500
@@ -0,0 +1,103 @@
+"""implements bookmark-based branching (EXPERIMENTAL)
+
+ - Disables creation of new branches (config: enable_branches=False).
+ - Requires an active bookmark on commit (config: require_bookmark=True).
+ - Doesn't move the active bookmark on update, only on commit.
+ - Requires '--rev' for moving an existing bookmark.
+ - Protects special bookmarks (config: protect=@).
+
+ flow related commands
+
+    :hg book NAME: create a new bookmark
+    :hg book NAME -r REV: move bookmark to revision (fast-forward)
+    :hg up|co NAME: switch to bookmark
+    :hg push -B .: push active bookmark
+"""
+from __future__ import absolute_import
+
+from mercurial.i18n import _
+from mercurial import (
+    bookmarks,
+    commands,
+    error,
+    extensions,
+    registrar,
+)
+
+MY_NAME = 'bookflow'
+
+configtable = {}
+configitem = registrar.configitem(configtable)
+
+configitem(MY_NAME, 'protect', ['@'])
+configitem(MY_NAME, 'require-bookmark', True)
+configitem(MY_NAME, 'enable-branches', False)
+
+cmdtable = {}
+command = registrar.command(cmdtable)
+
+def commit_hook(ui, repo, **kwargs):
+    active = repo._bookmarks.active
+    if active:
+        if active in ui.configlist(MY_NAME, 'protect'):
+            raise error.Abort(
+                _('cannot commit, bookmark {} is protected').format(active))
+        if not cwd_at_bookmark(repo, active):
+            raise error.Abort(
+       _('cannot commit, working directory out of sync with active bookmark'),
+                hint=_("run 'hg up {}'").format(active))
+    elif ui.configbool(MY_NAME, 'require-bookmark', True):
+        raise error.Abort(_('cannot commit without an active bookmark'))
+    return 0
+
+def bookmarks_update(orig, repo, parents, node):
+    if len(parents) == 2:
+        # called during commit
+        return orig(repo, parents, node)
+    else:
+        # called during update
+        return False
+
+def bookmarks_addbookmarks(
+        orig, repo, tr, names, rev=None, force=False, inactive=False):
+    if not rev:
+        marks = repo._bookmarks
+        for name in names:
+            if name in marks:
+                raise error.Abort(
+                    _("bookmark {} already exists, to move use the --rev option"
+                    ).format(name))
+    return orig(repo, tr, names, rev, force, inactive)
+
+def commands_commit(orig, ui, repo, *args, **opts):
+    commit_hook(ui, repo)
+    return orig(ui, repo, *args, **opts)
+
+def commands_pull(orig, ui, repo, *args, **opts):
+    rc = orig(ui, repo, *args, **opts)
+    active = repo._bookmarks.active
+    if active and not cwd_at_bookmark(repo, active):
+        ui.warn(_(
+            "working directory out of sync with active bookmark, run 'hg up {}'"
+        ).format(active))
+    return rc
+
+def commands_branch(orig, ui, repo, label=None, **opts):
+    if label and not opts.get(r'clean') and not opts.get(r'rev'):
+        raise error.Abort(
+         _("creating named branches is disabled and you should use bookmarks"),
+            hint="see 'hg help bookflow'")
+    return orig(ui, repo, label, **opts)
+
+def cwd_at_bookmark(repo, mark):
+    mark_id = repo._bookmarks[mark]
+    cur_id = repo.lookup('.')
+    return cur_id == mark_id
+
+def uisetup(ui):
+    extensions.wrapfunction(bookmarks, 'update', bookmarks_update)
+    extensions.wrapfunction(bookmarks, 'addbookmarks', bookmarks_addbookmarks)
+    extensions.wrapcommand(commands.table, 'commit', commands_commit)
+    extensions.wrapcommand(commands.table, 'pull', commands_pull)
+    if not ui.configbool(MY_NAME, 'enable-branches'):
+        extensions.wrapcommand(commands.table, 'branch', commands_branch)