Перехватчик Mercurial для запрета фиксации больших бинарных файлов

Я хочу иметь крючок Mercurial, который будет запускаться перед фиксацией транзакции, который прервет транзакцию, если фиксируемый двоичный файл превышает 1 мегабайт. Я нашел следующий код, который отлично работает, за исключением одной проблемы. Если мой набор изменений включает удаление файла, этот хук вызовет исключение.

Крюк (я использую pretxncommit = python:checksize.newbinsize):

from mercurial import context, util
from mercurial.i18n import _
import mercurial.node as dpynode

'''hooks to forbid adding binary file over a given size

Ensure the PYTHONPATH is pointing where hg_checksize.py is and setup your
repo .hg/hgrc like this:

[hooks]
pretxncommit = python:checksize.newbinsize
pretxnchangegroup = python:checksize.newbinsize
preoutgoing = python:checksize.nopull

[limits]
maxnewbinsize = 10240
'''

def newbinsize(ui, repo, node=None, **kwargs):
    '''forbid to add binary files over a given size'''
    forbid = False
    # default limit is 10 MB
    limit = int(ui.config('limits', 'maxnewbinsize', 10000000))
    tip = context.changectx(repo, 'tip').rev()
    ctx = context.changectx(repo, node)
    for rev in range(ctx.rev(), tip+1):
        ctx = context.changectx(repo, rev)
        print ctx.files()
        for f in ctx.files():
            fctx = ctx.filectx(f)
            filecontent = fctx.data()
            # check only for new files
            if not fctx.parents():
                if len(filecontent) > limit and util.binary(filecontent):
                    msg = 'new binary file %s of %s is too large: %ld > %ld\n'
                    hname = dpynode.short(ctx.node())
                    ui.write(_(msg) % (f, hname, len(filecontent), limit))
                    forbid = True
    return forbid

Исключение:

$  hg commit -m 'commit message'
error: pretxncommit hook raised an exception: apps/helpers/templatetags/include_extends.py@bced6272d8f4: not found in manifest
transaction abort!
rollback completed
abort: apps/helpers/templatetags/include_extends.py@bced6272d8f4: not found in manifest!

Я не знаком с написанием хуков Mercurial, поэтому я очень запутался в том, что происходит. Почему хуку важно, что файл был удален, если hg уже знает об этом? Есть ли способ исправить этот хук, чтобы он работал все время?

Обновление (решено): я изменил хук, чтобы отфильтровать файлы, которые были удалены в наборе изменений.

def newbinsize(ui, repo, node=None, **kwargs):
    '''forbid to add binary files over a given size'''
    forbid = False
    # default limit is 10 MB
    limit = int(ui.config('limits', 'maxnewbinsize', 10000000))
    ctx = repo[node]
    for rev in xrange(ctx.rev(), len(repo)):
        ctx = context.changectx(repo, rev)

        # do not check the size of files that have been removed
        # files that have been removed do not have filecontexts
        # to test for whether a file was removed, test for the existence of a filecontext
        filecontexts = list(ctx)
        def file_was_removed(f):
            """Returns True if the file was removed"""
            if f not in filecontexts:
                return True
            else:
                return False

        for f in itertools.ifilterfalse(file_was_removed, ctx.files()):
            fctx = ctx.filectx(f)
            filecontent = fctx.data()
            # check only for new files
            if not fctx.parents():
                if len(filecontent) > limit and util.binary(filecontent):
                    msg = 'new binary file %s of %s is too large: %ld > %ld\n'
                    hname = dpynode.short(ctx.node())
                    ui.write(_(msg) % (f, hname, len(filecontent), limit))
                    forbid = True
    return forbid

person hekevintran    schedule 31.03.2010    source источник


Ответы (2)


for f in ctx.files() будет включать удаленные файлы, их нужно отфильтровать.

(и вы можете заменить for rev in range(ctx.rev(), tip+1): на for rev in xrange(ctx.rev(), len(repo)): и удалить tip = ...)

Если вы используете современный hg, вы делаете не ctx = context.changectx(repo, node), а ctx = repo[node].

person tonfa    schedule 31.03.2010
comment
Как отфильтровать удаленные файлы из ctx.files()? - person hekevintran; 31.03.2010
comment
достаточно поймать исключение;) - person tonfa; 01.04.2010

Это действительно легко сделать с помощью shell-хука в недавнем Mercurial:

if hg locate -r tip "set:(added() or modified()) and binary() and size('>100k')"; then
  echo "bad files!"
  exit 1
else
  exit 0
fi

Что тут происходит? Во-первых, у нас есть набор файлов для поиска всех проблемных измененных файлов (см. «hg help filesets» в hg 1.9). Команда «найти» похожа на статус, за исключением того, что она просто перечисляет файлы и возвращает 0, если что-то находит. И мы указываем «-r tip», чтобы посмотреть ожидающий коммит.

person mpm    schedule 14.10.2011