[ Web Proxy ]
URL:
Viewing: https://raw.githubusercontent.com/githubdelegate/GitPython/master/git/objects/submodule/base.py [Back]  [Original]

from . import util
from .util import (
    mkhead,
    sm_name,
    sm_section,
    SubmoduleConfigParser,
    find_first_remote_branch
)
from git.objects.util import Traversable
from io import BytesIO                   # need a dict to set bloody .name field
from git.util import (
    Iterable,
    join_path_native,
    to_native_path_linux,
    RemoteProgress,
    rmtree,
    unbare_repo
)

from git.config import (
    SectionConstraint,
    GitConfigParser,
    cp
)
from git.exc import (
    InvalidGitRepositoryError,
    NoSuchPathError,
    RepositoryDirtyError
)
from git.compat import (
    string_types,
    defenc
)

import stat
import git

import os
import logging
import uuid

__all__ = ["Submodule", "UpdateProgress"]


log = logging.getLogger('git.objects.submodule.base')


class UpdateProgress(RemoteProgress):

    """Class providing detailed progress information to the caller who should
    derive from it and implement the ``update(...)`` message"""
    CLONE, FETCH, UPDWKTREE = [1 =v1.8.0: move submodule repository to new name)

        As .gitmodules will be changed, you would need to make a commit afterwards. The changed .gitmodules file
        will already be added to the index

        :return: this submodule instance
        """
        if self.name == new_name:
            return self

        # .git/config
        pw = self.repo.config_writer()
        # As we ourselves didn't write anything about submodules into the parent .git/config, we will not require
        # it to exist, and just ignore missing entries
        if pw.has_section(sm_section(self.name)):
            pw.rename_section(sm_section(self.name), sm_section(new_name))
        # end
        pw.release()

        # .gitmodules
        cw = self.config_writer(write=True).config
        cw.rename_section(sm_section(self.name), sm_section(new_name))
        cw.release()

        self._name = new_name

        # .git/modules
        mod = self.module()
        if mod.has_separate_working_tree():
            destination_module_abspath = self._module_abspath(self.repo, self.path, new_name)
            source_dir = mod.git_dir
            # Let's be sure the submodule name is not so obviously tied to a directory
            if destination_module_abspath.startswith(mod.git_dir):
                tmp_dir = self._module_abspath(self.repo, self.path, str(uuid.uuid4()))
                os.renames(source_dir, tmp_dir)
                source_dir = tmp_dir
            # end handle self-containment
            os.renames(source_dir, destination_module_abspath)
            self._write_git_file_and_module_config(mod.working_tree_dir, destination_module_abspath)
        # end move separate git repository

        return self

    #} END edit interface

    #{ Query Interface

    @unbare_repo
    def module(self):
        """:return: Repo instance initialized from the repository at our submodule path
        :raise InvalidGitRepositoryError: if a repository was not available. This could
            also mean that it was not yet initialized"""
        # late import to workaround circular dependencies
        module_checkout_abspath = self.abspath
        try:
            repo = git.Repo(module_checkout_abspath)
            if repo != self.repo:
                return repo
            # END handle repo uninitialized
        except (InvalidGitRepositoryError, NoSuchPathError):
            raise InvalidGitRepositoryError("No valid repository at %s" % module_checkout_abspath)
        else:
            raise InvalidGitRepositoryError("Repository at %r was not yet checked out" % module_checkout_abspath)
        # END handle exceptions

    def module_exists(self):
        """:return: True if our module exists and is a valid git repository. See module() method"""
        try:
            self.module()
            return True
        except Exception:
            return False
        # END handle exception

    def exists(self):
        """
        :return: True if the submodule exists, False otherwise. Please note that
            a submodule may exist (in the .gitmodules file) even though its module
            doesn't exist on disk"""
        # keep attributes for later, and restore them if we have no valid data
        # this way we do not actually alter the state of the object
        loc = locals()
        for attr in self._cache_attrs:
            try:
                if hasattr(self, attr):
                    loc[attr] = getattr(self, attr)
                # END if we have the attribute cache
            except (cp.NoSectionError, ValueError):
                # on PY3, this can happen apparently ... don't know why this doesn't happen on PY2
                pass
        # END for each attr
        self._clear_cache()

        try:
            try:
                self.path
                return True
            except Exception:
                return False
            # END handle exceptions
        finally:
            for attr in self._cache_attrs:
                if attr in loc:
                    setattr(self, attr, loc[attr])
                # END if we have a cache
            # END reapply each attribute
        # END handle object state consistency

    @property
    def branch(self):
        """:return: The branch instance that we are to checkout
        :raise InvalidGitRepositoryError: if our module is not yet checked out"""
        return mkhead(self.module(), self._branch_path)

    @property
    def branch_path(self):
        """
        :return: full (relative) path as string to the branch we would checkout
            from the remote and track"""
        return self._branch_path

    @property
    def branch_name(self):
        """:return: the name of the branch, which is the shortest possible branch name"""
        # use an instance method, for this we create a temporary Head instance
        # which uses a repository that is available at least ( it makes no difference )
        return git.Head(self.repo, self._branch_path).name

    @property
    def url(self):
        """:return: The url to the repository which our module-repository refers to"""
        return self._url

    @property
    def parent_commit(self):
        """:return: Commit instance with the tree containing the .gitmodules file
        :note: will always point to the current head's commit if it was not set explicitly"""
        if self._parent_commit is None:
            return self.repo.commit()
        return self._parent_commit

    @property
    def name(self):
        """:return: The name of this submodule. It is used to identify it within the
            .gitmodules file.
        :note: by default, the name is the path at which to find the submodule, but
            in git-python it should be a unique identifier similar to the identifiers
            used for remotes, which allows to change the path of the submodule
            easily
        """
        return self._name

    def config_reader(self):
        """
        :return: ConfigReader instance which allows you to qurey the configuration values
            of this submodule, as provided by the .gitmodules file
        :note: The config reader will actually read the data directly from the repository
            and thus does not need nor care about your working tree.
        :note: Should be cached by the caller and only kept as long as needed
        :raise IOError: If the .gitmodules file/blob could not be read"""
        return self._config_parser_constrained(read_only=True)

    def children(self):
        """
        :return: IterableList(Submodule, ...) an iterable list of submodules instances
            which are children of this submodule or 0 if the submodule is not checked out"""
        return self._get_intermediate_items(self)

    #} END query interface

    #{ Iterable Interface

    @classmethod
    def iter_items(cls, repo, parent_commit='HEAD'):
        """:return: iterator yielding Submodule instances available in the given repository"""
        pc = repo.commit(parent_commit)         # parent commit instance
        try:
            parser = cls._config_parser(repo, pc, read_only=True)
        except IOError:
            raise StopIteration
        # END handle empty iterator

        rt = pc.tree                                # root tree

        for sms in parser.sections():
            n = sm_name(sms)
            p = parser.get_value(sms, 'path')
            u = parser.get_value(sms, 'url')
            b = cls.k_head_default
            if parser.has_option(sms, cls.k_head_option):
                b = str(parser.get_value(sms, cls.k_head_option))
            # END handle optional information

            # get the binsha
            index = repo.index
            try:
                sm = rt[p]
            except KeyError:
                # try the index, maybe it was just added
                try:
                    entry = index.entries[index.entry_key(p, 0)]
                    sm = Submodule(repo, entry.binsha, entry.mode, entry.path)
                except KeyError:
                    raise InvalidGitRepositoryError(
                        "Gitmodule path %r did not exist in revision of parent commit %s" % (p, parent_commit))
                # END handle keyerror
            # END handle critical error

            # fill in remaining info - saves time as it doesn't have to be parsed again
            sm._name = n
            if pc != repo.commit():
                sm._parent_commit = pc
            # end set only if not most recent !
            sm._branch_path = git.Head.to_full_path(b)
            sm._url = u

            yield sm
        # END for each section

    #} END iterable interface

Web Proxy Viewer  |  New URL  |  Original Page