GitPython:如何检测文件是否被删除?
GitPython: How to detect if file is deleted?
我正在编写一个自动化脚本来处理 staged/unstaged 文件以获得预提交挂钩。
我想知道文件是否被 git(git status --porcelain -- {filename}
中的“D”或“D”)标记为已删除,以便我可以将其从列表中删除。如果可能的话,我想通过 GitPython API 来完成。
之前尝试的步骤:
我在文档中找不到任何与此用例相关的“删除”参考
使用 dir(item)
其中 item
是 repo.index.diff(None)
的 Diffable 揭示了以下成员:
['NULL_BIN_SHA', 'NULL_HEX_SHA', '__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__slots__', '__str__', '__subclasshook__', '_handle_diff_line', '_index_from_patch_format', '_index_from_raw_format', '_pick_best_path', 'a_blob', 'a_mode', 'a_path', 'a_rawpath', 'b_blob', 'b_mode', 'b_path', 'b_rawpath', 'change_type', 'copied_file', 'deleted_file', 'diff', 'new_file', 'raw_rename_from', 'raw_rename_to', 're_header', 'rename_from', 'rename_to', 'renamed', 'renamed_file', 'score']
其中 deleted_file
似乎是唯一明智的候选者 - 但它似乎没有反映 git status --porcelain 的结果,因为 [=43= 中的所有已删除文件]状态设置为deleted_file=False(与未删除的相同)。
现在我直接依赖 git 来断言文件是否被删除:
def _is_deleted(path: str):
files = _gitstatus()
return 'D' in files[path]
@lru_cache(maxsize=1)
def _gitstatus():
child = subprocess.Popen(["git", "status", "--porcelain"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out, err = [stream.decode('utf-8') if stream is not None else '' for stream in child.communicate()]
if child.returncode != 0:
raise Exception(err)
files = {
line[3:]: tuple(line[0:2])
for line in out.split('\n')
}
return files
但如果可能,我更愿意使用 GitPython API。知道如何实现上述功能的等效结果吗?
在命令行中,我会使用:
git diff --name-status
获取跟踪文件与索引相比的状态,
git diff --staged --name-status
获取暂存文件与当前提交相比的状态。
我认为您可以从 GitPython 的 diff api 中提取相同的信息:
hcommit = repo.head.commit
hcommit.diff() # diff tree against index
hcommit.diff('HEAD~1') # diff tree against previous tree
hcommit.diff(None) # diff tree against working tree
index = repo.index
index.diff() # diff index against itself yielding empty diff
index.diff(None) # diff index against working copy
index.diff('HEAD') # diff index against current HEAD tree
git diff
将是 index.diff(None)
,git diff --staged
将是 index.diff('HEAD')
或 hcommit.diff()
.
这会回答你的问题吗?
这是在使用 GitPython library, it's a bit involved but you can get the similar result to as git status
using the Repo.index.diff()
. This function is similar if not the same as the git.diff.Diff,您可以在此处的文档中查看如何过滤文件。
from git import Repo
repo = Repo()
if repo.is_dirty():
index = repo.index
for obj in index.diff(None).iter_change_type('D'):
print('File path', obj.b_path)
print('Change type', obj.change_type)
print('Is deleted', obj.deleted_file)
(git-test) ➜ (01/09 20:18) /tmp/git-test git:(master) ✗ git status
On branch master
Changes not staged for commit:
(use "git add/rm <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
deleted: test.txt
Untracked files:
(use "git add <file>..." to include in what will be committed)
git-test.py
no changes added to commit (use "git add" and/or "git commit -a")
(git-test) ➜ (01/09 20:19) /tmp/git-test git:(master) ✗ python git-test.py
File path test.txt
Change type D
Is deleted True
已更新以显示未跟踪、暂存和已删除的文件
from git import Repo
repo = Repo()
if repo.is_dirty():
index = repo.index
print('Untracked files', repo.untracked_files)
print('Staged files', [item.a_path for item in repo.index.diff('HEAD')])
for obj in index.diff(None):
print('File path', obj.b_path)
print('Change type', obj.change_type)
print('Is deleted', obj.deleted_file)
(git-test) ➜ (02/09 13:58) /tmp/git-test git:(master) ✗ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
new file: test-2.txt
Changes not staged for commit:
(use "git add/rm <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
deleted: test.txt
Untracked files:
(use "git add <file>..." to include in what will be committed)
.idea/
git-test.py
(git-test) ➜ (02/09 13:58) /tmp/git-test git:(master) ✗ python git-test.py
Untracked files ['.idea/.gitignore', '.idea/git-test.iml', '.idea/misc.xml', '.idea/modules.xml', '.idea/vcs.xml', 'git-test.py']
Staged files ['test-2.txt']
File path test.txt
Change type D
Is deleted True
我正在编写一个自动化脚本来处理 staged/unstaged 文件以获得预提交挂钩。
我想知道文件是否被 git(git status --porcelain -- {filename}
中的“D”或“D”)标记为已删除,以便我可以将其从列表中删除。如果可能的话,我想通过 GitPython API 来完成。
之前尝试的步骤:
我在文档中找不到任何与此用例相关的“删除”参考
使用
dir(item)
其中item
是repo.index.diff(None)
的 Diffable 揭示了以下成员:['NULL_BIN_SHA', 'NULL_HEX_SHA', '__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__slots__', '__str__', '__subclasshook__', '_handle_diff_line', '_index_from_patch_format', '_index_from_raw_format', '_pick_best_path', 'a_blob', 'a_mode', 'a_path', 'a_rawpath', 'b_blob', 'b_mode', 'b_path', 'b_rawpath', 'change_type', 'copied_file', 'deleted_file', 'diff', 'new_file', 'raw_rename_from', 'raw_rename_to', 're_header', 'rename_from', 'rename_to', 'renamed', 'renamed_file', 'score']
其中 deleted_file
似乎是唯一明智的候选者 - 但它似乎没有反映 git status --porcelain 的结果,因为 [=43= 中的所有已删除文件]状态设置为deleted_file=False(与未删除的相同)。
现在我直接依赖 git 来断言文件是否被删除:
def _is_deleted(path: str):
files = _gitstatus()
return 'D' in files[path]
@lru_cache(maxsize=1)
def _gitstatus():
child = subprocess.Popen(["git", "status", "--porcelain"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out, err = [stream.decode('utf-8') if stream is not None else '' for stream in child.communicate()]
if child.returncode != 0:
raise Exception(err)
files = {
line[3:]: tuple(line[0:2])
for line in out.split('\n')
}
return files
但如果可能,我更愿意使用 GitPython API。知道如何实现上述功能的等效结果吗?
在命令行中,我会使用:
git diff --name-status
获取跟踪文件与索引相比的状态,git diff --staged --name-status
获取暂存文件与当前提交相比的状态。
我认为您可以从 GitPython 的 diff api 中提取相同的信息:
hcommit = repo.head.commit hcommit.diff() # diff tree against index hcommit.diff('HEAD~1') # diff tree against previous tree hcommit.diff(None) # diff tree against working tree index = repo.index index.diff() # diff index against itself yielding empty diff index.diff(None) # diff index against working copy index.diff('HEAD') # diff index against current HEAD tree
git diff
将是 index.diff(None)
,git diff --staged
将是 index.diff('HEAD')
或 hcommit.diff()
.
这会回答你的问题吗?
这是在使用 GitPython library, it's a bit involved but you can get the similar result to as git status
using the Repo.index.diff()
. This function is similar if not the same as the git.diff.Diff,您可以在此处的文档中查看如何过滤文件。
from git import Repo
repo = Repo()
if repo.is_dirty():
index = repo.index
for obj in index.diff(None).iter_change_type('D'):
print('File path', obj.b_path)
print('Change type', obj.change_type)
print('Is deleted', obj.deleted_file)
(git-test) ➜ (01/09 20:18) /tmp/git-test git:(master) ✗ git status
On branch master
Changes not staged for commit:
(use "git add/rm <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
deleted: test.txt
Untracked files:
(use "git add <file>..." to include in what will be committed)
git-test.py
no changes added to commit (use "git add" and/or "git commit -a")
(git-test) ➜ (01/09 20:19) /tmp/git-test git:(master) ✗ python git-test.py
File path test.txt
Change type D
Is deleted True
已更新以显示未跟踪、暂存和已删除的文件
from git import Repo
repo = Repo()
if repo.is_dirty():
index = repo.index
print('Untracked files', repo.untracked_files)
print('Staged files', [item.a_path for item in repo.index.diff('HEAD')])
for obj in index.diff(None):
print('File path', obj.b_path)
print('Change type', obj.change_type)
print('Is deleted', obj.deleted_file)
(git-test) ➜ (02/09 13:58) /tmp/git-test git:(master) ✗ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
new file: test-2.txt
Changes not staged for commit:
(use "git add/rm <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
deleted: test.txt
Untracked files:
(use "git add <file>..." to include in what will be committed)
.idea/
git-test.py
(git-test) ➜ (02/09 13:58) /tmp/git-test git:(master) ✗ python git-test.py
Untracked files ['.idea/.gitignore', '.idea/git-test.iml', '.idea/misc.xml', '.idea/modules.xml', '.idea/vcs.xml', 'git-test.py']
Staged files ['test-2.txt']
File path test.txt
Change type D
Is deleted True