如何用git重写python __version__?

66 投票
6 回答
31432 浏览
提问于 2025-04-16 15:16

我想在我的模块里定义一个 __version__ 变量,这个变量应该在每次 git 提交时自动更新,就像 SVN 关键词 那样。请问在 Git 中有没有办法做到这一点?有没有人能提供一个有效的例子?

我考虑过使用 GitPython,但我不想增加额外的依赖,而且我希望从 SVN 仓库或压缩包下载模块的用户也能使用相同的版本控制方式(我不在乎那是什么难以理解的哈希值)。

补充说明:我面临的具体问题是,我需要运行一些模拟,这些模拟的结果依赖于模拟脚本的确切版本。因此每次我都需要将版本号和模拟结果一起保存。如果两者不同步,可能会导致非常严重的后果。

6 个回答

7

对于2018年看到这个问题的人,你可以使用Versioneer。一旦启用,它会自动根据构建时最新的Git标签来设置setup.py中的version和你模块中的__version__

举个例子,如果你在标签1.0.0时构建你的项目,Versioneer会把项目的版本设置为1.0.0。如果你再做两个提交,编辑一些内容但没有提交,然后再构建,Versioneer会把版本设置为类似1.0.0+2.g1076c97.dirty这样的格式。

当然,你可以自定义Versioneer识别哪些标签作为版本标签。

这也是像pandasmatplotlib这样的大型项目处理版本控制的方式。

15

除了Versioneer,还有一个选择是setuptools_scm

我成功地实现了一个和提问者很相似的功能,方法是往setup.py里添加了以下内容(或者根据需要进行修改):

from setuptools import setup
setup(
    ...,
    use_scm_version=True,
    setup_requires=['setuptools_scm'],
    ...,
)

为了让__version__能够自动更新,我还在我包的__init__.py里添加了这个:

from pkg_resources import get_distribution, DistributionNotFound
try:
    __version__ = get_distribution(__name__).version
except DistributionNotFound:
    # package is not installed
    pass
38

把这个功能放在打包的时候做,可能比每次提交后都做要好。

主要有两个选择:

  • 使用 git-archive 来打包,并使用 export-subst 属性。不过,能替换的内容有限,只能用 git log --format=... 中的占位符。例如,你可以在文件中写 __version__ = $Format:%H$,然后在 .gitattributes 文件里加上 <filename> export-subst,当你运行 git archive 时,这个内容会被替换成你正在打包的提交的完整哈希值。这基本上就是你想要的,但我更喜欢下一个选项。

  • 自己在打包过程中做这件事(通常是在编译包的构建过程中),并使用 git describe。这样你可以得到一个漂亮的字符串,比如 v1.7.4.1-59-ge3d3f7d,意思是“在标签 v1.7.4.1 之后的59次提交,当前提交是 ge3d3f7d”。然后你可以把这个字符串放到代码的合适位置,作为打包/构建的一部分。这就是Git本身的做法;结果会被写入一个文件,内容会被读取到makefile中,然后通过 -D 预处理选项传递到构建过程中,并直接放入各种文件名中(比如发布的压缩包)。

如果你真的想在每次提交后都做这个,可以使用一个提交后钩子,但那样只有你(和你给钩子的人)能用,而且很可能会出现不同步的情况——你还需要一个检出后钩子,等等。其实,最好是让需要这个版本号的过程自己去获取。

你也可以使用一个模糊/清理过滤器,这样会更接近你实际想要的效果(而不是单纯在每次提交后)。

撰写回答