4 from actor import Actor
5 from lazy import LazyMixin
10 class Commit(LazyMixin):
11 def __init__(self, repo, **kwargs):
13 Instantiate a new Commit
16 is the id of the commit
19 is a list of commit ids (will be converted into Commit instances)
22 is the correspdonding tree id (will be converted into a Tree object)
28 is the authored DateTime
31 is the committer string
34 is the committed DateTime
37 is the first line of the commit message
42 LazyMixin.__init__(self)
48 self.authored_date = None
50 self.committed_date = None
54 for k, v in kwargs.items():
58 if 'parents' in kwargs:
59 self.parents = map(lambda p: Commit(repo, **{'id': p}), kwargs['parents'])
61 self.tree = tree.Tree(repo, **{'id': kwargs['tree']})
64 temp = Commit.find_all(self.repo, self.id, **{'max_count': 1})[0]
65 self.parents = temp.parents
67 self.author = temp.author
68 self.authored_date = temp.authored_date
69 self.committer = temp.committer
70 self.committed_date = temp.committed_date
71 self.message = temp.message
78 def count(cls, repo, ref):
80 Count the number of commits reachable from this ref
86 is the ref from which to begin (SHA1 or name)
91 return len(repo.git.rev_list(ref).strip().splitlines())
94 def find_all(cls, repo, ref, **kwargs):
96 Find all commits matching the given criteria.
101 is the ref from which to begin (SHA1 or name)
104 is a Hash of optional arguments to git where
105 ``max_count`` is the maximum number of commits to fetch
106 ``skip`` is the number of commits to skip
111 options = {'pretty': 'raw'}
112 options.update(kwargs)
114 output = repo.git.rev_list(ref, **options)
115 return cls.list_from_string(repo, output)
118 def list_from_string(cls, repo, text):
120 Parse out commit information into a list of Commit objects
126 is the text output from the git command (raw format)
131 lines = [l for l in text.splitlines() if l.strip()]
136 id = lines.pop(0).split()[-1]
137 tree = lines.pop(0).split()[-1]
140 while lines and re.search(r'^parent', lines[0]):
141 parents.append(lines.pop(0).split()[-1])
142 author, authored_date = cls.actor(lines.pop(0))
143 committer, committed_date = cls.actor(lines.pop(0))
146 while lines and re.search(r'^ {4}', lines[0]):
147 messages.append(lines.pop(0).strip())
149 message = messages and messages[0] or ''
151 commits.append(Commit(repo, id=id, parents=parents, tree=tree, author=author, authored_date=authored_date,
152 committer=committer, committed_date=committed_date, message=message))
157 def diff(cls, repo, a, b = None, paths = None):
159 Show diffs between two trees:
168 is an optional named commit. Passing a list assumes you
169 wish to omit the second named commit and limit the diff to the
173 is a list of paths to limit the diff.
180 if isinstance(b, list):
185 paths.insert(0, "--")
190 text = repo.git.diff(*paths, **{'full_index': True})
191 return diff.Diff.list_from_string(repo, text)
196 d = self.repo.git.show(self.id, **{'full_index': True, 'pretty': 'raw'})
197 if re.search(r'diff --git a', d):
198 if not re.search(r'^diff --git a', d):
199 p = re.compile(r'.+?(diff --git a)', re.MULTILINE | re.DOTALL)
200 d = p.sub(r'diff --git a', d, 1)
203 return diff.Diff.list_from_string(self.repo, d)
205 return self.diff(self.repo, self.parents[0].id, self.id)
210 text = self.repo.git.diff(self.id, **{'numstat': True})
212 for line in text.splitlines():
213 (insertions, deletions, filename) = line.split("\t")
214 text2 += "%s\t%s\t%s\n" % (deletions, insertions, filename)
217 text = self.repo.git.diff(self.parents[0].id, self.id, **{'numstat': True})
218 return stats.Stats.list_from_string(self.repo, text)
221 """ Convert commit to string which is SHA1 """
225 return '<GitPython.Commit "%s">' % self.id
228 def actor(cls, line):
230 Parse out the actor (author or committer) info
233 [str (actor name and email), time (acted at time)]
235 m = re.search(r'^.+? (.*) (\d+) .*$', line)
236 actor, epoch = m.groups()
237 return [Actor.from_string(actor), time.gmtime(int(epoch))]