D6825: contrib: start building a library for simple hooks
This revision now requires changes to proceed. baymax added a comment. baymax requested changes to this revision. There seems to have been no activities on this Diff for the past 3 Months. By policy, we are automatically moving it out of the `need-review` state. Please, move it back to `need-review` without hesitation if this diff should still be discussed. :baymax:need-review-idle: REPOSITORY rHG Mercurial CHANGES SINCE LAST ACTION https://phab.mercurial-scm.org/D6825/new/ REVISION DETAIL https://phab.mercurial-scm.org/D6825 To: joerg.sonnenberger, #hg-reviewers, baymax Cc: durin42, indygreg, pulkit, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D6825: contrib: start building a library for simple hooks
durin42 added a comment. I like where this is going, but I wonder if these specific hooks can be written without being in-process Python hooks. Reason being that Python hooks use the unstable internals of hg and are semi-discouraged if they're avoidable. Thoughts? If you do stick with in-process for this, I definitely want a test included so we don't regress our own examples. ;) REPOSITORY rHG Mercurial CHANGES SINCE LAST ACTION https://phab.mercurial-scm.org/D6825/new/ REVISION DETAIL https://phab.mercurial-scm.org/D6825 To: joerg.sonnenberger, #hg-reviewers Cc: durin42, indygreg, pulkit, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D6825: contrib: start building a library for simple hooks
indygreg added a comment. I think we should go a step further and have some built-in hooks in Mercurial itself, like we do extensions. A good first implementation would be to define those hooks somewhere where they can be imported, add tests like they are standalone hooks. As a follow-up (read: slightly more work), we can define a new config syntax for using these hooks. e.g. `builtin:reject_new_heads`. What do others think? REPOSITORY rHG Mercurial CHANGES SINCE LAST ACTION https://phab.mercurial-scm.org/D6825/new/ REVISION DETAIL https://phab.mercurial-scm.org/D6825 To: joerg.sonnenberger, #hg-reviewers Cc: indygreg, pulkit, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D6825: contrib: start building a library for simple hooks
joerg.sonnenberger updated this revision to Diff 16431. REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D6825?vs=16430=16431 CHANGES SINCE LAST ACTION https://phab.mercurial-scm.org/D6825/new/ 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 by 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 the draft phase. 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: pulkit, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D6825: contrib: start building a library for simple hooks
pulkit added a comment. Thanks a lot for putting efforts here. I am +1 on this. I haven't looked at the code though, will try to review if no one else beats me to it. REPOSITORY rHG Mercurial CHANGES SINCE LAST ACTION https://phab.mercurial-scm.org/D6825/new/ REVISION DETAIL https://phab.mercurial-scm.org/D6825 To: joerg.sonnenberger, #hg-reviewers Cc: pulkit, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D6825: contrib: start building a library for simple hooks
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 ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel