index.py 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. # index.py -- File parser/write for the git index file
  2. # Copyright (C) 2008-2009 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. from dulwich.objects import sha_to_hex, hex_to_sha
  20. def read_cache_time(f):
  21. return struct.unpack(">LL", f.read(8))
  22. def write_cache_time(f, t):
  23. if isinstance(t, int):
  24. t = (t, 0)
  25. f.write(struct.pack(">LL", *t))
  26. def read_cache_entry(f):
  27. """Read an entry from a cache file.
  28. :param f: File-like object to read from
  29. :return: tuple with: inode, device, mode, uid, gid, size, sha, flags
  30. """
  31. beginoffset = f.tell()
  32. ctime = read_cache_time(f)
  33. mtime = read_cache_time(f)
  34. (ino, dev, mode, uid, gid, size, sha, flags, ) = \
  35. struct.unpack(">LLLLLL20sH", f.read(20 + 4 * 6 + 2))
  36. name = ""
  37. char = f.read(1)
  38. while char != "\0":
  39. name += char
  40. char = f.read(1)
  41. # Padding:
  42. real_size = ((f.tell() - beginoffset + 7) & ~7)
  43. f.seek(beginoffset + real_size)
  44. return (name, ctime, mtime, ino, dev, mode, uid, gid, size,
  45. sha_to_hex(sha), flags)
  46. def write_cache_entry(f, entry):
  47. """Write an index entry to a file.
  48. :param f: File object
  49. :param entry: Entry to write, tuple with:
  50. (name, ctime, mtime, ino, dev, mode, uid, gid, size, sha, flags)
  51. """
  52. beginoffset = f.tell()
  53. (name, ctime, mtime, ino, dev, mode, uid, gid, size, sha, flags) = entry
  54. write_cache_time(f, ctime)
  55. write_cache_time(f, mtime)
  56. f.write(struct.pack(">LLLLLL20sH", ino, dev, mode, uid, gid, size, hex_to_sha(sha), flags))
  57. f.write(name)
  58. f.write(chr(0))
  59. real_size = ((f.tell() - beginoffset + 7) & ~7)
  60. f.write("\0" * ((beginoffset + real_size) - f.tell()))
  61. def read_index(f):
  62. """Read an index file, yielding the individual entries."""
  63. header = f.read(4)
  64. if header != "DIRC":
  65. raise AssertionError("Invalid index file header: %r" % header)
  66. (version, num_entries) = struct.unpack(">LL", f.read(4 * 2))
  67. assert version in (1, 2)
  68. for i in range(num_entries):
  69. yield read_cache_entry(f)
  70. def read_index_dict(f):
  71. """Read an index file and return it as a dictionary.
  72. :param f: File object to read from
  73. """
  74. ret = {}
  75. for x in read_index(f):
  76. ret[x[0]] = tuple(x[1:])
  77. return ret
  78. def write_index(f, entries):
  79. """Write an index file.
  80. :param f: File-like object to write to
  81. :param entries: Iterable over the entries to write
  82. """
  83. f.write("DIRC")
  84. f.write(struct.pack(">LL", 2, len(entries)))
  85. for x in entries:
  86. write_cache_entry(f, x)
  87. def write_index_dict(f, entries):
  88. """Write an index file based on the contents of a dictionary.
  89. """
  90. entries_list = []
  91. for name in sorted(entries):
  92. entries_list.append((name,) + tuple(entries[name]))
  93. write_index(f, entries_list)
  94. class Index(object):
  95. def __init__(self, filename):
  96. self._filename = filename
  97. self.clear()
  98. self.read()
  99. def write(self):
  100. f = open(self._filename, 'w')
  101. try:
  102. write_index_dict(f, self._byname)
  103. finally:
  104. f.close()
  105. def read(self):
  106. f = open(self._filename, 'r')
  107. try:
  108. for x in read_index(f):
  109. self[x[0]] = tuple(x[1:])
  110. finally:
  111. f.close()
  112. def __len__(self):
  113. return len(self._byname)
  114. def __getitem__(self, name):
  115. return self._byname[name]
  116. def __iter__(self):
  117. return iter(self._byname)
  118. def get_sha1(self, path):
  119. return self[path][-2]
  120. def clear(self):
  121. self._byname = {}
  122. def __setitem__(self, name, x):
  123. assert isinstance(name, str)
  124. assert len(x) == 10
  125. # Remove the old entry if any
  126. self._byname[name] = x
  127. def iteritems(self):
  128. return self._byname.iteritems()
  129. def update(self, entries):
  130. for name, value in entries.iteritems():
  131. self[name] = value