| # Copyright 2014 The Chromium Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| import logging |
| |
| from infra.libs.decorators import cached_property |
| |
| from infra.libs.git2.util import CalledProcessError |
| from infra.libs.git2.util import INVALID |
| from infra.libs.git2.data import CommitData |
| |
| |
| LOGGER = logging.getLogger(__name__) |
| |
| |
| class Commit(object): |
| """Represents the identity of a commit in a git repo.""" |
| |
| def __init__(self, repo, hsh): |
| """ |
| @type repo: Repo |
| """ |
| assert CommitData.HASH_RE.match(hsh) |
| self._repo = repo |
| self._hsh = hsh |
| |
| # Comparison & Representation |
| def __eq__(self, other): |
| return (self is other) or ( |
| isinstance(other, Commit) and ( |
| self.hsh == other.hsh |
| ) |
| ) |
| |
| def __ne__(self, other): |
| return not (self == other) |
| |
| def __repr__(self): |
| return 'Commit({_repo!r}, {_hsh!r})'.format(**self.__dict__) |
| |
| # Accessors |
| # pylint: disable=W0212 |
| repo = property(lambda self: self._repo) |
| hsh = property(lambda self: self._hsh) |
| |
| # Properties |
| @cached_property |
| def data(self): |
| """Get a structured data representation of this commit.""" |
| try: |
| raw_data = self.repo.run('cat-file', 'commit', self.hsh) |
| except CalledProcessError: |
| return INVALID |
| return CommitData.from_raw(raw_data) |
| |
| @cached_property |
| def parent(self): |
| """Get the corresponding parent Commit() for this Commit(), or None. |
| |
| If self has more than one parent, returns INVALID. |
| """ |
| parents = self.data.parents |
| if len(parents) > 1: |
| LOGGER.error('Commit %r has more than one parent!', self.hsh) |
| return INVALID |
| return self.repo.get_commit(parents[0]) if parents else None |
| |
| # Methods |
| def alter(self, **kwargs): |
| """Get a new Commit which is the same as this one, except for alterations |
| specified by kwargs. |
| |
| This will intern the new Commit object into the Repo. |
| """ |
| return self.repo.get_commit( |
| self.repo.intern(self.data.alter(**kwargs), 'commit')) |
| |
| def notes(self, ref='refs/notes/commits'): |
| """Get git-notes content for this commit""" |
| return self.repo.notes(self.hsh, ref) |
| |
| def extra_footers(self): |
| """Get any extra footers for this commit as an OrderedDict of |
| {footer: [values]}. |
| |
| Extra footers are stored as git-notes on the refs/notes/extra_footers ref. |
| """ |
| footer_lines = [] |
| raw = self.notes('refs/notes/extra_footers') |
| if raw is not None: |
| _, footer_lines = CommitData.parse_raw_message( |
| [''] + raw.rstrip('\n').splitlines() |
| ) |
| return CommitData.frozen_dict_from_kv_pairs(footer_lines) |