summaryrefslogtreecommitdiff
path: root/lib/git/index.py
blob: 1e67d6d1e1e9237c6065ae9d158fb48a817cb8df (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# index.py
# Copyright (C) 2008, 2009 Michael Trier (mtrier@gmail.com) and contributors
#
# This module is part of GitPython and is released under
# the BSD License: http://www.opensource.org/licenses/bsd-license.php
"""
Module containing Index implementation, allowing to perform all kinds of index
manipulations such as querying and merging.
"""
import struct
import binascii
import mmap

class IndexEntry(tuple):
	"""
	Allows convenient access to IndexEntry data without completely unpacking it.
	
	Attributes usully accessed often are cached in the tuple whereas others are 
	unpacked on demand.
	"""

class Index(object):
	"""
	Implements an Index that can be manipulated using a native implementation in 
	order to save git command function calls wherever possible.
	
	It provides custom merging facilities and to create custom commits.
	"""
	__slots__ = ( "version", "entries" )
	
	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
		if stream is not None:
			self._read_from_stream(stream)
	
	def _read_entry(self, stream):
		"""Return: One entry of the given stream"""
		beginoffset = stream.tell()
		ctime = struct.unpack(">8s", stream.read(8))[0]
		mtime = struct.unpack(">8s", stream.read(8))[0]
		(dev, ino, mode, uid, gid, size, sha, flags) = \
			struct.unpack(">LLLLLL20sH", stream.read(20 + 4 * 6 + 2))
		path_size = flags & 0x0fff
		path = stream.read(path_size)
		
		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), path_size))
		
	
	def _read_header(self, stream):
		"""Return tuple(version_long, num_entries) from the given stream"""
		type_id = stream.read(4)
		if type_id != "DIRC":
			raise AssertionError("Invalid index file header: %r" % type_id)
		version, num_entries = struct.unpack(">LL", stream.read(4 * 2))
		assert version in (1, 2)
		return version, num_entries
		
	def _read_from_stream(self, stream):
		"""
		Initialize this instance with index values read from the given stream
		"""
		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[0]] = entry[1:]
			count += 1
		# END for each entry
	
	@classmethod
	def from_file(cls, file_path):
		"""
		Returns
			Index instance as recreated from the given stream.
			
		``file_pa ``
			File path pointing to git index file
		"""
		fp = open(file_path, "r")
		
		# try memory map for speed
		stream = fp
		try:
			stream = mmap.mmap(fp.fileno(), 0, access=mmap.ACCESS_READ)
		except Exception:
			pass
		# END memory mapping
		
		try:
			return cls(stream)
		finally:
			fp.close()
		
	def write(self, stream):
		"""
		Write the current state to the given stream
		
		``stream``
			File-like object
		
		Returns
			self
		"""
		raise NotImplementedError( "TODO" )