title: Mercurial hook to disallow committing large binary files
body: <p>I want to have a Mercurial hook that will run before committing a transaction that will abort the transaction if a binary file being committed is greater than 1 megabyte.  I found the following code which works fine except for one problem.  If my changeset involves removing a file, this hook will throw an exception.</p>
  
  <p>The hook (I'm using <code>pretxncommit = python:checksize.newbinsize</code>):
  </p>
  
  <pre><code>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) &gt; limit and util.binary(filecontent):
                      msg = 'new binary file %s of %s is too large: %ld &gt; %ld\n'
                      hname = dpynode.short(ctx.node())
                      ui.write(_(msg) % (f, hname, len(filecontent), limit))
                      forbid = True
      return forbid
  </code></pre>
  
  <p>The exception:</p>
  
  <pre><code>$  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!
  </code></pre>
  
  <p>I'm not familiar with writing Mercurial hooks, so I'm pretty confused about what's going on.  Why does the hook care that a file was removed if hg already knows about it?  Is there a way to fix this hook so that it works all the time?</p>
  
  <p><strong>Update (solved):</strong>
  I modified the hook to filter out files that were removed in the changeset.
  </p>
  
  <pre><code>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) &gt; limit and util.binary(filecontent):
                      msg = 'new binary file %s of %s is too large: %ld &gt; %ld\n'
                      hname = dpynode.short(ctx.node())
                      ui.write(_(msg) % (f, hname, len(filecontent), limit))
                      forbid = True
      return forbid
  </code></pre>