Support comments in commits

Gerrit represents changes to commits with a file change of /COMMIT_MSG
and magically offset file comments :/. In this patch I reproduce that
logic sufficiently well to match up on all the commits I have looked
at.

Since diffs vs the base should not show the old commit's commit
message, I have hinted to the diff function when to include the commit
message in the output.

Change-Id: I8adb9fa22b384cace88f114f770a3eb5d3a89f5c
This commit is contained in:
Robert Collins 2014-06-11 17:13:21 +12:00 committed by James E. Blair
parent 63a306c241
commit 99aa6ddda3
2 changed files with 87 additions and 3 deletions

View File

@ -12,8 +12,10 @@
# License for the specific language governing permissions and limitations
# under the License.
import datetime
import logging
import difflib
import itertools
import os
import re
@ -27,6 +29,74 @@ END = 1
LINENO = 0
LINE = 1
class GitTimeZone(datetime.tzinfo):
"""Because we can't have nice things."""
def __init__(self, offset_seconds):
self._offset = offset_seconds
def utcoffset(self, dt):
return datetime.timedelta(seconds=self._offset)
def dst(self, dt):
return datetime.timedelta(0)
def tzname(self, dt):
return None
class CommitContext(object):
"""A git.diff.Diff for commit messages."""
def decorateGitTime(self, seconds, tz):
dt = datetime.datetime.fromtimestamp(seconds, GitTimeZone(-tz))
return dt.strftime('%Y-%m-%d %H:%M:%S %Z%z')
def decorateMessage(self, commit):
"""Put the Gerrit commit metadata at the front of the message.
e.g.:
Parent: cc8a51ca (Initial commit) 1
Author: Robert Collins <rbtcollins@hp.com> 2
AuthorDate: 2014-05-27 14:05:47 +1200 3
Commit: Robert Collins <rbtcollins@hp.com> 4
CommitDate: 2014-05-27 14:07:57 +1200 5
6
"""
# NB: If folk report that commits have comments at the wrong place
# Then this function, which reproduces gerrit behaviour, will need
# to be fixed (e.g. by making the behaviour match more closely.
if not commit:
return []
if commit.parents:
parentsha = commit.parents[0].hexsha[:8]
else:
parentsha = None
author = commit.author
committer = commit.committer
author_date = self.decorateGitTime(
commit.authored_date, commit.author_tz_offset)
commit_date = self.decorateGitTime(
commit.committed_date, commit.committer_tz_offset)
return ["Parent: %s\n" % parentsha,
"Author: %s <%s>\n" % (author.name, author.email),
"AuthorDate: %s\n" % author_date,
"Commit: %s <%s>\n" % (committer.name, committer.email),
"CommitDate: %s\n" % commit_date,
"\n"] + commit.message.splitlines(True)
def __init__(self, old, new):
"""Create a CommitContext.
:param old: A git.objects.commit object or None.
:param new: A git.objects.commit object.
"""
self.rename_from = self.rename_to = None
self.diff = ''.join(difflib.unified_diff(
self.decorateMessage(old), self.decorateMessage(new),
fromfile="/a/COMMIT_MSG", tofile="/b/COMMIT_MSG"))
class DiffChunk(object):
def __init__(self):
self.oldlines = []
@ -258,13 +328,24 @@ class Repo(object):
return output_old, output_new
header_re = re.compile('@@ -(\d+)(,\d+)? \+(\d+)(,\d+)? @@')
def diff(self, old, new, context=10000):
def diff(self, old, new, context=10000, show_old_commit=False):
"""Create a diff from old to new.
Note that the commit message is also diffed, and listed as /COMMIT_MSG.
"""
repo = git.Repo(self.path)
#'-y', '-x', 'diff -C10', old, new, path).split('\n'):
oldc = repo.commit(old)
newc = repo.commit(new)
files = []
for diff_context in oldc.diff(newc, create_patch=True, U=context):
extra_contexts = []
if show_old_commit:
extra_contexts.append(CommitContext(oldc, newc))
else:
extra_contexts.append(CommitContext(None, newc))
contexts = itertools.chain(
extra_contexts, oldc.diff(newc, create_patch=True, U=context))
for diff_context in contexts:
# Each iteration of this is a file
f = DiffFile()
files.append(f)

View File

@ -235,12 +235,14 @@ This Screen
old_str = 'patchset %s' % self.old_revision_num
self.base_commit = old_revision.commit
old_comments = old_revision.comments
show_old_commit = True
else:
old_revision = None
self.old_revision_num = None
old_str = 'base'
self.base_commit = new_revision.parent
old_comments = []
show_old_commit = False
self.title = u'Diff of %s change %s from %s to patchset %s' % (
new_revision.change.project.name,
new_revision.change.number,
@ -291,7 +293,8 @@ This Screen
lines = [] # The initial set of lines to display
self.file_diffs = [{}, {}] # Mapping of fn -> DiffFile object (old, new)
# this is a list of files:
for i, diff in enumerate(repo.diff(self.base_commit, self.commit)):
for i, diff in enumerate(repo.diff(self.base_commit, self.commit,
show_old_commit=show_old_commit)):
if i > 0:
lines.append(urwid.Text(''))
self.file_diffs[gitrepo.OLD][diff.oldname] = diff