Example #1
0
    def from_line(cls, line):
        """:return: New RefLogEntry instance from the given revlog line.
		:param line: line without trailing newline
		:raise ValueError: If line could not be parsed"""
        try:
            info, msg = line.split("\t", 2)
        except ValueError:
            raise ValueError("line is missing tab separator")
            # END handle first plit
        oldhexsha = info[:40]
        newhexsha = info[41:81]
        for hexsha in (oldhexsha, newhexsha):
            if not cls._re_hexsha_only.match(hexsha):
                raise ValueError("Invalid hexsha: %s" % hexsha)
                # END if hexsha re doesn't match
                # END for each hexsha

        email_end = info.find(">", 82)
        if email_end == -1:
            raise ValueError("Missing token: >")
            # END handle missing end brace

        actor = Actor._from_string(info[82 : email_end + 1])
        time, tz_offset = parse_date(info[email_end + 2 :])

        return RefLogEntry((oldhexsha, newhexsha, actor, (time, tz_offset), msg))
Example #2
0
def parse_actor_and_date(line):
	"""Parse out the actor (author or committer) info from a line like::
	
		author Tom Preston-Werner <*****@*****.**> 1191999972 -0700
	
	:return: [Actor, int_seconds_since_epoch, int_timezone_offset]"""
	m = _re_actor_epoch.search(line)
	actor, epoch, offset = m.groups()
	return (Actor._from_string(actor), int(epoch), utctz_to_altz(offset))
Example #3
0
    def append_entry(cls, config_reader, filepath, oldbinsha, newbinsha, message):
        """Append a new log entry to the revlog at filepath.
		
		:param config_reader: configuration reader of the repository - used to obtain
			user information. May be None
		:param filepath: full path to the log file
		:param oldbinsha: binary sha of the previous commit
		:param newbinsha: binary sha of the current commit
		:param message: message describing the change to the reference
		:param write: If True, the changes will be written right away. Otherwise
			the change will not be written
		:return: RefLogEntry objects which was appended to the log
		:note: As we are append-only, concurrent access is not a problem as we 
			do not interfere with readers."""
        if len(oldbinsha) != 20 or len(newbinsha) != 20:
            raise ValueError("Shas need to be given in binary format")
            # END handle sha type
        assure_directory_exists(filepath, is_file=True)
        entry = RefLogEntry(
            (
                bin_to_hex(oldbinsha),
                bin_to_hex(newbinsha),
                Actor.committer(config_reader),
                (int(time.time()), time.altzone),
                message,
            )
        )

        lf = LockFile(filepath)
        lf._obtain_lock_or_raise()

        fd = open(filepath, "a")
        try:
            fd.write(repr(entry))
        finally:
            fd.close()
            lf._release_lock()
            # END handle write operation

        return entry
Example #4
0
	def create_from_tree(cls, repo, tree, message, parent_commits=None, head=False):
		"""Commit the given tree, creating a commit object.
		
		:param repo: Repo object the commit should be part of 
		:param tree: Tree object or hex or bin sha 
			the tree of the new commit
		:param message: Commit message. It may be an empty string if no message is provided.
			It will be converted to a string in any case.
		:param parent_commits:
			Optional Commit objects to use as parents for the new commit.
			If empty list, the commit will have no parents at all and become 
			a root commit.
			If None , the current head commit will be the parent of the 
			new commit object
		:param head:
			If True, the HEAD will be advanced to the new commit automatically.
			Else the HEAD will remain pointing on the previous commit. This could 
			lead to undesired results when diffing files.
			
		:return: Commit object representing the new commit
			
		:note:
			Additional information about the committer and Author are taken from the
			environment or from the pygit configuration, see git-commit-tree for 
			more information"""
		parents = parent_commits
		if parent_commits is None:
			try:
				parent_commits = [ repo.head.commit ]
			except ValueError:
				# empty repositories have no head commit
				parent_commits = list()
			# END handle parent commits
		# END if parent commits are unset
		
		# retrieve all additional information, create a commit object, and 
		# serialize it
		# Generally: 
		# * Environment variables override configuration values
		# * Sensible defaults are set according to the pygit documentation
		
		# COMMITER AND AUTHOR INFO
		cr = repo.config_reader()
		env = os.environ
		
		committer = Actor.committer(cr)
		author = Actor.author(cr)
		
		# PARSE THE DATES
		unix_time = int(time())
		offset = altzone
		
		author_date_str = env.get(cls.env_author_date, '')
		if author_date_str:
			author_time, author_offset = parse_date(author_date_str)
		else:
			author_time, author_offset = unix_time, offset
		# END set author time
		
		committer_date_str = env.get(cls.env_committer_date, '')
		if committer_date_str: 
			committer_time, committer_offset = parse_date(committer_date_str)
		else:
			committer_time, committer_offset = unix_time, offset
		# END set committer time
		
		# assume utf8 encoding
		enc_section, enc_option = cls.conf_encoding.split('.')
		conf_encoding = cr.get_value(enc_section, enc_option, cls.default_encoding)
		
		
		# if the tree is no object, make sure we create one - otherwise
		# the created commit object is invalid
		if isinstance(tree, str):
			tree = repo.tree(tree)
		# END tree conversion
		
		# CREATE NEW COMMIT
		new_commit = cls(repo, cls.NULL_BIN_SHA, tree, 
						author, author_time, author_offset, 
						committer, committer_time, committer_offset,
						message, parent_commits, conf_encoding)
		
		stream = StringIO()
		new_commit._serialize(stream)
		streamlen = stream.tell()
		stream.seek(0)
		
		istream = repo.odb.store(IStream(cls.type, streamlen, stream))
		new_commit.binsha = istream.binsha
		
		if head:
			# need late import here, importing pygit at the very beginning throws
			# as well ... 
			import pygit.refs
			try:
				repo.head.set_commit(new_commit, logmsg="commit: %s" % message)
			except ValueError:
				# head is not yet set to the ref our HEAD points to
				# Happens on first commit
				import pygit.refs
				master = pygit.refs.Head.create(repo, repo.head.ref, new_commit, logmsg="commit (initial): %s" % message)
				repo.head.set_reference(master, logmsg='commit: Switching to %s' % master)
			# END handle empty repositories
		# END advance head handling 
		
		return new_commit
