Git 签出新分支时自动删除*.pyc文件和其他空目录

Git 签出新分支时自动删除*.pyc文件和其他空目录,git,python,bash,Git,Python,Bash,因此,在使用git和python时,这里有一个有趣的情况,我相信在其他情况下也会发生这种情况 假设我使用文件夹/foo/进行git回购。我把/foo/program.py放在那个文件夹中。我运行program.py并创建program.pyc。我在.gitignore文件中有*.pyc,所以git不会跟踪它 现在让我们假设我创建了另一个分支dev。在这个dev分支中,我完全删除了/foo/文件夹 现在我切换回主分支,/foo/再次出现。我运行program.py,program.pyc文件再次出

因此,在使用git和python时,这里有一个有趣的情况,我相信在其他情况下也会发生这种情况

假设我使用文件夹/foo/进行git回购。我把/foo/program.py放在那个文件夹中。我运行program.py并创建program.pyc。我在.gitignore文件中有*.pyc,所以git不会跟踪它

现在让我们假设我创建了另一个分支dev。在这个dev分支中,我完全删除了/foo/文件夹

现在我切换回主分支,/foo/再次出现。我运行program.py,program.pyc文件再次出现。一切都很好

我切换回我的开发分支。/foo/目录应该消失。它只存在于主分支中,而不存在于开发分支中。然而,它仍然存在。为什么?因为被忽略的program.pyc文件阻止在切换分支时删除文件夹

此问题的解决方案是在切换分支之前递归删除所有*.pyc文件。我可以用这个命令轻松地做到这一点

find . -name "*.pyc" -exec rm '{}' ';'
问题是,几乎每次我更换分支时都要记住这样做,这很烦人。我可以为这个命令创建一个别名,但是我仍然必须记住每次更改分支时都要键入它。我还可以为git branch创建一个别名,但这也不行。git branch命令除了更改分支之外还做其他事情,我不想每次使用它时都删除所有pyc文件。见鬼,我甚至可能在非python回购协议中使用它,然后呢


有没有办法设置一个git钩子,它只在我更改分支时执行?或者是否有其他方法可以设置所有*.pyc文件在我切换分支时被擦除?

在.git/hooks/post checkout中有一个
post checkout
hook。可能有一个示例,可能名为.sample,也可能不可执行,具体取决于您的git版本。简短描述:它获取三个参数,前一个标头、新标头和一个标志,如果分支发生更改,则该标志为1;如果只是一个文件签出,则该标志为0。有关更多信息,请参见
man
!您应该能够编写一个shell脚本来执行您需要的操作,并将其放在那里

编辑:我意识到您希望进行预签出,以便签出自动清除变为空的目录。不过,没有预签出挂钩,因此您也必须使用脚本删除目录

另一项说明:
别名是gitconfig的一部分,可以是存储库的本地别名(在.git/config中,而不是~/.gitconfig中)。如果您选择使用别名(用于git签出,而不是git分支)执行此操作,那么您可以轻松地将它们放在与python相关的存储库中。同样在这种情况下,我会专门为此制作一个别名(例如,cc for checkout clean)。如果您不想清理pyc文件,您仍然可以使用签出(或它的另一种别名形式)。

只需复制和更新隐藏在注释中的一个好的解决方案:

将此shell脚本保存到文件
/path/to/repo/.git/hooks/post checkout
,并使其可执行

#! /bin/sh

# Start from the repository root.
cd ./$(git rev-parse --show-cdup)

# Delete .pyc files and empty directories.
find . -name "*.pyc" -delete
find . -type d -empty -delete

我的解决方案与git更加兼容: Git只删除通过签出删除任何文件的enpty目录。它不会搜索完整的工作副本树。这对于大型存储库或具有非常大的被忽略树的存储库非常有用,例如虚拟环境(按包),用于测试许多不同的Python版本等

我的第一次实施非常清楚地解释了这一原则: 仅清理与版本控制下的文件相关的pyc文件。这是因为效率和不必要的副作用

#!/bin/bash
# A hook that removes orphan "*.pyc" files for "*.py" beeing deleted.
# It doesn not clean anything e.g. for .py files deleted manually.
oldrev="$1"
newrev="$2"
# ignored param: branchcheckout="$3"

for x in $(git diff --name-only --diff-filter=DR $oldrev..$newrev | grep "\.py$")
do
    if test -a ${x}c && ! test -a ${x}; then
        rm ${x}c
    fi
done
post-checkout
hook接收三个有用的参数,这些参数允许在不搜索完整树的情况下准确地知道git-checkout删除了哪些文件

在阅读了这个问题之后,我将我的hook代码重写为Python,并根据您对空目录的要求对其进行了扩展

我完整的简短源代码(Python)在

文档字符串:

"""
A hook to git that removes orphan files "*.pyc" and "*.pyo" for "*.py"
beeing deleted or renamed by git checkout. It also removes their empty parent
directories.
Nothing is cleaned for .py files deleted manually or by "git rm" etc.
Place it to "my_local_repository/.git/hooks/post-checkout" and make it executable
"""
  • *.pyc文件的问题对于Python 3来说并不重要,因为如果没有父目录中的相关*.py*文件,
    \uuu pycache\uuu
    中的*.pyc文件就无法执行

  • 无需更改目录,因为每次都会在存储库的根目录中启动挂钩

  • 编译代码的缓存目录被彻底清理,因为它们从来都不重要(不参与任何二进制分发),而且为了高效,还因为按部分名称删除。pyc可能会很慢

另一个选择是根本不作为git问题解决,而是作为Python问题解决。您可以使用
pythondotwritebytecode
环境变量首先阻止Python编写.pyc文件。这样,当您切换分支时,就不会有任何东西需要清理了。

谢谢!另外,应该有一个预签出钩子。是的,git开发人员在添加钩子时似乎采取了相当保守的方法。不过,在你自己身上添加一个并不是特别困难!如果你看一下对签出后钩子的调用(在签出函数的底部),它调用一个单行包装函数,该函数获取哈希旧/新哈希和分支与文件签出标志,然后调用一个通用的run_钩子函数。它工作得很好/usr/bin/env bash find/path/to/repo/-name“*.pyc”-exec rm{}\;find/path/to/repo/-depth-type d-empty-execrmdir{};很好,谢谢。我加了两个钩子<代码>合并后-满足合并拉动<代码>签出后-满足分支签出的需要,并且在钩子中使用rebaseIn,我更喜欢使用'git diff'而不是'find'——效率更高:git diff——名称状态old