Skip to content

Commit f1e8f2e

Browse files
committed
Add treehash512.py
Add script for quickly getting the treehash of a certain commit.
1 parent 2e85973 commit f1e8f2e

File tree

2 files changed

+95
-0
lines changed

2 files changed

+95
-0
lines changed

README.md

+14
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,17 @@ For example:
4747
unittest-statistics.py src/test/test_bitcoin wallet_tests
4848
```
4949

50+
treehash512
51+
--------------
52+
53+
This script will show the SHA512 tree has for a certain commit, or HEAD
54+
by default.
55+
56+
Usage:
57+
58+
```bash
59+
treehash512.py [<commithash>]
60+
```
61+
62+
This should match the Tree-SHA512 commit metadata field added by
63+
github-merge.

treehash512.py

+81
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
#!/usr/bin/env python3
2+
# Copyright (c) 2017 The Bitcoin Core developers
3+
# Distributed under the MIT software license, see the accompanying
4+
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
5+
'''
6+
This script will show the SHA512 tree has for a certain commit, or HEAD
7+
by default.
8+
9+
Usage:
10+
11+
treehash512.py [<commithash>]
12+
'''
13+
import os
14+
from sys import stdin,stdout,stderr
15+
import sys
16+
import argparse
17+
import hashlib
18+
import subprocess
19+
20+
# External tools (can be overridden using environment)
21+
GIT = os.getenv('GIT','git')
22+
23+
def tree_sha512sum(commit='HEAD'):
24+
# request metadata for entire tree, recursively
25+
files = []
26+
blob_by_name = {}
27+
for line in subprocess.check_output([GIT, 'ls-tree', '--full-tree', '-r', commit]).splitlines():
28+
name_sep = line.index(b'\t')
29+
metadata = line[:name_sep].split() # perms, 'blob', blobid
30+
assert(metadata[1] == b'blob')
31+
name = line[name_sep+1:]
32+
files.append(name)
33+
blob_by_name[name] = metadata[2]
34+
35+
files.sort()
36+
# open connection to git-cat-file in batch mode to request data for all blobs
37+
# this is much faster than launching it per file
38+
p = subprocess.Popen([GIT, 'cat-file', '--batch'], stdout=subprocess.PIPE, stdin=subprocess.PIPE)
39+
overall = hashlib.sha512()
40+
for f in files:
41+
blob = blob_by_name[f]
42+
# request blob
43+
p.stdin.write(blob + b'\n')
44+
p.stdin.flush()
45+
# read header: blob, "blob", size
46+
reply = p.stdout.readline().split()
47+
assert(reply[0] == blob and reply[1] == b'blob')
48+
size = int(reply[2])
49+
# hash the blob data
50+
intern = hashlib.sha512()
51+
ptr = 0
52+
while ptr < size:
53+
bs = min(65536, size - ptr)
54+
piece = p.stdout.read(bs)
55+
if len(piece) == bs:
56+
intern.update(piece)
57+
else:
58+
raise IOError('Premature EOF reading git cat-file output')
59+
ptr += bs
60+
dig = intern.hexdigest()
61+
assert(p.stdout.read(1) == b'\n') # ignore LF that follows blob data
62+
# update overall hash with file hash
63+
overall.update(dig.encode("utf-8"))
64+
overall.update(" ".encode("utf-8"))
65+
overall.update(f)
66+
overall.update("\n".encode("utf-8"))
67+
p.stdin.close()
68+
if p.wait():
69+
raise IOError('Non-zero return value executing git cat-file')
70+
return overall.hexdigest()
71+
72+
def main():
73+
if len(sys.argv)>1:
74+
commit = sys.argv[1]
75+
else:
76+
commit = 'HEAD'
77+
print(tree_sha512sum(commit))
78+
79+
if __name__ == '__main__':
80+
main()
81+

0 commit comments

Comments
 (0)