4545
4646from .fun import (
4747 find_submodule_git_dir ,
48- find_worktree_git_dir ,
4948 is_git_dir ,
5049 rev_parse ,
5150 touch ,
@@ -234,6 +233,10 @@ def __init__(
234233 ) -> None :
235234 R"""Create a new :class:`Repo` instance.
236235
236+ .. note::
237+ Repositories using reftable may be opened, but GitPython's direct reference
238+ access does not support reftable.
239+
237240 :param path:
238241 The path to either the worktree directory or the .git directory itself::
239242
@@ -268,7 +271,11 @@ def __init__(
268271 :class:`Repo`
269272 """
270273
271- epath = path or os .getenv ("GIT_DIR" )
274+ git_dir_env = os .getenv ("GIT_DIR" )
275+ object_dir_env = os .getenv ("GIT_OBJECT_DIRECTORY" )
276+ if object_dir_env is not None :
277+ object_dir_env = osp .abspath (object_dir_env )
278+ epath = path or git_dir_env
272279 if not epath :
273280 epath = os .getcwd ()
274281 epath = os .fspath (epath )
@@ -290,37 +297,48 @@ def __init__(
290297 raise NoSuchPathError (epath )
291298
292299 # Walk up the path to find the `.git` dir.
293- curpath = epath
294- git_dir = None
300+ curpath = os .fspath (epath ) if epath is not None else ""
301+ git_dir : Optional [str ] = None
302+ explicit_git_dir = not path and bool (git_dir_env )
295303 while curpath :
296304 # ABOUT osp.NORMPATH
297305 # It's important to normalize the paths, as submodules will otherwise
298306 # initialize their repo instances with paths that depend on path-portions
299307 # that will not exist after being removed. It's just cleaner.
300- if (
301- osp .isfile (osp .join (curpath , "gitdir" ))
302- and osp .isfile (osp .join (curpath , "commondir" ))
303- and osp .isfile (osp .join (curpath , "HEAD" ))
304- ):
305- git_dir = curpath
306-
307- if "GIT_WORK_TREE" in os .environ :
308- self ._working_tree_dir = os .getenv ("GIT_WORK_TREE" )
309- else :
310- # Linked worktree administrative directories store the path to the
311- # worktree's .git file in their gitdir file (without "gitdir: " prefix).
312- with open (osp .join (git_dir , "gitdir" )) as fp :
313- worktree_gitfile = fp .read ().strip ()
308+ if not explicit_git_dir :
309+ dotgit = osp .join (curpath , ".git" )
310+ try :
311+ sm_gitpath = find_submodule_git_dir (dotgit )
312+ except OSError :
313+ break
314+ if sm_gitpath is not None :
315+ # Worktrees can use relative paths as of Git 2.48, so join to curpath.
316+ git_dir = osp .normpath (osp .join (curpath , os .fspath (sm_gitpath )))
317+ self ._working_tree_dir = curpath
318+ break
319+
320+ # Like Git, do not fall back to a bare repository or parent directory when
321+ # a non-directory .git entry exists but is not a valid gitfile.
322+ if osp .exists (dotgit ) and not osp .isdir (dotgit ):
323+ break
314324
315- if not osp .isabs (worktree_gitfile ):
316- worktree_gitfile = osp .normpath (osp .join (git_dir , worktree_gitfile ))
325+ if is_git_dir (curpath ):
326+ git_dir = curpath
327+ if osp .isfile (osp .join (curpath , "gitdir" )) and osp .isfile (osp .join (curpath , "commondir" )):
328+ if "GIT_WORK_TREE" in os .environ :
329+ self ._working_tree_dir = os .getenv ("GIT_WORK_TREE" )
330+ else :
331+ # Linked worktree administrative directories store the path to
332+ # the worktree's .git file in gitdir (without a "gitdir: " prefix).
333+ with open (osp .join (git_dir , "gitdir" )) as fp :
334+ worktree_gitfile = fp .read ().strip ()
317335
318- self ._working_tree_dir = osp .dirname (worktree_gitfile )
336+ if not osp .isabs (worktree_gitfile ):
337+ worktree_gitfile = osp .normpath (osp .join (git_dir , worktree_gitfile ))
319338
320- break
339+ self ._working_tree_dir = osp .dirname (worktree_gitfile )
340+ break
321341
322- if is_git_dir (curpath ):
323- git_dir = curpath
324342 # from man git-config : core.worktree
325343 # Set the path to the root of the working tree. If GIT_COMMON_DIR
326344 # environment variable is set, core.worktree is ignored and not used for
@@ -340,22 +358,7 @@ def __init__(
340358 self ._working_tree_dir = os .getenv ("GIT_WORK_TREE" )
341359 break
342360
343- dotgit = osp .join (curpath , ".git" )
344- sm_gitpath = find_submodule_git_dir (dotgit )
345- if sm_gitpath is not None :
346- git_dir = osp .normpath (sm_gitpath )
347-
348- sm_gitpath = find_submodule_git_dir (dotgit )
349- if sm_gitpath is None :
350- sm_gitpath = find_worktree_git_dir (dotgit )
351-
352- if sm_gitpath is not None :
353- # worktrees can use relative paths as of Git 2.48, so we join to curpath
354- git_dir = osp .normpath (osp .join (curpath , sm_gitpath ))
355- self ._working_tree_dir = curpath
356- break
357-
358- if not search_parent_directories :
361+ if explicit_git_dir or not search_parent_directories :
359362 break
360363 curpath , tail = osp .split (curpath )
361364 if not tail :
@@ -366,19 +369,23 @@ def __init__(
366369 raise InvalidGitRepositoryError (epath )
367370 self .git_dir = git_dir
368371
372+ common_dir_env = os .getenv ("GIT_COMMON_DIR" )
373+ if common_dir_env is not None :
374+ self ._common_dir = osp .abspath (common_dir_env )
375+ else :
376+ try :
377+ common_dir = os .fsdecode ((Path (self .git_dir ) / "commondir" ).read_bytes ()).rstrip ("\r \n " )
378+ self ._common_dir = osp .join (self .git_dir , common_dir )
379+ except OSError :
380+ self ._common_dir = ""
381+
369382 self ._bare = False
370383 try :
371384 self ._bare = self .config_reader ("repository" ).getboolean ("core" , "bare" )
372385 except Exception :
373386 # Let's not assume the option exists, although it should.
374387 pass
375388
376- try :
377- common_dir = (Path (self .git_dir ) / "commondir" ).read_text ().splitlines ()[0 ].strip ()
378- self ._common_dir = osp .join (self .git_dir , common_dir )
379- except OSError :
380- self ._common_dir = ""
381-
382389 # Adjust the working directory in case we are actually bare - we didn't know
383390 # that in the first place.
384391 if self ._bare :
@@ -387,9 +394,15 @@ def __init__(
387394
388395 self .working_dir : PathLike = self ._working_tree_dir or self .common_dir
389396 self .git = self .GitCommandWrapperType (self .working_dir )
397+ if common_dir_env is not None :
398+ self .git .update_environment (GIT_DIR = os .fspath (self .git_dir ), GIT_COMMON_DIR = os .fspath (self .common_dir ))
399+ elif git_dir_env is not None :
400+ self .git .update_environment (GIT_DIR = os .fspath (self .git_dir ))
401+ if object_dir_env is not None :
402+ self .git .update_environment (GIT_OBJECT_DIRECTORY = object_dir_env )
390403
391404 # Special handling, in special times.
392- rootpath = osp .join (self .common_dir , "objects" )
405+ rootpath = object_dir_env if object_dir_env is not None else osp .join (self .common_dir , "objects" )
393406 if issubclass (odbt , GitCmdObjectDB ):
394407 self .odb = odbt (rootpath , self .git )
395408 else :
@@ -990,7 +1003,7 @@ def _get_alternates(self) -> List[str]:
9901003 :return:
9911004 List of strings being pathnames of alternates
9921005 """
993- alternates_path = osp .join (self .common_dir , "objects" , "info" , "alternates" )
1006+ alternates_path = osp .join (self .odb . root_path () , "info" , "alternates" )
9941007
9951008 if osp .exists (alternates_path ):
9961009 with open (alternates_path , "rb" ) as f :
@@ -1011,7 +1024,7 @@ def _set_alternates(self, alts: List[str]) -> None:
10111024 The method does not check for the existence of the paths in `alts`, as the
10121025 caller is responsible.
10131026 """
1014- alternates_path = osp .join (self .common_dir , "objects" , "info" , "alternates" )
1027+ alternates_path = osp .join (self .odb . root_path () , "info" , "alternates" )
10151028 if not alts :
10161029 if osp .isfile (alternates_path ):
10171030 os .remove (alternates_path )
0 commit comments