1 from __future__
import division
, absolute_import
, unicode_literals
5 from cola
.git
import git
6 from cola
.observable
import Observable
8 # put summary at the end b/c it can contain
9 # any number of funky characters, including the separator
10 logfmt
= 'format:%H%x01%P%x01%d%x01%an%x01%ad%x01%ae%x01%s'
14 class CommitFactory(object):
21 cls
.root_generation
= 0
24 def new(cls
, sha1
=None, log_entry
=None):
25 if not sha1
and log_entry
:
28 commit
= cls
.commits
[sha1
]
29 if log_entry
and not commit
.parsed
:
30 commit
.parse(log_entry
)
31 cls
.root_generation
= max(commit
.generation
,
34 commit
= Commit(sha1
=sha1
,
37 cls
.root_generation
+= 1
38 commit
.generation
= max(commit
.generation
,
40 cls
.commits
[sha1
] = commit
44 class DAG(Observable
):
45 ref_updated
= 'ref_updated'
46 count_updated
= 'count_updated'
48 def __init__(self
, ref
, count
):
49 Observable
.__init
__(self
)
54 def set_ref(self
, ref
):
55 changed
= ref
!= self
.ref
58 self
.notify_observers(self
.ref_updated
)
61 def set_count(self
, count
):
62 changed
= count
!= self
.count
65 self
.notify_observers(self
.count_updated
)
68 def set_arguments(self
, args
):
71 if self
.set_count(args
.count
):
72 self
.overrides
['count'] = args
.count
74 if hasattr(args
, 'args') and args
.args
:
75 ref
= core
.list2cmdline(args
.args
)
77 self
.overrides
['ref'] = ref
79 def overridden(self
, opt
):
80 return opt
in self
.overrides
83 all_refs
= utils
.shell_split(self
.ref
)
85 all_refs
= all_refs
[all_refs
.index('--'):]
87 return [p
for p
in all_refs
if p
and core
.exists(p
)]
103 def __init__(self
, sha1
=None, log_entry
=None):
113 self
.generation
= CommitFactory
.root_generation
115 self
.parse(log_entry
)
117 def parse(self
, log_entry
, sep
=logsep
):
118 self
.sha1
= log_entry
[:40]
119 (parents
, tags
, author
, authdate
, email
, summary
) = \
120 log_entry
[41:].split(sep
, 5)
122 self
.summary
= summary
and summary
or ''
123 self
.author
= author
and author
or ''
124 self
.authdate
= authdate
or ''
125 self
.email
= email
and email
or ''
129 for parent_sha1
in parents
.split(' '):
130 parent
= CommitFactory
.new(sha1
=parent_sha1
)
131 parent
.children
.append(self
)
132 if generation
is None:
133 generation
= parent
.generation
+1
134 self
.parents
.append(parent
)
135 generation
= max(parent
.generation
+1, generation
)
136 self
.generation
= generation
139 for tag
in tags
[2:-1].split(', '):
145 def add_label(self
, tag
):
146 """Add tag/branch labels from `git log --decorate ....`"""
148 if tag
.startswith('tag: '):
149 tag
= tag
[5:] # tag: refs/
150 elif tag
.startswith('refs/remotes/'):
151 tag
= tag
[13:] # refs/remotes/
152 elif tag
.startswith('refs/heads/'):
153 tag
= tag
[11:] # refs/heads/
154 if tag
.endswith('/HEAD'):
157 # Git 2.4 Release Notes (draft)
158 # =============================
160 # Backward compatibility warning(s)
161 # ---------------------------------
163 # This release has a few changes in the user-visible output from
164 # Porcelain commands. These are not meant to be parsed by scripts, but
165 # the users still may want to be aware of the changes:
167 # * Output from "git log --decorate" (and "%d" format specifier used in
168 # the userformat "--format=<string>" parameter "git log" family of
169 # command takes) used to list "HEAD" just like other tips of branch
170 # names, separated with a comma in between. E.g.
172 # $ git log --decorate -1 master
173 # commit bdb0f6788fa5e3cacc4315e9ff318a27b2676ff4 (HEAD, master)
176 # This release updates the output slightly when HEAD refers to the tip
177 # of a branch whose name is also shown in the output. The above is
180 # $ git log --decorate -1 master
181 # commit bdb0f6788fa5e3cacc4315e9ff318a27b2676ff4 (HEAD -> master)
184 # C.f. http://thread.gmane.org/gmane.linux.kernel/1931234
186 head_arrow
= 'HEAD -> '
187 if tag
.startswith(head_arrow
):
188 self
.tags
.add('HEAD')
189 self
.tags
.add(tag
[len(head_arrow
):])
198 " sha1: " + self
.sha1
+ "\n"
199 " summary: " + self
.summary
+ "\n"
200 " author: " + self
.author
+ "\n"
201 " authdate: " + self
.authdate
+ "\n"
202 " parents: [" + ', '.join([p
.sha1
for p
in self
.parents
]) + "]\n"
203 " tags: [" + ', '.join(self
.tags
) + "]\n"
207 ''' Returns True if the node is a fork'''
208 return len(self
.children
) > 1
211 ''' Returns True if the node is a fork'''
212 return len(self
.parents
) > 1
215 class RepoReader(object):
217 def __init__(self
, ctx
, git
=git
):
222 self
._cmd
= ['git', 'log',
227 """Indicates that all data has been read"""
229 """Index into the cached commits"""
231 """List of commits objects in topological order"""
233 cached
= property(lambda self
: self
._cached
)
234 """Return True when no commits remain to be read"""
238 return len(self
._topo
_list
)
241 CommitFactory
.reset()
258 return self
._topo
_list
[self
._idx
]
263 if self
._proc
is None:
264 ref_args
= utils
.shell_split(self
.ctx
.ref
)
265 cmd
= self
._cmd
+ ['-%d' % self
.ctx
.count
] + ref_args
266 self
._proc
= core
.start_command(cmd
)
269 log_entry
= core
.readline(self
._proc
.stdout
).rstrip()
273 self
.returncode
= self
._proc
.returncode
277 sha1
= log_entry
[:40]
279 return self
._objects
[sha1
]
281 c
= CommitFactory
.new(log_entry
=log_entry
)
282 self
._objects
[c
.sha1
] = c
283 self
._topo
_list
.append(c
)
286 __next__
= next
# for Python 3
288 def __getitem__(self
, sha1
):
289 return self
._objects
[sha1
]
292 return self
._objects
.items()