~ubuntu-branches/ubuntu/oneiric/dulwich/oneiric

« back to all changes in this revision

Viewing changes to dulwich/pack.py

  • Committer: Bazaar Package Importer
  • Author(s): Jelmer Vernooij
  • Date: 2010-07-30 13:22:11 UTC
  • mfrom: (1.2.11 upstream) (8.1.10 sid)
  • Revision ID: james.westby@ubuntu.com-20100730132211-k6aop8v5z42mawef
Tags: 0.6.1-1
* New upstream release.
* Bump standards version to 3.9.1.

Show diffs side-by-side

added added

removed removed

Lines of Context:
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>
4
4
#
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
310
310
    def iterentries(self):
311
311
        """Iterate over the entries in this pack index.
312
312
 
313
 
        :yields: tuples with object name, offset in packfile and crc32 checksum.
 
313
        :return: iterator over tuples with object name, offset in packfile and
 
314
            crc32 checksum.
314
315
        """
315
316
        for i in range(len(self)):
316
317
            yield self._unpack_entry(i)
787
788
    def iterentries(self, progress=None):
788
789
        """Yield entries summarizing the contents of this pack.
789
790
 
790
 
        :param progress: Progress function, called with current and total object
791
 
            count.
792
 
        :yields: tuples with (sha, offset, crc32)
 
791
        :param progress: Progress function, called with current and total
 
792
            object count.
 
793
        :return: iterator of tuples with (sha, offset, crc32)
793
794
        """
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.
803
804
 
804
 
        :param progress: Progress function, called with current and total object
805
 
            count
 
805
        :param progress: Progress function, called with current and total
 
806
            object count
806
807
        :return: List of tuples with (sha, offset, crc32)
807
808
        """
808
809
        ret = list(self.iterentries(progress=progress))
814
815
 
815
816
        :param filename: Index filename.
816
817
        :param progress: Progress report function
 
818
        :return: Checksum of index file
817
819
        """
818
820
        entries = self.sorted_entries(progress=progress)
819
 
        write_pack_index_v1(filename, entries, self.calculate_checksum())
 
821
        f = GitFile(filename, 'wb')
 
822
        try:
 
823
            return write_pack_index_v1(f, entries, self.calculate_checksum())
 
824
        finally:
 
825
            f.close()
820
826
 
821
827
    def create_index_v2(self, filename, progress=None):
822
828
        """Create a version 2 index file for this data file.
823
829
 
824
830
        :param filename: Index filename.
825
831
        :param progress: Progress report function
 
832
        :return: Checksum of index file
826
833
        """
827
834
        entries = self.sorted_entries(progress=progress)
828
 
        write_pack_index_v2(filename, entries, self.calculate_checksum())
 
835
        f = GitFile(filename, 'wb')
 
836
        try:
 
837
            return write_pack_index_v2(f, entries, self.calculate_checksum())
 
838
        finally:
 
839
            f.close()
829
840
 
830
841
    def create_index(self, filename, progress=None,
831
842
                     version=2):
833
844
 
834
845
        :param filename: Index filename.
835
846
        :param progress: Progress report function
 
847
        :return: Checksum of index file
836
848
        """
837
849
        if version == 1:
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)
841
853
        else:
842
854
            raise ValueError("unknown index format %d" % version)
843
855
 
1034
1046
    :param filename: Path to the new pack file (without .pack extension)
1035
1047
    :param objects: Iterable over (object, path) tuples to write
1036
1048
    :param num_objects: Number of objects to write
 
1049
    :return: Tuple with checksum of pack file and index file
1037
1050
    """
1038
1051
    f = GitFile(filename + ".pack", 'wb')
1039
1052
    try:
1041
1054
    finally:
1042
1055
        f.close()
1043
1056
    entries.sort()
1044
 
    write_pack_index_v2(filename + ".idx", entries, data_sum)
 
1057
    f = GitFile(filename + ".idx", 'wb')
 
1058
    try:
 
1059
        return data_sum, write_pack_index_v2(f, entries, data_sum)
 
1060
    finally:
 
1061
        f.close()
1045
1062
 
1046
1063
 
1047
1064
def write_pack_data(f, objects, num_objects, window=10):
1091
1108
    return entries, f.write_sha()
1092
1109
 
1093
1110
 
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.
1096
1113
 
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
1101
1119
    """
1102
 
    f = GitFile(filename, 'wb')
1103
 
    try:
1104
 
        f = SHA1Writer(f)
1105
 
        fan_out_table = defaultdict(lambda: 0)
1106
 
        for (name, offset, entry_checksum) in entries:
1107
 
            fan_out_table[ord(name[0])] += 1
1108
 
        # Fan-out table
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)
1116
 
    finally:
1117
 
        f.close()
 
1120
    f = SHA1Writer(f)
 
1121
    fan_out_table = defaultdict(lambda: 0)
 
1122
    for (name, offset, entry_checksum) in entries:
 
1123
        fan_out_table[ord(name[0])] += 1
 
1124
    # Fan-out table
 
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()
1118
1133
 
1119
1134
 
1120
1135
def create_delta(base_buf, target_buf):
1242
1257
    return out
1243
1258
 
1244
1259
 
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.
1247
1262
 
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
1252
1268
    """
1253
 
    f = GitFile(filename, 'wb')
1254
 
    try:
1255
 
        f = SHA1Writer(f)
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
1261
 
        # Fan-out table
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:
1266
 
            f.write(name)
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)
1275
 
    finally:
1276
 
        f.close()
 
1269
    f = SHA1Writer(f)
 
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
 
1275
    # Fan-out table
 
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:
 
1280
        f.write(name)
 
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()
1277
1290
 
1278
1291
 
1279
1292
class Pack(object):
1281
1294
 
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)
 
1303
 
 
1304
    @classmethod
 
1305
    def from_lazy_objects(self, data_fn, idx_fn):
 
1306
        """Create a new pack object from callables to load pack data and 
 
1307
        index objects."""
 
1308
        ret = Pack("")
 
1309
        ret._data_load = data_fn
 
1310
        ret._idx_load = idx_fn
 
1311
        return ret
1288
1312
 
1289
1313
    @classmethod
1290
1314
    def from_objects(self, data, idx):
1291
1315
        """Create a new pack object from pack data and index objects."""
1292
1316
        ret = Pack("")
1293
 
        ret._data = data
1294
 
        ret._idx = idx
1295
 
        data.pack = ret
 
1317
        ret._data_load = lambda: data
 
1318
        ret._idx_load = lambda: idx
1296
1319
        return ret
1297
1320
 
1298
1321
    def name(self):
1303
1326
    def data(self):
1304
1327
        """The pack data object being used."""
1305
1328
        if self._data is None:
1306
 
            self._data = PackData(self._data_path)
 
1329
            self._data = self._data_load()
1307
1330
            self._data.pack = self
1308
1331
            assert len(self.index) == len(self._data)
1309
1332
            idx_stored_checksum = self.index.get_pack_checksum()
1320
1343
        :note: This may be an in-memory index
1321
1344
        """
1322
1345
        if self._idx is None:
1323
 
            self._idx = load_pack_index(self._idx_path)
 
1346
            self._idx = self._idx_load()
1324
1347
        return self._idx
1325
1348
 
1326
1349
    def close(self):