summaryrefslogtreecommitdiff
path: root/lib/git/index.py
diff options
context:
space:
mode:
Diffstat (limited to 'lib/git/index.py')
-rw-r--r--lib/git/index.py152
1 files changed, 127 insertions, 25 deletions
diff --git a/lib/git/index.py b/lib/git/index.py
index ad581ad4..c137d4da 100644
--- a/lib/git/index.py
+++ b/lib/git/index.py
@@ -22,55 +22,88 @@ class IndexEntry(tuple):
See the properties for a mapping between names and tuple indices.
"""
@property
- def path(self):
- return self[0]
-
- @property
def ctime(self):
"""
Returns
Tuple(int_time_seconds_since_epoch, int_nano_seconds) of the
file's creation time
"""
- return struct.unpack(">LL", self[1])
+ return struct.unpack(">LL", self[0])
@property
def mtime(self):
"""
See ctime property, but returns modification time
"""
- return struct.unpack(">LL", self[2])
+ return struct.unpack(">LL", self[1])
@property
def dev(self):
- return self[3]
+ """
+ Device ID
+ """
+ return self[2]
@property
def inode(self):
- return self[4]
+ """
+ Inode ID
+ """
+ return self[3]
@property
def mode(self):
- return self[5]
+ """
+ File Mode, compatible to stat module constants
+ """
+ return self[4]
@property
def uid(self):
- return self[6]
+ """
+ User ID
+ """
+ return self[5]
@property
def gid(self):
- return self[7]
+ """
+ Group ID
+ """
+ return self[6]
@property
def size(self):
- return self[8]
+ """
+ Uncompressed size of the blob
+
+ Note
+ Will be 0 if the stage is not 0 ( hence it is an unmerged entry )
+ """
+ return self[7]
@property
def sha(self):
- return self[9]
+ """
+ hex sha of the blob
+ """
+ return self[8]
@property
def stage(self):
+ """
+ Stage of the entry, either:
+ 0 = default stage
+ 1 = stage before a merge or common ancestor entry in case of a 3 way merge
+ 2 = stage of entries from the 'left' side of the merge
+ 3 = stage of entries from the right side of the merge
+ Note:
+ For more information, see http://www.kernel.org/pub/software/scm/git/docs/git-read-tree.html
+ """
+ return self[9]
+
+ @property
+ def path(self):
return self[10]
@@ -80,22 +113,25 @@ class Index(object):
order to save git command function calls wherever possible.
It provides custom merging facilities and to create custom commits.
+
+ ``Entries``
+ The index contains an entries dict whose keys are tuples of
"""
- __slots__ = ( "version", "entries" )
+ __slots__ = ( "version", "entries", "_extension_data" )
+ _VERSION = 2 # latest version we support
def __init__(self, stream = None):
"""
Initialize this Index instance, optionally from the given ``stream``
-
- Note
- Reading is based on the dulwich project.
"""
self.entries = dict()
- self.version = -1
+ self.version = self._VERSION
+ self._extension_data = ''
if stream is not None:
self._read_from_stream(stream)
- def _read_entry(self, stream):
+ @classmethod
+ def _read_entry(cls, stream):
"""Return: One entry of the given stream"""
beginoffset = stream.tell()
ctime = struct.unpack(">8s", stream.read(8))[0]
@@ -107,11 +143,11 @@ class Index(object):
real_size = ((stream.tell() - beginoffset + 8) & ~7)
data = stream.read((beginoffset + real_size) - stream.tell())
- return IndexEntry((path, ctime, mtime, dev, ino, mode, uid, gid, size,
- binascii.hexlify(sha), flags >> 12))
+ return IndexEntry((ctime, mtime, dev, ino, mode, uid, gid, size,
+ binascii.hexlify(sha), flags >> 12, path))
-
- def _read_header(self, stream):
+ @classmethod
+ def _read_header(cls, stream):
"""Return tuple(version_long, num_entries) from the given stream"""
type_id = stream.read(4)
if type_id != "DIRC":
@@ -123,15 +159,20 @@ class Index(object):
def _read_from_stream(self, stream):
"""
Initialize this instance with index values read from the given stream
+
+ Note
+ We explicitly do not clear the entries dict here to allow for reading
+ multiple chunks from multiple streams into the same Index instance
"""
self.version, num_entries = self._read_header(stream)
- self.entries = dict()
count = 0
while count < num_entries:
entry = self._read_entry(stream)
self.entries[(entry.path,entry.stage)] = entry
count += 1
# END for each entry
+ # this data chunk is the footer of the index, don't yet know what it is for
+ self._extension_data = stream.read(~0)
@classmethod
def from_file(cls, file_path):
@@ -141,6 +182,9 @@ class Index(object):
``file_pa ``
File path pointing to git index file
+
+ Note
+ Reading is based on the dulwich project.
"""
fp = open(file_path, "r")
@@ -157,6 +201,49 @@ class Index(object):
finally:
fp.close()
+
+ @classmethod
+ def to_file(cls, index, file_path):
+ """
+ Write the index data to the given file path.
+
+ ``index``
+ Index you wish to write.
+
+ ``file_path``
+ Path at which to write the index data. Please note that missing directories
+ will lead to an exception to be thrown.
+
+ Raise
+ IOError if the file could not be written
+ """
+ fp = open(file_path, "w")
+ try:
+ return index.write(fp)
+ finally:
+ fp.close()
+ # END exception handling
+
+
+ @classmethod
+ def _write_cache_entry(cls, stream, entry):
+ """
+ Write an IndexEntry to a stream
+ """
+ beginoffset = stream.tell()
+ stream.write(entry[0]) # ctime
+ stream.write(entry[1]) # mtime
+ path = entry[10]
+ plen = len(path) & 0x0fff # path length
+ assert plen == len(path), "Path %s too long to fit into index" % entry[10]
+ flags = plen | (entry[9] << 12)# stage and path length are 2 byte flags
+ stream.write(struct.pack(">LLLLLL20sH", entry[2], entry[3], entry[4],
+ entry[5], entry[6], entry[7], binascii.unhexlify(entry[8]), flags))
+ stream.write(path)
+ real_size = ((stream.tell() - beginoffset + 8) & ~7)
+ stream.write("\0" * ((beginoffset + real_size) - stream.tell()))
+
+
def write(self, stream):
"""
Write the current state to the given stream
@@ -166,5 +253,20 @@ class Index(object):
Returns
self
+
+ Note
+ Index writing based on the dulwich implementation
"""
- raise NotImplementedError( "TODO" )
+ # header
+ stream.write("DIRC")
+ stream.write(struct.pack(">LL", self.version, len(self.entries)))
+
+ # body
+ entries_sorted = self.entries.values()
+ entries_sorted.sort(key=lambda e: (e[10], e[9])) # use path/stage as sort key
+ for entry in entries_sorted:
+ self._write_cache_entry(stream, entry)
+ # END for each entry
+ # write extension_data which we currently cannot interprete
+ stream.write(self._extension_data)
+