背景
我正在编写一个Python程序来处理LFS错误消息,因为我在Bitbucket Server中有一些带有丢失LFS文件的repo。当您想找出repo中缺少哪些LFS文件时,运行git lfs fetch --all
时的输出并不是很有用。这就是为什么我要为它制作一个工具。也许当我完成它后,它可以以某种形式贡献给git项目?
代码片段
import subprocess
def git_lfs_fetch(repo_dir) -> list:
timeout_sec = 120
try:
completed_process = subprocess.run(
["git", "lfs", "fetch", "--all"], check=False, cwd=repo_dir,
capture_output=True, text=True, timeout=timeout_sec, shell=False)
return completed_process.stderr.split('n')
except subprocess.TimeoutExpired as ex:
print(f'ERROR Could not complete "{ex.cmd}" before timeout of {timeout_sec} s!')
print(ex.stderr)
return []
问题
有时,subprocess.run()
方法永远不会返回,因为git-lfs
进程永远不会退出。通常,在我的测试repos中运行git lfs fetch --all
命令需要几秒钟才能完成。作为一种变通方法,我在subprocess.run()
调用中添加了一个2分钟的timeout
。我想我可以从异常中从stderr获得我感兴趣的输出,因为git-lfs
已经完成了它应该做的一切。然而,这并没有帮助。Python似乎无法终止git
子进程。我从文档中了解到,它向进程发送了一个SIGKILL
,然后等待它退出。但它永远不会退出,即使设置了超时。
如果我从外部手动终止git-lfs
进程,我会从ex.stderr
打印出预期的输出,所以git-lfs
看起来确实完成了,我的解决方法也应该完成。
环境
Windows 10, 64 bit
Python 3.10.7
git version 2.39.0.windows.2
- 从
2.35.2.windows.1
升级
- 从
git-lfs/3.3.0 (GitHub; windows amd64; go 1.19.3; git 77deabdf)
- 从
git-lfs/3.0.2 (GitHub; windows amd64; go 1.17.2)
升级
- 从
修复我的解决方法
当我写这篇文章的时候,以一种典型的逃避方式,我有了一个想法。
由于Python无法强制终止git
子进程,我尝试直接使用git-lfs
,而不是让git
调用它。这使得超时有效。
subprocess.run(["git-lfs", "fetch", "--all"], ...)
我在找什么
我正在寻找的解决方案是找出git-lfs
和git
不能正确终止的原因。更好的办法是解决这个问题。
我在Linux和Windows上(几年前)从Java和C#调用git
时也遇到过类似的问题,即git
命令实际上完成了它应该做的所有事情,但git
进程从未终止。所以我认为;悬挂;这可能是git本身的一个问题。我真的很想知道为什么git-lfs
进程不会退出。我不知道从哪里开始找。
更新
我使用了@CharlesDuffy的提示来尝试strace
。我得到的一些输出并没有告诉我任何有用的东西。
在TaskManager中,我偶然发现有几个(7)ssh进程正在运行。在检查他们的命令行时,很明显他们与Git LFS有关,他们都在等待Bitbucket服务器的响应。向服务器发出的命令就是其中一个
ssh -p 7999 git@server-url "git-lfs-authenticate <path> download"
ssh -p 7999 git@server-url "git-lfs-transfer <path> download"
它们似乎是我看到问题并强行终止git-lfs
进程时遗留下来的进程。
我用Git lfs 3.3.0将Git升级到最新版本2.39.0,然后重试。起初它似乎工作得更好,但后来我的git-lfs
又卡住了。这次是从命令行运行,而不是从Python运行,任务管理器显示ssh.exe
正在运行这个命令行,并且似乎被卡住了:
ssh -oControlMaster=Auto -oControlPath=C:UsersusernameAppdataLocalTempsock-3553225979sock-%C -p 7999 git@server-url "git-lfs-transfer <path> download"
使用Git Bash
中的strace
,我无法从挂起的ssh.exe
进程中获得任何有用的信息,最终它在我试图再次连接strace
时死亡。
由于服务器上的ssh
守护进程存在于Bitbucket应用程序中,因此我认为下一步是为服务器端调试提供Atlassian支持。不过,我仍然缺乏一种可靠的方法来重现这个问题。
使用此方法
def git_lfs_fetch(repo_dir) -> list:
timeout_sec = 120
try:
completed_process = subprocess.run(
["git", "lfs", "fetch", "--all"], check=False, cwd=repo_dir,
capture_output=True, text=True, timeout=timeout_sec, shell=False)
return completed_process.stderr.split('n')
except subprocess.TimeoutExpired as ex:
print(f'ERROR Could not complete "{ex.cmd}" before timeout of {timeout_sec} s!')
print(ex.stderr)
return []