【问题标题】:Moving from CVS to Git: $Id$ equivalent?从 CVS 迁移到 Git:$Id$ 等价物?
【发布时间】:2010-09-27 21:35:59
【问题描述】:

我阅读了一堆关于简单源代码控制工具的问题,Git 似乎是一个合理的选择。我已经启动并运行它,到目前为止它运行良好。我喜欢 CVS 的一个方面是版本号的自动递增。

我知道这在分布式存储库中意义不大,但作为开发人员,我想要/需要这样的东西。让我解释一下原因:

我使用 Emacs。我会定期检查并寻找第三方软件包的 Lisp 源文件的新版本。假设我有一个文件 foo.el,根据标题,它是 1.3 版;如果我查看最新版本并看到它是 1.143 或 2.6 或其他什么,我知道我已经远远落后了。

如果我看到几个 40 个字符的哈希值,我将不知道哪个更晚,或者知道它晚了多少。如果我不得不手动检查 ChangeLogs 以了解我有多过时,我绝对会讨厌它。

作为一名开发人员,我想将我所看到的这种礼貌扩展到使用我的输出的人(也许我在开玩笑说任何人都是,但让我们暂时搁置一下)。我不想每次都要记住自己增加该死的数字,或者时间戳或类似的东西。那是真正的 PITA,我从经验中知道这一点。

那么我有什么选择呢?如果我无法获得 $Id:$ 等价物,我还能如何提供我正在寻找的东西?

我应该提一下,我的期望是最终用户不会安装 Git,即使他们安装了,也不会有本地存储库(实际上,我希望不会以这种方式提供它)。

