upload
This commit is contained in:
		@@ -0,0 +1,4 @@
 | 
			
		||||
"""Initialize the index package"""
 | 
			
		||||
# flake8: noqa
 | 
			
		||||
from .base import *
 | 
			
		||||
from .typ import *
 | 
			
		||||
							
								
								
									
										1401
									
								
								zero-cost-nas/.eggs/GitPython-3.1.31-py3.8.egg/git/index/base.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1401
									
								
								zero-cost-nas/.eggs/GitPython-3.1.31-py3.8.egg/git/index/base.py
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										444
									
								
								zero-cost-nas/.eggs/GitPython-3.1.31-py3.8.egg/git/index/fun.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										444
									
								
								zero-cost-nas/.eggs/GitPython-3.1.31-py3.8.egg/git/index/fun.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,444 @@
 | 
			
		||||
# Contains standalone functions to accompany the index implementation and make it
 | 
			
		||||
# more versatile
 | 
			
		||||
# NOTE: Autodoc hates it if this is a docstring
 | 
			
		||||
 | 
			
		||||
from io import BytesIO
 | 
			
		||||
from pathlib import Path
 | 
			
		||||
import os
 | 
			
		||||
from stat import (
 | 
			
		||||
    S_IFDIR,
 | 
			
		||||
    S_IFLNK,
 | 
			
		||||
    S_ISLNK,
 | 
			
		||||
    S_ISDIR,
 | 
			
		||||
    S_IFMT,
 | 
			
		||||
    S_IFREG,
 | 
			
		||||
    S_IXUSR,
 | 
			
		||||
)
 | 
			
		||||
import subprocess
 | 
			
		||||
 | 
			
		||||
from git.cmd import PROC_CREATIONFLAGS, handle_process_output
 | 
			
		||||
from git.compat import (
 | 
			
		||||
    defenc,
 | 
			
		||||
    force_text,
 | 
			
		||||
    force_bytes,
 | 
			
		||||
    is_posix,
 | 
			
		||||
    is_win,
 | 
			
		||||
    safe_decode,
 | 
			
		||||
)
 | 
			
		||||
from git.exc import UnmergedEntriesError, HookExecutionError
 | 
			
		||||
from git.objects.fun import (
 | 
			
		||||
    tree_to_stream,
 | 
			
		||||
    traverse_tree_recursive,
 | 
			
		||||
    traverse_trees_recursive,
 | 
			
		||||
)
 | 
			
		||||
from git.util import IndexFileSHA1Writer, finalize_process
 | 
			
		||||
from gitdb.base import IStream
 | 
			
		||||
from gitdb.typ import str_tree_type
 | 
			
		||||
 | 
			
		||||
import os.path as osp
 | 
			
		||||
 | 
			
		||||
from .typ import BaseIndexEntry, IndexEntry, CE_NAMEMASK, CE_STAGESHIFT
 | 
			
		||||
from .util import pack, unpack
 | 
			
		||||
 | 
			
		||||
# typing -----------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
from typing import Dict, IO, List, Sequence, TYPE_CHECKING, Tuple, Type, Union, cast
 | 
			
		||||
 | 
			
		||||
from git.types import PathLike
 | 
			
		||||
 | 
			
		||||
if TYPE_CHECKING:
 | 
			
		||||
    from .base import IndexFile
 | 
			
		||||
    from git.db import GitCmdObjectDB
 | 
			
		||||
    from git.objects.tree import TreeCacheTup
 | 
			
		||||
 | 
			
		||||
    # from git.objects.fun import EntryTupOrNone
 | 
			
		||||
 | 
			
		||||
# ------------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
S_IFGITLINK = S_IFLNK | S_IFDIR  # a submodule
 | 
			
		||||
CE_NAMEMASK_INV = ~CE_NAMEMASK
 | 
			
		||||
 | 
			
		||||
