| 
 | 1 | +from head import Head  | 
 | 2 | +from git.util import join_path  | 
 | 3 | +from gitdb.util import (  | 
 | 4 | +						join,  | 
 | 5 | +						file_contents_ro_filepath  | 
 | 6 | +					)  | 
 | 7 | + | 
 | 8 | +from git.objects.util import (  | 
 | 9 | +								Actor,   | 
 | 10 | +								parse_actor_and_date,  | 
 | 11 | +								Serializable,   | 
 | 12 | +								utctz_to_altz,  | 
 | 13 | +								altz_to_utctz_str,  | 
 | 14 | +							)  | 
 | 15 | + | 
 | 16 | +import os  | 
 | 17 | + | 
 | 18 | + | 
 | 19 | +__all__ = ["RefLog", "RefLogEntry"]  | 
 | 20 | + | 
 | 21 | + | 
 | 22 | +class RefLogEntry(tuple):  | 
 | 23 | +	"""Named tuple allowing easy access to the revlog data fields"""  | 
 | 24 | +	_fmt = "%s %s %s <%s> %i %s\t%s"  | 
 | 25 | +	__slots__ = tuple()  | 
 | 26 | +	  | 
 | 27 | +	def __repr__(self):  | 
 | 28 | +		"""Representation of ourselves in git reflog format"""  | 
 | 29 | +		act = self.actor  | 
 | 30 | +		time = self.time  | 
 | 31 | +		return self._fmt % (self.oldhexsha, self.newhexsha, act.name, act.email,   | 
 | 32 | +							time[0], altz_to_utctz_str(time[1]), self.message)  | 
 | 33 | +	  | 
 | 34 | +	@property  | 
 | 35 | +	def oldhexsha(self):  | 
 | 36 | +		"""The hexsha to the commit the ref pointed to before the change"""   | 
 | 37 | +		return self[0]  | 
 | 38 | +		  | 
 | 39 | +	@property  | 
 | 40 | +	def newhexsha(self):  | 
 | 41 | +		"""The hexsha to the commit the ref now points to, after the change"""  | 
 | 42 | +		return self[1]  | 
 | 43 | +		  | 
 | 44 | +	@property  | 
 | 45 | +	def actor(self):  | 
 | 46 | +		"""Actor instance, providing access"""  | 
 | 47 | +		return self[2]  | 
 | 48 | +		  | 
 | 49 | +	@property  | 
 | 50 | +	def time(self):  | 
 | 51 | +		"""time as tuple:  | 
 | 52 | +		  | 
 | 53 | +		* [0] = int(time)  | 
 | 54 | +		* [1] = int(timezone_offset) in time.altzone format """  | 
 | 55 | +		return self[3]  | 
 | 56 | +		  | 
 | 57 | +	@property  | 
 | 58 | +	def message(self):  | 
 | 59 | +		"""Message describing the operation that acted on the reference"""  | 
 | 60 | +		return self[4]  | 
 | 61 | +	  | 
 | 62 | +	@classmethod  | 
 | 63 | +	def new(self, oldhexsha, newhexsha, actor, time, tz_offset, message):  | 
 | 64 | +		""":return: New instance of a RefLogEntry"""  | 
 | 65 | +		if not isinstance(actor, Actor):  | 
 | 66 | +			raise ValueError("Need actor instance, got %s" % actor)  | 
 | 67 | +		# END check types   | 
 | 68 | +		return RefLogEntry((oldhexsha, newhexsha, actor, (time, tz_offset), message))  | 
 | 69 | +		  | 
 | 70 | +	@classmethod  | 
 | 71 | +	def from_line(self, line):  | 
 | 72 | +		""":return: New RefLogEntry instance from the given revlog line.  | 
 | 73 | +		:param line: line without trailing newline  | 
 | 74 | +		:raise ValueError: If line could not be parsed"""  | 
 | 75 | +		raise NotImplementedError("todo")  | 
 | 76 | + | 
 | 77 | +	  | 
 | 78 | +class RefLog(list, Serializable):  | 
 | 79 | +	"""A reflog contains reflog entries, each of which defines a certain state  | 
 | 80 | +	of the head in question. Custom query methods allow to retrieve log entries   | 
 | 81 | +	by date or by other criteria.  | 
 | 82 | +	  | 
 | 83 | +	Reflog entries are orded, the first added entry is first in the list, the last  | 
 | 84 | +	entry, i.e. the last change of the head or reference, is last in the list."""  | 
 | 85 | +	  | 
 | 86 | +	__slots__ = tuple()  | 
 | 87 | +	  | 
 | 88 | +	#{ Interface  | 
 | 89 | +	  | 
 | 90 | +	@classmethod  | 
 | 91 | +	def from_file(cls, filepath):  | 
 | 92 | +		"""  | 
 | 93 | +		:return: a new RefLog instance containing all entries from the reflog   | 
 | 94 | +			at the given filepath  | 
 | 95 | +		:param filepath: path to reflog   | 
 | 96 | +		:raise ValueError: If the file could not be read or was corrupted in some way"""  | 
 | 97 | +		inst = cls()  | 
 | 98 | +		fmap = file_contents_ro_filepath(filepath, stream=False, allow_mmap=True)  | 
 | 99 | +		try:  | 
 | 100 | +			inst._deserialize(fmap)  | 
 | 101 | +		finally:  | 
 | 102 | +			fmap.close()  | 
 | 103 | +		#END handle closing of handle   | 
 | 104 | +		return inst  | 
 | 105 | +	  | 
 | 106 | +	@classmethod  | 
 | 107 | +	def reflog_path(cls, ref):  | 
 | 108 | +		"""  | 
 | 109 | +		:return: string to absolute path at which the reflog of the given ref   | 
 | 110 | +			instance would be found. The path is not guaranteed to point to a valid   | 
 | 111 | +			file though.  | 
 | 112 | +		:param ref: SymbolicReference instance"""  | 
 | 113 | +		return join(ref.repo.git_dir, "logs", ref.path)  | 
 | 114 | +		  | 
 | 115 | +	@classmethod  | 
 | 116 | +	def iter_entries(cls, stream):  | 
 | 117 | +		"""  | 
 | 118 | +		:return: Iterator yielding RefLogEntry instances, one for each line read   | 
 | 119 | +			sfrom the given stream.  | 
 | 120 | +		:param stream: file-like object containing the revlog in its native format  | 
 | 121 | +			or basestring instance pointing to a file to read"""  | 
 | 122 | +		new_entry = RefLogEntry.from_line  | 
 | 123 | +		if isinstance(stream, basestring):  | 
 | 124 | +			stream = file_contents_ro_filepath(stream)  | 
 | 125 | +		#END handle stream type  | 
 | 126 | +		return (new_entry(line.strip()) for line in stream)  | 
 | 127 | +	  | 
 | 128 | +	def to_file(self, filepath):  | 
 | 129 | +		"""Write the contents of the reflog instance to a file at the given filepath.  | 
 | 130 | +		:param filepath: path to file, parent directories are assumed to exist"""  | 
 | 131 | +		fp = open(filepath, 'wb')  | 
 | 132 | +		try:  | 
 | 133 | +			self._serialize(fp)  | 
 | 134 | +		finally:  | 
 | 135 | +			fp.close()  | 
 | 136 | +		#END handle file streams  | 
 | 137 | +	  | 
 | 138 | +	#} END interface  | 
 | 139 | +	  | 
 | 140 | +	#{ Serializable Interface  | 
 | 141 | +	def _serialize(self, stream):  | 
 | 142 | +		lm1 = len(self) - 1  | 
 | 143 | +		write = stream.write()  | 
 | 144 | +		  | 
 | 145 | +		# write all entries  | 
 | 146 | +		for i, e in self:  | 
 | 147 | +			s = repr(e)  | 
 | 148 | +			if i != lm1:  | 
 | 149 | +				s += "\n"  | 
 | 150 | +			#END handle line separator  | 
 | 151 | +			write(s)  | 
 | 152 | +		#END for each entry  | 
 | 153 | +	  | 
 | 154 | +	def _deserialize(self, stream):  | 
 | 155 | +		new_entry = RefLogEntry.from_line  | 
 | 156 | +		append = self.append  | 
 | 157 | +		# NOTE: should use iter_entries, but this way it will be more direct and faster  | 
 | 158 | +		for line in stream:  | 
 | 159 | +			append(new_entry(line.strip()))  | 
 | 160 | +		#END handle deserializatoin  | 
 | 161 | +	#} END serializable interface  | 
0 commit comments