| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 4bd708d commit aec58a9
6 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -18,5 +18,6 @@ Contributors are: | |||
| 18 | 18 | -Bernard `Guyzmo` Pratz <guyzmo+gitpython+pub@m0g.net> | |
| 19 | 19 | -Timothy B. Hartman <tbhartman _at_ gmail.com> | |
| 20 | 20 | -Konstantin Popov <konstantin.popov.89 _at_ yandex.ru> | |
| 21 | + -Peter Jones <pjones _at_ redhat.com> | ||
| 21 | 22 | ||
| 22 | 23 | Portions derived from other open source works and are clearly marked. | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -75,7 +75,12 @@ def abspath(self): | |||
| 75 | 75 | ||
| 76 | 76 | @classmethod | |
| 77 | 77 | def _get_packed_refs_path(cls, repo): | |
| 78 | - return osp.join(repo.git_dir, 'packed-refs') | ||
| 78 | + try: | ||
| 79 | + commondir = open(osp.join(repo.git_dir, 'commondir'), 'rt').readlines()[0].strip() | ||
| 80 | + except (OSError, IOError): | ||
| 81 | + commondir = '.' | ||
| 82 | + repodir = osp.join(repo.git_dir, commondir) | ||
| 83 | + return osp.join(repodir, 'packed-refs') | ||
| 79 | 84 | ||
| 80 | 85 | @classmethod | |
| 81 | 86 | def _iter_packed_refs(cls, repo): | |
@@ -122,13 +127,13 @@ def dereference_recursive(cls, repo, ref_path): | |||
| 122 | 127 | # END recursive dereferencing | |
| 123 | 128 | ||
| 124 | 129 | @classmethod | |
| 125 | - def _get_ref_info(cls, repo, ref_path): | ||
| 130 | + def _get_ref_info_helper(cls, repo, repodir, ref_path): | ||
| 126 | 131 | """Return: (str(sha), str(target_ref_path)) if available, the sha the file at | |
| 127 | 132 | rela_path points to, or None. target_ref_path is the reference we | |
| 128 | 133 | point to, or None""" | |
| 129 | 134 | tokens = None | |
| 130 | 135 | try: | |
| 131 | - with open(osp.join(repo.git_dir, ref_path), 'rt') as fp: | ||
| 136 | + with open(osp.join(repodir, ref_path), 'rt') as fp: | ||
| 132 | 137 | value = fp.read().rstrip() | |
| 133 | 138 | # Don't only split on spaces, but on whitespace, which allows to parse lines like | |
| 134 | 139 | # 60b64ef992065e2600bfef6187a97f92398a9144 branch 'master' of git-server:/path/to/repo | |
@@ -159,6 +164,22 @@ def _get_ref_info(cls, repo, ref_path): | |||
| 159 | 164 | ||
| 160 | 165 | raise ValueError("Failed to parse reference information from %r" % ref_path) | |
| 161 | 166 | ||
| 167 | + @classmethod | ||
| 168 | + def _get_ref_info(cls, repo, ref_path): | ||
| 169 | + """Return: (str(sha), str(target_ref_path)) if available, the sha the file at | ||
| 170 | + rela_path points to, or None. target_ref_path is the reference we | ||
| 171 | + point to, or None""" | ||
| 172 | + try: | ||
| 173 | + return cls._get_ref_info_helper(repo, repo.git_dir, ref_path) | ||
| 174 | + except ValueError: | ||
| 175 | + try: | ||
| 176 | + commondir = open(osp.join(repo.git_dir, 'commondir'), 'rt').readlines()[0].strip() | ||
| 177 | + except (OSError, IOError): | ||
| 178 | + commondir = '.' | ||
| 179 | + | ||
| 180 | + repodir = osp.join(repo.git_dir, commondir) | ||
| 181 | + return cls._get_ref_info_helper(repo, repodir, ref_path) | ||
| 182 | + | ||
| 162 | 183 | def _get_object(self): | |
| 163 | 184 | """ | |
| 164 | 185 | :return: | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -32,7 +32,7 @@ | |||
| 32 | 32 | from git.util import Actor, finalize_process, decygpath, hex_to_bin | |
| 33 | 33 | import os.path as osp | |
| 34 | 34 | ||
| 35 | - from .fun import rev_parse, is_git_dir, find_submodule_git_dir, touch | ||
| 35 | + from .fun import rev_parse, is_git_dir, find_submodule_git_dir, touch, find_worktree_git_dir | ||
| 36 | 36 | import gc | |
| 37 | 37 | import gitdb | |
| 38 | 38 | ||
@@ -138,10 +138,15 @@ def __init__(self, path=None, odbt=DefaultDBType, search_parent_directories=Fals | |||
| 138 | 138 | self._working_tree_dir = os.getenv('GIT_WORK_TREE', os.path.dirname(self.git_dir)) | |
| 139 | 139 | break | |
| 140 | 140 | ||
| 141 | - sm_gitpath = find_submodule_git_dir(osp.join(curpath, '.git')) | ||
| 141 | + dotgit = osp.join(curpath, '.git') | ||
| 142 | + sm_gitpath = find_submodule_git_dir(dotgit) | ||
| 142 | 143 | if sm_gitpath is not None: | |
| 143 | 144 | self.git_dir = osp.normpath(sm_gitpath) | |
| 144 | - sm_gitpath = find_submodule_git_dir(osp.join(curpath, '.git')) | ||
| 145 | + | ||
| 146 | + sm_gitpath = find_submodule_git_dir(dotgit) | ||
| 147 | + if sm_gitpath is None: | ||
| 148 | + sm_gitpath = find_worktree_git_dir(dotgit) | ||
| 149 | + | ||
| 145 | 150 | if sm_gitpath is not None: | |
| 146 | 151 | self.git_dir = _expand_path(sm_gitpath) | |
| 147 | 152 | self._working_tree_dir = curpath | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,5 +1,6 @@ | |||
| 1 | 1 | """Package with general repository related functions""" | |
| 2 | 2 | import os | |
| 3 | + import stat | ||
| 3 | 4 | from string import digits | |
| 4 | 5 | ||
| 5 | 6 | from git.compat import xrange | |
@@ -17,7 +18,7 @@ | |||
| 17 | 18 | ||
| 18 | 19 | ||
| 19 | 20 | __all__ = ('rev_parse', 'is_git_dir', 'touch', 'find_submodule_git_dir', 'name_to_object', 'short_to_long', 'deref_tag', | |
| 20 | - 'to_commit') | ||
| 21 | + 'to_commit', 'find_worktree_git_dir') | ||
| 21 | 22 | ||
| 22 | 23 | ||
| 23 | 24 | def touch(filename): | |
@@ -47,6 +48,25 @@ def is_git_dir(d): | |||
| 47 | 48 | return False | |
| 48 | 49 | ||
| 49 | 50 | ||
| 51 | + def find_worktree_git_dir(dotgit): | ||
| 52 | + """Search for a gitdir for this worktree.""" | ||
| 53 | + try: | ||
| 54 | + statbuf = os.stat(dotgit) | ||
| 55 | + except OSError: | ||
| 56 | + return None | ||
| 57 | + if not stat.S_ISREG(statbuf.st_mode): | ||
| 58 | + return None | ||
| 59 | + | ||
| 60 | + try: | ||
| 61 | + lines = open(dotgit, 'r').readlines() | ||
| 62 | + for key, value in [line.strip().split(': ') for line in lines]: | ||
| 63 | + if key == 'gitdir': | ||
| 64 | + return value | ||
| 65 | + except ValueError: | ||
| 66 | + pass | ||
| 67 | + return None | ||
| 68 | + | ||
| 69 | + | ||
| 50 | 70 | def find_submodule_git_dir(d): | |
| 51 | 71 | """Search for a submodule repo.""" | |
| 52 | 72 | if is_git_dir(d): | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,10 +1,14 @@ | |||
| 1 | 1 | from io import BytesIO | |
| 2 | 2 | from stat import S_IFDIR, S_IFREG, S_IFLNK | |
| 3 | + from os import stat | ||
| 4 | + import os.path as osp | ||
| 5 | + | ||
| 3 | 6 | try: | |
| 4 | - from unittest import skipIf | ||
| 7 | + from unittest import skipIf, SkipTest | ||
| 5 | 8 | except ImportError: | |
| 6 | - from unittest2 import skipIf | ||
| 9 | + from unittest2 import skipIf, SkipTest | ||
| 7 | 10 | ||
| 11 | + from git import Git | ||
| 8 | 12 | from git.compat import PY3 | |
| 9 | 13 | from git.index import IndexFile | |
| 10 | 14 | from git.index.fun import ( | |
@@ -14,13 +18,18 @@ | |||
| 14 | 18 | traverse_tree_recursive, | |
| 15 | 19 | traverse_trees_recursive, | |
| 16 | 20 | tree_to_stream, | |
| 17 | - tree_entries_from_data | ||
| 21 | + tree_entries_from_data, | ||
| 22 | + ) | ||
| 23 | + from git.repo.fun import ( | ||
| 24 | + find_worktree_git_dir | ||
| 18 | 25 | ) | |
| 19 | 26 | from git.test.lib import ( | |
| 27 | + assert_true, | ||
| 20 | 28 | TestBase, | |
| 21 | - with_rw_repo | ||
| 29 | + with_rw_repo, | ||
| 30 | + with_rw_directory | ||
| 22 | 31 | ) | |
| 23 | - from git.util import bin_to_hex | ||
| 32 | + from git.util import bin_to_hex, cygpath, join_path_native | ||
| 24 | 33 | from gitdb.base import IStream | |
| 25 | 34 | from gitdb.typ import str_tree_type | |
| 26 | 35 | ||
@@ -254,6 +263,29 @@ def test_tree_traversal_single(self): | |||
| 254 | 263 | assert entries | |
| 255 | 264 | # END for each commit | |
| 256 | 265 | ||
| 266 | + @with_rw_directory | ||
| 267 | + def test_linked_worktree_traversal(self, rw_dir): | ||
| 268 | + """Check that we can identify a linked worktree based on a .git file""" | ||
| 269 | + git = Git(rw_dir) | ||
| 270 | + if git.version_info[:3] < (2, 5, 1): | ||
| 271 | + raise SkipTest("worktree feature unsupported") | ||
| 272 | + | ||
| 273 | + rw_master = self.rorepo.clone(join_path_native(rw_dir, 'master_repo')) | ||
| 274 | + branch = rw_master.create_head('aaaaaaaa') | ||
| 275 | + worktree_path = join_path_native(rw_dir, 'worktree_repo') | ||
| 276 | + if Git.is_cygwin(): | ||
| 277 | + worktree_path = cygpath(worktree_path) | ||
| 278 | + rw_master.git.worktree('add', worktree_path, branch.name) | ||
| 279 | + | ||
| 280 | + dotgit = osp.join(worktree_path, ".git") | ||
| 281 | + statbuf = stat(dotgit) | ||
| 282 | + assert_true(statbuf.st_mode & S_IFREG) | ||
| 283 | + | ||
| 284 | + gitdir = find_worktree_git_dir(dotgit) | ||
| 285 | + self.assertIsNotNone(gitdir) | ||
| 286 | + statbuf = stat(gitdir) | ||
| 287 | + assert_true(statbuf.st_mode & S_IFDIR) | ||
| 288 | + | ||
| 257 | 289 | @skipIf(PY3, 'odd types returned ... maybe figure it out one day') | |
| 258 | 290 | def test_tree_entries_from_data_with_failing_name_decode_py2(self): | |
| 259 | 291 | r = tree_entries_from_data(b'100644 \x9f\0aaa') | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -22,6 +22,7 @@ | |||
| 22 | 22 | NoSuchPathError, | |
| 23 | 23 | Head, | |
| 24 | 24 | Commit, | |
| 25 | + Object, | ||
| 25 | 26 | Tree, | |
| 26 | 27 | IndexFile, | |
| 27 | 28 | Git, | |
@@ -911,22 +912,28 @@ def test_is_ancestor(self): | |||
| 911 | 912 | self.assertRaises(GitCommandError, repo.is_ancestor, i, j) | |
| 912 | 913 | ||
| 913 | 914 | @with_rw_directory | |
| 914 | - def test_work_tree_unsupported(self, rw_dir): | ||
| 915 | + def test_git_work_tree_dotgit(self, rw_dir): | ||
| 916 | + """Check that we find .git as a worktree file and find the worktree | ||
| 917 | + based on it.""" | ||
| 915 | 918 | git = Git(rw_dir) | |
| 916 | 919 | if git.version_info[:3] < (2, 5, 1): | |
| 917 | 920 | raise SkipTest("worktree feature unsupported") | |
| 918 | 921 | ||
| 919 | 922 | rw_master = self.rorepo.clone(join_path_native(rw_dir, 'master_repo')) | |
| 920 | - rw_master.git.checkout('HEAD~10') | ||
| 923 | + branch = rw_master.create_head('aaaaaaaa') | ||
| 921 | 924 | worktree_path = join_path_native(rw_dir, 'worktree_repo') | |
| 922 | 925 | if Git.is_cygwin(): | |
| 923 | 926 | worktree_path = cygpath(worktree_path) | |
| 924 | - try: | ||
| 925 | - rw_master.git.worktree('add', worktree_path, 'master') | ||
| 926 | - except Exception as ex: | ||
| 927 | - raise AssertionError(ex, "It's ok if TC not running from `master`.") | ||
| 927 | + rw_master.git.worktree('add', worktree_path, branch.name) | ||
| 928 | + | ||
| 929 | + # this ensures that we can read the repo's gitdir correctly | ||
| 930 | + repo = Repo(worktree_path) | ||
| 931 | + self.assertIsInstance(repo, Repo) | ||
| 928 | 932 | ||
| 929 | - self.failUnlessRaises(InvalidGitRepositoryError, Repo, worktree_path) | ||
| 933 | + # this ensures we're able to actually read the refs in the tree, which | ||
| 934 | + # means we can read commondir correctly. | ||
| 935 | + commit = repo.head.commit | ||
| 936 | + self.assertIsInstance(commit, Object) | ||
| 930 | 937 | ||
| 931 | 938 | @with_rw_directory | |
| 932 | 939 | def test_git_work_tree_env(self, rw_dir): | |
| Back | FazBrowse Home | New Git URL |
0 commit comments