【问题讨论】:

    标签: git version-control cvs keyword-substitution


    【解决方案1】:

    不确定这是否会出现在 Git 中。致quote Linus

    “关键字替换的整个概念完全是愚蠢的。它是 如果您愿意,可以在实际内容跟踪的“外部”进行微不足道的操作 在将树作为 tar-ball 等发布时拥有它。”

    不过,检查日志非常容易 - 如果您正在跟踪 foo.el 的稳定分支,您可以看到稳定分支的日志中有哪些新提交不在您的本地副本中。如果要模拟CVS的内部版本号,可以比较最后一次提交的时间戳。

    编辑:您应该为此编写或使用其他人的脚本,当然,不要手动执行此操作。

    【讨论】:

    • 是的,我读过一长串关于关键字扩展的电子邮件。 Linus 的态度几乎足以让我完全摆脱 git。
    • 是的,有时他不够礼貌,但他通常是正确的,而且他肯定是在关键字扩展的话题上。
    • 版本跟踪是必要的。除了纯开发之外,git 还用于许多其他基础设施,人们可以阅读代码以确定它是否有意义。但是,当使用修订控制系统来跟踪具有任意内容的文件时,您必须有一些了解正式版本的方法。 git,更别说git log不在推送.ini、.conf、.html等文件的机器上。
    • Linus 只评论了关键字扩展的一个方面——发布跟踪。但这不是它的唯一目的。那句话清楚地表明了一个男人的态度,但对这个主题没有任何用处。一个典型的“以高尚的方式陈述显而易见的事情”的政治伎俩,人群就是你的。问题是人群从定义上讲是愚蠢的,因为它只有一个大脑。这清楚地解释了 git 和关键字扩展的情况。一个白痴说“不”,每个人都欢呼!
    • @AnrDaemon 不仅如此,git now 还通过ident 属性添加了对$Id$ 的支持,正如此处另一个答案中提到的那样,这表明即使 git 本身也不受 Linus 意见的影响。
    【解决方案2】:

    如果我理解正确,基本上,您想知道自上次更新以来给定文件发生了多少次提交。

    首先获取远程源中的更改,但不要将它们合并到您的master 分支中:

    % git fetch
    

    然后获取您的master 分支和远程origin/master 之间给定文件发生的更改的日志。

    % git log master..origin/master foo.el
    

    这将为您提供自您上次将 origin/master 合并到您的 master 以来远程存储库中发生的所有提交的日志消息。

    如果您只想计算更改的数量,请将其发送到 wc。像这样说:

    % git rev-list master..origin/master foo.el | wc -l
    

    【讨论】:

    • 所以,不要使用日志:git rev-list master..origin/master | wc -l
    【解决方案3】:

    如果您只是希望人们能够了解他们已经过时了多少,Git 可以通过几种相当简单的方式告知他们这一点。例如,他们比较他们的主干和你的主干上最后一次提交的日期。他们可以使用git cherry 查看您的主干中发生了多少次提交,而他们的主干中不存在。

    如果这就是您想要的全部,我会寻找一种不提供版本号的方法。

    另外,除非您确定他们需要,否则我不会费心向任何人提供礼貌。 :)

    【讨论】:

    • 如果可以比较日期,请将 DateTImeStamp 放入文件中。 git 除了开发人员之外还有很多其他用例。现场 IT 人员需要知道当前正在排除故障的工作站上的 .INI 或 .conf 文件是否与当前文件相近。
    • 仅仅一个时间戳就足够了吗?错误的分支可能有一个吸引人的时间戳,但仍然不太正确。
    【解决方案4】:

    使用 Git 存储库所做的事情是使用 tag 对象。这可用于使用任何类型的字符串标记提交,并可用于标记版本。您可以使用git tag 命令在存储库中查看这些标签,该命令会返回所有标签。

    签出标签很容易。例如,如果有标签v1.1,您可以像这样将该标签签出到分支:

    git checkout -b v1.1
    

    由于它是顶级对象,您将看到该提交的全部历史记录,并且能够运行差异、进行更改和合并。

    不仅如此,标签仍然存在,即使它所在的分支已被删除而没有合并回主线。

    【讨论】:

    • 那么,有没有办法让 git 自动将此标签插入文件中?谢谢!
    • 如果您的意思是关键字扩展?据我所知不是。如果您正在构建产品,您可以将信息作为构建脚本的一部分并将其插入到您构建的产品中的某个位置。试试 man git-describe,它给出了最新的标签、自这个标签以来的提交次数以及当前的哈希值。
    • 是的,标签和其他相关信息现在可以通过gitattributes(5)export-subst功能由git自动编辑到文件中。这当然需要使用git archive 来创建版本,并且只有在生成的 tar 文件中才能看到替换编辑。
    【解决方案5】:

    如果 $Keywords$ 对您来说是必不可少的,那么也许您可以尝试查看 Mercurial 来代替?它有一个 hgkeyword 扩展来实现你想要的。 Mercurial 作为 DVCS 还是很有趣的。

    【讨论】:

      【解决方案6】:

      正如我写的before

      使用 Bazaar 等 DSCM 工具无法自动生成显示合理版本号的 Id 标签,因为每个人的开发线都可能与其他人不同。因此,有人可能会引用文件的“1.41”版本,但该文件的“1.41”版本是不同的。

      基本上,$Id$ 对于 Bazaar、Git 和其他分布式源代码管理工具没有任何意义。

      【讨论】:

      • 是的,我在发布之前阅读了该内容,这就是为什么我要求针对潜在问题提供更通用的解决方案。我认为单个文件拥有版本号的愿望是合法的,因为 git 无法提供解决方案。
      • 这不是 Git 的无能,而是所有分布式 SCM 的无能。如果您真的想要有意义的版本号,请使用 Subversion、CVS 或其他一些集中式系统。
      • 只吐出哈希而不是“版本号”有什么问题?我想要内部脚本上的日志语句、调试网页和“--version”选项,这些选项可以很容易地告诉我哪个版本在哪里运行,这样我就可以检查那个特定的哈希,看看它为什么会这样。它简化了已部署应用程序的管理....而且我不希望某个提交挂钩将每个提交都视为对其中包含 $Id$ 标记的每个文件的更改。
      • 您正在处理的文件的哈希值与“版本”相同,只要您可以通过该 id 在 git 中查找它
      • @Brian - 根据 OP 的编辑,最终用户想知道版本号,但无权访问 git 或 git 日志。在这种情况下,哈希是一个无意义的数字,而不是版本号。 DSCM 无法帮助解决这一需求。
      【解决方案7】:

      SHA 只是版本的一种表示(尽管是规范的)。 git describe 命令提供了其他命令并且做得很好。

      例如,当我在我的 Java memcached client 源的主分支中运行 git describe 时,我得到这个:

      2.2-16-gc0cd61a
      

      这说明了两件重要的事情:

      1. 自 2.2 以来,此树中恰好有 16 次提交
      2. exact 源代码树可以显示在任何其他人的克隆上。

      假设,例如,您将 version 文件与源代码打包在一起(甚至重写了所有内容以进行分发)以显示该数字。假设打包的版本是2.2-12-g6c4ae7a(不是发行版,而是有效的版本)。

      您现在可以准确地看到您落后了多远(4 次提交),并且您可以准确地看到哪 4 次提交:

      # The RHS of the .. can be origin/master or empty, or whatever you want.
      % git log --pretty=format:"%h %an %s" 2.2-12-g6c4ae7a..2.2-16-gc0cd61a
      c0cd61a Dustin Sallings More tries to get a timeout.
      8c489ff Dustin Sallings Made the timeout test run on every protocol on every bui
      fb326d5 Dustin Sallings Added a test for bug 35.
      fba04e9 Valeri Felberg Support passing an expiration date into CAS operations.
      

      【讨论】:

      • 在合并开发分支时使用它会失败,而主分支有一些修补程序。自上一个版本以来的提交次数将会改变。哈希不可靠,因为有人可以使用 filter-branch 或其他东西重建整个事物。
      • 这篇文章只描述了学习信息,而不是将其嵌入到您的可执行文件中。为此,您需要在构建之前运行git describe 命令,将输出保存在头文件中,或者将值嵌入代码中。
      【解决方案8】:

      由于您使用 Emacs,您可能会很幸运 :)

      我偶然遇到了这个问题,几天前我也偶然发现了Lively,这是一个 Emacs 包,它允许在您的文档中包含生动的 Emacs Lisp 片段。老实说,我没有尝试过,但是当我读到这篇文章时,我想到了。

      【讨论】:

        【解决方案9】:

        现在 Git 支持 $Id:$。要为文件 README 启用它,您可以将“README ident”放入 .gitattributes。支持文件名上的通配符。详情请见man gitattributes

        【讨论】:

        • 这为您提供了 blob 的 sha1,但不是提交的 sha1。有用,只是不能作为提交的标识符。
        • git 没有像提到的$Id$ 这样的任何关键字扩展机制。被存储的正是你得到的。无论如何,该版本属于构成提交的文件的完整集合,而不是特定于一个文件(这个想法是 RCS 时代的残余,或者可能是 SCCS 应该归咎于这里......因为 CVS 只是一个美化了 RCS 的前端,而 SVN 试图成为类似 CVS 的工作,它卡住了。)。
        【解决方案10】:

        这不是 OP 的无理要求。

        我的用例是:

        1. 我将 Git 用于我自己的个人代码,因此不与他人合作。
        2. 我将系统 Bash 脚本保存在那里,当它们准备好时可能会进入 /usr/local/bin

        我使用三台不同的机器,上面有相同的 Git 存储库。很高兴知道我目前在/usr/local/bin 中拥有的文件的“版本”,而无需手动执行“diff -u ”。

        对于那些持否定态度的人,请记住还有其他用例。并非每个人都使用 Git 进行协作,将 Git 存储库中的文件作为他们的“最终”位置。

        无论如何,我这样做的方式是在存储库中创建一个属性文件,如下所示:

        cat .git/info/attributes
        # see man gitattributes
        *.sh ident
        *.pl ident
        *.cgi ident
        

        然后将 $Id$ 放在文件中的某个位置(我喜欢放在 shebang 之后)。

        提交。请注意,这不会像我预期的那样自动进行扩展。你必须重新co文件,例如,

        git commit foo.sh
        rm foo.sh
        git co foo.sh
        

        然后你会看到展开,例如:

        $ head foo.sh
        #!/bin/sh
        
        # $Id: e184834e6757aac77fd0f71344934b1cd774e6d4 $
        

        How do I enable the ident string for a Git repository? 中有一些很好的信息。

        【讨论】:

        • 需要注意的是,这确实标识了当前文件的(blob),而不是当前提交
        • git co 应该做什么?我收到错误消息“git: 'co' is not a git command. See 'git --help'.”应该it be git checkout
        【解决方案11】:

        我遇到了同样的问题。我需要一个比哈希字符串更简单的版本,并且可供使用该工具的人使用,而无需连接到存储库。

        我使用 Git 预提交挂钩并更改了我的脚本以能够自动更新自身。

        我根据完成的提交数量来确定版本。这是一个轻微的竞争条件,因为两个人可以同时提交并且都认为他们提交的是相同的版本号,但是我们在这个项目上没有很多开发人员。

        例如,我有一个我签入的 Ruby 脚本,我将这段代码添加到其中 - 这是非常简单的代码,因此如果您要签入不同语言的内容,很容易移植到不同的语言(虽然显然这不会很容易地与不可运行的签入(例如文本文件)一起使用)。我添加了:

        MYVERSION = '1.090'
        ## Call script to do updateVersion from .git/hooks/pre-commit
        def updateVersion
          # We add 1 because the next commit is probably one more - though this is a race
          commits = %x[git log #{$0} | grep '^commit ' | wc -l].to_i + 1
          vers = "1.%0.3d" % commits
        
          t = File.read($0)
          t.gsub!(/^MYVERSION = '(.*)'$/, "MYVERSION = '#{vers}'")
          bak = $0+'.bak'
          File.open(bak,'w') { |f| f.puts t }
          perm = File.stat($0).mode & 0xfff
          File.rename(bak,$0)
          File.chmod(perm,$0)
          exit
        end
        

        然后我在脚本中添加了一个命令行选项 (-updateVersion),所以如果我将其称为“工具 -updateVersion”,那么它只会为修改“MYVERSION”值的工具调用 updateVersion,然后退出(如果你愿意,你也可以让它更新其他文件,如果它们也被打开)。

        设置完成后,我转到 Git 头并在 .git/hooks/pre-commit 中创建一个可执行的单行 bash 脚本。

        脚本只是更改到 Git 目录的头部,并使用 -updateVersion 调用我的脚本。

        每次我签入 pre-commit 脚本时都会运行它,它使用 -updateVersion 运行我的脚本,然后根据提交的数量更新 MYVERSION 变量。魔法!

        【讨论】:

        • 那么您的 Ruby 脚本是否必须被称为 updateVersion 才能拥有 git updateVersion ?请放一些它是如何调用的示例。
        • 我为正在检查的脚本创建了一个选项 (-updateVersion),该脚本调用了“updateVersion”函数(在这种情况下,我试图更改脚本本身的版本号) .然后我只做一个单线 shell 命令,用 -updateVersion 调用我的脚本,然后它在每次签入之前更新自己。
        【解决方案12】:

        我同意那些认为令牌替换属于构建工具而不是版本控制工具的人。

        您应该有一些自动发布工具来在标记发布时设置源中的版本 ID。

        【讨论】:

        • .INI .conf 和 .txt 通常没有构建工具。
        • 但是您可以编写一个发布脚本,该脚本获取当前的 Git 标记并将其写入文件或类似的东西。
        【解决方案13】:

        RCS ID 非常适合单文件项目,但对于其他任何项目,$Id$ 都没有说明该项目(除非您强制虚拟签入到虚拟版本文件)。

        仍然有人可能对如何在每个文件级别或提交级别获得 $Author$、$Date$、$Revision$、$RCSfile$ 等的等价物感兴趣(如何将它们放在某些关键字的位置)是另一个问题)。我对这些没有答案,但请参阅更新这些的要求,尤其是当文件(现在在 Git 中)源自 RCS 兼容系统 (CVS) 时。

        如果源代码与任何 Git 存储库分开分发(我也是这样做的),那么这些关键字可能会很有趣。我的解决方案是这样的:

        每个项目都有自己的目录,在项目根目录中我有一个名为 .version 的文本文件,其内容描述了当前版本(导出源时将使用的名称)。

        在为下一个版本工作时,脚本会提取 .version 编号、一些 Git 版本描述符(如 git describe)和 .build 中的单调内部版本号(加上主机和日期)到自动生成的源文件链接到最终程序,因此您可以了解它是从什么来源以及何时构建的。

        我在单独的分支中开发新功能,我做的第一件事是将n(用于“下一个”)添加到.version 字符串(来自同一个根的多个分支将使用相同的临时.version数字)。在发布之前,我决定合并哪些分支(希望所有分支都具有相同的.version)。在提交合并之前,我将.version 更新为下一个数字(主要或次要更新,取决于合并的功能)。

        【讨论】:

          【解决方案14】:

          我也来自 SCCS、RCS 和 CVS (%W% %G% %U%)。

          我也遇到过类似的挑战。我想知道一段代码在运行它的任何系统上的版本。该系统可能连接到也可能不连接到任何网络。系统可能安装了也可能没有安装 Git。系统可能安装了也可能没有安装 GitHub 存储库。

          我希望对几种类型的代码(.sh、.go、.yml、.xml 等)使用相同的解决方案。我希望任何不了解 Git 或 GitHub 的人都能够回答这个问题“你在运行什么版本?”

          所以,我为几个 Git 命令编写了我所谓的包装器。我用它来标记带有版本号和一些信息的文件。它解决了我的挑战。它可能会帮助你。

          https://github.com/BradleyA/markit

          git clone https://github.com/BradleyA/markit
          cd markit
          

          【讨论】:

            【解决方案15】:

            要将扩展应用于存储库中所有子目录中的所有文件,请将.gitattributes 文件添加到存储库的顶级目录(即您通常放置.gitignore 文件的位置),其中包含:

            * ident
            

            要查看此效果,您需要先对文件进行有效签出,例如以任何方式删除或编辑它们。然后恢复它们:

            git checkout .
            

            你应该看到$Id$被替换为:

            $Id: ea701b0bb744c90c620f315e2438bc6b764cdb87 $
            

            来自man gitattributes

            身份

            当为路径设置属性 ident 时,Git 会替换 带有 $Id: 的 blob 对象,后跟 40 个字符的十六进制 blob 对象 名称,然后是结帐时的美元符号 $。任何开始的字节序列 以 $Id: 结尾的工作树文件中的 $Id$ 在签入时替换为 $Id$。

            每次提交文件的新版本时,此 ID 都会更改。

            【讨论】:

              【解决方案16】:

              如果您希望 git 提交信息 可访问 到您的代码中,那么您必须 执行预构建步骤以获取它。在 C/C++ 的 bash 中,它可能看起来像这样:

              prebuild.sh

              #!/bin/bash
              commit=$(git rev-parse HEAD)
              tag=$(git describe --tags --always ${commit})
              cat <<EOF >version.c
              #include "version.h"
              const char* git_tag="${tag}";
              const char* git_commit="${commit}";
              EOF
              

              version.h 看起来像:

              #pragma once
              const char* git_tag;
              const char* git_commit;
              

              然后,在您的代码#include "version.h" 中需要它的任何地方,并根据需要引用git_taggit_commit

              你的Makefile 可能有这样的东西:

              all: package
              version:
                ./prebuild.sh
              package: version
                # the normal build stuff for your project
              

              这样做的好处是:

              • 获取当前正确的构建值 不论分支、合并樱桃采摘等。

              prepublish.sh 的这种实现有以下缺点:

              • 即使git_tag/git_commit 没有改变,也会强制重新编译。
              • 它不考虑尚未提交但影响构建的本地修改文件。
                • 使用git describe --tags --always --dirty 捕获该用例。
              • 污染全局命名空间。

              可以避免这些问题的发烧友prebuild.sh 留给读者作为练习。

              【讨论】:

                【解决方案17】:

                为了自己解决这个问题,我创建了一个小的“hack”作为提交后挂钩:

                echo | tee --append *
                git checkout *
                

                this post on my blog 中记录了更多详细信息。

                【讨论】:

                  【解决方案18】:

                  标签名称和其他相关信息现在可以通过gitattributes(5)export-subst 功能由 Git 自动直接编辑到文件中。这当然需要使用git archive 来创建发布,并且只有在生成的 tar 文件中才能看到替换编辑。

                  例如在.gitattributes 文件中放入以下行:

                  * export-subst
                  

                  然后在源文件中你可以像这样添加一行:

                  #ident  "@(#)PROJECTNAME:FILENAME:$Format:%D:%ci:%cN:%h$"
                  

                  并且它将在由例如git archive v1.2.0.90 创建的版本中扩展为如下所示:

                  #ident  "@(#)PROJECTNAME:FILENAME:HEAD -> master, tag: v1.2.0.90:2020-04-03 18:40:44 -0700:Greg A. Woods:e48f949"
                  

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 2019-07-02
                    • 1970-01-01
                    • 2011-07-06
                    • 1970-01-01
                    • 1970-01-01
                    • 2012-02-08
                    • 1970-01-01
                    相关资源
                    最近更新 更多