-
Notifications
You must be signed in to change notification settings - Fork 0
/
data.py
130 lines (91 loc) · 3.04 KB
/
data.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
import hashlib
import json
import os
import shutil
from collections import namedtuple
from contextlib import contextmanager
# Will be initialized in cli.main()
GIT_DIR = None
@contextmanager
def change_git_dir(new_dir):
global GIT_DIR
old_dir = GIT_DIR
GIT_DIR = f'{new_dir}/.ugit'
yield
GIT_DIR = old_dir
def init():
os.makedirs(GIT_DIR)
os.makedirs(f'{GIT_DIR}/objects')
RefValue = namedtuple('RefValue', ['symbolic', 'value'])
def update_ref(ref, value, deref=True):
ref = _get_ref_internal(ref, deref)[0]
assert value.value
if value.symbolic:
value = f'ref: {value.value}'
else:
value = value.value
ref_path = f'{GIT_DIR}/{ref}'
os.makedirs(os.path.dirname(ref_path), exist_ok=True)
with open(ref_path, 'w') as f:
f.write(value)
def get_ref(ref, deref=True):
return _get_ref_internal(ref, deref)[1]
def delete_ref(ref, deref=True):
reg = _get_ref_internal(ref, deref)[0]
os.remove(f'{GIT_DIR}/{ref}')
def _get_ref_internal(ref, deref):
ref_path = f'{GIT_DIR}/{ref}'
value = None
if os.path.isfile(ref_path):
with open(ref_path) as f:
value = f.read().strip()
symbolic = bool(value) and value.startswith('ref:')
if symbolic:
value = value.split(':', 1)[1].strip()
if deref:
return _get_ref_internal(value, deref=True)
return ref, RefValue(symbolic=symbolic, value=value)
def iter_refs(prefix='', deref=True):
refs = ['HEAD', 'MERGE_HEAD']
for root, _, filenames in os.walk(f'{GIT_DIR}/refs/'):
root = os.path.relpath(root, GIT_DIR)
refs.extend(f'{root}/{name}' for name in filenames)
for ref_name in refs:
if not ref_name.startswith(prefix):
continue
ref = get_ref(ref_name, deref=deref)
if ref.value:
yield ref_name, ref
@contextmanager
def get_index():
index = {}
if os.path.isfile(f'{GIT_DIR}/index'):
with open(f'{GIT_DIR}/index') as f:
index = json.load(f)
yield index
with open(f'{GIT_DIR}/index', 'w') as f:
json.dump(index, f)
def hash_object(data, type_='blob'):
obj = type_.encode() + b'\x00' + data
oid = hashlib.sha1(obj).hexdigest()
with open(f'{GIT_DIR}/objects/{oid}', 'wb') as out:
out.write(obj)
return oid
def get_object(oid, expected='blob'):
with open(f'{GIT_DIR}/objects/{oid}', 'rb') as f:
obj = f.read()
type_, _, content = obj.partition(b'\x00')
type_ = type_.decode()
if expected is not None:
assert type_ == expected, f'Expected {expected}, got {type_}'
return content
def object_exists(oid):
return os.path.isfile(f'{GIT_DIR}/objects/{oid}')
def fetch_object_if_missing(oid, remote_git_dir):
if object_exists(oid):
return
remote_git_dir += '/.ugit'
shutil.copy(f'{remote_git_dir}/objects/{oid}', f'{GIT_DIR}/objects/{oid}')
def push_object(oid, remote_git_dir):
remote_git_dir += '/.ugit'
shutil.copy(f'{GIT_DIR}/objects/{oid}', f'{remote_git_dir}/objects/{oid}')