Source

hgban / ban-changesets.py

The default branch has multiple heads

#!/usr/bin/env python

'''ban-changesets is a Mercurial extension which sets up a pretxncommit hook.
Any changesets to be pushed / pulled / bundled into the repository will have their
changeset hashes compared against a list of banned hashes in repo/.hgbannedchangesets.
If any new hash is already in this file of banned hashes then the entire
changegroup will be rejected.

Enable the ban-changesets just like any other Mercurial extension by adding the
following to your hgrc

[extensions]
ban-changesets = /path/to/ban-changesets
'''

import os.path, re
from mercurial import hg

#print "Checking For Banned Changesets!"

def getSetOfBannedChangesets(repo):
    try:
        bannedChangesetsPath = repo.wjoin('.hgbannedchangesets')
        f = open(bannedChangesetsPath, 'r')
        banned = set()
        changesetPat = re.compile(r"(^[0-9a-fA-F]+).*")
        for line in f:
            m = re.match(changesetPat, line)
            if m:
                banned.add(m.group(1))
        f.close()
        return banned
    except:
        return {}

def changesetIsBanned(node, bannedChangesets):
    for p in bannedChangesets:
        if re.search(p, node):
            return True
    return False

def checkForBannedChangesets(ui, repo, **kwargs):
    node = kwargs.get('node')
    if node:
        bannedChangesets = getSetOfBannedChangesets(repo)
        startRev = int(repo[node])
        descendantRevs = list(repo.changelog.descendants(startRev))
        descendantRevs.append(startRev)

        rejectedChangesets = set()
        for rev in descendantRevs:
            changeset = repo[rev].hex()
            if changesetIsBanned(changeset, bannedChangesets):
                rejectedChangesets.add(changeset)

        if len(rejectedChangesets) > 0:
            repoName = os.path.basename(repo.root)
            ui.warn('The ban-changeset extension rejected the %s opertion on the repository \'%s\' due to the changeset(s):\n' % (kwargs.get('source'), repoName))
            for changeset in rejectedChangesets:
                ui.warn('    %s\n' % changeset)
            if (len(rejectedChangesets) < len(descendantRevs)):
                if len(rejectedChangesets) == 1:
                    ui.warn('Rebase, transplant, or otherwise move any valid changesets in the source repository which are derived from this rejected changeset. Strip the banned changesets, and then redo the operation.\n')
                else:
                    ui.warn('Rebase, transplant, or otherwise move any valid changesets in the source repository which are derived from these rejected changesets. Strip the banned changesets, and then redo the operation.\n')
            return True # We found a banned changeset, return True (exit code 1) which causes the changegroup addition to be aborted.
    return False # No banned changesets were found. The changegroup addition can go ahead.

def reposetup(ui, repo):
    ui.setconfig("hooks", "pretxnchangegroup.ban-changesets", checkForBannedChangesets)