如何用 git 重写 python __version__?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/5581722/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How can I rewrite python __version__ with git?
提问by btel
I would like to define a __version__
variable in my module which should be automatically updated on git commit similarly to what SVN keywordsdo. Is there a way to achieve it in Git? Does anyone have a working example?
我想__version__
在我的模块中定义一个变量,它应该在 git commit 上自动更新,类似于SVN 关键字所做的。有没有办法在 Git 中实现它?有没有人有一个工作示例?
I considered using GitPython, but I would not like to introduce another dependency and I want users who download the module from SVN repo or as a zip package to have the same versioning scheme (I do not care that is some illegible hash).
我考虑过使用GitPython,但我不想引入另一个依赖项,我希望从 SVN repo 下载模块或作为 zip 包下载模块的用户具有相同的版本控制方案(我不在乎这是一些难以辨认的哈希)。
Edit: My particular problem is that I have to run simulations whose result depend on the exact version of the simulation script. Therefore each time I have to store version number together with the simulation results. If both run out of sync, it may have very dire outcomes.
编辑:我的特殊问题是我必须运行模拟,其结果取决于模拟脚本的确切版本。因此,每次我都必须将版本号与模拟结果一起存储。如果两者不同步,可能会产生非常可怕的结果。
采纳答案by Cascabel
It might be better to do this as part of your packaging, rather than after every commit.
最好将此作为打包的一部分,而不是在每次提交之后执行。
There are two primary options:
有两个主要选项:
Use
git-archive
to package, and use theexport-subst
attribute. Unfortunately, the things you can substitute in are limited to the placeholders fromgit log --format=...
. For example, you could write__version__ = $Format:%H$
in your file, put<filename> export-subst
in your .gitattributes, and when you rungit archive
, that'd be changed to the full hash of the commit you're archiving with. This is just about what you're asking for, but I do prefer the next option.Do it yourself as part of a packaging process (often a build process for compiled packages), and use
git describe
. That will get you a nice pretty string likev1.7.4.1-59-ge3d3f7d
, meaning "59 commits past the tagv1.7.4.1
, at commitge3d3f7d
" which you can then insert somehow into the right place in your code as you package/build. This is what Git itself does; the result is dumped to a file, whose contents are read into the makefile and then passed into the build via a-D
preprocessor option, and placed into various filenames (e.g. the release tarball) directly.
使用
git-archive
包装,并使用export-subst
属性。不幸的是,您可以替换的内容仅限于git log --format=...
. 例如,您可以__version__ = $Format:%H$
在您的文件中写入,放入<filename> export-subst
您的 .gitattributes,当您运行时git archive
,它会更改为您正在归档的提交的完整哈希值。这正是您所要求的,但我更喜欢下一个选项。作为打包过程(通常是编译包的构建过程)的一部分,自己动手,并使用
git describe
. 这将为您提供一个漂亮漂亮的字符串,例如v1.7.4.1-59-ge3d3f7d
“59 次提交超过标记v1.7.4.1
,在提交时ge3d3f7d
”,然后您可以在打包/构建时以某种方式将其插入代码中的正确位置。这就是 Git 本身所做的;结果被转储到一个文件中,其内容被读入makefile,然后通过-D
预处理器选项传递到构建中,并直接放入各种文件名(例如发布tarball)中。
If you really, really want to do this after every commit, you could, with a post-commit hook, but then only you (and those you give the hook to) will have it, and it's very very possible to get out of sync - you'll also have to have a post-checkout hook, and so on and so on. It's really better for whatever processes that create something needing this version number to get it themselves.
如果你真的,真的想在每次提交后都这样做,你可以使用 post-commit 钩子,但是只有你(和那些你给钩子的人)会拥有它,而且很可能不同步- 你还必须有一个结帐后挂钩,等等。对于任何创建需要这个版本号的东西来自己获取它的过程来说,这真的更好。
You could also use a smudge/clean filter, which would be more like what you actually want (rather than simply after every commit).
您还可以使用污迹/清洁过滤器,这将更像您真正想要的(而不是在每次提交之后)。
回答by jpyams
For those finding this question in 2018 you can also use Versioneer. Once enabled, it automatically sets version
in setup.py
and __version__
in your module based on the latest Git tag at time of build.
对于那些在 2018 年发现此问题的人,您还可以使用Versioneer。启用后,它将自动设置version
的setup.py
,并__version__
基于在构建时的最新的Git标签的模块中。
For example, if you build your project at tag 1.0.0
, Versioneer will set the project's version to 1.0.0. If you do two more commits, edit some without checking in, and build, Versioneer will set the version to something like 1.0.0+2.g1076c97.dirty
.
例如,如果您在 tag 处构建项目1.0.0
,Versioneer 会将项目的版本设置为 1.0.0。如果你再做两次提交,在不签入的情况下编辑一些,然后构建,Versioneer 会将版本设置为类似1.0.0+2.g1076c97.dirty
.
Of course you can customize which tags Versioneer recognizes as version tags.
当然,您可以自定义 Versioneer 将哪些标签识别为版本标签。
This is also how larger projects like pandasand matplotlibhandle their versioning.
这也是像pandas和matplotlib这样的大型项目处理它们的版本的方式。
回答by Pincopallino
Another possibility other than Versioneer is setuptools_scm
.
除了 Versioneer 之外的另一种可能性是setuptools_scm
.
I have successfully implemented something very similar to the OP by adding the following to setup.py
(or by modifying it accordingly):
通过添加以下内容setup.py
(或相应地修改它),我成功地实现了与 OP 非常相似的东西:
from setuptools import setup
setup(
...,
use_scm_version=True,
setup_requires=['setuptools_scm'],
...,
)
and, in order to have __version__
updated automatically, added this to __init__.py
of my package:
并且,为了__version__
自动更新,将此添加到__init__.py
我的包中:
from pkg_resources import get_distribution, DistributionNotFound
try:
__version__ = get_distribution(__name__).version
except DistributionNotFound:
# package is not installed
pass
回答by Jean-Marc Volle
You can use the following code to retrieve your current git commit version (reported as either a commit id on a branch or as a tag if a tag has been added:
您可以使用以下代码来检索当前的 git 提交版本(报告为分支上的提交 ID,如果添加了标签,则报告为标签:
from git import Repo
def GetGitVersion():
'''report the git commit/branch/tag on which we are '''
repo = Repo(".", search_parent_directories=True)
git = repo.git
branchOrTag=git.rev_parse('--abbrev-ref', 'HEAD')
if branchOrTag == 'HEAD':
# get tag name
# since several tags are sometime put on the same commit we want to retrieve all of them
# and use the last one as reference
# Note:
# branchOrTag=`git describe --tags --exact-match` does not provided the latest created tag in case several point to the same place
currentSha=git.rev_parse('--verify','HEAD')
# list all tags on the current sha using most recent first:
allTags=git.tag('--points-at',currentSha,'--sort=-creatordate')
print (allTags)
allTagsArray=allTags.split(' ') #create an array (assuming tags are separated by space)
# if we checkouted a commit with no tag associated, the allTagsArray is empty we can use directly the sha value
if len(allTagsArray) == 0:
branchOrTag=git.rev-rev_parse('--short','HEAD') # take the short sha
else:
branchOrTag=allTagsArray[0] #first from the list
else:
#add the head commit id on the current branch
branchOrTag="{}[{}]".format(branchOrTag,git.rev_parse('--short', 'HEAD'))
return branchOrTag
if __name__ == "__main__":
print (GetGitVersion())
回答by ingyhere
(1) One option is to get the version number in real-time from the release version written into a published package. To do this, you would add a dependency to __init__.py
, release the product with something like setup.py
and at runtime execute python3 setup.py --version
. This approach uses the lightweight importlib_metadata module[importlib_metadata
(for pre Python 3.8) and importlib.metadata
(for Python 3.8+)]:
(1) 一种选择是从写入发布包的发布版本中实时获取版本号。要做到这一点,您需要添加一个依赖项__init__.py
,使用类似setup.py
和在运行时执行的东西来发布产品python3 setup.py --version
。这种方法使用轻量级的 importlib_metadata 模块[ importlib_metadata
(适用于 Python 3.8 之前的版本)和importlib.metadata
(适用于 Python 3.8+)]:
from importlib.metadata import version, PackageNotFoundError
# pre-3.8 import statement
# from importlib_metadata import version, PackageNotFoundError
VERSION_FALLBACK = "0.0.0"
try:
__version__ = version(__name__)
except PackageNotFoundError:
# package is not installed
# assign signal or sane value as a default
__version__ = VERSION_FALLBACK
pass
This implements metadata recommendations from PEP 566. If you release with setuptools>=42.0.0
this works great, and it may work with packages released by other tooling, also.
这实现了来自PEP 566 的元数据建议。如果你用setuptools>=42.0.0
这个发布很好用,它也可以与其他工具发布的包一起使用。
(2) A second option is to do something with Git to collect the last tag value (assuming you are tagging the application). Then increment the point version number. Then replace the value in the init file with the new value and tag with the new value.
(2) 第二种选择是用 Git 做一些事情来收集最后一个标签值(假设你正在标记应用程序)。然后增加点版本号。然后用新值替换 init 文件中的值并用新值标记。
# get and increment semantic version
version=$( git tag --list | sort -rV | head -1 ); # v0.1.1
version_point=${version##*.} # 1
version_point=$((${version_point} + 1)) # 2
version="${version%.*}.${version_point}" # v0.1.2
# replace in __init__.py (NOTE: THIS OVERWRITES!)
cat __init.py__ | sed -e "s/VERSION=.*/VERSION=${version}/i" > __init__.py
git add __init__.py && git commit -m "Updated version in __init__.py"
git tag -a ${version} -m "Latest tagged version"