__all__ = (
 | 
			
		||||
    "write_cache",
 | 
			
		||||
    "read_cache",
 | 
			
		||||
    "write_tree_from_cache",
 | 
			
		||||
    "entry_key",
 | 
			
		||||
    "stat_mode_to_index_mode",
 | 
			
		||||
    "S_IFGITLINK",
 | 
			
		||||
    "run_commit_hook",
 | 
			
		||||
    "hook_path",
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def hook_path(name: str, git_dir: PathLike) -> str:
 | 
			
		||||
    """:return: path to the given named hook in the given git repository directory"""
 | 
			
		||||
    return osp.join(git_dir, "hooks", name)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _has_file_extension(path):
 | 
			
		||||
    return osp.splitext(path)[1]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def run_commit_hook(name: str, index: "IndexFile", *args: str) -> None:
 | 
			
		||||
    """Run the commit hook of the given name. Silently ignores hooks that do not exist.
 | 
			
		||||
 | 
			
		||||
    :param name: name of hook, like 'pre-commit'
 | 
			
		||||
    :param index: IndexFile instance
 | 
			
		||||
    :param args: arguments passed to hook file
 | 
			
		||||
    :raises HookExecutionError:"""
 | 
			
		||||
    hp = hook_path(name, index.repo.git_dir)
 | 
			
		||||
    if not os.access(hp, os.X_OK):
 | 
			
		||||
        return None
 | 
			
		||||
 | 
			
		||||
    env = os.environ.copy()
 | 
			
		||||
    env["GIT_INDEX_FILE"] = safe_decode(str(index.path))
 | 
			
		||||
    env["GIT_EDITOR"] = ":"
 | 
			
		||||
    cmd = [hp]
 | 
			
		||||
    try:
 | 
			
		||||
        if is_win and not _has_file_extension(hp):
 | 
			
		||||
            # Windows only uses extensions to determine how to open files
 | 
			
		||||
            # (doesn't understand shebangs). Try using bash to run the hook.
 | 
			
		||||
            relative_hp = Path(hp).relative_to(index.repo.working_dir).as_posix()
 | 
			
		||||
            cmd = ["bash.exe", relative_hp]
 | 
			
		||||
 | 
			
		||||
        cmd = subprocess.Popen(
 | 
			
		||||
            cmd + list(args),
 | 
			
		||||
            env=env,
 | 
			
		||||
            stdout=subprocess.PIPE,
 | 
			
		||||
            stderr=subprocess.PIPE,
 | 
			
		||||
            cwd=index.repo.working_dir,
 | 
			
		||||
            close_fds=is_posix,
 | 
			
		||||
            creationflags=PROC_CREATIONFLAGS,
 | 
			
		||||
        )
 | 
			
		||||
    except Exception as ex:
 | 
			
		||||
        raise HookExecutionError(hp, ex) from ex
 | 
			
		||||
    else:
 | 
			
		||||
        stdout_list: List[str] = []
 | 
			
		||||
        stderr_list: List[str] = []
 | 
			
		||||
        handle_process_output(cmd, stdout_list.append, stderr_list.append, finalize_process)
 | 
			
		||||
        stdout = "".join(stdout_list)
 | 
			
		||||
        stderr = "".join(stderr_list)
 | 
			
		||||
        if cmd.returncode != 0:
 | 
			
		||||
            stdout = force_text(stdout, defenc)
 | 
			
		||||
            stderr = force_text(stderr, defenc)
 | 
			
		||||
            raise HookExecutionError(hp, cmd.returncode, stderr, stdout)
 | 
			
		||||
    # end handle return code
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def stat_mode_to_index_mode(mode: int) -> int:
 | 
			
		||||
    """Convert the given mode from a stat call to the corresponding index mode
 | 
			
		||||
    and return it"""
 | 
			
		||||
    if S_ISLNK(mode):  # symlinks
 | 
			
		||||
        return S_IFLNK
 | 
			
		||||
    if S_ISDIR(mode) or S_IFMT(mode) == S_IFGITLINK:  # submodules
 | 
			
		||||
        return S_IFGITLINK
 | 
			
		||||
    return S_IFREG | (mode & S_IXUSR and 0o755 or 0o644)  # blobs with or without executable bit
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def write_cache(
 | 
			
		||||
    entries: Sequence[Union[BaseIndexEntry, "IndexEntry"]],
 | 
			
		||||
    stream: IO[bytes],
 | 
			
		||||
    extension_data: Union[None, bytes] = None,
 | 
			
		||||
    ShaStreamCls: Type[IndexFileSHA1Writer] = IndexFileSHA1Writer,
 | 
			
		||||
) -> None:
 | 
			
		||||
    """Write the cache represented by entries to a stream
 | 
			
		||||
 | 
			
		||||
    :param entries: **sorted** list of entries
 | 
			
		||||
    :param stream: stream to wrap into the AdapterStreamCls - it is used for
 | 
			
		||||
        final output.
 | 
			
		||||
 | 
			
		||||
    :param ShaStreamCls: Type to use when writing to the stream. It produces a sha
 | 
			
		||||
        while writing to it, before the data is passed on to the wrapped stream
 | 
			
		||||
 | 
			
		||||
    :param extension_data: any kind of data to write as a trailer, it must begin
 | 
			
		||||
        a 4 byte identifier, followed by its size ( 4 bytes )"""
 | 
			
		||||
    # wrap the stream into a compatible writer
 | 
			
		||||
    stream_sha = ShaStreamCls(stream)
 | 
			
		||||
 | 
			
		||||
    tell = stream_sha.tell
 | 
			
		||||
    write = stream_sha.write
 | 
			
		||||
 | 
			
		||||
    # header
 | 
			
		||||
    version = 2
 | 
			
		||||
    write(b"DIRC")
 | 
			
		||||
    write(pack(">LL", version, len(entries)))
 | 
			
		||||
 | 
			
		||||
    # body
 | 
			
		||||
    for entry in entries:
 | 
			
		||||
        beginoffset = tell()
 | 
			
		||||
        write(entry.ctime_bytes)  # ctime
 | 
			
		||||
        write(entry.mtime_bytes)  # mtime
 | 
			
		||||
        path_str = str(entry.path)
 | 
			
		||||
        path: bytes = force_bytes(path_str, encoding=defenc)
 | 
			
		||||
        plen = len(path) & CE_NAMEMASK  # path length
 | 
			
		||||
        assert plen == len(path), "Path %s too long to fit into index" % entry.path
 | 
			
		||||
        flags = plen | (entry.flags & CE_NAMEMASK_INV)  # clear possible previous values
 | 
			
		||||
        write(
 | 
			
		||||
            pack(
 | 
			
		||||
                ">LLLLLL20sH",
 | 
			
		||||
                entry.dev,
 | 
			
		||||
                entry.inode,
 | 
			
		||||
                entry.mode,
 | 
			
		||||
                entry.uid,
 | 
			
		||||
                entry.gid,
 | 
			
		||||
                entry.size,
 | 
			
		||||
                entry.binsha,
 | 
			
		||||
                flags,
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
        write(path)
 | 
			
		||||
        real_size = (tell() - beginoffset + 8) & ~7
 | 
			
		||||
        write(b"\0" * ((beginoffset + real_size) - tell()))
 | 
			
		||||
    # END for each entry
 | 
			
		||||
 | 
			
		||||
    # write previously cached extensions data
 | 
			
		||||
    if extension_data is not None:
 | 
			
		||||
        stream_sha.write(extension_data)
 | 
			
		||||
 | 
			
		||||
    # write the sha over the content
 | 
			
		||||
    stream_sha.write_sha()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def read_header(stream: IO[bytes]) -> Tuple[int, int]:
 | 
			
		||||
    """Return tuple(version_long, num_entries) from the given stream"""
 | 
			
		||||
    type_id = stream.read(4)
 | 
			
		||||
    if type_id != b"DIRC":
 | 
			
		||||
        raise AssertionError("Invalid index file header: %r" % type_id)
 | 
			
		||||
    unpacked = cast(Tuple[int, int], unpack(">LL", stream.read(4 * 2)))
 | 
			
		||||
    version, num_entries = unpacked
 | 
			
		||||
 | 
			
		||||
    # TODO: handle version 3: extended data, see read-cache.c
 | 
			
		||||
    assert version in (1, 2)
 | 
			
		||||
    return version, num_entries
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def entry_key(*entry: Union[BaseIndexEntry, PathLike, int]) -> Tuple[PathLike, int]:
 | 
			
		||||
    """:return: Key suitable to be used for the index.entries dictionary
 | 
			
		||||
    :param entry: One instance of type BaseIndexEntry or the path and the stage"""
 | 
			
		||||
 | 
			
		||||
    # def is_entry_key_tup(entry_key: Tuple) -> TypeGuard[Tuple[PathLike, int]]:
 | 
			
		||||
    #     return isinstance(entry_key, tuple) and len(entry_key) == 2
 | 
			
		||||
 | 
			
		||||
    if len(entry) == 1:
 | 
			
		||||
        entry_first = entry[0]
 | 
			
		||||
        assert isinstance(entry_first, BaseIndexEntry)
 | 
			
		||||
        return (entry_first.path, entry_first.stage)
 | 
			
		||||
    else:
 | 
			
		||||
        # assert is_entry_key_tup(entry)
 | 
			
		||||
        entry = cast(Tuple[PathLike, int], entry)
 | 
			
		||||
        return entry
 | 
			
		||||
    # END handle entry
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def read_cache(
 | 
			
		||||
    stream: IO[bytes],
 | 
			
		||||
) -> Tuple[int, Dict[Tuple[PathLike, int], "IndexEntry"], bytes, bytes]:
 | 
			
		||||
    """Read a cache file from the given stream
 | 
			
		||||
 | 
			
		||||
    :return: tuple(version, entries_dict, extension_data, content_sha)
 | 
			
		||||
 | 
			
		||||
      * version is the integer version number
 | 
			
		||||
      * entries dict is a dictionary which maps IndexEntry instances to a path at a stage
 | 
			
		||||
      * extension_data is '' or 4 bytes of type + 4 bytes of size + size bytes
 | 
			
		||||
      * content_sha is a 20 byte sha on all cache file contents"""
 | 
			
		||||
    version, num_entries = read_header(stream)
 | 
			
		||||
    count = 0
 | 
			
		||||
    entries: Dict[Tuple[PathLike, int], "IndexEntry"] = {}
 | 
			
		||||
 | 
			
		||||
    read = stream.read
 | 
			
		||||
    tell = stream.tell
 | 
			
		||||
    while count < num_entries:
 | 
			
		||||
        beginoffset = tell()
 | 
			
		||||
        ctime = unpack(">8s", read(8))[0]
 | 
			
		||||
        mtime = unpack(">8s", read(8))[0]
 | 
			
		||||
        (dev, ino, mode, uid, gid, size, sha, flags) = unpack(">LLLLLL20sH", read(20 + 4 * 6 + 2))
 | 
			
		||||
        path_size = flags & CE_NAMEMASK
 | 
			
		||||
        path = read(path_size).decode(defenc)
 | 
			
		||||
 | 
			
		||||
        real_size = (tell() - beginoffset + 8) & ~7
 | 
			
		||||
        read((beginoffset + real_size) - tell())
 | 
			
		||||
        entry = IndexEntry((mode, sha, flags, path, ctime, mtime, dev, ino, uid, gid, size))
 | 
			
		||||
        # entry_key would be the method to use, but we safe the effort
 | 
			
		||||
        entries[(path, entry.stage)] = entry
 | 
			
		||||
        count += 1
 | 
			
		||||
    # END for each entry
 | 
			
		||||
 | 
			
		||||
    # the footer contains extension data and a sha on the content so far
 | 
			
		||||
    # Keep the extension footer,and verify we have a sha in the end
 | 
			
		||||
    # Extension data format is:
 | 
			
		||||
    # 4 bytes ID
 | 
			
		||||
    # 4 bytes length of chunk
 | 
			
		||||
    # repeated 0 - N times
 | 
			
		||||
    extension_data = stream.read(~0)
 | 
			
		||||
    assert (
 | 
			
		||||
        len(extension_data) > 19
 | 
			
		||||
    ), "Index Footer was not at least a sha on content as it was only %i bytes in size" % len(extension_data)
 | 
			
		||||
 | 
			
		||||
    content_sha = extension_data[-20:]
 | 
			
		||||
 | 
			
		||||
    # truncate the sha in the end as we will dynamically create it anyway
 | 
			
		||||
    extension_data = extension_data[:-20]
 | 
			
		||||
 | 
			
		||||
    return (version, entries, extension_data, content_sha)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def write_tree_from_cache(
 | 
			
		||||
    entries: List[IndexEntry], odb: "GitCmdObjectDB", sl: slice, si: int = 0
 | 
			
		||||
) -> Tuple[bytes, List["TreeCacheTup"]]:
 | 
			
		||||
    """Create a tree from the given sorted list of entries and put the respective
 | 
			
		||||
    trees into the given object database
 | 
			
		||||
 | 
			
		||||
    :param entries: **sorted** list of IndexEntries
 | 
			
		||||
    :param odb: object database to store the trees in
 | 
			
		||||
    :param si: start index at which we should start creating subtrees
 | 
			
		||||
    :param sl: slice indicating the range we should process on the entries list
 | 
			
		||||
    :return: tuple(binsha, list(tree_entry, ...)) a tuple of a sha and a list of
 | 
			
		||||
        tree entries being a tuple of hexsha, mode, name"""
 | 
			
		||||
    tree_items: List["TreeCacheTup"] = []
 | 
			
		||||
 | 
			
		||||
    ci = sl.start
 | 
			
		||||
    end = sl.stop
 | 
			
		||||
    while ci < end:
 | 
			
		||||
        entry = entries[ci]
 | 
			
		||||
        if entry.stage != 0:
 | 
			
		||||
            raise UnmergedEntriesError(entry)
 | 
			
		||||
        # END abort on unmerged
 | 
			
		||||
        ci += 1
 | 
			
		||||
        rbound = entry.path.find("/", si)
 | 
			
		||||
        if rbound == -1:
 | 
			
		||||
            # its not a tree
 | 
			
		||||
            tree_items.append((entry.binsha, entry.mode, entry.path[si:]))
 | 
			
		||||
        else:
 | 
			
		||||
            # find common base range
 | 
			
		||||
            base = entry.path[si:rbound]
 | 
			
		||||
            xi = ci
 | 
			
		||||
            while xi < end:
 | 
			
		||||
                oentry = entries[xi]
 | 
			
		||||
                orbound = oentry.path.find("/", si)
 | 
			
		||||
                if orbound == -1 or oentry.path[si:orbound] != base:
 | 
			
		||||
                    break
 | 
			
		||||
                # END abort on base mismatch
 | 
			
		||||
                xi += 1
 | 
			
		||||
            # END find common base
 | 
			
		||||
 | 
			
		||||
            # enter recursion
 | 
			
		||||
            # ci - 1 as we want to count our current item as well
 | 
			
		||||
            sha, _tree_entry_list = write_tree_from_cache(entries, odb, slice(ci - 1, xi), rbound + 1)
 | 
			
		||||
            tree_items.append((sha, S_IFDIR, base))
 | 
			
		||||
 | 
			
		||||
            # skip ahead
 | 
			
		||||
            ci = xi
 | 
			
		||||
        # END handle bounds
 | 
			
		||||
    # END for each entry
 | 
			
		||||
 | 
			
		||||
    # finally create the tree
 | 
			
		||||
    sio = BytesIO()
 | 
			
		||||
    tree_to_stream(tree_items, sio.write)  # writes to stream as bytes, but doesn't change tree_items
 | 
			
		||||
    sio.seek(0)
 | 
			
		||||
 | 
			
		||||
    istream = odb.store(IStream(str_tree_type, len(sio.getvalue()), sio))
 | 
			
		||||
    return (istream.binsha, tree_items)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _tree_entry_to_baseindexentry(tree_entry: "TreeCacheTup", stage: int) -> BaseIndexEntry:
 | 
			
		||||
    return BaseIndexEntry((tree_entry[1], tree_entry[0], stage << CE_STAGESHIFT, tree_entry[2]))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def aggressive_tree_merge(odb: "GitCmdObjectDB", tree_shas: Sequence[bytes]) -> List[BaseIndexEntry]:
 | 
			
		||||
    """
 | 
			
		||||
    :return: list of BaseIndexEntries representing the aggressive merge of the given
 | 
			
		||||
        trees. All valid entries are on stage 0, whereas the conflicting ones are left
 | 
			
		||||
        on stage 1, 2 or 3, whereas stage 1 corresponds to the common ancestor tree,
 | 
			
		||||
        2 to our tree and 3 to 'their' tree.
 | 
			
		||||
    :param tree_shas: 1, 2 or 3 trees as identified by their binary 20 byte shas
 | 
			
		||||
        If 1 or two, the entries will effectively correspond to the last given tree
 | 
			
		||||
        If 3 are given, a 3 way merge is performed"""
 | 
			
		||||
    out: List[BaseIndexEntry] = []
 | 
			
		||||
 | 
			
		||||
    # one and two way is the same for us, as we don't have to handle an existing
 | 
			
		||||
    # index, instrea
 | 
			
		||||
    if len(tree_shas) in (1, 2):
 | 
			
		||||
        for entry in traverse_tree_recursive(odb, tree_shas[-1], ""):
 | 
			
		||||
            out.append(_tree_entry_to_baseindexentry(entry, 0))
 | 
			
		||||
        # END for each entry
 | 
			
		||||
        return out
 | 
			
		||||
    # END handle single tree
 | 
			
		||||
 | 
			
		||||
    if len(tree_shas) > 3:
 | 
			
		||||
        raise ValueError("Cannot handle %i trees at once" % len(tree_shas))
 | 
			
		||||
 | 
			
		||||
    # three trees
 | 
			
		||||
    for base, ours, theirs in traverse_trees_recursive(odb, tree_shas, ""):
 | 
			
		||||
        if base is not None:
 | 
			
		||||
            # base version exists
 | 
			
		||||
            if ours is not None:
 | 
			
		||||
                # ours exists
 | 
			
		||||
                if theirs is not None:
 | 
			
		||||
                    # it exists in all branches, if it was changed in both
 | 
			
		||||
                    # its a conflict, otherwise we take the changed version
 | 
			
		||||
                    # This should be the most common branch, so it comes first
 | 
			
		||||
                    if (base[0] != ours[0] and base[0] != theirs[0] and ours[0] != theirs[0]) or (
 | 
			
		||||
                        base[1] != ours[1] and base[1] != theirs[1] and ours[1] != theirs[1]
 | 
			
		||||
                    ):
 | 
			
		||||
                        # changed by both
 | 
			
		||||
                        out.append(_tree_entry_to_baseindexentry(base, 1))
 | 
			
		||||
                        out.append(_tree_entry_to_baseindexentry(ours, 2))
 | 
			
		||||
                        out.append(_tree_entry_to_baseindexentry(theirs, 3))
 | 
			
		||||
                    elif base[0] != ours[0] or base[1] != ours[1]:
 | 
			
		||||
                        # only we changed it
 | 
			
		||||
                        out.append(_tree_entry_to_baseindexentry(ours, 0))
 | 
			
		||||
                    else:
 | 
			
		||||
                        # either nobody changed it, or they did. In either
 | 
			
		||||
                        # case, use theirs
 | 
			
		||||
                        out.append(_tree_entry_to_baseindexentry(theirs, 0))
 | 
			
		||||
                    # END handle modification
 | 
			
		||||
                else:
 | 
			
		||||
 | 
			
		||||
                    if ours[0] != base[0] or ours[1] != base[1]:
 | 
			
		||||
                        # they deleted it, we changed it, conflict
 | 
			
		||||
                        out.append(_tree_entry_to_baseindexentry(base, 1))
 | 
			
		||||
                        out.append(_tree_entry_to_baseindexentry(ours, 2))
 | 
			
		||||
                    # else:
 | 
			
		||||
                    #   we didn't change it, ignore
 | 
			
		||||
                    #   pass
 | 
			
		||||
                    # END handle our change
 | 
			
		||||
                # END handle theirs
 | 
			
		||||
            else:
 | 
			
		||||
                if theirs is None:
 | 
			
		||||
                    # deleted in both, its fine - its out
 | 
			
		||||
                    pass
 | 
			
		||||
                else:
 | 
			
		||||
                    if theirs[0] != base[0] or theirs[1] != base[1]:
 | 
			
		||||
                        # deleted in ours, changed theirs, conflict
 | 
			
		||||
                        out.append(_tree_entry_to_baseindexentry(base, 1))
 | 
			
		||||
                        out.append(_tree_entry_to_baseindexentry(theirs, 3))
 | 
			
		||||
                    # END theirs changed
 | 
			
		||||
                    # else:
 | 
			
		||||
                    #   theirs didn't change
 | 
			
		||||
                    #   pass
 | 
			
		||||
                # END handle theirs
 | 
			
		||||
            # END handle ours
 | 
			
		||||
        else:
 | 
			
		||||
            # all three can't be None
 | 
			
		||||
            if ours is None:
 | 
			
		||||
                # added in their branch
 | 
			
		||||
                assert theirs is not None
 | 
			
		||||
                out.append(_tree_entry_to_baseindexentry(theirs, 0))
 | 
			
		||||
            elif theirs is None:
 | 
			
		||||
                # added in our branch
 | 
			
		||||
                out.append(_tree_entry_to_baseindexentry(ours, 0))
 | 
			
		||||
            else:
 | 
			
		||||
                # both have it, except for the base, see whether it changed
 | 
			
		||||
                if ours[0] != theirs[0] or ours[1] != theirs[1]:
 | 
			
		||||
                    out.append(_tree_entry_to_baseindexentry(ours, 2))
 | 
			
		||||
                    out.append(_tree_entry_to_baseindexentry(theirs, 3))
 | 
			
		||||
                else:
 | 
			
		||||
                    # it was added the same in both
 | 
			
		||||
                    out.append(_tree_entry_to_baseindexentry(ours, 0))
 | 
			
		||||
                # END handle two items
 | 
			
		||||
            # END handle heads
 | 
			
		||||
        # END handle base exists
 | 
			
		||||
    # END for each entries tuple
 | 
			
		||||
 | 
			
		||||
    return out
 | 
			
		||||
							
								
								
									
										191
									
								
								zero-cost-nas/.eggs/GitPython-3.1.31-py3.8.egg/git/index/typ.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										191
									
								
								zero-cost-nas/.eggs/GitPython-3.1.31-py3.8.egg/git/index/typ.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,191 @@
 | 
			
		||||
"""Module with additional types used by the index"""
 | 
			
		||||
 | 
			
		||||
from binascii import b2a_hex
 | 
			
		||||
from pathlib import Path
 | 
			
		||||
 | 
			
		||||
from .util import pack, unpack
 | 
			
		||||
from git.objects import Blob
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# typing ----------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
from typing import NamedTuple, Sequence, TYPE_CHECKING, Tuple, Union, cast, List
 | 
			
		||||
 | 
			
		||||
from git.types import PathLike
 | 
			
		||||
 | 
			
		||||
if TYPE_CHECKING:
 | 
			
		||||
    from git.repo import Repo
 | 
			
		||||
 | 
			
		||||
StageType = int
 | 
			
		||||
 | 
			
		||||
# ---------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
__all__ = ("BlobFilter", "BaseIndexEntry", "IndexEntry", "StageType")
 | 
			
		||||
 | 
			
		||||
# { Invariants
 | 
			
		||||
CE_NAMEMASK = 0x0FFF
 | 
			
		||||
CE_STAGEMASK = 0x3000
 | 
			
		||||
CE_EXTENDED = 0x4000
 | 
			
		||||
CE_VALID = 0x8000
 | 
			
		||||
CE_STAGESHIFT = 12
 | 
			
		||||
 | 
			
		||||
# } END invariants
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class BlobFilter(object):
 | 
			
		||||
 | 
			
		||||
    """
 | 
			
		||||
    Predicate to be used by iter_blobs allowing to filter only return blobs which
 | 
			
		||||
    match the given list of directories or files.
 | 
			
		||||
 | 
			
		||||
    The given paths are given relative to the repository.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    __slots__ = "paths"
 | 
			
		||||
 | 
			
		||||
    def __init__(self, paths: Sequence[PathLike]) -> None:
 | 
			
		||||
        """
 | 
			
		||||
        :param paths:
 | 
			
		||||
            tuple or list of paths which are either pointing to directories or
 | 
			
		||||
            to files relative to the current repository
 | 
			
		||||
        """
 | 
			
		||||
        self.paths = paths
 | 
			
		||||
 | 
			
		||||
    def __call__(self, stage_blob: Tuple[StageType, Blob]) -> bool:
 | 
			
		||||
        blob_pathlike: PathLike = stage_blob[1].path
 | 
			
		||||
        blob_path: Path = blob_pathlike if isinstance(blob_pathlike, Path) else Path(blob_pathlike)
 | 
			
		||||
        for pathlike in self.paths:
 | 
			
		||||
            path: Path = pathlike if isinstance(pathlike, Path) else Path(pathlike)
 | 
			
		||||
            # TODO: Change to use `PosixPath.is_relative_to` once Python 3.8 is no longer supported.
 | 
			
		||||
            filter_parts: List[str] = path.parts
 | 
			
		||||
            blob_parts: List[str] = blob_path.parts
 | 
			
		||||
            if len(filter_parts) > len(blob_parts):
 | 
			
		||||
                continue
 | 
			
		||||
            if all(i == j for i, j in zip(filter_parts, blob_parts)):
 | 
			
		||||
                return True
 | 
			
		||||
        return False
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class BaseIndexEntryHelper(NamedTuple):
 | 
			
		||||
    """Typed namedtuple to provide named attribute access for BaseIndexEntry.
 | 
			
		||||
    Needed to allow overriding __new__ in child class to preserve backwards compat."""
 | 
			
		||||
 | 
			
		||||
    mode: int
 | 
			
		||||
    binsha: bytes
 | 
			
		||||
    flags: int
 | 
			
		||||
    path: PathLike
 | 
			
		||||
    ctime_bytes: bytes = pack(">LL", 0, 0)
 | 
			
		||||
    mtime_bytes: bytes = pack(">LL", 0, 0)
 | 
			
		||||
    dev: int = 0
 | 
			
		||||
    inode: int = 0
 | 
			
		||||
    uid: int = 0
 | 
			
		||||
    gid: int = 0
 | 
			
		||||
    size: int = 0
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class BaseIndexEntry(BaseIndexEntryHelper):
 | 
			
		||||
 | 
			
		||||
    """Small Brother of an index entry which can be created to describe changes
 | 
			
		||||
    done to the index in which case plenty of additional information is not required.
 | 
			
		||||
 | 
			
		||||
    As the first 4 data members match exactly to the IndexEntry type, methods
 | 
			
		||||
    expecting a BaseIndexEntry can also handle full IndexEntries even if they
 | 
			
		||||
    use numeric indices for performance reasons.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    def __new__(
 | 
			
		||||
        cls,
 | 
			
		||||
        inp_tuple: Union[
 | 
			
		||||
            Tuple[int, bytes, int, PathLike],
 | 
			
		||||
            Tuple[int, bytes, int, PathLike, bytes, bytes, int, int, int, int, int],
 | 
			
		||||
        ],
 | 
			
		||||
    ) -> "BaseIndexEntry":
 | 
			
		||||
        """Override __new__ to allow construction from a tuple for backwards compatibility"""
 | 
			
		||||
        return super().__new__(cls, *inp_tuple)
 | 
			
		||||
 | 
			
		||||
    def __str__(self) -> str:
 | 
			
		||||
        return "%o %s %i\t%s" % (self.mode, self.hexsha, self.stage, self.path)
 | 
			
		||||
 | 
			
		||||
    def __repr__(self) -> str:
 | 
			
		||||
        return "(%o, %s, %i, %s)" % (self.mode, self.hexsha, self.stage, self.path)
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def hexsha(self) -> str:
 | 
			
		||||
        """hex version of our sha"""
 | 
			
		||||
        return b2a_hex(self.binsha).decode("ascii")
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def stage(self) -> int:
 | 
			
		||||
        """Stage of the entry, either:
 | 
			
		||||
 | 
			
		||||
            * 0 = default stage
 | 
			
		||||
            * 1 = stage before a merge or common ancestor entry in case of a 3 way merge
 | 
			
		||||
            * 2 = stage of entries from the 'left' side of the merge
 | 
			
		||||
            * 3 = stage of entries from the right side of the merge
 | 
			
		||||
 | 
			
		||||
        :note: For more information, see http://www.kernel.org/pub/software/scm/git/docs/git-read-tree.html
 | 
			
		||||
        """
 | 
			
		||||
        return (self.flags & CE_STAGEMASK) >> CE_STAGESHIFT
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def from_blob(cls, blob: Blob, stage: int = 0) -> "BaseIndexEntry":
 | 
			
		||||
        """:return: Fully equipped BaseIndexEntry at the given stage"""
 | 
			
		||||
        return cls((blob.mode, blob.binsha, stage << CE_STAGESHIFT, blob.path))
 | 
			
		||||
 | 
			
		||||
    def to_blob(self, repo: "Repo") -> Blob:
 | 
			
		||||
        """:return: Blob using the information of this index entry"""
 | 
			
		||||
        return Blob(repo, self.binsha, self.mode, self.path)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class IndexEntry(BaseIndexEntry):
 | 
			
		||||
 | 
			
		||||
    """Allows convenient access to IndexEntry data without completely unpacking it.
 | 
			
		||||
 | 
			
		||||
    Attributes usully accessed often are cached in the tuple whereas others are
 | 
			
		||||
    unpacked on demand.
 | 
			
		||||
 | 
			
		||||
    See the properties for a mapping between names and tuple indices."""
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def ctime(self) -> Tuple[int, int]:
 | 
			
		||||
        """
 | 
			
		||||
        :return:
 | 
			
		||||
            Tuple(int_time_seconds_since_epoch, int_nano_seconds) of the
 | 
			
		||||
            file's creation time"""
 | 
			
		||||
        return cast(Tuple[int, int], unpack(">LL", self.ctime_bytes))
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def mtime(self) -> Tuple[int, int]:
 | 
			
		||||
        """See ctime property, but returns modification time"""
 | 
			
		||||
        return cast(Tuple[int, int], unpack(">LL", self.mtime_bytes))
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def from_base(cls, base: "BaseIndexEntry") -> "IndexEntry":
 | 
			
		||||
        """
 | 
			
		||||
        :return:
 | 
			
		||||
            Minimal entry as created from the given BaseIndexEntry instance.
 | 
			
		||||
            Missing values will be set to null-like values
 | 
			
		||||
 | 
			
		||||
        :param base: Instance of type BaseIndexEntry"""
 | 
			
		||||
        time = pack(">LL", 0, 0)
 | 
			
		||||
        return IndexEntry((base.mode, base.binsha, base.flags, base.path, time, time, 0, 0, 0, 0, 0))
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def from_blob(cls, blob: Blob, stage: int = 0) -> "IndexEntry":
 | 
			
		||||
        """:return: Minimal entry resembling the given blob object"""
 | 
			
		||||
        time = pack(">LL", 0, 0)
 | 
			
		||||
        return IndexEntry(
 | 
			
		||||
            (
 | 
			
		||||
                blob.mode,
 | 
			
		||||
                blob.binsha,
 | 
			
		||||
                stage << CE_STAGESHIFT,
 | 
			
		||||
                blob.path,
 | 
			
		||||
                time,
 | 
			
		||||
                time,
 | 
			
		||||
                0,
 | 
			
		||||
                0,
 | 
			
		||||
                0,
 | 
			
		||||
                0,
 | 
			
		||||
                blob.size,
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
							
								
								
									
										119
									
								
								zero-cost-nas/.eggs/GitPython-3.1.31-py3.8.egg/git/index/util.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										119
									
								
								zero-cost-nas/.eggs/GitPython-3.1.31-py3.8.egg/git/index/util.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,119 @@
 | 
			
		||||
"""Module containing index utilities"""
 | 
			
		||||
from functools import wraps
 | 
			
		||||
import os
 | 
			
		||||
import struct
 | 
			
		||||
import tempfile
 | 
			
		||||
 | 
			
		||||
from git.compat import is_win
 | 
			
		||||
 | 
			
		||||
import os.path as osp
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# typing ----------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
from typing import Any, Callable, TYPE_CHECKING
 | 
			
		||||
 | 
			
		||||
from git.types import PathLike, _T
 | 
			
		||||
 | 
			
		||||
if TYPE_CHECKING:
 | 
			
		||||
    from git.index import IndexFile
 | 
			
		||||
 | 
			
		||||
# ---------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
__all__ = ("TemporaryFileSwap", "post_clear_cache", "default_index", "git_working_dir")
 | 
			
		||||
 | 
			
		||||
# { Aliases
 | 
			
		||||
pack = struct.pack
 | 
			
		||||
unpack = struct.unpack
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# } END aliases
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TemporaryFileSwap(object):
 | 
			
		||||
 | 
			
		||||
    """Utility class moving a file to a temporary location within the same directory
 | 
			
		||||
    and moving it back on to where on object deletion."""
 | 
			
		||||
 | 
			
		||||
    __slots__ = ("file_path", "tmp_file_path")
 | 
			
		||||
 | 
			
		||||
    def __init__(self, file_path: PathLike) -> None:
 | 
			
		||||
        self.file_path = file_path
 | 
			
		||||
        self.tmp_file_path = str(self.file_path) + tempfile.mktemp("", "", "")
 | 
			
		||||
        # it may be that the source does not exist
 | 
			
		||||
        try:
 | 
			
		||||
            os.rename(self.file_path, self.tmp_file_path)
 | 
			
		||||
        except OSError:
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
    def __del__(self) -> None:
 | 
			
		||||
        if osp.isfile(self.tmp_file_path):
 | 
			
		||||
            if is_win and osp.exists(self.file_path):
 | 
			
		||||
                os.remove(self.file_path)
 | 
			
		||||
            os.rename(self.tmp_file_path, self.file_path)
 | 
			
		||||
        # END temp file exists
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# { Decorators
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def post_clear_cache(func: Callable[..., _T]) -> Callable[..., _T]:
 | 
			
		||||
    """Decorator for functions that alter the index using the git command. This would
 | 
			
		||||
    invalidate our possibly existing entries dictionary which is why it must be
 | 
			
		||||
    deleted to allow it to be lazily reread later.
 | 
			
		||||
 | 
			
		||||
    :note:
 | 
			
		||||
        This decorator will not be required once all functions are implemented
 | 
			
		||||
        natively which in fact is possible, but probably not feasible performance wise.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    @wraps(func)
 | 
			
		||||
    def post_clear_cache_if_not_raised(self: "IndexFile", *args: Any, **kwargs: Any) -> _T:
 | 
			
		||||
        rval = func(self, *args, **kwargs)
 | 
			
		||||
        self._delete_entries_cache()
 | 
			
		||||
        return rval
 | 
			
		||||
 | 
			
		||||
    # END wrapper method
 | 
			
		||||
 | 
			
		||||
    return post_clear_cache_if_not_raised
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def default_index(func: Callable[..., _T]) -> Callable[..., _T]:
 | 
			
		||||
    """Decorator assuring the wrapped method may only run if we are the default
 | 
			
		||||
    repository index. This is as we rely on git commands that operate
 | 
			
		||||
    on that index only."""
 | 
			
		||||
 | 
			
		||||
    @wraps(func)
 | 
			
		||||
    def check_default_index(self: "IndexFile", *args: Any, **kwargs: Any) -> _T:
 | 
			
		||||
        if self._file_path != self._index_path():
 | 
			
		||||
            raise AssertionError(
 | 
			
		||||
                "Cannot call %r on indices that do not represent the default git index" % func.__name__
 | 
			
		||||
            )
 | 
			
		||||
        return func(self, *args, **kwargs)
 | 
			
		||||
 | 
			
		||||
    # END wrapper method
 | 
			
		||||
 | 
			
		||||
    return check_default_index
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def git_working_dir(func: Callable[..., _T]) -> Callable[..., _T]:
 | 
			
		||||
    """Decorator which changes the current working dir to the one of the git
 | 
			
		||||
    repository in order to assure relative paths are handled correctly"""
 | 
			
		||||
 | 
			
		||||
    @wraps(func)
 | 
			
		||||
    def set_git_working_dir(self: "IndexFile", *args: Any, **kwargs: Any) -> _T:
 | 
			
		||||
        cur_wd = os.getcwd()
 | 
			
		||||
        os.chdir(str(self.repo.working_tree_dir))
 | 
			
		||||
        try:
 | 
			
		||||
            return func(self, *args, **kwargs)
 | 
			
		||||
        finally:
 | 
			
		||||
            os.chdir(cur_wd)
 | 
			
		||||
        # END handle working dir
 | 
			
		||||
 | 
			
		||||
    # END wrapper
 | 
			
		||||
 | 
			
		||||
    return set_git_working_dir
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# } END decorators
 | 
			
		||||
		Reference in New Issue
	
	Block a user