GitPython `repo.index.commit()` 生成持久性 git.exe 实例,持有 repo 的句柄
GitPython `repo.index.commit()` spawns persistent git.exe instance, holds handles to repo
我正在尝试使用 GitPython 进行一些回购操作,但是 运行 我的应用程序出现问题,在我不期望的地方打开句柄。
这个问题令人震惊,似乎调用 repo.index.commit()
会导致目录句柄(可能是 .git\
中的内容)。稍后这会导致我的应用程序尝试执行的其他操作失败。
这是一个工作单元测试:
import unittest
import git
import tempfile
import os.path
class Test(unittest.TestCase):
def testCreateRepo(self):
with tempfile.TemporaryDirectory(prefix=(__loader__.name) + "_") as mydir:
# MAKE NEW REPO
repo = git.Repo.init(path=os.path.join(mydir, "newRepo"), mkdir=True)
self.assertTrue(os.path.isdir(os.path.join(repo.working_dir, ".git")), "Failed to make new repo?")
# MAKE FILE, COMMIT REPO
testFileName = "testFile.txt"
open(os.path.join(repo.working_dir, testFileName) , "w").close()
repo.index.add([testFileName])
self.assertTrue(repo.is_dirty())
####
# COMMENTING THIS OUT --> TEST PASSES
repo.index.commit("added initial test file")
self.assertFalse(repo.is_dirty())
####
# adding this does not affect the handle
git.cmd.Git.clear_cache()
print("done") # exception thrown right after this, on __exit__
PermissionError: [WinError 32] 进程无法访问该文件,因为它正被另一个进程使用:'C:\Users\%USER%\AppData\Local\Temp\EXAMPLE_gitpython_v3kbrly_\newRepo'
再深入一点,似乎 gitPython 生成了 git.exe 进程的多个实例,并且每个实例都持有 repo root 文件夹的句柄 newRepo
.
- 在错误前立即设置一个断点,使用 sysinternals/handle 查看
newRepo
的打开句柄 ... git.exe(准确地说是 git.exe 的 4 个单独的 PID )
- 使用 sysinternals/procexp 我可以看到它们都是从日食中产生的-->python
单步执行,实际上是对 repo.index.commit() 的调用导致生成 git.exe(s)。
与 gitpython 开发人员合作,我找到了答案:
由于 gitpython 的内部缓存行为,您必须强制进行垃圾回收,并告诉 repo 清除它的缓存。我做的是后者,但是用错了对象。
在清理您的目录之前必须添加以下内容(__exit__()
我的 with:
/context-manager 子句,在上面的代码中)
import gc
gc.collect()
repo.git.clear_cache()
那些似乎不服从 least-surprise :) 希望 api 可以在未来得到改进。
我正在尝试使用 GitPython 进行一些回购操作,但是 运行 我的应用程序出现问题,在我不期望的地方打开句柄。
这个问题令人震惊,似乎调用 repo.index.commit()
会导致目录句柄(可能是 .git\
中的内容)。稍后这会导致我的应用程序尝试执行的其他操作失败。
这是一个工作单元测试:
import unittest
import git
import tempfile
import os.path
class Test(unittest.TestCase):
def testCreateRepo(self):
with tempfile.TemporaryDirectory(prefix=(__loader__.name) + "_") as mydir:
# MAKE NEW REPO
repo = git.Repo.init(path=os.path.join(mydir, "newRepo"), mkdir=True)
self.assertTrue(os.path.isdir(os.path.join(repo.working_dir, ".git")), "Failed to make new repo?")
# MAKE FILE, COMMIT REPO
testFileName = "testFile.txt"
open(os.path.join(repo.working_dir, testFileName) , "w").close()
repo.index.add([testFileName])
self.assertTrue(repo.is_dirty())
####
# COMMENTING THIS OUT --> TEST PASSES
repo.index.commit("added initial test file")
self.assertFalse(repo.is_dirty())
####
# adding this does not affect the handle
git.cmd.Git.clear_cache()
print("done") # exception thrown right after this, on __exit__
PermissionError: [WinError 32] 进程无法访问该文件,因为它正被另一个进程使用:'C:\Users\%USER%\AppData\Local\Temp\EXAMPLE_gitpython_v3kbrly_\newRepo'
再深入一点,似乎 gitPython 生成了 git.exe 进程的多个实例,并且每个实例都持有 repo root 文件夹的句柄 newRepo
.
- 在错误前立即设置一个断点,使用 sysinternals/handle 查看
newRepo
的打开句柄 ... git.exe(准确地说是 git.exe 的 4 个单独的 PID ) - 使用 sysinternals/procexp 我可以看到它们都是从日食中产生的-->python
单步执行,实际上是对 repo.index.commit() 的调用导致生成 git.exe(s)。
与 gitpython 开发人员合作,我找到了答案:
由于 gitpython 的内部缓存行为,您必须强制进行垃圾回收,并告诉 repo 清除它的缓存。我做的是后者,但是用错了对象。
在清理您的目录之前必须添加以下内容(__exit__()
我的 with:
/context-manager 子句,在上面的代码中)
import gc
gc.collect()
repo.git.clear_cache()
那些似乎不服从 least-surprise :) 希望 api 可以在未来得到改进。