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:
parent
63a306c241
commit
99aa6ddda3
@ -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)
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user