D6825: contrib: start building a library for simple hooks

joerg.sonnenberger (Joerg Sonnenberger) phabricator at mercurial-scm.org
Sat Sep 7 08:50:55 EDT 2019


joerg.sonnenberger created this revision.
Herald added a subscriber: mercurial-devel.
Herald added a reviewer: hg-reviewers.

REVISION SUMMARY
  Many workflows depend on hooks to enforce certain policies, e.g. to
  prevent forced pushes. The Mercurial Guide includes some cases and
  Google can help finding others, but it can save users a lot of time
  if hg itself has a couple of examples for further customization.

REPOSITORY
  rHG Mercurial

REVISION DETAIL
  https://phab.mercurial-scm.org/D6825

AFFECTED FILES
  contrib/hooks/enforce_draft_commits.py
  contrib/hooks/reject_merge_commits.py
  contrib/hooks/reject_new_heads.py

CHANGE DETAILS

diff --git a/contrib/hooks/reject_new_heads.py b/contrib/hooks/reject_new_heads.py
new file mode 100644
--- /dev/null
+++ b/contrib/hooks/reject_new_heads.py
@@ -0,0 +1,25 @@
+# This hook checks branches touched new changesets have at most one
+# open head. It can be used to enforce policies for merge-before-push
+# or rebase-before-push. It does not handle pre-existing hydras.
+#
+# Usage:
+# [hooks]
+# pretxnclose.reject_new_heads = python:../reject_new_heads.py:hook
+
+from mercurial import (
+    error,
+)
+from mercurial.i18n import _
+
+def hook(ui, repo, hooktype, node = None, **kwargs):
+    if hooktype != "pretxnclose":
+       raise error.Abort(_('Unsupported hook type %s') % hooktype)
+    ctx = repo.unfiltered()[node]
+    branches = set()
+    for rev in repo.changelog.revs(start=ctx.rev()):
+        rev = repo[rev]
+        branches.add(rev.branch())
+    for branch in branches:
+        if len(repo.revs("head() and not closed() and branch(%s)", branch)) > 1:
+            raise error.Abort(_('Changes on branch "%s" resulted '
+                                'in multiple heads') % branch)
diff --git a/contrib/hooks/reject_merge_commits.py b/contrib/hooks/reject_merge_commits.py
new file mode 100644
--- /dev/null
+++ b/contrib/hooks/reject_merge_commits.py
@@ -0,0 +1,27 @@
+# This hook checks new changesets for merge commits. Merge commits are allowed
+# only between different branches, i.e. merging a feature branch into the main
+# development branch. This can be used to enforce policies for linear commit
+# histories.
+#
+# Usage:
+# [hooks]
+# pretxnchangegroup.reject_merge_commits = python:../reject_merge_commits.py:hook
+
+from mercurial import (
+    error,
+)
+from mercurial.i18n import _
+
+def hook(ui, repo, hooktype, node = None, **kwargs):
+    if hooktype != "pretxnchangegroup":
+       raise error.Abort(_('Unsupported hook type %s'), hooktype)
+
+    ctx = repo.unfiltered()[node]
+    for rev in repo.changelog.revs(start=ctx.rev()):
+        rev = repo[rev]
+        parents = rev.parents()
+        if len(parents) < 2:
+            continue
+        if all(repo[p].branch() == rev.branch() for p in parents):
+            raise error.Abort(_('%s rejected as merge on the same branch. '
+                                'Please consider rebase.') % rev)
diff --git a/contrib/hooks/enforce_draft_commits.py b/contrib/hooks/enforce_draft_commits.py
new file mode 100644
--- /dev/null
+++ b/contrib/hooks/enforce_draft_commits.py
@@ -0,0 +1,25 @@
+# This hook checks that all new changesets are in drafts. This allows
+# enforcing policies for work-in-progress changes in overlay repositories,
+# i.e. a shared hidden repositories with different views for work-in-progress
+# code and public history.
+#
+# Usage:
+# [hooks]
+# pretxnclose-phase.enforce_draft_commits = python:../enforce_draft_commits.py:hook
+
+from mercurial import (
+    error,
+    phases,
+)
+from mercurial.i18n import _
+
+def hook(ui, repo, hooktype, node = None, **kwargs):
+    if hooktype != "pretxnclose-phase":
+       raise error.Abort(_('Unsupported hook type %s'), hooktype)
+    ctx = repo.unfiltered()[node]
+    if kwargs['oldphase']:
+        raise error.Abort(_('Phase change from %s to %s for %s rejected') %
+                            (kwargs['oldphase'], kwargs['phase'], ctx))
+    elif kwargs['phase'] != 'draft':
+        raise error.Abort(_('New changeset %s in phase %s rejected') %
+                            (ctx, kwargs['phase']))



To: joerg.sonnenberger, #hg-reviewers
Cc: mercurial-devel


More information about the Mercurial-devel mailing list