1
# pack.py -- For dealing wih packed git objects.
1
# pack.py -- For dealing with packed git objects.
2
2
# Copyright (C) 2007 James Westby <jw+debian@jameswestby.net>
3
# Copryight (C) 2008-2009 Jelmer Vernooij <jelmer@samba.org>
3
# Copyright (C) 2008-2009 Jelmer Vernooij <jelmer@samba.org>
5
5
# This program is free software; you can redistribute it and/or
6
6
# modify it under the terms of the GNU General Public License
787
788
def iterentries(self, progress=None):
788
789
"""Yield entries summarizing the contents of this pack.
790
:param progress: Progress function, called with current and total object
792
:yields: tuples with (sha, offset, crc32)
791
:param progress: Progress function, called with current and total
793
:return: iterator of tuples with (sha, offset, crc32)
794
795
for offset, type, obj, crc32 in self.iterobjects(progress=progress):
795
796
assert isinstance(offset, int)
801
802
def sorted_entries(self, progress=None):
802
803
"""Return entries in this pack, sorted by SHA.
804
:param progress: Progress function, called with current and total object
805
:param progress: Progress function, called with current and total
806
807
:return: List of tuples with (sha, offset, crc32)
808
809
ret = list(self.iterentries(progress=progress))
815
816
:param filename: Index filename.
816
817
:param progress: Progress report function
818
:return: Checksum of index file
818
820
entries = self.sorted_entries(progress=progress)
819
write_pack_index_v1(filename, entries, self.calculate_checksum())
821
f = GitFile(filename, 'wb')
823
return write_pack_index_v1(f, entries, self.calculate_checksum())
821
827
def create_index_v2(self, filename, progress=None):
822
828
"""Create a version 2 index file for this data file.
824
830
:param filename: Index filename.
825
831
:param progress: Progress report function
832
:return: Checksum of index file
827
834
entries = self.sorted_entries(progress=progress)
828
write_pack_index_v2(filename, entries, self.calculate_checksum())
835
f = GitFile(filename, 'wb')
837
return write_pack_index_v2(f, entries, self.calculate_checksum())
830
841
def create_index(self, filename, progress=None,
834
845
:param filename: Index filename.
835
846
:param progress: Progress report function
847
:return: Checksum of index file
838
self.create_index_v1(filename, progress)
850
return self.create_index_v1(filename, progress)
839
851
elif version == 2:
840
self.create_index_v2(filename, progress)
852
return self.create_index_v2(filename, progress)
842
854
raise ValueError("unknown index format %d" % version)
1044
write_pack_index_v2(filename + ".idx", entries, data_sum)
1057
f = GitFile(filename + ".idx", 'wb')
1059
return data_sum, write_pack_index_v2(f, entries, data_sum)
1047
1064
def write_pack_data(f, objects, num_objects, window=10):
1091
1108
return entries, f.write_sha()
1094
def write_pack_index_v1(filename, entries, pack_checksum):
1111
def write_pack_index_v1(f, entries, pack_checksum):
1095
1112
"""Write a new pack index file.
1097
:param filename: The filename of the new pack index file.
1114
:param f: A file-like object to write to
1098
1115
:param entries: List of tuples with object name (sha), offset_in_pack,
1099
1116
and crc32_checksum.
1100
1117
:param pack_checksum: Checksum of the pack file.
1118
:return: The SHA of the written index file
1102
f = GitFile(filename, 'wb')
1105
fan_out_table = defaultdict(lambda: 0)
1106
for (name, offset, entry_checksum) in entries:
1107
fan_out_table[ord(name[0])] += 1
1109
for i in range(0x100):
1110
f.write(struct.pack(">L", fan_out_table[i]))
1111
fan_out_table[i+1] += fan_out_table[i]
1112
for (name, offset, entry_checksum) in entries:
1113
f.write(struct.pack(">L20s", offset, name))
1114
assert len(pack_checksum) == 20
1115
f.write(pack_checksum)
1121
fan_out_table = defaultdict(lambda: 0)
1122
for (name, offset, entry_checksum) in entries:
1123
fan_out_table[ord(name[0])] += 1
1125
for i in range(0x100):
1126
f.write(struct.pack(">L", fan_out_table[i]))
1127
fan_out_table[i+1] += fan_out_table[i]
1128
for (name, offset, entry_checksum) in entries:
1129
f.write(struct.pack(">L20s", offset, name))
1130
assert len(pack_checksum) == 20
1131
f.write(pack_checksum)
1132
return f.write_sha()
1120
1135
def create_delta(base_buf, target_buf):
1245
def write_pack_index_v2(filename, entries, pack_checksum):
1260
def write_pack_index_v2(f, entries, pack_checksum):
1246
1261
"""Write a new pack index file.
1248
:param filename: The filename of the new pack index file.
1263
:param f: File-like object to write to
1249
1264
:param entries: List of tuples with object name (sha), offset_in_pack, and
1250
1265
crc32_checksum.
1251
1266
:param pack_checksum: Checksum of the pack file.
1267
:return: The SHA of the index file written
1253
f = GitFile(filename, 'wb')
1256
f.write('\377tOc') # Magic!
1257
f.write(struct.pack(">L", 2))
1258
fan_out_table = defaultdict(lambda: 0)
1259
for (name, offset, entry_checksum) in entries:
1260
fan_out_table[ord(name[0])] += 1
1262
for i in range(0x100):
1263
f.write(struct.pack(">L", fan_out_table[i]))
1264
fan_out_table[i+1] += fan_out_table[i]
1265
for (name, offset, entry_checksum) in entries:
1267
for (name, offset, entry_checksum) in entries:
1268
f.write(struct.pack(">L", entry_checksum))
1269
for (name, offset, entry_checksum) in entries:
1270
# FIXME: handle if MSBit is set in offset
1271
f.write(struct.pack(">L", offset))
1272
# FIXME: handle table for pack files > 8 Gb
1273
assert len(pack_checksum) == 20
1274
f.write(pack_checksum)
1270
f.write('\377tOc') # Magic!
1271
f.write(struct.pack(">L", 2))
1272
fan_out_table = defaultdict(lambda: 0)
1273
for (name, offset, entry_checksum) in entries:
1274
fan_out_table[ord(name[0])] += 1
1276
for i in range(0x100):
1277
f.write(struct.pack(">L", fan_out_table[i]))
1278
fan_out_table[i+1] += fan_out_table[i]
1279
for (name, offset, entry_checksum) in entries:
1281
for (name, offset, entry_checksum) in entries:
1282
f.write(struct.pack(">L", entry_checksum))
1283
for (name, offset, entry_checksum) in entries:
1284
# FIXME: handle if MSBit is set in offset
1285
f.write(struct.pack(">L", offset))
1286
# FIXME: handle table for pack files > 8 Gb
1287
assert len(pack_checksum) == 20
1288
f.write(pack_checksum)
1289
return f.write_sha()
1279
1292
class Pack(object):
1282
1295
def __init__(self, basename):
1283
1296
self._basename = basename
1284
self._data_path = self._basename + ".pack"
1285
self._idx_path = self._basename + ".idx"
1286
1297
self._data = None
1287
1298
self._idx = None
1299
self._idx_path = self._basename + ".idx"
1300
self._data_path = self._basename + ".pack"
1301
self._data_load = lambda: PackData(self._data_path)
1302
self._idx_load = lambda: load_pack_index(self._idx_path)
1305
def from_lazy_objects(self, data_fn, idx_fn):
1306
"""Create a new pack object from callables to load pack data and
1309
ret._data_load = data_fn
1310
ret._idx_load = idx_fn
1290
1314
def from_objects(self, data, idx):
1291
1315
"""Create a new pack object from pack data and index objects."""
1317
ret._data_load = lambda: data
1318
ret._idx_load = lambda: idx
1298
1321
def name(self):