]> ruderich.org/simon Gitweb - config/dotfiles.git/blob - shell/bin/git-evtag-compute-py
shell: set GOTOOLCHAIN=local
[config/dotfiles.git] / shell / bin / git-evtag-compute-py
1 #!/usr/bin/env python3
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     assert objid is not None
45     p = subprocess.Popen(['git', 'cat-file', '--batch'],
46                          stdin=subprocess.PIPE,
47                          stdout=subprocess.PIPE,
48                          close_fds=True,
49                          cwd=repo)
50     p.stdin.write(objid.encode('ascii') + b'\n')
51     p.stdin.close()
52     (objid,objtype,lenstr) = p.stdout.readline().decode('ascii').split(None, 2)
53     olen = int(lenstr)
54     lenstr = lenstr.strip()
55     buf = "{0} {1}\000".format(objtype, lenstr).encode('ascii')
56     checksum_bytes(objtype, buf)
57
58     stats[objtype] += 1
59
60     if objtype == 'commit':
61         buf = p.stdout.readline()
62         olen -= checksum_bytes(objtype, buf)
63         (treestr, treeobjid) = buf.decode('ascii').split(None, 1)
64         treeobjid = treeobjid.strip()
65         assert treestr == 'tree'
66     else:
67         treeobjid = None
68         
69     while olen > 0:
70         b = p.stdout.read(min(8192, olen))
71         bytes_read = checksum_bytes(objtype, b)
72         olen -= bytes_read
73     if olen > 0:
74         raise ValueError("Failed to read {0} bytes from object".format(olen))
75     p.wait()
76     if p.returncode != 0:
77         raise subprocess.CalledProcessError(p.returncode, 'git cat-file')
78     return treeobjid
79
80 def checksum_tree(repo, path, objid):
81     checksum_object(repo, objid)
82     p = subprocess.Popen(['git', 'ls-tree', objid],
83                          stdin=DEVNULL,
84                          stdout=subprocess.PIPE,
85                          close_fds=True,
86                          cwd=repo)
87     for line in p.stdout:
88         (mode, otype, subid, fname) = line.decode('ascii').split(None, 3)
89         fname = fname.strip()
90         if otype == 'blob':
91             checksum_object(repo, subid)
92         elif otype == 'tree':
93             checksum_tree(repo, os.path.join(path, fname), subid)
94         elif otype == 'commit':
95             checksum_repo(os.path.join(repo, path, fname), subid)
96         else:
97             assert False
98     p.wait()
99     if p.returncode != 0:
100         raise subprocess.CalledProcessError(p.returncode, 'git ls-tree')
101
102 def checksum_repo(repo, objid):
103     treeid = checksum_object(repo, objid)
104     checksum_tree(repo, '.', treeid)
105
106 checksum_repo('.', opts.rev)
107
108 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']))
109 print("Git-EVTag-v0-SHA512: {0}".format(csum.hexdigest()))