summaryrefslogtreecommitdiff
path: root/git/repo/base.py
diff options
context:
space:
mode:
Diffstat (limited to 'git/repo/base.py')
-rw-r--r--git/repo/base.py117
1 files changed, 70 insertions, 47 deletions
diff --git a/git/repo/base.py b/git/repo/base.py
index 99e87643..6cc56031 100644
--- a/git/repo/base.py
+++ b/git/repo/base.py
@@ -3,12 +3,13 @@
#
# This module is part of GitPython and is released under
# the BSD License: http://www.opensource.org/licenses/bsd-license.php
-
import logging
import os
import re
import warnings
+from gitdb.exc import BadObject
+
from git.cmd import (
Git,
handle_process_output
@@ -25,7 +26,7 @@ from git.index import IndexFile
from git.objects import Submodule, RootModule, Commit
from git.refs import HEAD, Head, Reference, TagReference
from git.remote import Remote, add_progress, to_progress_instance
-from git.util import Actor, finalize_process, decygpath, hex_to_bin, expand_path
+from git.util import Actor, finalize_process, decygpath, hex_to_bin, expand_path, remove_password_if_present
import os.path as osp
from .fun import rev_parse, is_git_dir, find_submodule_git_dir, touch, find_worktree_git_dir
@@ -34,20 +35,10 @@ import gitdb
# typing ------------------------------------------------------
-from git.types import TBD, PathLike
-from typing_extensions import Literal
-from typing import (Any,
- BinaryIO,
- Callable,
- Dict,
- Iterator,
- List,
- Mapping,
- Optional,
- TextIO,
- Tuple,
- Type,
- Union,
+from git.types import TBD, PathLike, Lit_config_levels
+from typing import (Any, BinaryIO, Callable, Dict,
+ Iterator, List, Mapping, Optional, Sequence,
+ TextIO, Tuple, Type, Union,
NamedTuple, cast, TYPE_CHECKING)
if TYPE_CHECKING: # only needed for types
@@ -55,7 +46,6 @@ if TYPE_CHECKING: # only needed for types
from git.refs.symbolic import SymbolicReference
from git.objects import TagObject, Blob, Tree # NOQA: F401
-Lit_config_levels = Literal['system', 'global', 'user', 'repository']
# -----------------------------------------------------------
@@ -91,8 +81,8 @@ class Repo(object):
git = cast('Git', None) # Must exist, or __del__ will fail in case we raise on `__init__()`
working_dir = None # type: Optional[PathLike]
_working_tree_dir = None # type: Optional[PathLike]
- git_dir = None # type: Optional[PathLike]
- _common_dir = None # type: Optional[PathLike]
+ git_dir = "" # type: PathLike
+ _common_dir = "" # type: PathLike
# precompiled regex
re_whitespace = re.compile(r'\s+')
@@ -219,7 +209,7 @@ class Repo(object):
common_dir = open(osp.join(self.git_dir, 'commondir'), 'rt').readlines()[0].strip()
self._common_dir = osp.join(self.git_dir, common_dir)
except OSError:
- self._common_dir = None
+ self._common_dir = ""
# adjust the wd in case we are actually bare - we didn't know that
# in the first place
@@ -231,10 +221,11 @@ class Repo(object):
self.git = self.GitCommandWrapperType(self.working_dir)
# special handling, in special times
- args = [osp.join(self.common_dir, 'objects')]
+ rootpath = osp.join(self.common_dir, 'objects')
if issubclass(odbt, GitCmdObjectDB):
- args.append(self.git)
- self.odb = odbt(*args)
+ self.odb = odbt(rootpath, self.git)
+ else:
+ self.odb = odbt(rootpath)
def __enter__(self) -> 'Repo':
return self
@@ -276,13 +267,14 @@ class Repo(object):
# Description property
def _get_description(self) -> str:
- filename = osp.join(self.git_dir, 'description') if self.git_dir else ""
+ if self.git_dir:
+ filename = osp.join(self.git_dir, 'description')
with open(filename, 'rb') as fp:
return fp.read().rstrip().decode(defenc)
def _set_description(self, descr: str) -> None:
-
- filename = osp.join(self.git_dir, 'description') if self.git_dir else ""
+ if self.git_dir:
+ filename = osp.join(self.git_dir, 'description')
with open(filename, 'wb') as fp:
fp.write((descr + '\n').encode(defenc))
@@ -411,7 +403,17 @@ class Repo(object):
def tag(self, path: PathLike) -> TagReference:
""":return: TagReference Object, reference pointing to a Commit or Tag
:param path: path to the tag reference, i.e. 0.1.5 or tags/0.1.5 """
- return TagReference(self, path)
+ full_path = self._to_full_tag_path(path)
+ return TagReference(self, full_path)
+
+ @staticmethod
+ def _to_full_tag_path(path):
+ if path.startswith(TagReference._common_path_default + '/'):
+ return path
+ if path.startswith(TagReference._common_default + '/'):
+ return Reference._common_path_default + '/' + path
+ else:
+ return TagReference._common_path_default + '/' + path
def create_head(self, path: PathLike, commit: str = 'HEAD',
force: bool = False, logmsg: Optional[str] = None
@@ -422,7 +424,7 @@ class Repo(object):
:return: newly created Head Reference"""
return Head.create(self, path, commit, force, logmsg)
- def delete_head(self, *heads: HEAD, **kwargs: Any) -> None:
+ def delete_head(self, *heads: 'SymbolicReference', **kwargs: Any) -> None:
"""Delete the given heads
:param kwargs: Additional keyword arguments to be passed to git-branch"""
@@ -441,7 +443,7 @@ class Repo(object):
"""Delete the given tag references"""
return TagReference.delete(self, *tags)
- def create_remote(self, name: str, url: PathLike, **kwargs: Any) -> Remote:
+ def create_remote(self, name: str, url: str, **kwargs: Any) -> Remote:
"""Create a new remote.
For more information, please see the documentation of the Remote.create
@@ -450,7 +452,7 @@ class Repo(object):
:return: Remote reference"""
return Remote.create(self, name, url, **kwargs)
- def delete_remote(self, remote: 'Remote') -> Type['Remote']:
+ def delete_remote(self, remote: 'Remote') -> str:
"""Delete the given remote."""
return Remote.remove(self, remote)
@@ -468,12 +470,11 @@ class Repo(object):
elif config_level == "global":
return osp.normpath(osp.expanduser("~/.gitconfig"))
elif config_level == "repository":
- if self._common_dir:
- return osp.normpath(osp.join(self._common_dir, "config"))
- elif self.git_dir:
- return osp.normpath(osp.join(self.git_dir, "config"))
- else:
+ repo_dir = self._common_dir or self.git_dir
+ if not repo_dir:
raise NotADirectoryError
+ else:
+ return osp.normpath(osp.join(repo_dir, "config"))
raise ValueError("Invalid configuration level: %r" % config_level)
@@ -514,7 +515,7 @@ class Repo(object):
return GitConfigParser(self._get_config_path(config_level), read_only=False, repo=self)
def commit(self, rev: Optional[TBD] = None
- ) -> Union['SymbolicReference', Commit, 'TagObject', 'Blob', 'Tree', None]:
+ ) -> Union['SymbolicReference', Commit, 'TagObject', 'Blob', 'Tree']:
"""The Commit object for the specified revision
:param rev: revision specifier, see git-rev-parse for viable options.
@@ -546,7 +547,7 @@ class Repo(object):
return self.head.commit.tree
return self.rev_parse(str(rev) + "^{tree}")
- def iter_commits(self, rev: Optional[TBD] = None, paths: Union[PathLike, List[PathLike]] = '',
+ def iter_commits(self, rev: Optional[TBD] = None, paths: Union[PathLike, Sequence[PathLike]] = '',
**kwargs: Any) -> Iterator[Commit]:
"""A list of Commit objects representing the history of a given ref/commit
@@ -618,12 +619,31 @@ class Repo(object):
raise
return True
+ def is_valid_object(self, sha: str, object_type: str = None) -> bool:
+ try:
+ complete_sha = self.odb.partial_to_complete_sha_hex(sha)
+ object_info = self.odb.info(complete_sha)
+ if object_type:
+ if object_info.type == object_type.encode():
+ return True
+ else:
+ log.debug("Commit hash points to an object of type '%s'. Requested were objects of type '%s'",
+ object_info.type.decode(), object_type)
+ return False
+ else:
+ return True
+ except BadObject:
+ log.debug("Commit hash is invalid.")
+ return False
+
def _get_daemon_export(self) -> bool:
- filename = osp.join(self.git_dir, self.DAEMON_EXPORT_FILE) if self.git_dir else ""
+ if self.git_dir:
+ filename = osp.join(self.git_dir, self.DAEMON_EXPORT_FILE)
return osp.exists(filename)
def _set_daemon_export(self, value: object) -> None:
- filename = osp.join(self.git_dir, self.DAEMON_EXPORT_FILE) if self.git_dir else ""
+ if self.git_dir:
+ filename = osp.join(self.git_dir, self.DAEMON_EXPORT_FILE)
fileexists = osp.exists(filename)
if value and not fileexists:
touch(filename)
@@ -639,7 +659,8 @@ class Repo(object):
"""The list of alternates for this repo from which objects can be retrieved
:return: list of strings being pathnames of alternates"""
- alternates_path = osp.join(self.git_dir, 'objects', 'info', 'alternates') if self.git_dir else ""
+ if self.git_dir:
+ alternates_path = osp.join(self.git_dir, 'objects', 'info', 'alternates')
if osp.exists(alternates_path):
with open(alternates_path, 'rb') as f:
@@ -993,8 +1014,6 @@ class Repo(object):
def _clone(cls, git: 'Git', url: PathLike, path: PathLike, odb_default_type: Type[GitCmdObjectDB],
progress: Optional[Callable], multi_options: Optional[List[str]] = None, **kwargs: Any
) -> 'Repo':
- progress_checked = to_progress_instance(progress)
-
odbt = kwargs.pop('odbt', odb_default_type)
# when pathlib.Path or other classbased path is passed
@@ -1017,13 +1036,16 @@ class Repo(object):
if multi_options:
multi = ' '.join(multi_options).split(' ')
proc = git.clone(multi, Git.polish_url(url), clone_path, with_extended_output=True, as_process=True,
- v=True, universal_newlines=True, **add_progress(kwargs, git, progress_checked))
- if progress_checked:
- handle_process_output(proc, None, progress_checked.new_message_handler(),
+ v=True, universal_newlines=True, **add_progress(kwargs, git, progress))
+ if progress:
+ handle_process_output(proc, None, to_progress_instance(progress).new_message_handler(),
finalize_process, decode_streams=False)
else:
(stdout, stderr) = proc.communicate()
- log.debug("Cmd(%s)'s unused stdout: %s", getattr(proc, 'args', ''), stdout)
+ cmdline = getattr(proc, 'args', '')
+ cmdline = remove_password_if_present(cmdline)
+
+ log.debug("Cmd(%s)'s unused stdout: %s", cmdline, stdout)
finalize_process(proc, stderr=stderr)
# our git command could have a different working dir than our actual
@@ -1142,7 +1164,8 @@ class Repo(object):
None if we are not currently rebasing.
"""
- rebase_head_file = osp.join(self.git_dir, "REBASE_HEAD") if self.git_dir else ""
+ if self.git_dir:
+ rebase_head_file = osp.join(self.git_dir, "REBASE_HEAD")
if not osp.isfile(rebase_head_file):
return None
return self.commit(open(rebase_head_file, "rt").readline().strip())