docs: def parse_config(filename):
    """
    Parses a versionupgrade configuration file. Example:
  
        tag v{VERSION}
        branch v{VERSION}
        message Prepare {VERSION} release
        upgrade setup.py:  version = '{VERSION}'
        upgrade __init__.py:__version__ = '{VERSION}'
        sub docs/changelog/v{VERSION}.md:# v{VERSION} (unreleased):# v{VERSION} ({DATE})
  
    Available commands:
  
      - tag: Create a Git tag with the specified name.
      - branch: Create a Git branch with the specified name.
      - message: The commit message for upgraded version numbers.
      - upgrade: Upgrade the version number in the file matching the pattern.
                 The same file may be listed multiple times. The pattern may
                 actually be a regular expression and will be searched in
                 every line of the file.
      - sub: Specify a file where the part of the file matching the first string
             will be replaced by the second string.
  
    Returns a #Config object.
    """
  
    tag = None
    branch = None
    message = 'Prepare {VERSION} release.'
    upgrades = {}
    subs = {}
  
    with open(filename) as fp:
      for i, line in enumerate(fp):
        line = line.strip()
        if not line or line.startswith('#'): continue
  
        key, sep, value = line.partition(' ')
        if not key or not value:
          raise ValueError('invalid configuration file at line {}'.format(i+1))
  
        if key == 'tag':
          tag = value.strip()
        elif key == 'branch':
          branch = value.strip()
        elif key == 'message':
          message = value.strip()
        elif key == 'upgrade':
          filename, sep, pattern = value.partition(':')
          if not filename or not sep or not pattern or '{VERSION}' not in pattern:
            raise ValueError('invalid upgrade argument at line {}'.format(i+1))
          upgrade = upgrades.setdefault(filename, [])
          upgrade.append(pattern)
        elif key == 'sub':
          filename, sep, pattern = value.partition(':')
          pattern = pattern.partition(':')[::2]
          if not pattern[0] or not pattern[1]:
            raise ValueError('invalid sub argument at line {}'.format(i+1))
          subs.setdefault(filename, []).append(pattern)
        else:
          raise ValueError('invalid command {!r} at line {}'.format(key, i+1))
  
    return Config(tag, branch, message, upgrades, subs)