]> ruderich.org/simon Gitweb - config/dotfiles.git/blob - shell/bin/git-evtag-compute-py
zsh/rc: prepend "--" in X alias
[config/dotfiles.git] / shell / bin / git-evtag-compute-py
1 #!/usr/bin/env python
2 #
3 # An implementation of Git-EVTag in a mixture of Python and
4 # git-as-subprocess.  Slower than C, but easier to understand
5 # and test.
6
7 # For correct submodule handling, requires the working directory have
8 # submodule checkouts.
9
10 from __future__ import print_function
11
12 import os
13 import sys
14 import argparse
15 import subprocess
16 import hashlib
17
18 try:
19     from subprocess import DEVNULL # pylint: disable=no-name-in-module
20 except ImportError:
21     import os
22     DEVNULL = open(os.devnull, 'wb')
23
24 parser = argparse.ArgumentParser(description="Compute Git-EVTag checksum")
25 parser.add_argument('rev', help='Revision to checksum')
26 opts = parser.parse_args()
27
28 csum = hashlib.sha512()
29
30 stats = {'commit': 0,
31          'blob': 0,
32          'tree': 0,
33          'commitbytes': 0,
34          'blobbytes': 0,
35          'treebytes': 0}
36
37 def checksum_bytes(otype, buf):
38     blen = len(buf)
39     csum.update(buf)
40     stats[otype + 'bytes'] += blen
41     return blen
42
43 def checksum_object(repo, objid):
44     p = subprocess.Popen(['git', 'cat-file', '--batch'],
45                          stdin=subprocess.PIPE,
46                          stdout=subprocess.PIPE,
47                          close_fds=True,
48                          cwd=repo)
49     p.stdin.write(objid + '\n')
50     p.stdin.close()
51     (objid,objtype,lenstr) = p.stdout.readline().split(None, 2)
52     olen = int(lenstr)
53     lenstr = lenstr.strip()
54     buf = "{0} {1}\000".format(objtype, lenstr)
55     checksum_bytes(objtype, buf)
56
57     stats[objtype] += 1
58
59     if objtype == 'commit':
60         buf = p.stdout.readline()
61         olen -= checksum_bytes(objtype, buf)
62         (treestr, treeobjid) = buf.split(None, 1)
63         treeobjid = treeobjid.strip()
64         assert treestr == 'tree'
65     else:
66         treeobjid = None
67         
68     while olen > 0:
69         b = p.stdout.read(min(8192, olen))
70         bytes_read = checksum_bytes(objtype, b)
71         olen -= bytes_read
72     if olen > 0:
73         raise ValueError("Failed to read {0} bytes from object".format(olen))
74     p.wait()
75     if p.returncode != 0:
76         raise subprocess.CalledProcessError(p.returncode, 'git cat-file')
77     return treeobjid
78
79 def checksum_tree(repo, objid):
80     checksum_object(repo, objid)
81     p = subprocess.Popen(['git', 'ls-tree', objid],
82                          stdin=DEVNULL,
83                          stdout=subprocess.PIPE,
84                          close_fds=True,
85                          cwd=repo)
86     for line in p.stdout:
87         (mode, otype, subid, fname) = line.split(None, 3)
88         fname = fname.strip()
89         if otype == 'blob':
90             checksum_object(repo, subid)
91         elif otype == 'tree':
92             checksum_tree(repo, subid)
93         elif otype == 'commit':
94             checksum_repo(os.path.join(repo, fname), subid)
95         else:
96             assert False
97     p.wait()
98     if p.returncode != 0:
99         raise subprocess.CalledProcessError(p.returncode, 'git ls-tree')
100
101 def checksum_repo(repo, objid):
102     treeid = checksum_object(repo, objid)
103     checksum_tree(repo, treeid)
104
105 checksum_repo('.', opts.rev)
106
107 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']))
108 print("Git-EVTag-v0-SHA512: {0}".format(csum.hexdigest()))