Source code for maggit.gitObjects.tree

# 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 .blob import Blob

_setattr = object.__setattr__

__all__ = ('Tree',)

# Just a helper function to join path together
pjoin  = b'/'.join

class TreeDict(collections.Mapping):
    def __init__(self, repo, raw_mapping):
        self.repo = repo
        self.mapping = raw_mapping

    def __getitem__(self, name):
        mode, sha_or_obj = self.mapping[name]
        if isinstance(sha_or_obj, GitObject):
            return mode, sha_or_obj

        obj = self._create_object(mode, sha_or_obj)
        self.mapping[name] = (mode, obj)
        return mode, obj

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

    def __iter__(self):
        return iter(self.mapping)

    def get_sha(self, name):
        mode, sha_or_obj  = self.mapping[name]
        try:
            return sha_or_obj.sha
        except AttributeError:
            return sha_or_obj

    def _create_object(self, mode, _sha):
        if mode ==  b"40000":
            return Tree(self.repo, _sha)
        else:
            return Blob(self.repo, _sha)

[docs]class Tree(GitObject): """ A blob object. Attributes: entries(unmutable mapping) : This is the entries of the tree. """ __slots__ = ('entries',) def _read(self): _entries = self.repo.db.tree_content(self._sha) _setattr(self, 'entries', TreeDict(self.repo, _entries)) def __getitem__(self, name): """Return the entry corresponding to the name""" return self.entries[name] def is_entry_diff(self, other, path): """Check if path is in both trees and if the two subfiles/subtrees differ Arguments: other(Tree): The other tree objet to compare from path(bytes path) : The path to check Returns: - False if there is no diff - True if the path is in both tree, but content differs - The tree that content the path if the path is in only one tree """ sentries = self.entries oentries = other.entries for p in path.split(b'/'): try: sentries = sentries[p][1] except KeyError: # self doesn't contain the path return other try: oentries = oentries[p][1] except KeyError: # other doesn't contain the path return self if sentries == oentries: # Either entre or parent subtree is equal # Thanks to git hash we know that the final path will # be equal return False # We reach the end of the path and entries are differents return True def gen_entries_diff(self, other, subset=None): """Compare the twe tree and return four sets: - equal : the entries that are in both self and other and are equal is both - diff : the entries that are in both self and other but are not equal - in_self : the entries that are only in self - in_other : the entries that are only in other """ sentries = set(self.entries.mapping.keys()) oentries = set(other.entries.mapping.keys()) if subset is not None: sentries &= subset oentries &= subset in_both = sentries & oentries only_in_s = sentries - in_both only_in_o = oentries - in_both sget_sha = lambda k, m=self.entries.mapping: m[k][1] oget_sha = lambda k, m=other.entries.mapping: m[k][1] equal = set(k for k in in_both if sget_sha(k)==oget_sha(k)) diff = in_both - equal return equal, diff, only_in_s, only_in_o def gen_diff_map(self, other): equal, diff, in_self, in_other = self.gen_entries_diff(other) result = { k:'=' for k in equal} result.update((k,'!') for k in diff) result.update((k,'<') for k in in_self) result.update((k,'>') for k in in_other) return result def gen_full_diff_map(self, other): _, diff, in_self, in_other = self.gen_entries_diff(other) result = {} for d in diff: sentry = self.entries[d][1] oentry = other.entries[d][1] if type(sentry) == Tree and type(oentry) == Tree: sub_diff = sentry.gen_full_diff_map(oentry) result.update((pjoin(d, e),sd) for e, sd in sub_diff.items()) elif type(sentry) == Blob and type(oentry) == Blob: result[d] = '!' for n in in_self: sentry = self.entries[n][1] if type(sentry) == Tree: sub_new = sentry.list_all_files() result.update((pjoin(n, e),'<') for e in sub_new) else: result[n] = '<' for o in in_other: oentry = other.entries[d][1] if type(oentry) == Tree: sub_old = oentry.list_all_files() result.update((pjoin(o, e),'>') for e in sub_old) else: result[o] = '>' return result def list_all_files(self): result = [] for e in self.entries: if type(self.entries[e]) == Tree: result.extend(pjoin(e, i) for i in self.entries[e].list_all_files()) else: result.append(e) return result