如何用git重写python __version__?

时间:2011-04-07 13:18:43

标签: python git

我想在我的模块中定义一个__version__变量,该变量应该在git commit上自动更新,类似于SVN keywords。有没有办法在Git中实现它?有没有人有一个有效的例子?

我考虑使用GitPython,但我不想介绍另一个依赖项,我希望用户从SVN repo下载模块或作为zip包具有相同的版本控制方案(我不在乎这是一些难以辨认的哈希)。

编辑:我的特殊问题是我必须运行模拟,其结果取决于模拟脚本的确切版本。因此,每次我必须将版本号与模拟结果一起存储。如果两者都不同步,则可能会产生非常可怕的结果。

6 个答案:

答案 0 :(得分:30)

最好将此作为包装的一部分,而不是在每次提交后执行。

有两个主要选项:

  • 使用git-archive打包,然后使用export-subst attribute。不幸的是,你可以替代的东西仅限于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)。

如果你真的,真的想在每次提交后都这样做,你可以使用post-commit钩子,但是只有你(以及那些你提供钩子的人)才会拥有它,而且很有可能获得它不同步 - 你还必须有一个post-checkout挂钩,依此类推。对于创建需要此版本号的任何进程来自己获取它真的更好。

你也可以使用涂抹/清洁过滤器,它更像你真正想要的(而不是简单地在每次提交后)。

答案 1 :(得分:5)

对于那些在2018年发现此问题的人,您也可以使用Versioneer。启用后,它将在构建时根据最新的Git标签自动在模块version中的setup.py__version__中设置{}。

例如,如果您在标签1.0.0上构建项目,则Versioneer会将项目的版本设置为1.0.0。如果再执行两次提交,而无需检入就进行编辑,然后进行构建,则Versioneer会将版本设置为1.0.0+2.g1076c97.dirty之类。

您当然可以自定义Versioneer识别为版本标签的标签。

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

答案 2 :(得分:0)

除Versioneer之外的另一种可能性是setuptools_scm

通过在setup.py中添加以下内容(或相应地对其进行修改),我已经成功实现了与OP非常相似的功能:

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

答案 3 :(得分:0)

您可以使用以下代码来检索当前的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())

答案 4 :(得分:0)

(1)一种选择是从发布到已发布程序包中的发行版本中实时获取版本号。为此,您需要向__init__.py添加一个依赖项,并使用类似setup.py的方式发布产品,并在运行时执行python3 setup.py --version。此方法使用the lightweight importlib_metadata module [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

这实现了PEP 566中的元数据推荐。如果您使用setuptools>=42.0.0发行,则效果很好,也可以与其他工具发行的软件包一起使用。

(2)第二种选择是使用Git进行一些操作以收集最后的标记值(假设您正在标记应用程序)。然后增加点的版本号。然后用新值替换初始化文件中的值,并用新值标记。

# 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"

答案 5 :(得分:0)

我的方法是将文件“version.txt”打包到包含命令输出的目录(比如“元数据”)中

git describe --long --tags --dirty --always >metadata/version.txt

连同 Python 源文件。

在运行 Python 应用程序的脚本中,有一个命令

export GIT_VERSION=`cat metadata/version.txt`

在应用程序启动之前运行。

代码可以在启动时运行

git_version = os.getenv('GIT_VERSION', None)

阅读