bin/pick: Add support for adding notes on patches

This is pretty useful for keeping track of why a patch isn't landed, or
who I'm waiting on feedback from.

Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/23472>
This commit is contained in:
Dylan Baker
2023-04-20 11:27:21 -07:00
committed by Marge Bot
parent 41394187aa
commit 0630a599fc
2 changed files with 66 additions and 4 deletions

View File

@@ -44,6 +44,7 @@ if typing.TYPE_CHECKING:
resolution: typing.Optional[int]
main_sha: typing.Optional[str]
because_sha: typing.Optional[str]
notes: typing.Optional[str] = attr.ib(None)
IS_FIX = re.compile(r'^\s*fixes:\s*([a-f0-9]{6,40})', flags=re.MULTILINE | re.IGNORECASE)
# FIXME: I dislike the duplication in this regex, but I couldn't get it to work otherwise
@@ -121,6 +122,7 @@ class Commit:
resolution: Resolution = attr.ib(Resolution.UNRESOLVED)
main_sha: typing.Optional[str] = attr.ib(None)
because_sha: typing.Optional[str] = attr.ib(None)
notes: typing.Optional[str] = attr.ib(None)
def to_json(self) -> 'CommitDict':
d: typing.Dict[str, typing.Any] = attr.asdict(self)
@@ -131,7 +133,8 @@ class Commit:
@classmethod
def from_json(cls, data: 'CommitDict') -> 'Commit':
c = cls(data['sha'], data['description'], data['nominated'], main_sha=data['main_sha'], because_sha=data['because_sha'])
c = cls(data['sha'], data['description'], data['nominated'], main_sha=data['main_sha'],
because_sha=data['because_sha'], notes=data['notes'])
c.nomination_type = NominationType(data['nomination_type'])
if data['resolution'] is not None:
c.resolution = Resolution(data['resolution'])
@@ -201,6 +204,14 @@ class Commit:
assert v
await ui.feedback(f'{self.sha} ({self.description}) committed successfully')
async def update_notes(self, ui: 'UI', notes: typing.Optional[str]) -> None:
self.notes = notes
async with ui.git_lock:
ui.save()
v = await commit_state(message=f'Updates notes for {self.sha}')
assert v
await ui.feedback(f'{self.sha} ({self.description}) notes updated successfully')
async def get_new_commits(sha: str) -> typing.List[typing.Tuple[str, str]]:
# Try to get the authoritative upstream main

View File

@@ -47,6 +47,13 @@ class RootWidget(urwid.Frame):
super().__init__(*args, **kwargs)
self.ui = ui
class CommitList(urwid.ListBox):
def __init__(self, *args, ui: 'UI', **kwargs):
super().__init__(*args, **kwargs)
self.ui = ui
def keypress(self, size: int, key: str) -> typing.Optional[str]:
if key == 'q':
raise urwid.ExitMainLoop()
@@ -101,6 +108,23 @@ class CommitWidget(urwid.Text):
return None
class FocusAwareEdit(urwid.Edit):
"""An Edit type that signals when it comes into and leaves focus."""
signals = urwid.Edit.signals + ['focus_changed']
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.__is_focus = False
def render(self, size: typing.Tuple[int], focus: bool = False) -> urwid.Canvas:
if focus != self.__is_focus:
self._emit("focus_changed", focus)
self.__is_focus = focus
return super().render(size, focus)
@attr.s(slots=True)
class UI:
@@ -112,6 +136,7 @@ class UI:
commit_list: typing.List['urwid.Button'] = attr.ib(factory=lambda: urwid.SimpleFocusListWalker([]), init=False)
feedback_box: typing.List['urwid.Text'] = attr.ib(factory=lambda: urwid.SimpleFocusListWalker([]), init=False)
notes: 'FocusAwareEdit' = attr.ib(factory=lambda: FocusAwareEdit('', multiline=True), init=False)
header: 'urwid.Text' = attr.ib(factory=lambda: urwid.Text('Mesa Stable Picker', align='center'), init=False)
body: 'urwid.Columns' = attr.ib(attr.Factory(lambda s: s._make_body(), True), init=False)
footer: 'urwid.Columns' = attr.ib(attr.Factory(lambda s: s._make_footer(), True), init=False)
@@ -122,10 +147,36 @@ class UI:
new_commits: typing.List['core.Commit'] = attr.ib(factory=list, init=False)
git_lock: asyncio.Lock = attr.ib(factory=asyncio.Lock, init=False)
def _get_current_commit(self) -> typing.Optional['core.Commit']:
entry = self.commit_list.get_focus()[0]
return entry.original_widget.commit if entry is not None else None
def _change_notes_cb(self) -> None:
commit = self._get_current_commit()
if commit and commit.notes:
self.notes.set_edit_text(commit.notes)
else:
self.notes.set_edit_text('')
def _change_notes_focus_cb(self, notes: 'FocusAwareEdit', focus: 'bool') -> 'None':
# in the case of coming into focus we don't want to do anything
if focus:
return
commit = self._get_current_commit()
if commit is None:
return
text: str = notes.get_edit_text()
if text != commit.notes:
asyncio.ensure_future(commit.update_notes(self, text))
def _make_body(self) -> 'urwid.Columns':
commits = urwid.ListBox(self.commit_list)
commits = CommitList(self.commit_list, ui=self)
feedback = urwid.ListBox(self.feedback_box)
return urwid.Columns([urwid.LineBox(commits), urwid.LineBox(feedback)])
urwid.connect_signal(self.commit_list, 'modified', self._change_notes_cb)
notes = urwid.Filler(self.notes)
urwid.connect_signal(self.notes, 'focus_changed', self._change_notes_focus_cb)
return urwid.Columns([urwid.LineBox(commits), urwid.Pile([urwid.LineBox(notes), urwid.LineBox(feedback)])])
def _make_footer(self) -> 'urwid.Columns':
body = [
@@ -134,7 +185,7 @@ class UI:
urwid.Text('[C]herry Pick'),
urwid.Text('[D]enominate'),
urwid.Text('[B]ackport'),
urwid.Text('[A]pply additional patch')
urwid.Text('[A]pply additional patch'),
]
return urwid.Columns(body)