index.py 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. # index.py -- File parser/write for the git index file
  2. # Copryight (C) 2008 Jelmer Vernooij <jelmer@samba.org>
  3. # This program is free software; you can redistribute it and/or
  4. # modify it under the terms of the GNU General Public License
  5. # as published by the Free Software Foundation; version 2
  6. # of the License or (at your opinion) any later version of the license.
  7. #
  8. # This program is distributed in the hope that it will be useful,
  9. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. # GNU General Public License for more details.
  12. #
  13. # You should have received a copy of the GNU General Public License
  14. # along with this program; if not, write to the Free Software
  15. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
  16. # MA 02110-1301, USA.
  17. """Parser for the git index file format."""
  18. import struct
  19. def read_cache_time(f):
  20. return struct.unpack(">LL", f.read(8))
  21. def write_cache_time(f, t):
  22. if isinstance(t, int):
  23. t = (t, 0)
  24. f.write(struct.pack(">LL", *t))
  25. def read_cache_entry(f):
  26. """Read an entry from a cache file.
  27. :param f: File-like object to read from
  28. :return: tuple with: inode, device, mode, uid, gid, size, sha, flags
  29. """
  30. beginoffset = f.tell()
  31. ctime = read_cache_time(f)
  32. mtime = read_cache_time(f)
  33. (ino, dev, mode, uid, gid, size, sha, flags, ) = \
  34. struct.unpack(">LLLLLL20sH", f.read(20 + 4 * 6 + 2))
  35. name = ""
  36. char = f.read(1)
  37. while char != "\0":
  38. name += char
  39. char = f.read(1)
  40. # Padding:
  41. real_size = ((f.tell() - beginoffset + 7) & ~7)
  42. f.seek(beginoffset + real_size)
  43. return (name, ctime, mtime, ino, dev, mode, uid, gid, size, sha, flags)
  44. def write_cache_entry(f, entry):
  45. """Write an index entry to a file.
  46. :param f: File object
  47. :param entry: Entry to write, tuple with:
  48. (name, ctime, mtime, ino, dev, mode, uid, gid, size, sha, flags)
  49. """
  50. beginoffset = f.tell()
  51. (name, ctime, mtime, ino, dev, mode, uid, gid, size, sha, flags) = entry
  52. write_cache_time(f, ctime)
  53. write_cache_time(f, mtime)
  54. f.write(struct.pack(">LLLLLL20sH", ino, dev, mode, uid, gid, size, sha, flags))
  55. f.write(name)
  56. f.write(chr(0))
  57. real_size = ((f.tell() - beginoffset + 7) & ~7)
  58. f.write("\0" * ((beginoffset + real_size) - f.tell()))
  59. def read_index(f):
  60. """Read an index file, yielding the individual entries."""
  61. header = f.read(4)
  62. if header != "DIRC":
  63. raise AssertionError("Invalid index file header: %r" % header)
  64. (version, num_entries) = struct.unpack(">LL", f.read(4 * 2))
  65. assert version in (1, 2)
  66. for i in range(num_entries):
  67. yield read_cache_entry(f)
  68. def read_index_dict(f):
  69. """Read an index file and return it as a dictionary.
  70. :param f: File object to read from
  71. """
  72. ret = {}
  73. for x in read_index(f):
  74. ret[x[0]] = tuple(x[1:])
  75. return ret
  76. def write_index(f, entries):
  77. """Write an index file.
  78. :param f: File-like object to write to
  79. :param entries: Iterable over the entries to write
  80. """
  81. f.write("DIRC")
  82. f.write(struct.pack(">LL", 2, len(entries)))
  83. for x in entries:
  84. write_cache_entry(f, x)
  85. def write_index_dict(f, entries):
  86. """Write an index file based on the contents of a dictionary.
  87. """
  88. entries_list = []
  89. for name in sorted(entries):
  90. entries_list.append((name,) + tuple(entries[name]))
  91. write_index(f, entries_list)
  92. class Index(object):
  93. def __init__(self, filename):
  94. self._filename = filename
  95. self.clear()
  96. self.read()
  97. def write(self):
  98. f = open(self._filename, 'w')
  99. try:
  100. write_index_dict(f, self._byname)
  101. finally:
  102. f.close()
  103. def read(self):
  104. f = open(self._filename, 'r')
  105. try:
  106. for x in read_index(f):
  107. self[x[0]] = tuple(x[1:])
  108. finally:
  109. f.close()
  110. def __len__(self):
  111. return len(self._byname)
  112. def __getitem__(self, name):
  113. return self._byname[name]
  114. def get_sha1(self, path):
  115. return self[path][-2]
  116. def clear(self):
  117. self._byname = {}
  118. def __setitem__(self, name, x):
  119. assert isinstance(name, str)
  120. assert len(x) == 10
  121. # Remove the old entry if any
  122. self._byname[name] = x
  123. def iteritems(self):
  124. return self._byname.iteritems()
  125. def update(self, entries):
  126. for name, value in entries.iteritems():
  127. self[name] = value