X-Git-Url: https://ruderich.org/simon/gitweb/?a=blobdiff_plain;f=shell%2Fbin%2Fgit-evtag-compute-py;fp=shell%2Fbin%2Fgit-evtag-compute-py;h=0babc5c32a6e52e420ed1c98d75955f100a00b21;hb=4af4ee14bf8b6d8938ba6615c1af4389453a213a;hp=0000000000000000000000000000000000000000;hpb=3838df1c0ce1c544d6522f100530b60fb7d81015;p=config%2Fdotfiles.git diff --git a/shell/bin/git-evtag-compute-py b/shell/bin/git-evtag-compute-py new file mode 100755 index 0000000..0babc5c --- /dev/null +++ b/shell/bin/git-evtag-compute-py @@ -0,0 +1,108 @@ +#!/usr/bin/env python +# +# An implementation of Git-EVTag in a mixture of Python and +# git-as-subprocess. Slower than C, but easier to understand +# and test. +# +# For correct submodule handling, requires the working directory have +# submodule checkouts. + +from __future__ import print_function + +import os +import sys +import argparse +import subprocess +import hashlib + +try: + from subprocess import DEVNULL # pylint: disable=no-name-in-module +except ImportError: + import os + DEVNULL = open(os.devnull, 'wb') + +parser = argparse.ArgumentParser(description="Compute Git-EVTag checksum") +parser.add_argument('rev', help='Revision to checksum') +opts = parser.parse_args() + +csum = hashlib.sha512() + +stats = {'commit': 0, + 'blob': 0, + 'tree': 0, + 'commitbytes': 0, + 'blobbytes': 0, + 'treebytes': 0} + +def checksum_bytes(otype, buf): + blen = len(buf) + csum.update(buf) + stats[otype + 'bytes'] += blen + return blen + +def checksum_object(repo, objid): + p = subprocess.Popen(['git', 'cat-file', '--batch'], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + close_fds=True, + cwd=repo) + p.stdin.write(objid + '\n') + p.stdin.close() + (objid,objtype,lenstr) = p.stdout.readline().split(None, 2) + olen = int(lenstr) + lenstr = lenstr.strip() + buf = "{0} {1}\000".format(objtype, lenstr) + checksum_bytes(objtype, buf) + + stats[objtype] += 1 + + if objtype == 'commit': + buf = p.stdout.readline() + olen -= checksum_bytes(objtype, buf) + (treestr, treeobjid) = buf.split(None, 1) + treeobjid = treeobjid.strip() + assert treestr == 'tree' + else: + treeobjid = None + + while olen > 0: + b = p.stdout.read(min(8192, olen)) + bytes_read = checksum_bytes(objtype, b) + olen -= bytes_read + if olen > 0: + raise ValueError("Failed to read {0} bytes from object".format(olen)) + p.wait() + if p.returncode != 0: + raise subprocess.CalledProcessError(p.returncode, 'git cat-file') + return treeobjid + +def checksum_tree(repo, objid): + checksum_object(repo, objid) + p = subprocess.Popen(['git', 'ls-tree', objid], + stdin=DEVNULL, + stdout=subprocess.PIPE, + close_fds=True, + cwd=repo) + for line in p.stdout: + (mode, otype, subid, fname) = line.split(None, 3) + fname = fname.strip() + if otype == 'blob': + checksum_object(repo, subid) + elif otype == 'tree': + checksum_tree(repo, subid) + elif otype == 'commit': + checksum_repo(os.path.join(repo, fname), subid) + else: + assert False + p.wait() + if p.returncode != 0: + raise subprocess.CalledProcessError(p.returncode, 'git ls-tree') + +def checksum_repo(repo, objid): + treeid = checksum_object(repo, objid) + checksum_tree(repo, treeid) + +checksum_repo('.', opts.rev) + +print("# git-evtag comment: submodules={0} commits={1} ({2}) trees={3} ({4}) blobs={5} ({6})".format(stats['commit']-1, stats['commit'], stats['commitbytes'], stats['tree'], stats['treebytes'], stats['blob'], stats['blobbytes'])) +print("Git-EVTag-v0-SHA512: {0}".format(csum.hexdigest()))