从CVS迁移到Git:$Id$的等效物是什么?

133

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

我知道在分布式仓库中这不太合适,但作为开发人员,我需要类似这样的东西。让我解释一下原因:

我使用Emacs。定期地,我会查找第三方软件包的Lisp源文件的新版本。假设我有一个文件foo.el,根据头部,它的版本为1.3;如果我查看最新版本,看到它是1.143或2.6或其他什么的,我就知道我已经相当滞后了。

如果我看到的是一对40个字符的哈希值,我就不知道哪个更晚,也无法知道它比现在更晚多少。如果我必须手动检查ChangeLogs以了解我有多么过时,那绝对会让我非常讨厌。

作为开发人员,我希望将这种礼貌(正如我所见,)扩展给使用我的输出的人(也许我在自己开玩笑,但暂且不论)。我不想每次都自己记得递增版本号或时间戳之类的东西。我知道这是一件真正的麻烦事,从经验上来讲。

那么我有什么其他选择呢?如果不能获得$Id:$等价物,我该如何提供我要找的东西?

需要注意的是,我的期望是最终用户将没有安装Git,即使他们安装了,也不会有本地仓库(实际上,我预计不会以这种方式提供)。

19个回答

68

SHA只是版本的一种表示(尽管是规范的)。git describe 命令提供了其他选项,并且执行得非常好。

例如,在我的Java memcached 客户端源码的主分支中运行 git describe 命令,我会得到这个:

2.2-16-gc0cd61a

这里说了两个重要的事情:

  1. 自2.2版本以来,该源码树共有16次提交
  2. 任何人都可以显示出完全相同的源代码树(不仅是你自己)。

例如,假设您使用源代码包装了一个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.

1
使用此方法会失败,因为您正在合并develop分支,而master分支已经有了一些热修复。自上一个版本以来的提交数量将发生变化。哈希值不可靠,因为某人可以使用filter-branch或其他工具重新构建整个项目。 - LeMike
这篇文章仅描述了学习信息的过程,而没有将其嵌入到可执行文件中。为此,您需要在构建之前运行git describe命令,将输出保存在头文件中或以其他方式将该值嵌入到代码中。 - Jesse Chisholm

57

现在,Git 支持 $Id:$。 要为文件 README 启用它,您需要在 .gitattributes 中放置 "README ident"。 文件名通配符受支持。 有关详细信息,请参见man gitattributes


14
这会给你提供 blob 的 sha1,但不是 commit 的 sha1。虽然有用,但不作为 commit 的标识符。 - Stephen Jennings
4
Git没有像 $Id$ 所提到的关键字扩展机制。存储的内容就是你得到的内容。无论如何,版本属于组成提交的所有文件的完整集合,而不是特定的一个文件。(这个想法是从 RCS 时代遗留下来的,或者可能是 SCCS 的错...因为 CVS 只是一个 RCS 的华丽前端,而 SVN 则试图成为类似 CVS 的工具,所以它一直被沿用着。) - vonbrand

33

这并不是提问者一个不合理的要求。

我的使用情况如下:

  1. 我使用Git来管理个人代码,因此不需要与他人协作。
  2. 我将系统Bash脚本存储在其中,这些脚本可能会在准备好时进入/usr/local/bin

我使用三台不同的机器上都有相同的Git仓库。能够知道我当前在/usr/local/bin中拥有的文件的“版本”,而不必手动执行“diff -u <repo version> <version in /usr/local/bin>”将会很好。

对于那些持否定态度的人,请记住还有其他用例存在。并非所有人都使用Git进行协作工作,并且Git仓库中的文件也不一定是最终位置。

无论如何,我所做的方法是在仓库中创建一个类似以下的attributes文件:

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

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

提交更改。请注意,这并不会自动执行扩展操作,你需要重新检出文件,例如:

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

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

$ head foo.sh
#!/bin/sh

# $Id: e184834e6757aac77fd0f71344934b1cd774e6d4 $

一些好的信息在 如何为Git存储库启用标识字符串?中。


3
需要注意的是,这只识别当前文件(blob),而不是当前提交(commit)。 - CharlesB
3
git co应该是想执行git checkout命令,但因为co不是一个有效的git命令而出现错误提示。可以通过将git checkout设置为别名来解决此问题,具体方法可以参考此链接:https://dev59.com/f2Yq5IYBdhLWcg3wbgHk。请注意,别名可能会影响到其他人的用户配置,所以请谨慎使用。 - Peter Mortensen
@peter:co只是一个别名,例如: git config --global alias.ci commit; git config --global alias.st status; git config --global alias.co checkout。 - karsten

23

不确定这是否会出现在Git中。引用Linus

“关键字替换的整个概念都是非常愚蠢的。如果想要在发布树形结构(tar-balls等)时进行关键字替换,那么在实际的内容跟踪之外进行操作是微不足道的。”

不过检查日志还是相当容易的——如果你正在跟踪foo.el的稳定分支,你可以查看稳定分支的日志,看看哪些新提交不在你的本地副本中。如果你想模拟CVS的内部版本号,可以比较最后一次提交的时间戳。

编辑:当然,你应该编写或使用其他人的脚本来完成这个任务,而不是手动操作。


