FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

Fix repository discovery precedence by Byron · Pull Request #2218 · gitpython-developers/GitPython · GitHub

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension .py  (3) .rst  (1) No extension  (1) All 3 file types selected
Viewed files
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Unified
Split
Hide whitespace
Diff view
Unified
Split
Hide whitespace
2 changes: 1 addition & 1 deletion VERSION
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3.1.59
3.1.60
1 change: 1 addition & 0 deletions doc/source/changes.rst
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Security fixes for

* https://github.com/gitpython-developers/GitPython/security/advisories/GHSA-g5vv-9gxw-82hx
* https://github.com/gitpython-developers/GitPython/security/advisories/GHSA-whh4-5q6c-9v3x
* https://github.com/gitpython-developers/GitPython/security/advisories/GHSA-239g-whfq-7xj9

If you can, also try and provide feedback on the upcoming v4 branch
https://github.com/gitpython-developers/GitPython/pull/2177 - patches welcome.
Expand Down
111 changes: 62 additions & 49 deletions git/repo/base.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@

from .fun import (
find_submodule_git_dir,
find_worktree_git_dir,
is_git_dir,
rev_parse,
touch,
Expand Down Expand Up @@ -234,6 +233,10 @@ def __init__(
) -> None:
R"""Create a new :class:`Repo` instance.

.. note::
Repositories using reftable may be opened, but GitPython's direct reference
access does not support reftable.

:param path:
The path to either the worktree directory or the .git directory itself::

Expand Down Expand Up @@ -268,7 +271,11 @@ def __init__(
:class:`Repo`
"""

epath = path or os.getenv("GIT_DIR")
git_dir_env = os.getenv("GIT_DIR")
object_dir_env = os.getenv("GIT_OBJECT_DIRECTORY")
if object_dir_env is not None:
object_dir_env = osp.abspath(object_dir_env)
epath = path or git_dir_env
if not epath:
epath = os.getcwd()
epath = os.fspath(epath)
Expand All @@ -290,37 +297,48 @@ def __init__(
raise NoSuchPathError(epath)

# Walk up the path to find the `.git` dir.
curpath = epath
git_dir = None
curpath = os.fspath(epath) if epath is not None else ""
git_dir: Optional[str] = None
explicit_git_dir = not path and bool(git_dir_env)
while curpath:
# ABOUT osp.NORMPATH
# It's important to normalize the paths, as submodules will otherwise
# initialize their repo instances with paths that depend on path-portions
# that will not exist after being removed. It's just cleaner.
if (
osp.isfile(osp.join(curpath, "gitdir"))
and osp.isfile(osp.join(curpath, "commondir"))
and osp.isfile(osp.join(curpath, "HEAD"))
):
git_dir = curpath

if "GIT_WORK_TREE" in os.environ:
self._working_tree_dir = os.getenv("GIT_WORK_TREE")
else:
# Linked worktree administrative directories store the path to the
# worktree's .git file in their gitdir file (without "gitdir: " prefix).
with open(osp.join(git_dir, "gitdir")) as fp:
worktree_gitfile = fp.read().strip()
if not explicit_git_dir:
dotgit = osp.join(curpath, ".git")
try:
sm_gitpath = find_submodule_git_dir(dotgit)
except OSError:
break
if sm_gitpath is not None:
# Worktrees can use relative paths as of Git 2.48, so join to curpath.
git_dir = osp.normpath(osp.join(curpath, os.fspath(sm_gitpath)))
self._working_tree_dir = curpath
break

# Like Git, do not fall back to a bare repository or parent directory when
# a non-directory .git entry exists but is not a valid gitfile.
if osp.exists(dotgit) and not osp.isdir(dotgit):
break

if not osp.isabs(worktree_gitfile):
worktree_gitfile = osp.normpath(osp.join(git_dir, worktree_gitfile))
if is_git_dir(curpath):
git_dir = curpath
if osp.isfile(osp.join(curpath, "gitdir")) and osp.isfile(osp.join(curpath, "commondir")):
if "GIT_WORK_TREE" in os.environ:
self._working_tree_dir = os.getenv("GIT_WORK_TREE")
else:
# Linked worktree administrative directories store the path to
# the worktree's .git file in gitdir (without a "gitdir: " prefix).
with open(osp.join(git_dir, "gitdir")) as fp:
worktree_gitfile = fp.read().strip()

self._working_tree_dir = osp.dirname(worktree_gitfile)
if not osp.isabs(worktree_gitfile):
worktree_gitfile = osp.normpath(osp.join(git_dir, worktree_gitfile))

break
self._working_tree_dir = osp.dirname(worktree_gitfile)
break

if is_git_dir(curpath):
git_dir = curpath
# from man git-config : core.worktree
# Set the path to the root of the working tree. If GIT_COMMON_DIR
# environment variable is set, core.worktree is ignored and not used for
Expand All @@ -340,22 +358,7 @@ def __init__(
self._working_tree_dir = os.getenv("GIT_WORK_TREE")
break

dotgit = osp.join(curpath, ".git")
sm_gitpath = find_submodule_git_dir(dotgit)
if sm_gitpath is not None:
git_dir = osp.normpath(sm_gitpath)

sm_gitpath = find_submodule_git_dir(dotgit)
if sm_gitpath is None:
sm_gitpath = find_worktree_git_dir(dotgit)

if sm_gitpath is not None:
# worktrees can use relative paths as of Git 2.48, so we join to curpath
git_dir = osp.normpath(osp.join(curpath, sm_gitpath))
self._working_tree_dir = curpath
break

if not search_parent_directories:
if explicit_git_dir or not search_parent_directories:
break
curpath, tail = osp.split(curpath)
if not tail:
Expand All @@ -366,19 +369,23 @@ def __init__(
raise InvalidGitRepositoryError(epath)
self.git_dir = git_dir

common_dir_env = os.getenv("GIT_COMMON_DIR")
if common_dir_env is not None:
self._common_dir = osp.abspath(common_dir_env)
else:
try:
common_dir = os.fsdecode((Path(self.git_dir) / "commondir").read_bytes()).rstrip("\r\n")
self._common_dir = osp.join(self.git_dir, common_dir)
except OSError:
self._common_dir = ""

self._bare = False
try:
self._bare = self.config_reader("repository").getboolean("core", "bare")
except Exception:
# Let's not assume the option exists, although it should.
pass

try:
common_dir = (Path(self.git_dir) / "commondir").read_text().splitlines()[0].strip()
self._common_dir = osp.join(self.git_dir, common_dir)
except OSError:
self._common_dir = ""

# Adjust the working directory in case we are actually bare - we didn't know
# that in the first place.
if self._bare:
Expand All @@ -387,9 +394,15 @@ def __init__(

self.working_dir: PathLike = self._working_tree_dir or self.common_dir
self.git = self.GitCommandWrapperType(self.working_dir)
if common_dir_env is not None:
self.git.update_environment(GIT_DIR=os.fspath(self.git_dir), GIT_COMMON_DIR=os.fspath(self.common_dir))
elif git_dir_env is not None:
self.git.update_environment(GIT_DIR=os.fspath(self.git_dir))
if object_dir_env is not None:
self.git.update_environment(GIT_OBJECT_DIRECTORY=object_dir_env)

# Special handling, in special times.
rootpath = osp.join(self.common_dir, "objects")
rootpath = object_dir_env if object_dir_env is not None else osp.join(self.common_dir, "objects")
Comment thread
Byron marked this conversation as resolved.
if issubclass(odbt, GitCmdObjectDB):
self.odb = odbt(rootpath, self.git)
else:
Expand Down Expand Up @@ -990,7 +1003,7 @@ def _get_alternates(self) -> List[str]:
:return:
List of strings being pathnames of alternates
"""
alternates_path = osp.join(self.common_dir, "objects", "info", "alternates")
alternates_path = osp.join(self.odb.root_path(), "info", "alternates")

if osp.exists(alternates_path):
with open(alternates_path, "rb") as f:
Expand All @@ -1011,7 +1024,7 @@ def _set_alternates(self, alts: List[str]) -> None:
The method does not check for the existence of the paths in `alts`, as the
caller is responsible.
"""
alternates_path = osp.join(self.common_dir, "objects", "info", "alternates")
alternates_path = osp.join(self.odb.root_path(), "info", "alternates")
if not alts:
if osp.isfile(alternates_path):
os.remove(alternates_path)
Expand Down
110 changes: 71 additions & 39 deletions git/repo/fun.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -60,22 +60,61 @@ def touch(filename: str) -> str:
def is_git_dir(d: PathLike) -> bool:
"""This is taken from the git setup.c:is_git_directory function.

.. note::
This function recognizes repositories using reftable through their
compatibility files, but GitPython's direct reference access does not support
reftable.

:raise git.exc.WorkTreeRepositoryUnsupported:
If it sees a worktree directory. It's quite hacky to do that here, but at least
clearly indicates that we don't support it. There is the unlikely danger to
throw if we see directories which just look like a worktree dir, but are none.
"""
if osp.isdir(d):
if (osp.isdir(osp.join(d, "objects")) or "GIT_OBJECT_DIRECTORY" in os.environ) and osp.isdir(
osp.join(d, "refs")
):
headref = osp.join(d, "HEAD")
return osp.isfile(headref) or (osp.islink(headref) and os.readlink(headref).startswith("refs"))
elif (
osp.isfile(osp.join(d, "gitdir"))
and osp.isfile(osp.join(d, "commondir"))
and osp.isfile(osp.join(d, "gitfile"))
):
headref = osp.join(d, "HEAD")
if osp.islink(headref):
try:
valid_head = os.readlink(headref).startswith("refs/")
except OSError:
valid_head = False
else:
try:
with open(headref, "rb") as fp:
head = fp.read(256)
except OSError:
valid_head = False
else:
valid_head = (head.startswith(b"ref:") and head[4:].lstrip().startswith(b"refs/")) or bool(
re.match(rb"(?:[0-9A-Fa-f]{64}|[0-9A-Fa-f]{40})", head)
)
Comment thread
Byron marked this conversation as resolved.
Comment thread
Byron marked this conversation as resolved.

common_dir = os.getenv("GIT_COMMON_DIR")
if common_dir == "":
return False
if common_dir is None:
common_dir_file = Path(d) / "commondir"
try:
common_dir = os.fsdecode(common_dir_file.read_bytes()).rstrip("\r\n")
except FileNotFoundError:
if osp.lexists(common_dir_file):
return False
common_dir = os.fspath(d)
except (OSError, UnicodeError):
return False
else:
if not common_dir:
return False
try:
common_dir = osp.realpath(osp.join(d, common_dir))
except (OSError, ValueError):
return False

object_dir = os.getenv("GIT_OBJECT_DIRECTORY")
if object_dir is None:
object_dir = osp.join(common_dir, "objects")
if valid_head and osp.isdir(object_dir) and osp.isdir(osp.join(common_dir, "refs")):
Comment thread
Byron marked this conversation as resolved.
return True
if osp.isfile(osp.join(d, "gitdir")) and osp.isfile(osp.join(d, "commondir")) and osp.isfile(headref):
raise WorkTreeRepositoryUnsupported(d)
return False

Expand All @@ -84,46 +123,39 @@ def find_worktree_git_dir(dotgit: PathLike) -> Optional[str]:
"""Search for a gitdir for this worktree."""
try:
statbuf = os.stat(dotgit)
except OSError:
except (FileNotFoundError, NotADirectoryError):
return None
if not stat.S_ISREG(statbuf.st_mode):
if not stat.S_ISREG(statbuf.st_mode) or statbuf.st_size > (1 << 20):
return None

try:
lines = Path(dotgit).read_text().splitlines()
for key, value in [line.strip().split(": ") for line in lines]:
if key == "gitdir":
return value
except ValueError:
pass
return None
with open(dotgit, "rb") as fp:
content_bytes = fp.read(statbuf.st_size)
if len(content_bytes) != statbuf.st_size:
return None
content = os.fsdecode(content_bytes).rstrip("\r\n")
except (OSError, UnicodeError):
return None
return content[8:] if len(content) >= 9 and content.startswith("gitdir: ") else None


def find_submodule_git_dir(d: PathLike) -> Optional[PathLike]:
"""Search for a submodule repo."""
if is_git_dir(d):
return d

try:
with open(d) as fp:
content = fp.read().rstrip()
except IOError:
# It's probably not a file.
pass
else:
if content.startswith("gitdir: "):
path = content[8:]

if Git.is_cygwin():
# Cygwin creates submodules prefixed with `/cygdrive/...`.
# Cygwin git understands Cygwin paths much better than Windows ones.
# Also the Cygwin tests are assuming Cygwin paths.
path = cygpath(path)
if not osp.isabs(path):
path = osp.normpath(osp.join(osp.dirname(d), path))
return find_submodule_git_dir(path)
# END handle exception
return None
path = find_worktree_git_dir(d)
if path is None:
return None

if Git.is_cygwin():
# Cygwin creates submodules prefixed with `/cygdrive/...`.
# Cygwin git understands Cygwin paths much better than Windows ones.
# Also the Cygwin tests are assuming Cygwin paths.
path = cygpath(path)
if not osp.isabs(path):
path = osp.normpath(osp.join(osp.dirname(d), path))
return path if is_git_dir(path) else None


def short_to_long(odb: "GitCmdObjectDB", hexsha: str) -> Optional[bytes]:
Expand Down
Loading
Loading

Back | FazBrowse Home | New Git URL