You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@bloodhound.apache.org by gj...@apache.org on 2012/10/16 22:06:19 UTC
svn commit: r1398968 [28/28] - in /incubator/bloodhound/trunk/trac: ./
contrib/ doc/ doc/api/ doc/utils/ sample-plugins/
sample-plugins/permissions/ sample-plugins/workflow/ trac/ trac/admin/
trac/admin/templates/ trac/admin/tests/ trac/db/ trac/db/tes...
Modified: incubator/bloodhound/trunk/trac/tracopt/ticket/deleter.py
URL: http://svn.apache.org/viewvc/incubator/bloodhound/trunk/trac/tracopt/ticket/deleter.py?rev=1398968&r1=1398967&r2=1398968&view=diff
==============================================================================
--- incubator/bloodhound/trunk/trac/tracopt/ticket/deleter.py (original)
+++ incubator/bloodhound/trunk/trac/tracopt/ticket/deleter.py Tue Oct 16 20:06:09 2012
@@ -20,6 +20,7 @@ from trac.ticket.model import Ticket
from trac.ticket.web_ui import TicketModule
from trac.util import get_reporter_id
from trac.util.datefmt import from_utimestamp
+from trac.util.presentation import captioned_button
from trac.util.translation import _
from trac.web.api import IRequestFilter, IRequestHandler, ITemplateStreamFilter
from trac.web.chrome import ITemplateProvider, add_notice, add_stylesheet
@@ -67,9 +68,12 @@ class TicketDeleter(Component):
return tag.form(
tag.div(
tag.input(type='hidden', name='action', value='delete'),
- tag.input(type='submit', value=_('Delete'),
- title=_('Delete ticket')),
- class_='inlinebuttons'),
+ tag.input(type='submit',
+ value=captioned_button(req, u'â', # 'EN DASH'
+ _("Delete")),
+ title=_('Delete ticket'),
+ class_="trac-delete"),
+ class_="inlinebuttons"),
action='#', method='get')
def delete_comment():
@@ -81,10 +85,12 @@ class TicketDeleter(Component):
value='delete-comment'),
tag.input(type='hidden', name='cnum', value=cnum),
tag.input(type='hidden', name='cdate', value=cdate),
- tag.input(type='submit', value=_('Delete'),
- title=_('Delete comment %(num)s',
- num=cnum)),
- class_='inlinebuttons'),
+ tag.input(type='submit',
+ value=captioned_button(req, u'â', # 'EN DASH'
+ _("Delete")),
+ title=_('Delete comment %(num)s', num=cnum),
+ class_="trac-delete"),
+ class_="inlinebuttons"),
action='#', method='get')
buffer = StreamBuffer()
Modified: incubator/bloodhound/trunk/trac/tracopt/versioncontrol/git/PyGIT.py
URL: http://svn.apache.org/viewvc/incubator/bloodhound/trunk/trac/tracopt/versioncontrol/git/PyGIT.py?rev=1398968&r1=1398967&r2=1398968&view=diff
==============================================================================
--- incubator/bloodhound/trunk/trac/tracopt/versioncontrol/git/PyGIT.py (original)
+++ incubator/bloodhound/trunk/trac/tracopt/versioncontrol/git/PyGIT.py Tue Oct 16 20:06:09 2012
@@ -31,12 +31,70 @@ import weakref
__all__ = ['GitError', 'GitErrorSha', 'Storage', 'StorageFactory']
+
+def terminate(process):
+ """Python 2.5 compatibility method.
+ os.kill is not available on Windows before Python 2.7.
+ In Python 2.6 subprocess.Popen has a terminate method.
+ (It also seems to have some issues on Windows though.)
+ """
+
+ def terminate_win(process):
+ import ctypes
+ PROCESS_TERMINATE = 1
+ handle = ctypes.windll.kernel32.OpenProcess(PROCESS_TERMINATE,
+ False,
+ process.pid)
+ ctypes.windll.kernel32.TerminateProcess(handle, -1)
+ ctypes.windll.kernel32.CloseHandle(handle)
+
+ def terminate_nix(process):
+ import os
+ import signal
+ return os.kill(process.pid, signal.SIGTERM)
+
+ if sys.platform == 'win32':
+ return terminate_win(process)
+ return terminate_nix(process)
+
+
class GitError(Exception):
pass
class GitErrorSha(GitError):
pass
+# Helper functions
+
+def parse_commit(raw):
+ """Parse the raw content of a commit (as given by `git cat-file -p <rev>`).
+
+ Return the commit message and a dict of properties.
+ """
+ if not raw:
+ raise GitErrorSha
+ lines = raw.splitlines()
+ if not lines:
+ raise GitErrorSha
+ line = lines.pop(0)
+ props = {}
+ multiline = multiline_key = None
+ while line:
+ if line[0] == ' ':
+ if not multiline:
+ multiline_key = key
+ multiline = [props[multiline_key][-1]]
+ multiline.append(line[1:])
+ else:
+ key, value = line.split(None, 1)
+ props.setdefault(key, []).append(value.strip())
+ line = lines.pop(0)
+ if multiline and (not line or key != multiline_key):
+ props[multiline_key][-1] = '\n'.join(multiline)
+ multiline = None
+ return '\n'.join(lines), props
+
+
class GitCore(object):
"""Low-level wrapper around git executable"""
@@ -252,7 +310,7 @@ class Storage(object):
try:
g = GitCore(git_bin=git_bin)
[v] = g.version().splitlines()
- _, _, version = v.strip().split()
+ version = v.strip().split()[2]
# 'version' has usually at least 3 numeric version
# components, e.g.::
# 1.5.4.2
@@ -299,6 +357,19 @@ class Storage(object):
self.logger = log
+ self.commit_encoding = None
+
+ # caches
+ self.__rev_cache = None
+ self.__rev_cache_lock = Lock()
+
+ # cache the last 200 commit messages
+ self.__commit_msg_cache = SizedDict(200)
+ self.__commit_msg_lock = Lock()
+
+ self.__cat_file_pipe = None
+ self.__cat_file_pipe_lock = Lock()
+
if git_fs_encoding is not None:
# validate encoding name
codecs.lookup(git_fs_encoding)
@@ -318,31 +389,21 @@ class Storage(object):
self.logger.error("GIT control files missing in '%s'" % git_dir)
if os.path.exists(__git_file_path('.git')):
self.logger.error("entry '.git' found in '%s'"
- " -- maybe use that folder instead..."
+ " -- maybe use that folder instead..."
% git_dir)
raise GitError("GIT control files not found, maybe wrong "
"directory?")
- self.logger.debug("PyGIT.Storage instance %d constructed" % id(self))
-
self.repo = GitCore(git_dir, git_bin=git_bin)
- self.commit_encoding = None
-
- # caches
- self.__rev_cache = None
- self.__rev_cache_lock = Lock()
-
- # cache the last 200 commit messages
- self.__commit_msg_cache = SizedDict(200)
- self.__commit_msg_lock = Lock()
-
- self.__cat_file_pipe = None
+ self.logger.debug("PyGIT.Storage instance %d constructed" % id(self))
def __del__(self):
- if self.__cat_file_pipe is not None:
- self.__cat_file_pipe.stdin.close()
- self.__cat_file_pipe.wait()
+ with self.__cat_file_pipe_lock:
+ if self.__cat_file_pipe is not None:
+ self.__cat_file_pipe.stdin.close()
+ terminate(self.__cat_file_pipe)
+ self.__cat_file_pipe.wait()
#
# cache handling
@@ -587,20 +648,39 @@ class Storage(object):
return self.verifyrev('HEAD')
def cat_file(self, kind, sha):
- if self.__cat_file_pipe is None:
- self.__cat_file_pipe = self.repo.cat_file_batch()
-
- self.__cat_file_pipe.stdin.write(sha + '\n')
- self.__cat_file_pipe.stdin.flush()
- _sha, _type, _size = self.__cat_file_pipe.stdout.readline().split()
-
- if _type != kind:
- raise TracError("internal error (got unexpected object kind "
- "'%s')" % k)
-
- size = int(_size)
- return self.__cat_file_pipe.stdout.read(size + 1)[:size]
+ with self.__cat_file_pipe_lock:
+ if self.__cat_file_pipe is None:
+ self.__cat_file_pipe = self.repo.cat_file_batch()
+ try:
+ self.__cat_file_pipe.stdin.write(sha + '\n')
+ self.__cat_file_pipe.stdin.flush()
+
+ split_stdout_line = self.__cat_file_pipe.stdout.readline() \
+ .split()
+ if len(split_stdout_line) != 3:
+ raise GitError("internal error (could not split line "
+ "'%s')" % (split_stdout_line,))
+
+ _sha, _type, _size = split_stdout_line
+
+ if _type != kind:
+ raise GitError("internal error (got unexpected object "
+ "kind '%s', expected '%s')"
+ % (_type, kind))
+
+ size = int(_size)
+ return self.__cat_file_pipe.stdout.read(size + 1)[:size]
+ except:
+ # There was an error, we should close the pipe to get to a
+ # consistent state (Otherwise it happens that next time we
+ # call cat_file we get payload from previous call)
+ self.logger.debug("closing cat_file pipe")
+ self.__cat_file_pipe.stdin.close()
+ terminate(self.__cat_file_pipe)
+ self.__cat_file_pipe.wait()
+ self.__cat_file_pipe = None
+
def verifyrev(self, rev):
"""verify/lookup given revision object and return a sha id or None
if lookup failed
@@ -737,19 +817,7 @@ class Storage(object):
# cache miss
raw = self.cat_file('commit', commit_id)
raw = unicode(raw, self.get_commit_encoding(), 'replace')
- lines = raw.splitlines()
-
- if not lines:
- raise GitErrorSha
-
- line = lines.pop(0)
- props = {}
- while line:
- key, value = line.split(None, 1)
- props.setdefault(key, []).append(value.strip())
- line = lines.pop(0)
-
- result = ('\n'.join(lines), props)
+ result = parse_commit(raw)
self.__commit_msg_cache[commit_id] = result
@@ -821,31 +889,6 @@ class Storage(object):
change = {}
next_path = []
- def terminate(process):
- """Python 2.5 compatibility method.
- os.kill is not available on Windows before Python 2.7.
- In Python 2.6 subprocess.Popen has a terminate method.
- (It also seems to have some issues on Windows though.)
- """
-
- def terminate_win(process):
- import ctypes
- PROCESS_TERMINATE = 1
- handle = ctypes.windll.kernel32.OpenProcess(PROCESS_TERMINATE,
- False,
- process.pid)
- ctypes.windll.kernel32.TerminateProcess(handle, -1)
- ctypes.windll.kernel32.CloseHandle(handle)
-
- def terminate_nix(process):
- import os
- import signal
- return os.kill(process.pid, signal.SIGTERM)
-
- if sys.platform == 'win32':
- return terminate_win(process)
- return terminate_nix(process)
-
def name_status_gen():
p[:] = [self.repo.log_pipe('--pretty=format:%n%H',
'--name-status', sha, '--', base_path)]
Modified: incubator/bloodhound/trunk/trac/tracopt/versioncontrol/git/git_fs.py
URL: http://svn.apache.org/viewvc/incubator/bloodhound/trunk/trac/tracopt/versioncontrol/git/git_fs.py?rev=1398968&r1=1398967&r2=1398968&view=diff
==============================================================================
--- incubator/bloodhound/trunk/trac/tracopt/versioncontrol/git/git_fs.py (original)
+++ incubator/bloodhound/trunk/trac/tracopt/versioncontrol/git/git_fs.py Tue Oct 16 20:06:09 2012
@@ -37,7 +37,7 @@ from tracopt.versioncontrol.git import P
class GitCachedRepository(CachedRepository):
- """Git-specific cached repository
+ """Git-specific cached repository.
Passes through {display,short,normalize}_rev
"""
@@ -61,7 +61,7 @@ class GitCachedRepository(CachedReposito
class GitCachedChangeset(CachedChangeset):
- """Git-specific cached changeset
+ """Git-specific cached changeset.
Handles get_branches()
"""
@@ -85,7 +85,7 @@ def intersperse(sep, iterable):
"""The 'intersperse' generator takes an element and an iterable and
intersperses that element between the elements of the iterable.
- inspired by Haskell's Data.List.intersperse
+ inspired by Haskell's ``Data.List.intersperse``
"""
for i, item in enumerate(iterable):
@@ -94,12 +94,12 @@ def intersperse(sep, iterable):
# helper
def _parse_user_time(s):
- """parse author/committer attribute lines and return
- (user,timestamp)
+ """Parse author or committer attribute lines and return
+ corresponding ``(user, timestamp)`` pair.
"""
user, time, tz_str = s.rsplit(None, 2)
- tz = FixedOffset((int(tz_str)*6)/10, tz_str)
+ tz = FixedOffset((int(tz_str) * 6) / 10, tz_str)
time = datetime.fromtimestamp(float(time), tz)
return user, time
@@ -112,7 +112,7 @@ class GitConnector(Component):
self._version = None
try:
- self._version = PyGIT.Storage.git_version(git_bin=self._git_bin)
+ self._version = PyGIT.Storage.git_version(git_bin=self.git_bin)
except PyGIT.GitError, e:
self.log.error("GitError: " + str(e))
@@ -155,7 +155,7 @@ class GitConnector(Component):
title=to_unicode(e), rel='nofollow')
def get_wiki_syntax(self):
- yield (r'(?:\b|!)r?[0-9a-fA-F]{%d,40}\b' % self._wiki_shortrev_len,
+ yield (r'(?:\b|!)r?[0-9a-fA-F]{%d,40}\b' % self.wiki_shortrev_len,
lambda fmt, sha, match:
self._format_sha_link(fmt, sha.startswith('r')
and sha[1:] or sha, sha))
@@ -166,40 +166,42 @@ class GitConnector(Component):
# IRepositoryConnector methods
- _persistent_cache = BoolOption('git', 'persistent_cache', 'false',
- """enable persistent caching of commit tree""")
+ persistent_cache = BoolOption('git', 'persistent_cache', 'false',
+ """Enable persistent caching of commit tree.""")
- _cached_repository = BoolOption('git', 'cached_repository', 'false',
- """wrap `GitRepository` in `CachedRepository`""")
+ cached_repository = BoolOption('git', 'cached_repository', 'false',
+ """Wrap `GitRepository` in `CachedRepository`.""")
- _shortrev_len = IntOption('git', 'shortrev_len', 7,
- """length rev sha sums should be tried to be abbreviated to
- (must be >= 4 and <= 40)
+ shortrev_len = IntOption('git', 'shortrev_len', 7,
+ """The length at which a sha1 should be abbreviated to (must
+ be >= 4 and <= 40).
""")
- _wiki_shortrev_len = IntOption('git', 'wiki_shortrev_len', 40,
- """minimum length of hex-string for which auto-detection as sha id is
- performed.
- (must be >= 4 and <= 40)
- """)
-
- _trac_user_rlookup = BoolOption('git', 'trac_user_rlookup', 'false',
- """enable reverse mapping of git email addresses to trac user ids""")
+ wiki_shortrev_len = IntOption('git', 'wikishortrev_len', 40,
+ """The minimum length of an hex-string for which
+ auto-detection as sha1 is performed (must be >= 4 and <= 40).
+ """)
- _use_committer_id = BoolOption('git', 'use_committer_id', 'true',
- """use git-committer id instead of git-author id as changeset owner
+ trac_user_rlookup = BoolOption('git', 'trac_user_rlookup', 'false',
+ """Enable reverse mapping of git email addresses to trac user ids
+ (costly if you have many users).""")
+
+ use_committer_id = BoolOption('git', 'use_committer_id', 'true',
+ """Use git-committer id instead of git-author id for the
+ changeset ''Author'' field.
""")
- _use_committer_time = BoolOption('git', 'use_committer_time', 'true',
- """use git-committer-author timestamp instead of git-author timestamp
- as changeset timestamp
+ use_committer_time = BoolOption('git', 'use_committer_time', 'true',
+ """Use git-committer timestamp instead of git-author timestamp
+ for the changeset ''Timestamp'' field.
""")
- _git_fs_encoding = Option('git', 'git_fs_encoding', 'utf-8',
- """define charset encoding of paths within git repository""")
+ git_fs_encoding = Option('git', 'git_fs_encoding', 'utf-8',
+ """Define charset encoding of paths within git repositories.""")
- _git_bin = PathOption('git', 'git_bin', '/usr/bin/git',
- """path to git executable (relative to trac project folder!)""")
+ git_bin = PathOption('git', 'git_bin', '/usr/bin/git',
+ """Path to git executable (relative to the Trac configuration folder,
+ so better use an absolute path here).""")
def get_supported_types(self):
@@ -209,11 +211,11 @@ class GitConnector(Component):
"""GitRepository factory method"""
assert type == 'git'
- if not (4 <= self._shortrev_len <= 40):
- raise TracError("shortrev_len must be withing [4..40]")
+ if not (4 <= self.shortrev_len <= 40):
+ raise TracError("[git] shortrev_len setting must be within [4..40]")
- if not (4 <= self._wiki_shortrev_len <= 40):
- raise TracError("wiki_shortrev_len must be withing [4..40]")
+ if not (4 <= self.wiki_shortrev_len <= 40):
+ raise TracError("[git] wikishortrev_len must be within [4..40]")
if not self._version:
raise TracError("GIT backend not available")
@@ -223,12 +225,12 @@ class GitConnector(Component):
(self._version['v_str'],
self._version['v_min_str']))
- if self._trac_user_rlookup:
+ if self.trac_user_rlookup:
def rlookup_uid(email):
- """reverse map 'real name <us...@domain.tld>' addresses to trac
- user ids
+ """Reverse map 'real name <us...@domain.tld>' addresses to trac
+ user ids.
- returns None if lookup failed
+ :return: `None` if lookup failed
"""
try:
@@ -250,16 +252,16 @@ class GitConnector(Component):
return None
repos = GitRepository(dir, params, self.log,
- persistent_cache=self._persistent_cache,
- git_bin=self._git_bin,
- git_fs_encoding=self._git_fs_encoding,
- shortrev_len=self._shortrev_len,
+ persistent_cache=self.persistent_cache,
+ git_bin=self.git_bin,
+ git_fs_encoding=self.git_fs_encoding,
+ shortrev_len=self.shortrev_len,
rlookup_uid=rlookup_uid,
- use_committer_id=self._use_committer_id,
- use_committer_time=self._use_committer_time,
+ use_committer_id=self.use_committer_id,
+ use_committer_time=self.use_committer_time,
)
- if self._cached_repository:
+ if self.cached_repository:
repos = GitCachedRepository(self.env, repos, self.log)
self.log.debug("enabled CachedRepository for '%s'" % dir)
else:
@@ -369,15 +371,19 @@ class GitRepository(Repository):
self.logger = log
self.gitrepo = path
self.params = params
- self._shortrev_len = max(4, min(shortrev_len, 40))
+ self.shortrev_len = max(4, min(shortrev_len, 40))
self.rlookup_uid = rlookup_uid
- self._use_committer_time = use_committer_time
- self._use_committer_id = use_committer_id
+ self.use_committer_time = use_committer_time
+ self.use_committer_id = use_committer_id
- self.git = PyGIT.StorageFactory(path, log, not persistent_cache,
- git_bin=git_bin,
- git_fs_encoding=git_fs_encoding) \
- .getInstance()
+ try:
+ self.git = PyGIT.StorageFactory(path, log, not persistent_cache,
+ git_bin=git_bin,
+ git_fs_encoding=git_fs_encoding) \
+ .getInstance()
+ except PyGIT.GitError, e:
+ raise TracError("%s does not appear to be a Git "
+ "repository." % path)
Repository.__init__(self, 'git:'+path, self.params, log)
@@ -406,7 +412,7 @@ class GitRepository(Repository):
def short_rev(self, rev):
return self.git.shortrev(self.normalize_rev(rev),
- min_len=self._shortrev_len)
+ min_len=self.shortrev_len)
def get_node(self, path, rev=None, historian=None):
return GitNode(self, path, rev, self.log, None, historian)
@@ -646,22 +652,37 @@ class GitChangeset(Changeset):
if _children:
props['children'] = _children
+ committer, author = self._get_committer_and_author()
# use 1st author/committer as changeset owner/timestamp
- if repos._use_committer_time:
- _, time_ = _parse_user_time(props['committer'][0])
+ c_user = a_user = c_time = a_time = None
+ if committer:
+ c_user, c_time = _parse_user_time(committer)
+ if author:
+ a_user, a_time = _parse_user_time(author)
+
+ if repos.use_committer_time:
+ time = c_time or a_time
else:
- _, time_ = _parse_user_time(props['author'][0])
+ time = a_time or c_time
- if repos._use_committer_id:
- user_, _ = _parse_user_time(props['committer'][0])
+ if repos.use_committer_id:
+ user = c_user or a_user
else:
- user_, _ = _parse_user_time(props['author'][0])
+ user = a_user or c_user
# try to resolve email address to trac uid
- user_ = repos.rlookup_uid(user_) or user_
+ user = repos.rlookup_uid(user) or user
- Changeset.__init__(self, repos, rev=sha, message=msg, author=user_,
- date=time_)
+ Changeset.__init__(self, repos, rev=sha, message=msg, author=user,
+ date=time)
+
+ def _get_committer_and_author(self):
+ committer = author = None
+ if 'committer' in self.props:
+ committer = self.props['committer'][0]
+ if 'author' in self.props:
+ author = self.props['author'][0]
+ return committer, author
def get_properties(self):
properties = {}
@@ -672,13 +693,10 @@ class GitChangeset(Changeset):
if 'children' in self.props:
properties['Children'] = self.props['children']
- if 'committer' in self.props:
- properties['git-committer'] = \
- _parse_user_time(self.props['committer'][0])
-
- if 'author' in self.props:
- properties['git-author'] = \
- _parse_user_time(self.props['author'][0])
+ committer, author = self._get_committer_and_author()
+ if author != committer:
+ properties['git-committer'] = _parse_user_time(committer)
+ properties['git-author'] = _parse_user_time(author)
branches = list(self.repos.git.get_branch_contains(self.rev,
resolve=True))
Modified: incubator/bloodhound/trunk/trac/tracopt/versioncontrol/git/tests/PyGIT.py
URL: http://svn.apache.org/viewvc/incubator/bloodhound/trunk/trac/tracopt/versioncontrol/git/tests/PyGIT.py?rev=1398968&r1=1398967&r2=1398968&view=diff
==============================================================================
--- incubator/bloodhound/trunk/trac/tracopt/versioncontrol/git/tests/PyGIT.py (original)
+++ incubator/bloodhound/trunk/trac/tracopt/versioncontrol/git/tests/PyGIT.py Tue Oct 16 20:06:09 2012
@@ -14,7 +14,7 @@
import unittest
from trac.test import locate
-from tracopt.versioncontrol.git.PyGIT import GitCore, Storage
+from tracopt.versioncontrol.git.PyGIT import GitCore, Storage, parse_commit
class GitTestCase(unittest.TestCase):
@@ -32,6 +32,111 @@ class GitTestCase(unittest.TestCase):
self.assertTrue(v['v_compatible'])
+class TestParseCommit(unittest.TestCase):
+ commit2240a7b = '''\
+tree b19535236cfb6c64b798745dd3917dafc27bcd0a
+parent 30aaca4582eac20a52ac7b2ec35bdb908133e5b1
+parent 5a0dc7365c240795bf190766eba7a27600be3b3e
+author Linus Torvalds <to...@linux-foundation.org> 1323915958 -0800
+committer Linus Torvalds <to...@linux-foundation.org> 1323915958 -0800
+mergetag object 5a0dc7365c240795bf190766eba7a27600be3b3e
+ type commit
+ tag tytso-for-linus-20111214A
+ tagger Theodore Ts'o <ty...@mit.edu> 1323890113 -0500
+
+ tytso-for-linus-20111214
+ -----BEGIN PGP SIGNATURE-----
+ Version: GnuPG v1.4.10 (GNU/Linux)
+
+ iQIcBAABCAAGBQJO6PXBAAoJENNvdpvBGATwpuEP/2RCxmdWYZ8/6Z6pmTh3hHN5
+ fx6HckTdvLQOvbQs72wzVW0JKyc25QmW2mQc5z3MjSymjf/RbEKihPUITRNbHrTD
+ T2sP/lWu09AKLioEg4ucAKn/A7Do3UDIkXTszvVVP/t2psVPzLeJ1njQKra14Nyz
+ o0+gSlnwuGx9WaxfR+7MYNs2ikdSkXIeYsiFAOY4YOxwwC99J/lZ0YaNkbI7UBtC
+ yu2XLIvPboa5JZXANq2G3VhVIETMmOyRTCC76OAXjqkdp9nLFWDG0ydqQh0vVZwL
+ xQGOmAj+l3BNTE0QmMni1w7A0SBU3N6xBA5HN6Y49RlbsMYG27aN54Fy5K2R41I3
+ QXVhBL53VD6b0KaITcoz7jIGIy6qk9Wx+2WcCYtQBSIjL2YwlaJq0PL07+vRamex
+ sqHGDejcNY87i6AV0DP6SNuCFCi9xFYoAoMi9Wu5E9+T+Vck0okFzW/luk/FvsSP
+ YA5Dh+vISyBeCnWQvcnBmsUQyf8d9MaNnejZ48ath+GiiMfY8USAZ29RAG4VuRtS
+ 9DAyTTIBA73dKpnvEV9u4i8Lwd8hRVMOnPyOO785NwEXk3Ng08pPSSbMklW6UfCY
+ 4nr5UNB13ZPbXx4uoAvATMpCpYxMaLEdxmeMvgXpkekl0hHBzpVDey1Vu9fb/a5n
+ dQpo6WWG9HIJ23hOGAGR
+ =n3Lm
+ -----END PGP SIGNATURE-----
+
+Merge tag 'tytso-for-linus-20111214' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4
+
+* tag 'tytso-for-linus-20111214' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4:
+ ext4: handle EOF correctly in ext4_bio_write_page()
+ ext4: remove a wrong BUG_ON in ext4_ext_convert_to_initialized
+ ext4: correctly handle pages w/o buffers in ext4_discard_partial_buffers()
+ ext4: avoid potential hang in mpage_submit_io() when blocksize < pagesize
+ ext4: avoid hangs in ext4_da_should_update_i_disksize()
+ ext4: display the correct mount option in /proc/mounts for [no]init_itable
+ ext4: Fix crash due to getting bogus eh_depth value on big-endian systems
+ ext4: fix ext4_end_io_dio() racing against fsync()
+
+.. using the new signed tag merge of git that now verifies the gpg
+signature automatically. Yay. The branchname was just 'dev', which is
+prettier. I'll tell Ted to use nicer tag names for future cases.
+'''
+
+ def test_parse(self):
+ msg, props = parse_commit(self.commit2240a7b)
+ self.assertTrue(msg)
+ self.assertTrue(props)
+ self.assertEquals(
+ ['30aaca4582eac20a52ac7b2ec35bdb908133e5b1',
+ '5a0dc7365c240795bf190766eba7a27600be3b3e'],
+ props['parent'])
+ self.assertEquals(
+ ['Linus Torvalds <to...@linux-foundation.org> 1323915958 -0800'],
+ props['author'])
+ self.assertEquals(props['author'], props['committer'])
+
+ # Merge tag
+ self.assertEquals(['''\
+object 5a0dc7365c240795bf190766eba7a27600be3b3e
+type commit
+tag tytso-for-linus-20111214A
+tagger Theodore Ts\'o <ty...@mit.edu> 1323890113 -0500
+
+tytso-for-linus-20111214
+-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v1.4.10 (GNU/Linux)
+
+iQIcBAABCAAGBQJO6PXBAAoJENNvdpvBGATwpuEP/2RCxmdWYZ8/6Z6pmTh3hHN5
+fx6HckTdvLQOvbQs72wzVW0JKyc25QmW2mQc5z3MjSymjf/RbEKihPUITRNbHrTD
+T2sP/lWu09AKLioEg4ucAKn/A7Do3UDIkXTszvVVP/t2psVPzLeJ1njQKra14Nyz
+o0+gSlnwuGx9WaxfR+7MYNs2ikdSkXIeYsiFAOY4YOxwwC99J/lZ0YaNkbI7UBtC
+yu2XLIvPboa5JZXANq2G3VhVIETMmOyRTCC76OAXjqkdp9nLFWDG0ydqQh0vVZwL
+xQGOmAj+l3BNTE0QmMni1w7A0SBU3N6xBA5HN6Y49RlbsMYG27aN54Fy5K2R41I3
+QXVhBL53VD6b0KaITcoz7jIGIy6qk9Wx+2WcCYtQBSIjL2YwlaJq0PL07+vRamex
+sqHGDejcNY87i6AV0DP6SNuCFCi9xFYoAoMi9Wu5E9+T+Vck0okFzW/luk/FvsSP
+YA5Dh+vISyBeCnWQvcnBmsUQyf8d9MaNnejZ48ath+GiiMfY8USAZ29RAG4VuRtS
+9DAyTTIBA73dKpnvEV9u4i8Lwd8hRVMOnPyOO785NwEXk3Ng08pPSSbMklW6UfCY
+4nr5UNB13ZPbXx4uoAvATMpCpYxMaLEdxmeMvgXpkekl0hHBzpVDey1Vu9fb/a5n
+dQpo6WWG9HIJ23hOGAGR
+=n3Lm
+-----END PGP SIGNATURE-----'''], props['mergetag'])
+
+ # Message
+ self.assertEquals("""Merge tag 'tytso-for-linus-20111214' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4
+
+* tag 'tytso-for-linus-20111214' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4:
+ ext4: handle EOF correctly in ext4_bio_write_page()
+ ext4: remove a wrong BUG_ON in ext4_ext_convert_to_initialized
+ ext4: correctly handle pages w/o buffers in ext4_discard_partial_buffers()
+ ext4: avoid potential hang in mpage_submit_io() when blocksize < pagesize
+ ext4: avoid hangs in ext4_da_should_update_i_disksize()
+ ext4: display the correct mount option in /proc/mounts for [no]init_itable
+ ext4: Fix crash due to getting bogus eh_depth value on big-endian systems
+ ext4: fix ext4_end_io_dio() racing against fsync()
+
+.. using the new signed tag merge of git that now verifies the gpg
+signature automatically. Yay. The branchname was just 'dev', which is
+prettier. I'll tell Ted to use nicer tag names for future cases.""", msg)
+
+
#class GitPerformanceTestCase(unittest.TestCase):
# """Performance test. Not really a unit test.
# Not self-contained: Needs a git repository and prints performance result
@@ -185,6 +290,7 @@ def suite():
git = locate("git")
if git:
suite.addTest(unittest.makeSuite(GitTestCase, 'test'))
+ suite.addTest(unittest.makeSuite(TestParseCommit, 'test'))
else:
print("SKIP: tracopt/versioncontrol/git/tests/PyGIT.py (git cli "
"binary, 'git', not found)")