Source code for maggit.gitObjects.commit

# This file is part of maggit.
#
# Copyright 2015 Matthieu Gautier <dev@mgautier.fr>
#
# Pit is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Pit is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# Additional permission under the GNU Affero GPL version 3 section 7:
#
# If you modify this Program, or any covered work, by linking or
# combining it with other code, such other code is not for that reason
# alone subject to any of the requirements of the GNU Affero GPL
# version 3.
#
# You should have received a copy of the GNU Affero General Public License
# along with maggit.  If not, see http://www.gnu.org/licenses
#
# In summary:
# - You can use this program for no cost.
# - You can use this program for both personal and commercial reasons.
# - You do not have to share your own program's code which uses this program.
# - You have to share modifications (e.g bug-fixes, improvements) you've made to this program.

import collections
from .gitObject import GitObject
from .tree import Tree
from .utils import parse_signature

_setattr = object.__setattr__

__all__ = ('Commit',)

class CommitPseudoList(collections.Sequence):
    def __init__(self, repo, sequence):
        self.repo = repo
        self.sequence = sequence
        self._refs = dict()

    def __getitem__(self, index):
        sha  = self.sequence[index]
        try:
            return self._refs[sha]
        except KeyError:
            obj = Commit(self.repo, sha)
            self._refs[sha] = obj
            return obj

    def __len__(self):
        return len(self.sequence)

    def get_sha(self, index):
        return self.sequences[index]

[docs]class Commit(GitObject): """ A commit object. Attributes: tree(:class:`~maggit.Tree`) : The tree object associated with the commit. parents(tuple) : The parents of the commits. Most of the time, there will only one parent. In case of branch merge, there will be more than one parent. author(:class:`~maggit.Person`) : The author of the commit. author_date(timedate) : When the commit was created. committer(:class:`~maggit.Person`) : The committer of the commit. committer_date(timedate) : When the commit was committed. first_line(str) : The first line of the commit message. message(str) : The full commit message (including the first line). """ __slots__ = ('tree', '_tree', 'parents', 'author', 'author_date', '_author', 'committer', 'committer_date', '_committer', 'first_line', 'message', '_messageLines') def _compute_tree(self): return Tree(self.repo, self._tree) def _compute_first_line(self): return self._messageLines[0].decode('utf8') def _compute_message(self): return b'\n'.join(self._messageLines).decode('utf8') def _compute_author(self): author, date = parse_signature(self._author) _setattr(self, 'author_date', date) return author def _compute_author_date(self): author, date = parse_signature(self._author) _setattr(self, 'author', author) return date def _compute_committer(self): committer, date = parse_signature(self._committer) _setattr(self, 'committer_date', date) return committer def _compute_committer_date(self): committer, date = parse_signature(self._committer) _setattr(self, 'committer', committer) return date def _read(self): (treesha, parents, messageLines, author, committer) = self.repo.db.commit_content(self._sha) _setattr(self, '_tree', treesha) _setattr(self, 'parents', CommitPseudoList(self.repo, parents)) _setattr(self, '_messageLines', messageLines) _setattr(self, '_author', author) _setattr(self, '_committer', committer) def get_first_appearances(self, root=None, depthLimit=None): """Return the first appearances for all entry in root. This is mostly equivalent to `{path:Entry(commit, path).get_first_appearance() for path in commit.tree.entries}` (if root is None). But a way more performant as diving into history is made once. Arguments: root(bytes path): In wich subdirectory we must look. depthLimit(int) : The commit limit number we go in history If depthLimit is specified, and for a entry the first appearance is older than depthLimit, the entry will be present in the dictionnary with a None value. Returns: A dict of (bytes, :class:`maggit.Commit`). """ entries_left = set(self.tree.entries.keys()) root = root.split(b'/') if root else () result = {} current = self c_trees = [current.tree] for r in root: c_trees.append(c_trees[-1][r]) depth = 0 while entries_left: depth = depth + 1 if depthLimit and depth>depthLimit: result.update((e,None) for e in entries_left) break try: parent = current.parents[0] except IndexError: # No parent result.update((e,current) for e in entries_left) break if parent._tree == c_trees[0]._sha: # Tree is equal, advance in history current = parent # No need to change c_trees to p_trees, they are equals continue p_trees = [parent.tree] need_continue = False for i, r in enumerate(root, 1): p_trees.append(p_trees[-1][r]) if p_trees[-1] is c_trees[i]: current = parent c_trees[:i] = p_trees need_continue = True break if need_continue: continue # Ok, now we've got two trees where some entries differ # Do the job _, diff, in_current, _ = c_trees[-1].gen_entries_diff(p_trees[-1], entries_left) diff |= in_current result.update((e,current) for e in diff) entries_left -= diff current = parent c_trees = p_trees return result