30
没错,我读了那一长串有关关键词扩展的电子邮件的部分内容。Linus的态度几乎让我对git彻底失去了兴趣。 - Joe Casadonte
15
有时他缺乏礼貌,但通常是正确的,在关键词扩展方面,他绝对是正确的。 - Bombe
1
版本跟踪是必要的。Git 在除了纯开发之外的许多其他基础设施中都被使用,人们可以阅读代码以确定其是否合理。但是,当一个修订控制系统用于跟踪具有任意内容的文件时,您必须有一些方法来知道官方发布的版本。Git,更不用说 Git 日志了,不在 .ini、.conf、.html 和其他文件被推送的机器上。 - rjt
10
Linus评论了关键词扩展的一个方面——版本跟踪,但这并不是它唯一的目的。那句话清楚地展示了一个人的态度,但对主题没有任何有用的陈述。这是一种典型的“以崇高的方式陈述显而易见的事实”的政治手法,成功地掌控了群众。问题在于,群众总体来说是愚蠢的,因为他们共用同一颗大脑。这清楚地解释了Git和关键词扩展的情况。一个白痴说“不”,所有人都欢呼! - AnrDaemon
2
@AnrDaemon 不仅如此,git现在通过ident属性已经添加了对$Id$的支持,正如另一个答案中提到的那样,这表明即使是git本身也不受Linus观点的限制。 - orip
显示剩余2条评论

21

正如我之前所写的

使用分布式版本控制工具(如Bazaar、Git等)自动生成显示合理版本号的ID标签是不可能的,因为每个人的开发线路都可能与其他人不同。因此,有人可能引用文件的“1.41”版本,但你的这个文件的“1.41”版本却不同。

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


7
没错,我在发布之前就读过那篇文章,这也是我要求更一般性的解决方案的原因。我认为希望每个文件都有一个版本号是合理的,而 Git 无法提供解决方案也是合理的。 - Joe Casadonte
值得注意的是,bzr 已经启用了关键词扩展功能。http://wiki.bazaar.canonical.com/KeywordExpansion - chrishiestand
8
直接输出哈希值与使用“版本号”有何不同?我想在内部脚本生成日志、调试网页和使用“--version”选项,以便轻松查看正在运行的代码版本,并能检出具体的哈希值来确定其行为。这简化了已部署应用的管理...我不想让提交钩子认为每次提交都是对所有带有 $Id$ 标记的文件进行更改。 - nairbv
1
只要您可以通过该ID在Git中查找到它,您正在处理的文件的哈希将与“版本”相同。 - Erik Aronesty
2
@Brian - 根据原帖的编辑,最终用户想知道版本号,但没有访问git或git日志的权限。在这种情况下,哈希是一个无意义的数字,而不是版本号。分布式源代码管理系统(DSCM)对解决这个需求没有任何帮助。 - Jesse Chisholm
显示剩余3条评论

10
我有同样的问题。我需要一个比哈希字符串更简单的版本,并且可供使用该工具的人使用,而无需连接到存储库。
我使用了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),如果我将其作为“tool -updateVersion”调用,则只会调用修改自身中的“MYVERSION”值的工具的updateVersion函数,然后退出(如果需要,您还可以更新其他文件)。
设置完成后,我转到Git head并在.git/hooks/pre-commit中创建一个可执行的一行bash脚本。
该脚本简单地更改到Git目录的head,并使用-updateVersion调用我的脚本。
每次我检查pre-commit脚本时都会运行它,该脚本使用-updateVersion运行我的脚本,然后根据提交数量更新MYVERSION变量。神奇!

那么你的 Ruby 脚本必须被命名为 updateVersion 才能使用 git updateVersion 吗?请提供一些调用示例。 - rjt
我在脚本中添加了一个选项(-updateVersion),调用“updateVersion”函数(在这种情况下,我试图更改脚本本身的版本号)。然后,我只需创建一个一行命令的 shell 命令,调用带有-updateVersion参数的脚本,然后在每次提交之前更新自己。 - David Ljung Madison Stellar

9
如果$Keywords$对你很重要,那么也许你可以尝试看看Mercurial。它有一个hgkeyword扩展程序,可以实现你想要的功能。无论如何,Mercurial作为DVCS也非常有趣。

8

在Git存储库中,常用的操作之一是使用tag对象。这可以用于标记提交,并可用于标记版本。您可以使用git tag命令查看存储库中的标记,该命令将返回所有标记。

检出标记很容易。例如,如果有一个标记为v1.1,则可以像这样将该标记检出到分支:

git checkout -b v1.1

作为顶级对象,您将看到该提交的整个历史记录,并能够运行差异、进行更改和合并。

而且,标签是永久存在的,即使它所在的分支已被删除而没有合并回主线。


7
有没有一种方法可以让git自动将这个标签插入文件中?谢谢! - Joe Casadonte
1
如果你的意思是关键字扩展?据我所知并没有。如果你正在构建产品,你可以在构建脚本中获取信息,并将其插入到构建的产品中的某个位置。尝试使用 man git-describe 命令,它会给出最新的标签、自该标签以来的提交次数和当前哈希值。 - Abizern
是的,通过gitattributes(5)export-subst功能,标签和其他相关信息现在可以自动地被git编辑到文件中。当然,这需要使用git archive来创建发布版本,并且只有在生成的tar文件中才能看到替换编辑。 - Greg A. Woods

6

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

* ident

为了看到这个效果,您需要首先对文件进行有效的检出,例如删除或以任何方式编辑它们。然后使用以下命令还原它们:
git checkout .

你应该看到$Id$被替换成类似以下的内容:

$Id: ea701b0bb744c90c620f315e2438bc6b764cdb87 $

来自man gitattributes

ident

当路径的属性设置为 ident 时,Git会在检出时将blob对象中的$Id$替换为$Id:跟着40个字符的十六进制blob对象名称,再跟一个美元符号$。任何以$Id:开头,以$结尾的字节序列都会在工作树文件中被替换为$Id$。

每次提交新版本时,这个ID都会更改。


4

标签名称和其他相关信息现在可以通过Git的export-subst特性直接自动编辑到文件中。 当然,这需要使用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"

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接