Thread changes
Use the dependency information of the most recent revisions to create a threaded view of changes. Also, use a more efficient query to collect the children of a change in the change view. Change-Id: I1cbef9fe7c4f1822b8541e5b4d0e7d2fe2e180a9
This commit is contained in:
parent
84e072e172
commit
b6f20ee70b
@ -142,6 +142,10 @@ commentlinks:
|
||||
# of the default side-by-side:
|
||||
# diff-view: unified
|
||||
|
||||
# Dependent changes are displayed as "threads" in the change list by
|
||||
# default. To disable this behavior, uncomment the following line:
|
||||
# thread-changes: false
|
||||
|
||||
# Uncomment the following lines to Hide comments by default that match
|
||||
# certain criteria. You can toggle their display with 't'. Currently
|
||||
# the only supported criterion is "author".
|
||||
|
@ -110,6 +110,7 @@ class ConfigSchema(object):
|
||||
'change-list-query': str,
|
||||
'diff-view': str,
|
||||
'hide-comments': self.hide_comments,
|
||||
'thread-changes': bool,
|
||||
})
|
||||
return schema
|
||||
|
||||
@ -200,6 +201,8 @@ class Config(object):
|
||||
for h in self.config.get('hide-comments', []):
|
||||
self.hide_comments.append(re.compile(h['author']))
|
||||
|
||||
self.thread_changes = self.config.get('thread-changes', True)
|
||||
|
||||
def getServer(self, name=None):
|
||||
for server in self.config['servers']:
|
||||
if name is None or name == server['name']:
|
||||
|
@ -606,8 +606,10 @@ class DatabaseSession(object):
|
||||
return None
|
||||
|
||||
def getRevisionsByParent(self, parent):
|
||||
if isinstance(parent, basestring):
|
||||
parent = (parent,)
|
||||
try:
|
||||
return self.session().query(Revision).filter_by(parent=parent).all()
|
||||
return self.session().query(Revision).filter(Revision.parent.in_(parent)).all()
|
||||
except sqlalchemy.orm.exc.NoResultFound:
|
||||
return []
|
||||
|
||||
|
@ -745,11 +745,10 @@ class ChangeView(urwid.WidgetWrap):
|
||||
|
||||
# Handle needed-by
|
||||
children = {}
|
||||
for revision in change.revisions:
|
||||
children.update((r.change.key, r.change.subject)
|
||||
for r in session.getRevisionsByParent(revision.commit)
|
||||
if (r.change.status != 'MERGED' and
|
||||
r == r.change.revisions[-1]))
|
||||
children.update((r.change.key, r.change.subject)
|
||||
for r in session.getRevisionsByParent([revision.commit for revision in change.revisions])
|
||||
if (r.change.status != 'MERGED' and
|
||||
r == r.change.revisions[-1]))
|
||||
self._updateDependenciesWidget(children,
|
||||
self.needed_by, self.needed_by_rows,
|
||||
header='Needed by:')
|
||||
|
@ -23,6 +23,29 @@ from gertty import sync
|
||||
from gertty.view import change as view_change
|
||||
import gertty.view
|
||||
|
||||
|
||||
class ThreadStack(object):
|
||||
def __init__(self):
|
||||
self.stack = []
|
||||
|
||||
def push(self, change, children):
|
||||
self.stack.append([change, children])
|
||||
|
||||
def pop(self):
|
||||
change = None
|
||||
while self.stack:
|
||||
if self.stack[-1][1]:
|
||||
# handle children at the tip
|
||||
return self.stack[-1][1].pop(0)
|
||||
else:
|
||||
# current tip has no children, walk up
|
||||
self.stack.pop()
|
||||
continue
|
||||
return None
|
||||
|
||||
def countChildren(self):
|
||||
return [len(x[1]) for x in self.stack]
|
||||
|
||||
class ChangeRow(urwid.Button):
|
||||
change_focus_map = {None: 'focused',
|
||||
'unreviewed-change': 'focused-unreviewed-change',
|
||||
@ -60,7 +83,11 @@ class ChangeRow(urwid.Button):
|
||||
else:
|
||||
style = 'unreviewed-change'
|
||||
self.row_style.set_attr_map({None: style})
|
||||
self.subject.set_text(change.subject)
|
||||
if hasattr(change, '_subject'):
|
||||
subject = change._subject
|
||||
else:
|
||||
subject = change.subject
|
||||
self.subject.set_text(subject)
|
||||
self.number.set_text(str(change.number))
|
||||
self.project.set_text(change.project.name.split('/')[-1])
|
||||
self.owner.set_text(change.owner_name)
|
||||
@ -176,6 +203,15 @@ class ChangeListView(urwid.WidgetWrap):
|
||||
change_list = reversed(lst)
|
||||
else:
|
||||
change_list = lst
|
||||
if self.app.config.thread_changes:
|
||||
change_list = self._threadChanges(change_list)
|
||||
new_rows = []
|
||||
if len(self.listbox.body):
|
||||
focus_pos = self.listbox.focus_position
|
||||
focus_row = self.listbox.body[focus_pos]
|
||||
else:
|
||||
focus_pos = 0
|
||||
focus_row = None
|
||||
for change in change_list:
|
||||
row = self.change_rows.get(change.key)
|
||||
if not row:
|
||||
@ -187,14 +223,78 @@ class ChangeListView(urwid.WidgetWrap):
|
||||
else:
|
||||
row.update(change, self.categories)
|
||||
unseen_keys.remove(change.key)
|
||||
new_rows.append(row)
|
||||
i += 1
|
||||
self.listbox.body[:] = new_rows
|
||||
if focus_row in self.listbox.body:
|
||||
pos = self.listbox.body.index(focus_row)
|
||||
else:
|
||||
pos = min(focus_pos, len(self.listbox.body)-1)
|
||||
self.listbox.body.set_focus(pos)
|
||||
if lst:
|
||||
self.header.update(self.categories)
|
||||
for key in unseen_keys:
|
||||
row = self.change_rows[key]
|
||||
self.listbox.body.remove(row)
|
||||
del self.change_rows[key]
|
||||
|
||||
def _threadChanges(self, changes):
|
||||
ret = []
|
||||
stack = ThreadStack()
|
||||
children = {}
|
||||
commits = {}
|
||||
orphans = changes[:]
|
||||
for change in changes:
|
||||
for revision in change.revisions:
|
||||
commits[revision.commit] = change
|
||||
for change in changes:
|
||||
revision = change.revisions[-1]
|
||||
parent = commits.get(revision.parent, None)
|
||||
if parent:
|
||||
if parent.revisions[-1].commit != revision.parent:
|
||||
# Our parent is an outdated revision. This could
|
||||
# cause a cycle, so skip. This change will not
|
||||
# appear in the thread, but will still appear in
|
||||
# the list. TODO: use color to indicate it
|
||||
# depends on an outdated change.
|
||||
continue
|
||||
if change in orphans:
|
||||
orphans.remove(change)
|
||||
v = children.get(parent, [])
|
||||
v.append(change)
|
||||
children[parent] = v
|
||||
if orphans:
|
||||
change = orphans.pop(0)
|
||||
else:
|
||||
change = None
|
||||
while change:
|
||||
prefix = ''
|
||||
stack_children = stack.countChildren()
|
||||
for i, nchildren in enumerate(stack_children):
|
||||
if nchildren:
|
||||
if i+1 == len(stack_children):
|
||||
prefix += u'\u251c'
|
||||
else:
|
||||
prefix += u'\u2502'
|
||||
else:
|
||||
if i+1 == len(stack_children):
|
||||
prefix += u'\u2514'
|
||||
else:
|
||||
prefix += u' '
|
||||
if i+1 == len(stack_children):
|
||||
prefix += u'\u2500'
|
||||
else:
|
||||
prefix += u' '
|
||||
subject = '%s%s' % (prefix, change.subject)
|
||||
change._subject = subject
|
||||
ret.append(change)
|
||||
if change in children:
|
||||
stack.push(change, children[change])
|
||||
change = stack.pop()
|
||||
if (not change) and orphans:
|
||||
change = orphans.pop(0)
|
||||
assert len(ret) == len(changes)
|
||||
return ret
|
||||
|
||||
def clearChangeList(self):
|
||||
for key, value in self.change_rows.iteritems():
|
||||
self.listbox.body.remove(value)
|
||||
|
Loading…
x
Reference in New Issue
Block a user