summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDominic <yobmod@gmail.com>2021-07-31 15:05:26 +0100
committerGitHub <noreply@github.com>2021-07-31 15:05:26 +0100
commitb8d932b9abd03c4bcadb85e8c3c857ab65c28075 (patch)
tree8374334f4c3e6f03c5197e49bbc3f346949d25d8
parent1d324337c799f8b40a73eaca12cad1ab10055daf (diff)
downloadgitpython-b8d932b9abd03c4bcadb85e8c3c857ab65c28075.tar.gz
Update symbolic.py
-rw-r--r--git/refs/symbolic.py742
1 files changed, 43 insertions, 699 deletions
diff --git a/git/refs/symbolic.py b/git/refs/symbolic.py
index 1bd215d8..9b74d87f 100644
--- a/git/refs/symbolic.py
+++ b/git/refs/symbolic.py
@@ -1,721 +1,65 @@
import os
-from git.compat import defenc
-from git.objects import Object
-from git.objects.commit import Commit
-from git.util import (
- join_path,
- join_path_native,
- to_native_path_linux,
- assure_directory_exists,
- hex_to_bin,
- LockedFD
-)
-from gitdb.exc import (
- BadObject,
- BadName
-)
-from .log import RefLog, RefLogEntry
+from git.util import join_path
-# typing ------------------------------------------------------------------
-
-from typing import Any, Iterator, List, Match, Optional, Tuple, Type, TypeVar, Union, TYPE_CHECKING, cast # NOQA
-from git.types import Commit_ish, PathLike, TBD, Literal # NOQA
-
-if TYPE_CHECKING:
- from git.repo import Repo
- from git.refs import Reference, Head, TagReference, RemoteReference
- from git.config import GitConfigParser
- from git.objects.commit import Actor
-
-T_References = TypeVar('T_References', bound='SymbolicReference')
-
-# ------------------------------------------------------------------------------
-
-
-__all__ = ["SymbolicReference"]
-
-
-def _git_dir(repo: 'Repo', path: PathLike) -> PathLike:
- """ Find the git dir that's appropriate for the path"""
- name = f"{path}"
- if name in ['HEAD', 'ORIG_HEAD', 'FETCH_HEAD', 'index', 'logs']:
- return repo.git_dir
- return repo.common_dir
-
-
-class SymbolicReference(object):
-
- """Represents a special case of a reference such that this reference is symbolic.
- It does not point to a specific commit, but to another Head, which itself
- specifies a commit.
-
- A typical example for a symbolic reference is HEAD."""
- __slots__ = ("repo", "path")
- _resolve_ref_on_create = False
- _points_to_commits_only = True
- _common_path_default = ""
- _remote_common_path_default = "refs/remotes"
- _id_attribute_ = "name"
+from .head import Head
- def __init__(self, repo: 'Repo', path: PathLike, check_path: bool = False) -> None:
- self.repo = repo
- self.path = str(path)
- def __str__(self) -> str:
- return self.path
+__all__ = ["RemoteReference"]
- def __repr__(self) -> str:
- return '<git.%s "%s">' % (self.__class__.__name__, self.path)
-
- def __eq__(self, other) -> bool:
- if hasattr(other, 'path'):
- return self.path == other.path
- return False
-
- def __ne__(self, other) -> bool:
- return not (self == other)
-
- def __hash__(self):
- return hash(self.path)
-
- @property
- def name(self) -> str:
- """
- :return:
- In case of symbolic references, the shortest assumable name
- is the path itself."""
- return str(self.path)
-
- @property
- def abspath(self) -> PathLike:
- return join_path_native(_git_dir(self.repo, self.path), self.path)
-
- @classmethod
- def _get_packed_refs_path(cls, repo: 'Repo') -> str:
- return os.path.join(repo.common_dir, 'packed-refs')
-
- @classmethod
- def _iter_packed_refs(cls, repo: 'Repo') -> Iterator[Tuple[str, str]]:
- """Returns an iterator yielding pairs of sha1/path pairs for the corresponding refs.
- :note: The packed refs file will be kept open as long as we iterate"""
- try:
- with open(cls._get_packed_refs_path(repo), 'rt', encoding='UTF-8') as fp:
- for line in fp:
- line = line.strip()
- if not line:
- continue
- if line.startswith('#'):
- # "# pack-refs with: peeled fully-peeled sorted"
- # the git source code shows "peeled",
- # "fully-peeled" and "sorted" as the keywords
- # that can go on this line, as per comments in git file
- # refs/packed-backend.c
- # I looked at master on 2017-10-11,
- # commit 111ef79afe, after tag v2.15.0-rc1
- # from repo https://github.com/git/git.git
- if line.startswith('# pack-refs with:') and 'peeled' not in line:
- raise TypeError("PackingType of packed-Refs not understood: %r" % line)
- # END abort if we do not understand the packing scheme
- continue
- # END parse comment
+# typing ------------------------------------------------------------------
- # skip dereferenced tag object entries - previous line was actual
- # tag reference for it
- if line[0] == '^':
- continue
+from typing import Any, Iterator, NoReturn, Union, TYPE_CHECKING
+from git.types import PathLike
- yield cast(Tuple[str, str], tuple(line.split(' ', 1)))
- # END for each line
- except OSError:
- return None
- # END no packed-refs file handling
- # NOTE: Had try-finally block around here to close the fp,
- # but some python version wouldn't allow yields within that.
- # I believe files are closing themselves on destruction, so it is
- # alright.
- @classmethod
- def dereference_recursive(cls, repo: 'Repo', ref_path: PathLike) -> str:
- """
- :return: hexsha stored in the reference at the given ref_path, recursively dereferencing all
- intermediate references as required
- :param repo: the repository containing the reference at ref_path"""
- while True:
- hexsha, _ref_path_out = cls._get_ref_info(repo, ref_path)
- if hexsha is not None:
- return hexsha
- # END recursive dereferencing
+if TYPE_CHECKING:
+ from git.repo import Repo
+ from git import Remote
- @classmethod
- def _get_ref_info_helper(cls, repo: 'Repo', ref_path: PathLike) -> Union[Tuple[str, None], Tuple[None, PathLike]]:
- """Return: (str(sha), str(target_ref_path)) if available, the sha the file at
- rela_path points to, or None. target_ref_path is the reference we
- point to, or None"""
- tokens: Union[List[str], Tuple[str, str], None] = None
- repodir = _git_dir(repo, ref_path)
- try:
- with open(os.path.join(repodir, ref_path), 'rt', encoding='UTF-8') as fp:
- value = fp.read().rstrip()
- # Don't only split on spaces, but on whitespace, which allows to parse lines like
- # 60b64ef992065e2600bfef6187a97f92398a9144 branch 'master' of git-server:/path/to/repo
- tokens = value.split()
- assert(len(tokens) != 0)
- except OSError:
- # Probably we are just packed, find our entry in the packed refs file
- # NOTE: We are not a symbolic ref if we are in a packed file, as these
- # are excluded explicitly
- for sha, path in cls._iter_packed_refs(repo):
- if path != ref_path:
- continue
- # sha will be used
- tokens = sha, path
- break
- # END for each packed ref
- # END handle packed refs
- if tokens is None:
- raise ValueError("Reference at %r does not exist" % ref_path)
+# ------------------------------------------------------------------------------
- # is it a reference ?
- if tokens[0] == 'ref:':
- return (None, tokens[1])
- # its a commit
- if repo.re_hexsha_only.match(tokens[0]):
- return (tokens[0], None)
+class RemoteReference(Head):
- raise ValueError("Failed to parse reference information from %r" % ref_path)
+ """Represents a reference pointing to a remote head."""
+ _common_path_default = Head._remote_common_path_default
@classmethod
- def _get_ref_info(cls, repo: 'Repo', ref_path: PathLike
- ) -> Union[Tuple[str, None], Tuple[None, PathLike]]:
- """Return: (str(sha), str(target_ref_path)) if available, the sha the file at
- rela_path points to, or None. target_ref_path is the reference we
- point to, or None"""
- return cls._get_ref_info_helper(repo, ref_path)
-
- def _get_object(self) -> Commit_ish:
- """
- :return:
- The object our ref currently refers to. Refs can be cached, they will
- always point to the actual object as it gets re-created on each query"""
- # have to be dynamic here as we may be a tag which can point to anything
- # Our path will be resolved to the hexsha which will be used accordingly
- return Object.new_from_sha(self.repo, hex_to_bin(self.dereference_recursive(self.repo, self.path)))
-
- def _get_commit(self) -> 'Commit':
- """
- :return:
- Commit object we point to, works for detached and non-detached
- SymbolicReferences. The symbolic reference will be dereferenced recursively."""
- obj = self._get_object()
- if obj.type == 'tag':
- obj = obj.object
- # END dereference tag
-
- if obj.type != Commit.type:
- raise TypeError("Symbolic Reference pointed to object %r, commit was required" % obj)
- # END handle type
- return obj
-
- def set_commit(self, commit: Union[Commit, 'SymbolicReference', str],
- logmsg: Union[str, None] = None) -> None:
- """As set_object, but restricts the type of object to be a Commit
-
- :raise ValueError: If commit is not a Commit object or doesn't point to
- a commit
- :return: self"""
- # check the type - assume the best if it is a base-string
- invalid_type = False
- if isinstance(commit, Object):
- invalid_type = commit.type != Commit.type
- commit = cast('Commit', commit)
- elif isinstance(commit, SymbolicReference):
- invalid_type = commit.object.type != Commit.type
- else:
- try:
- commit = self.repo.rev_parse(commit)
- invalid_type = commit.type != Commit.type
- except (BadObject, BadName) as e:
- raise ValueError("Invalid object: %s" % commit) from e
- # END handle exception
- # END verify type
-
- if invalid_type:
- raise ValueError("Need commit, got %r" % commit)
- # END handle raise
-
- # we leave strings to the rev-parse method below
- self.set_object(commit, logmsg)
-
- # return self
- return None
-
- def set_object(self, object: Union[Commit_ish, 'SymbolicReference'],
- logmsg: Union[str, None] = None
- ) -> 'SymbolicReference': # @ReservedAssignment
- """Set the object we point to, possibly dereference our symbolic reference first.
- If the reference does not exist, it will be created
-
- :param object: a refspec, a SymbolicReference or an Object instance. SymbolicReferences
- will be dereferenced beforehand to obtain the object they point to
- :param logmsg: If not None, the message will be used in the reflog entry to be
- written. Otherwise the reflog is not altered
- :note: plain SymbolicReferences may not actually point to objects by convention
- :return: self"""
- if isinstance(object, SymbolicReference):
- object = object.object # @ReservedAssignment
- # END resolve references
-
- is_detached = True
- try:
- is_detached = self.is_detached
- except ValueError:
- pass
- # END handle non-existing ones
-
- if is_detached:
- return self.set_reference(object, logmsg)
-
- # set the commit on our reference
- return self._get_reference().set_object(object, logmsg)
-
- commit = cast('Commit', property(_get_commit, set_commit, doc="Query or set commits directly"))
- object = property(_get_object, set_object, doc="Return the object our ref currently refers to") # type: ignore
- # reference = property(_get_reference, set_reference, doc="Return the object our ref currently refers to")
-
- def _get_reference(self
- ) -> Union['Head', 'RemoteReference', 'TagReference', 'Reference']:
- """:return: Reference Object we point to
- :raise TypeError: If this symbolic reference is detached, hence it doesn't point
- to a reference, but to a commit"""
- sha, target_ref_path = self._get_ref_info(self.repo, self.path)
- if target_ref_path is None:
- raise TypeError("%s is a detached symbolic reference as it points to %r" % (self, sha))
- return self.from_path(self.repo, target_ref_path)
-
- def set_reference(self, ref: Union[str, Commit_ish, 'SymbolicReference'], logmsg: Union[str, None] = None
- ) -> 'SymbolicReference':
- """Set ourselves to the given ref. It will stay a symbol if the ref is a Reference.
- Otherwise an Object, given as Object instance or refspec, is assumed and if valid,
- will be set which effectively detaches the refererence if it was a purely
- symbolic one.
-
- :param ref: SymbolicReference instance, Object instance or refspec string
- Only if the ref is a SymbolicRef instance, we will point to it. Everything
- else is dereferenced to obtain the actual object.
- :param logmsg: If set to a string, the message will be used in the reflog.
- Otherwise, a reflog entry is not written for the changed reference.
- The previous commit of the entry will be the commit we point to now.
-
- See also: log_append()
-
- :return: self
- :note: This symbolic reference will not be dereferenced. For that, see
- ``set_object(...)``"""
- write_value = None
- obj = None
- if isinstance(ref, SymbolicReference):
- write_value = "ref: %s" % ref.path
- elif isinstance(ref, Object):
- obj = ref
- write_value = ref.hexsha
- elif isinstance(ref, str):
- try:
- obj = self.repo.rev_parse(ref + "^{}") # optionally deref tags
- write_value = obj.hexsha
- except (BadObject, BadName) as e:
- raise ValueError("Could not extract object from %s" % ref) from e
- # END end try string
- else:
- raise ValueError("Unrecognized Value: %r" % ref)
- # END try commit attribute
-
- # typecheck
- if obj is not None and self._points_to_commits_only and obj.type != Commit.type:
- raise TypeError("Require commit, got %r" % obj)
- # END verify type
-
- oldbinsha: bytes = b''
- if logmsg is not None:
- try:
- oldbinsha = self.commit.binsha
- except ValueError:
- oldbinsha = Commit.NULL_BIN_SHA
- # END handle non-existing
- # END retrieve old hexsha
-
- fpath = self.abspath
- assure_directory_exists(fpath, is_file=True)
-
- lfd = LockedFD(fpath)
- fd = lfd.open(write=True, stream=True)
- ok = True
- try:
- fd.write(write_value.encode('ascii') + b'\n')
- lfd.commit()
- ok = True
- finally:
- if not ok:
- lfd.rollback()
- # Adjust the reflog
- if logmsg is not None:
- self.log_append(oldbinsha, logmsg)
-
- return self
-
- reference: Union['Head', 'RemoteReference', 'TagReference', 'Reference']
- reference = property(_get_reference, set_reference, doc="Reference Object we point to") # type: ignore
- ref = reference
- # @ property
- # def reference(self) -> Union['Head', 'RemoteReference', 'TagReference', 'Reference']:
- # return self._get_reference()
-
- # @ reference.setter
- # def reference(self, ref: Union[str, Commit_ish, 'SymbolicReference'], logmsg: Union[str, None] = None
- # ) -> 'SymbolicReference':
- # return self.set_reference(ref=ref, logmsg=logmsg)
-
- def is_valid(self) -> bool:
- """
- :return:
- True if the reference is valid, hence it can be read and points to
- a valid object or reference."""
- try:
- self.object
- except (OSError, ValueError):
- return False
- else:
- return True
-
- @ property
- def is_detached(self):
- """
- :return:
- True if we are a detached reference, hence we point to a specific commit
- instead to another reference"""
- try:
- self.ref
- return False
- except TypeError:
- return True
-
- def log(self) -> 'RefLog':
- """
- :return: RefLog for this reference. Its last entry reflects the latest change
- applied to this reference
-
- .. note:: As the log is parsed every time, its recommended to cache it for use
- instead of calling this method repeatedly. It should be considered read-only."""
- return RefLog.from_file(RefLog.path(self))
-
- def log_append(self, oldbinsha: bytes, message: Union[str, None],
- newbinsha: Union[bytes, None] = None) -> 'RefLogEntry':
- """Append a logentry to the logfile of this ref
-
- :param oldbinsha: binary sha this ref used to point to
- :param message: A message describing the change
- :param newbinsha: The sha the ref points to now. If None, our current commit sha
- will be used
- :return: added RefLogEntry instance"""
- # NOTE: we use the committer of the currently active commit - this should be
- # correct to allow overriding the committer on a per-commit level.
- # See https://github.com/gitpython-developers/GitPython/pull/146
- try:
- committer_or_reader: Union['Actor', 'GitConfigParser'] = self.commit.committer
- except ValueError:
- committer_or_reader = self.repo.config_reader()
- # end handle newly cloned repositories
- if newbinsha is None:
- newbinsha = self.commit.binsha
-
- if message is None:
- message = ''
-
- return RefLog.append_entry(committer_or_reader, RefLog.path(self), oldbinsha, newbinsha, message)
-
- def log_entry(self, index: int) -> RefLogEntry:
- """:return: RefLogEntry at the given index
- :param index: python list compatible positive or negative index
-
- .. note:: This method must read part of the reflog during execution, hence
- it should be used sparringly, or only if you need just one index.
- In that case, it will be faster than the ``log()`` method"""
- return RefLog.entry_at(RefLog.path(self), index)
-
- @ classmethod
- def to_full_path(cls, path: Union[PathLike, 'SymbolicReference']) -> str:
- """
- :return: string with a full repository-relative path which can be used to initialize
- a Reference instance, for instance by using ``Reference.from_path``"""
- if isinstance(path, SymbolicReference):
- path = path.path
- full_ref_path = str(path)
- if not cls._common_path_default:
- return full_ref_path
-
- if not str(path).startswith(cls._common_path_default + "/"):
- full_ref_path = '%s/%s' % (cls._common_path_default, path)
- return full_ref_path
+ def iter_items(cls, repo: 'Repo', common_path: Union[PathLike, None] = None,
+ remote: Union['Remote', None] = None, *args: Any, **kwargs: Any
+ ) -> Iterator['RemoteReference']:
+ """Iterate remote references, and if given, constrain them to the given remote"""
+ common_path = common_path or cls._common_path_default
+ if remote is not None:
+ common_path = join_path(common_path, str(remote))
+ # END handle remote constraint
+ # super is Reference
+ return super(RemoteReference, cls).iter_items(repo, common_path)
@ classmethod
- def delete(cls, repo: 'Repo', path: PathLike) -> None:
- """Delete the reference at the given path
-
- :param repo:
- Repository to delete the reference from
-
- :param path:
- Short or full path pointing to the reference, i.e. refs/myreference
- or just "myreference", hence 'refs/' is implied.
- Alternatively the symbolic reference to be deleted"""
- full_ref_path = cls.to_full_path(path)
- abs_path = os.path.join(repo.common_dir, full_ref_path)
- if os.path.exists(abs_path):
- os.remove(abs_path)
- else:
- # check packed refs
- pack_file_path = cls._get_packed_refs_path(repo)
+ def delete(cls, repo: 'Repo', *refs: 'RemoteReference', **kwargs: Any) -> None:
+ """Delete the given remote references
+
+ :note:
+ kwargs are given for comparability with the base class method as we
+ should not narrow the signature."""
+ repo.git.branch("-d", "-r", *refs)
+ # the official deletion method will ignore remote symbolic refs - these
+ # are generally ignored in the refs/ folder. We don't though
+ # and delete remainders manually
+ for ref in refs:
try:
- with open(pack_file_path, 'rb') as reader:
- new_lines = []
- made_change = False
- dropped_last_line = False
- for line_bytes in reader:
- line = line_bytes.decode(defenc)
- _, _, line_ref = line.partition(' ')
- line_ref = line_ref.strip()
- # keep line if it is a comment or if the ref to delete is not
- # in the line
- # If we deleted the last line and this one is a tag-reference object,
- # we drop it as well
- if (line.startswith('#') or full_ref_path != line_ref) and \
- (not dropped_last_line or dropped_last_line and not line.startswith('^')):
- new_lines.append(line)
- dropped_last_line = False
- continue
- # END skip comments and lines without our path
-
- # drop this line
- made_change = True
- dropped_last_line = True
-
- # write the new lines
- if made_change:
- # write-binary is required, otherwise windows will
- # open the file in text mode and change LF to CRLF !
- with open(pack_file_path, 'wb') as fd:
- fd.writelines(line.encode(defenc) for line in new_lines)
-
+ os.remove(os.path.join(repo.common_dir, ref.path))
except OSError:
- pass # it didn't exist at all
-
- # delete the reflog
- reflog_path = RefLog.path(cls(repo, full_ref_path))
- if os.path.isfile(reflog_path):
- os.remove(reflog_path)
- # END remove reflog
-
- @ classmethod
- def _create(cls: Type[T_References], repo: 'Repo', path: PathLike, resolve: bool,
- reference: Union[str, 'SymbolicReference'],
- force: bool, logmsg: Union[str, None] = None) -> T_References:
- """internal method used to create a new symbolic reference.
- If resolve is False, the reference will be taken as is, creating
- a proper symbolic reference. Otherwise it will be resolved to the
- corresponding object and a detached symbolic reference will be created
- instead"""
- git_dir = _git_dir(repo, path)
- full_ref_path = cls.to_full_path(path)
- abs_ref_path = os.path.join(git_dir, full_ref_path)
-
- # figure out target data
- target = reference
- if resolve:
- target = repo.rev_parse(str(reference))
-
- if not force and os.path.isfile(abs_ref_path):
- target_data = str(target)
- if isinstance(target, SymbolicReference):
- target_data = target.path
- if not resolve:
- target_data = "ref: " + target_data
- with open(abs_ref_path, 'rb') as fd:
- existing_data = fd.read().decode(defenc).strip()
- if existing_data != target_data:
- raise OSError("Reference at %r does already exist, pointing to %r, requested was %r" %
- (full_ref_path, existing_data, target_data))
- # END no force handling
-
- ref = cls(repo, full_ref_path)
- ref.set_reference(target, logmsg)
- return ref
-
- @classmethod
- def create(cls: Type[T_References], repo: 'Repo', path: PathLike,
- reference: Union[str, 'SymbolicReference'] = 'SymbolicReference',
- logmsg: Union[str, None] = None, force: bool = False, **kwargs: Any) -> T_References:
- """Create a new symbolic reference, hence a reference pointing , to another reference.
-
- :param repo:
- Repository to create the reference in
-
- :param path:
- full path at which the new symbolic reference is supposed to be
- created at, i.e. "NEW_HEAD" or "symrefs/my_new_symref"
-
- :param reference:
- The reference to which the new symbolic reference should point to.
- If it is a ref to a commit'ish, the symbolic ref will be detached.
-
- :param force:
- if True, force creation even if a symbolic reference with that name already exists.
- Raise OSError otherwise
-
- :param logmsg:
- If not None, the message to append to the reflog. Otherwise no reflog
- entry is written.
-
- :return: Newly created symbolic Reference
-
- :raise OSError:
- If a (Symbolic)Reference with the same name but different contents
- already exists.
-
- :note: This does not alter the current HEAD, index or Working Tree"""
- return cls._create(repo, path, cls._resolve_ref_on_create, reference, force, logmsg)
-
- def rename(self, new_path: str, force: bool = False) -> 'SymbolicReference':
- """Rename self to a new path
-
- :param new_path:
- Either a simple name or a full path, i.e. new_name or features/new_name.
- The prefix refs/ is implied for references and will be set as needed.
- In case this is a symbolic ref, there is no implied prefix
-
- :param force:
- If True, the rename will succeed even if a head with the target name
- already exists. It will be overwritten in that case
-
- :return: self
- :raise OSError: In case a file at path but a different contents already exists """
- new_path = self.to_full_path(new_path)
- if self.path == new_path:
- return self
-
- new_abs_path = os.path.join(_git_dir(self.repo, new_path), new_path)
- cur_abs_path = os.path.join(_git_dir(self.repo, self.path), self.path)
- if os.path.isfile(new_abs_path):
- if not force:
- # if they point to the same file, its not an error
- with open(new_abs_path, 'rb') as fd1:
- f1 = fd1.read().strip()
- with open(cur_abs_path, 'rb') as fd2:
- f2 = fd2.read().strip()
- if f1 != f2:
- raise OSError("File at path %r already exists" % new_abs_path)
- # else: we could remove ourselves and use the otherone, but
- # but clarity we just continue as usual
- # END not force handling
- os.remove(new_abs_path)
- # END handle existing target file
-
- dname = os.path.dirname(new_abs_path)
- if not os.path.isdir(dname):
- os.makedirs(dname)
- # END create directory
-
- os.rename(cur_abs_path, new_abs_path)
- self.path = new_path
-
- return self
-
- @classmethod
- def _iter_items(cls: Type[T_References], repo: 'Repo', common_path: Union[PathLike, None] = None
- ) -> Iterator[T_References]:
- if common_path is None:
- common_path = cls._common_path_default
- rela_paths = set()
-
- # walk loose refs
- # Currently we do not follow links
- for root, dirs, files in os.walk(join_path_native(repo.common_dir, common_path)):
- if 'refs' not in root.split(os.sep): # skip non-refs subfolders
- refs_id = [d for d in dirs if d == 'refs']
- if refs_id:
- dirs[0:] = ['refs']
- # END prune non-refs folders
-
- for f in files:
- if f == 'packed-refs':
- continue
- abs_path = to_native_path_linux(join_path(root, f))
- rela_paths.add(abs_path.replace(to_native_path_linux(repo.common_dir) + '/', ""))
- # END for each file in root directory
- # END for each directory to walk
-
- # read packed refs
- for _sha, rela_path in cls._iter_packed_refs(repo):
- if rela_path.startswith(str(common_path)):
- rela_paths.add(rela_path)
- # END relative path matches common path
- # END packed refs reading
-
- # return paths in sorted order
- for path in sorted(rela_paths):
- try:
- yield cls.from_path(repo, path)
- except ValueError:
- continue
- # END for each sorted relative refpath
-
- @classmethod
- # type: ignore[override]
- def iter_items(cls, repo: 'Repo', common_path: Union[PathLike, None] = None, *args, **kwargs):
- """Find all refs in the repository
-
- :param repo: is the Repo
-
- :param common_path:
- Optional keyword argument to the path which is to be shared by all
- returned Ref objects.
- Defaults to class specific portion if None assuring that only
- refs suitable for the actual class are returned.
-
- :return:
- git.SymbolicReference[], each of them is guaranteed to be a symbolic
- ref which is not detached and pointing to a valid ref
-
- List is lexicographically sorted
- The returned objects represent actual subclasses, such as Head or TagReference"""
- return (r for r in cls._iter_items(repo, common_path) if r.__class__ == SymbolicReference or not r.is_detached)
-
- @classmethod
- def from_path(cls, repo: 'Repo', path: PathLike) -> Union['Head', 'RemoteReference', 'TagReference', 'Reference']:
- """
- :param path: full .git-directory-relative path name to the Reference to instantiate
- :note: use to_full_path() if you only have a partial path of a known Reference Type
- :return:
- Instance of type Reference, Head, or Tag
- depending on the given path"""
- if not path:
- raise ValueError("Cannot create Reference from %r" % path)
-
- # Names like HEAD are inserted after the refs module is imported - we have an import dependency
- # cycle and don't want to import these names in-function
- from . import HEAD, Head, RemoteReference, TagReference, Reference
- for ref_type in (HEAD, Head, RemoteReference, TagReference, Reference, SymbolicReference):
+ pass
try:
- instance = ref_type(repo, path)
- if instance.__class__ == SymbolicReference and instance.is_detached:
- raise ValueError("SymbolRef was detached, we drop it")
- return instance
- except ValueError:
+ os.remove(os.path.join(repo.git_dir, ref.path))
+ except OSError:
pass
- # END exception handling
- # END for each type to try
- raise ValueError("Could not find reference type suitable to handle path %r" % path)
+ # END for each ref
- def is_remote(self):
- """:return: True if this symbolic reference points to a remote branch"""
- return self.path.startswith(self._remote_common_path_default + "/")
+ @ classmethod
+ def create(cls, *args: Any, **kwargs: Any) -> NoReturn:
+ """Used to disable this method"""
+ raise TypeError("Cannot explicitly create remote references")