support git worktrees
This commit is contained in:
parent
68d846314c
commit
9afda875cb
8 changed files with 77 additions and 35 deletions
1
tests/samples/projects/git-worktree/dot_git
Normal file
1
tests/samples/projects/git-worktree/dot_git
Normal file
|
@ -0,0 +1 @@
|
||||||
|
gitdir: ../git/.git/worktrees/git-worktree
|
0
tests/samples/projects/git-worktree/emptyfile.txt
Normal file
0
tests/samples/projects/git-worktree/emptyfile.txt
Normal file
|
@ -0,0 +1 @@
|
||||||
|
ref: refs/heads/worktree-detection-branch
|
|
@ -0,0 +1 @@
|
||||||
|
68d846314cd2c2fd51502924a7b644d6cf3d8904
|
|
@ -0,0 +1 @@
|
||||||
|
../..
|
|
@ -0,0 +1 @@
|
||||||
|
../../../../git-worktree/.git
|
|
@ -452,8 +452,23 @@ class ProjectTestCase(TestCase):
|
||||||
expected = 'WakaTime WARNING Regex error (unbalanced parenthesis at position 15) for disable git submodules pattern: \\(invalid regex)'
|
expected = 'WakaTime WARNING Regex error (unbalanced parenthesis at position 15) for disable git submodules pattern: \\(invalid regex)'
|
||||||
self.assertEquals(expected, actual)
|
self.assertEquals(expected, actual)
|
||||||
|
|
||||||
|
def test_git_worktree_detected(self):
|
||||||
|
tempdir = tempfile.mkdtemp()
|
||||||
|
shutil.copytree('tests/samples/projects/git-worktree', os.path.join(tempdir, 'git-wt'))
|
||||||
|
shutil.copytree('tests/samples/projects/git', os.path.join(tempdir, 'git'))
|
||||||
|
shutil.move(os.path.join(tempdir, 'git-wt', 'dot_git'), os.path.join(tempdir, 'git-wt', '.git'))
|
||||||
|
shutil.move(os.path.join(tempdir, 'git', 'dot_git'), os.path.join(tempdir, 'git', '.git'))
|
||||||
|
|
||||||
|
entity = os.path.join(tempdir, 'git-wt', 'emptyfile.txt')
|
||||||
|
|
||||||
|
self.shared(
|
||||||
|
expected_project='git',
|
||||||
|
expected_branch='worktree-detection-branch',
|
||||||
|
entity=entity,
|
||||||
|
)
|
||||||
|
|
||||||
@log_capture()
|
@log_capture()
|
||||||
def test_git_find_path_from_submodule(self, logs):
|
def test_git_path_from_gitdir_link_file(self, logs):
|
||||||
logging.disable(logging.NOTSET)
|
logging.disable(logging.NOTSET)
|
||||||
|
|
||||||
tempdir = tempfile.mkdtemp()
|
tempdir = tempfile.mkdtemp()
|
||||||
|
@ -464,7 +479,7 @@ class ProjectTestCase(TestCase):
|
||||||
path = os.path.join(tempdir, 'git', 'asubmodule')
|
path = os.path.join(tempdir, 'git', 'asubmodule')
|
||||||
|
|
||||||
git = Git(None)
|
git = Git(None)
|
||||||
result = git._find_path_from_submodule(path)
|
result = git._path_from_gitdir_link_file(path)
|
||||||
|
|
||||||
expected = os.path.realpath(os.path.join(tempdir, 'git', '.git', 'modules', 'asubmodule'))
|
expected = os.path.realpath(os.path.join(tempdir, 'git', '.git', 'modules', 'asubmodule'))
|
||||||
self.assertEquals(expected, result)
|
self.assertEquals(expected, result)
|
||||||
|
@ -472,7 +487,7 @@ class ProjectTestCase(TestCase):
|
||||||
self.assertNothingLogged(logs)
|
self.assertNothingLogged(logs)
|
||||||
|
|
||||||
@log_capture()
|
@log_capture()
|
||||||
def test_git_find_path_from_submodule_handles_exceptions(self, logs):
|
def test_git_path_from_gitdir_link_file_handles_exceptions(self, logs):
|
||||||
logging.disable(logging.NOTSET)
|
logging.disable(logging.NOTSET)
|
||||||
|
|
||||||
tempdir = tempfile.mkdtemp()
|
tempdir = tempfile.mkdtemp()
|
||||||
|
@ -485,7 +500,7 @@ class ProjectTestCase(TestCase):
|
||||||
|
|
||||||
git = Git(None)
|
git = Git(None)
|
||||||
path = os.path.join(tempdir, 'git', 'asubmodule')
|
path = os.path.join(tempdir, 'git', 'asubmodule')
|
||||||
result = git._find_path_from_submodule(path)
|
result = git._path_from_gitdir_link_file(path)
|
||||||
|
|
||||||
self.assertIsNone(result)
|
self.assertIsNone(result)
|
||||||
self.assertNothingPrinted()
|
self.assertNothingPrinted()
|
||||||
|
@ -498,7 +513,7 @@ class ProjectTestCase(TestCase):
|
||||||
|
|
||||||
git = Git(None)
|
git = Git(None)
|
||||||
path = os.path.join(tempdir, 'git', 'asubmodule')
|
path = os.path.join(tempdir, 'git', 'asubmodule')
|
||||||
result = git._find_path_from_submodule(path)
|
result = git._path_from_gitdir_link_file(path)
|
||||||
|
|
||||||
self.assertIsNone(result)
|
self.assertIsNone(result)
|
||||||
self.assertNothingPrinted()
|
self.assertNothingPrinted()
|
||||||
|
|
|
@ -35,17 +35,9 @@ class Git(BaseProject):
|
||||||
def branch(self):
|
def branch(self):
|
||||||
head = self._head_file
|
head = self._head_file
|
||||||
if head:
|
if head:
|
||||||
try:
|
line = self._first_line_of_file(head)
|
||||||
with open(head, 'r', encoding='utf-8') as fh:
|
if line is not None:
|
||||||
return self._get_branch_from_head_file(fh.readline())
|
return self._get_branch_from_head_file(line)
|
||||||
except UnicodeDecodeError: # pragma: nocover
|
|
||||||
try:
|
|
||||||
with open(head, 'r', encoding=sys.getfilesystemencoding()) as fh:
|
|
||||||
return self._get_branch_from_head_file(fh.readline())
|
|
||||||
except:
|
|
||||||
log.traceback(logging.WARNING)
|
|
||||||
except IOError: # pragma: nocover
|
|
||||||
log.traceback(logging.WARNING)
|
|
||||||
return u('master')
|
return u('master')
|
||||||
|
|
||||||
def _find_git_config_file(self, path):
|
def _find_git_config_file(self, path):
|
||||||
|
@ -56,12 +48,22 @@ class Git(BaseProject):
|
||||||
self._project_name = os.path.basename(path)
|
self._project_name = os.path.basename(path)
|
||||||
self._head_file = os.path.join(path, '.git', 'HEAD')
|
self._head_file = os.path.join(path, '.git', 'HEAD')
|
||||||
return True
|
return True
|
||||||
if self._submodules_supported_for_path(path):
|
|
||||||
submodule_path = self._find_path_from_submodule(path)
|
link_path = self._path_from_gitdir_link_file(path)
|
||||||
if submodule_path:
|
if link_path:
|
||||||
self._project_name = os.path.basename(path)
|
|
||||||
self._head_file = os.path.join(submodule_path, 'HEAD')
|
# first check if this is a worktree
|
||||||
|
if self._is_worktree(link_path):
|
||||||
|
self._project_name = self._project_from_worktree(link_path)
|
||||||
|
self._head_file = os.path.join(link_path, 'HEAD')
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
# next check if this is a submodule
|
||||||
|
if self._submodules_supported_for_path(path):
|
||||||
|
self._project_name = os.path.basename(path)
|
||||||
|
self._head_file = os.path.join(link_path, 'HEAD')
|
||||||
|
return True
|
||||||
|
|
||||||
split_path = os.path.split(path)
|
split_path = os.path.split(path)
|
||||||
if split_path[1] == '':
|
if split_path[1] == '':
|
||||||
return False
|
return False
|
||||||
|
@ -99,30 +101,50 @@ class Git(BaseProject):
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _find_path_from_submodule(self, path):
|
def _is_worktree(self, link_path):
|
||||||
|
return os.path.basename(os.path.dirname(link_path)) == 'worktrees'
|
||||||
|
|
||||||
|
def _path_from_gitdir_link_file(self, path):
|
||||||
link = os.path.join(path, '.git')
|
link = os.path.join(path, '.git')
|
||||||
if not os.path.isfile(link):
|
if not os.path.isfile(link):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
line = self._first_line_of_file(link)
|
||||||
|
if line is not None:
|
||||||
|
return self._path_from_gitdir_string(path, line)
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _path_from_gitdir_string(self, path, line):
|
||||||
|
if line.startswith('gitdir: '):
|
||||||
|
subpath = line[len('gitdir: '):].strip()
|
||||||
|
if os.path.isfile(os.path.join(path, subpath, 'HEAD')):
|
||||||
|
return os.path.realpath(os.path.join(path, subpath))
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _project_from_worktree(self, link_path):
|
||||||
|
commondir = os.path.join(link_path, 'commondir')
|
||||||
|
if os.path.isfile(commondir):
|
||||||
|
line = self._first_line_of_file(commondir)
|
||||||
|
if line:
|
||||||
|
gitdir = os.path.abspath(os.path.join(link_path, line))
|
||||||
|
if os.path.basename(gitdir) == '.git':
|
||||||
|
return os.path.basename(os.path.dirname(gitdir))
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _first_line_of_file(self, filepath):
|
||||||
try:
|
try:
|
||||||
with open(link, 'r', encoding='utf-8') as fh:
|
with open(filepath, 'r', encoding='utf-8') as fh:
|
||||||
return self._get_path_from_submodule_link(path, fh.readline())
|
return fh.readline().strip()
|
||||||
except UnicodeDecodeError:
|
except UnicodeDecodeError:
|
||||||
try:
|
try:
|
||||||
with open(link, 'r', encoding=sys.getfilesystemencoding()) as fh:
|
with open(filepath, 'r', encoding=sys.getfilesystemencoding()) as fh:
|
||||||
return self._get_path_from_submodule_link(path, fh.readline())
|
return fh.readline().strip()
|
||||||
except:
|
except:
|
||||||
log.traceback(logging.WARNING)
|
log.traceback(logging.WARNING)
|
||||||
except IOError:
|
except IOError:
|
||||||
log.traceback(logging.WARNING)
|
log.traceback(logging.WARNING)
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def _get_path_from_submodule_link(self, path, line):
|
|
||||||
if line.startswith('gitdir: '):
|
|
||||||
subpath = line[len('gitdir: '):].strip()
|
|
||||||
if os.path.isfile(os.path.join(path, subpath, 'config')) and \
|
|
||||||
os.path.isfile(os.path.join(path, subpath, 'HEAD')):
|
|
||||||
return os.path.realpath(os.path.join(path, subpath))
|
|
||||||
|
|
||||||
return None
|
|
||||||
|
|
Loading…
Reference in a new issue