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'])) 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/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)