Example #5
0
	def blame(self, rev, file):
		"""The blame information for the given file at the given revision.

		:parm rev: revision specifier, see pygit-rev-parse for viable options.
		:return:
			list: [git.Commit, list: [<line>]]
			A list of tuples associating a Commit object with a list of lines that 
			changed within the given commit. The Commit objects will be given in order
			of appearance."""
		data = self.git.blame(rev, '--', file, p=True)
		commits = dict()
		blames = list()
		info = None

		for line in data.splitlines(False):
			parts = self.re_whitespace.split(line, 1)
			firstpart = parts[0]
			if self.re_hexsha_only.search(firstpart):
				# handles 
				# 634396b2f541a9f2d58b00be1a07f0c358b999b3 1 1 7		- indicates blame-data start
				# 634396b2f541a9f2d58b00be1a07f0c358b999b3 2 2
				digits = parts[-1].split(" ")
				if len(digits) == 3:
					info = {'id': firstpart}
					blames.append([None, []])
				# END blame data initialization
			else:
				m = self.re_author_committer_start.search(firstpart)
				if m:
					# handles: 
					# author Tom Preston-Werner
					# author-mail <*****@*****.**>
					# author-time 1192271832
					# author-tz -0700
					# committer Tom Preston-Werner
					# committer-mail <*****@*****.**>
					# committer-time 1192271832
					# committer-tz -0700  - IGNORED BY US
					role = m.group(0)
					if firstpart.endswith('-mail'):
						info["%s_email" % role] = parts[-1]
					elif firstpart.endswith('-time'):
						info["%s_date" % role] = int(parts[-1])
					elif role == firstpart:
						info[role] = parts[-1]
					# END distinguish mail,time,name
				else:
					# handle
					# filename lib/grit.rb
					# summary add Blob
					# <and rest>
					if firstpart.startswith('filename'):
						info['filename'] = parts[-1]
					elif firstpart.startswith('summary'):
						info['summary'] = parts[-1]
					elif firstpart == '':
						if info:
							sha = info['id']
							c = commits.get(sha)
							if c is None:
								c = Commit(	 self, hex_to_bin(sha),
											 author=Actor._from_string(info['author'] + ' ' + info['author_email']),
											 authored_date=info['author_date'],
											 committer=Actor._from_string(info['committer'] + ' ' + info['committer_email']),
											 committed_date=info['committer_date'],
											 message=info['summary'])
								commits[sha] = c
							# END if commit objects needs initial creation
							m = self.re_tab_full_line.search(line)
							text,  = m.groups()
							blames[-1][0] = c
							blames[-1][1].append( text )
							info = None
						# END if we collected commit info
					# END distinguish filename,summary,rest
				# END distinguish author|committer vs filename,summary,rest
			# END distinguish hexsha vs other information
		return blames