# 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.
from . import gitObjects
from . import refs
from .sha import Sha
import os
from maggit import db
[docs]class Repo(db.Repo):
"""This is the central piece of a git repository.
"""
@property
def HEAD(self):
"""The current checkouted branch"""
return refs.Branche(self, self.refs.HEAD)
@property
def branches(self):
"""A dict of branches in the repo"""
return {k:refs.Branche(self, k) for k in self.refs.branches}
@property
def tags(self):
"""A dict of tags in the repo"""
return {k:refs.Tag(self, k) for k in self.refs.tags}
[docs] def get_object(self, sha):
"""Get a git object for the sha.
Arguments:
sha(:class:`~maggit.Sha`) : The sha of the object.
Returns:
A :class:`~maggit.gitObjects.GitObject` object for this sha.
Raises:
:Exception: If sha doesn't name a object.
"""
sha = Sha.cast(sha)
type_ = gitObjects.byte_to_class(self.db.object_type(sha))
return type_(self, sha)
[docs] def get_full_sha(self, prefix):
"""Return the full Sha of the prefix
Arguments:
prefix(bytes): The beginning of a sha.
Returns:
The :class:`~maggit.Sha` corresponding.
Exemples:
>>> repo.get_full_sha(b'bf09f0a9')
<Sha b'bf09f0a9...'>
Raises:
:Exception: If number of object corresponding to prefix is not equal to one.
"""
sha = self.db.get_full_sha(prefix)
return Sha(sha)
[docs]class Entry:
"""A Entry represent a entry (file or directory) at a specific time.
We can somehow see a repository as a complex 2 dimentionnal array.
Commits (and so the history) are rows.
Files (and Trees) are columns.
In this situations, Entry are the cells of this array.
Arguments:
commit(:class:`maggit.Commit`) : The commit associated to the Entry
path(bytes path) : The path of the file to look at. The path must be a
bytes where directory are separated by b'/'.
Raise:
KeyError if the path is not existing.
"""
def __init__(self, commit, path):
self.commit = commit
self.path = path
current = commit.tree
for part in path.split(b'/'):
mode, current = current[part]
self.mode = mode
self.gitObject = current
@property
def repo(self):
return self.commit.repo
[docs] def is_blob(self):
"""Return True if the entry is corresponding to a blob object"""
return type(self.gitObject) == gitObjects.Blob
@property
def parents(self):
"""The previous versions of the files.
Previous versions can be equal to the current one if the current commit
introduce no change on this file.
The length of the parents will most of the time be 1 but may be greater
in case of merge.
"""
return (Entry(p, self.path) for p in self.commit.parents)
[docs] def get_first_appearance(self):
"""Return the commit who firstly introduce the current version of the change.
Returns:
A :class:`maggit.Commit`
Exemples:
>>> first_appearance_commit = this_entry.get_first_appearance()
>>> # first_appearance_commit is the first one, so previous version differs
>>> parent = first_appearance_commit.parents[0]
>>> assert Entry(first_appearance_commit, this_entry.path).gitObject != Entry(parent, this_entry.path).gitObject
>>> # from this_commit to first_appearance_commit, there is no change
>>> current = this_entry.commit
>>> while current != first_appearance_commit:
... assert Entry(current, this_entry.path).gitObject == this_entry.gitObject
... current = current.parents[0]
"""
current = self.commit
while True:
try:
parent = current.parents[0]
except IndexError:
# No parent
return current
c_tree = current.tree
p_tree = parent.tree
if c_tree.is_entry_diff(p_tree, self.path):
# There is a diff, current is the commit who introduce
# this versions.
return current
# No diff for this path
# Continue check in the history
current = parent