从CVS迁移到Git:等效于$ Id $?


124

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

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

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

相反,如果我看到几个40个字符的散列,我将不知道是哪个以后的时间,也不会知道以后的时间多少。如果我不得不手动检查ChangeLogs以了解我过时了,我绝对会讨厌它。

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

那我有什么选择呢?如果我无法获得等价的$ Id:$,我还能如何提供所需的内容?

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

Answers:


67

SHA只是版本的一种表示形式(尽管是规范的)。该git describe命令提供了其他功能,并且效果很好。

例如,当我git describeJava memcached客户端源的master分支中运行时,得到以下信息:

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
当母版有一些修补程序时,合并开发分支后使用此方法将失败。自上一版本以来的提交次数将更改。哈希是不可靠的,因为有人可以用filter-branch或某种东西重建整个事物。
LeMike

本文仅描述学习信息,而不是将其嵌入可执行文件中。为此,您需要git describe在构建之前运行命令,将输出保存在头文件中,或者将值嵌入代码中。
杰西·奇斯霍尔姆

55

到目前为止,在Git中已经支持$ Id:$了。要为文件README启用它,您可以将“ README ident”放入.gitattributes中。支持使用文件名通配符。有关详细信息,请参见man gitattributes


14
这为您提供了blob的sha1,但没有提交的sha1。有用,只是不作为提交的标识符。
斯蒂芬·詹宁斯

4
git没有像上面$Id$提到的任何关键字扩展机制。储存起来的正是您所得到的。无论如何,该版本属于构成一次提交的文件的完整集合,而不是特别属于一个文件(这个想法是RCS时代的残余,或者也许应该归咎于SCCS ...因为CVS只是一个夸大了RCS的前端,而SVN试图成为CVS式的,它卡住了。)
vonbrand

32

这不是OP的不合理要求。

我的用例是:

  1. 我将Git用于自己的个人代码,因此无需与他人合作。
  2. 我将系统Bash脚本保存在其中,/usr/local/bin准备就绪时可能会进入其中。

我使用三台单独的计算机,并在其上具有相同的Git存储库。/usr/local/bin无需手动进行“ diff -u <repo版本> </ usr / local / bin>中的版本”就可以知道我当前所在文件的“版本”。

对于那些对您不利的人,请记住还有其他用例。并非每个人都使用Git进行协作工作,因为Git存储库中的文件是其“最终”位置。

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

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存储库启用ident字符串中提供了一些很好的信息


3
应该注意的是,这确实标识了当前文件的(blob),而不是当前的提交
CharlesB 2013年

3
什么是git co应该做的?我得到错误信息“ git: 'co' is not a git command. See 'git --help'.”如果这是git checkout
彼得·莫滕森

23

不确定这是否会出现在Git中。要引用莱纳斯

“关键字替换的整个概念完全是愚蠢的。如果您想在进行诸如tar-balls之类的发行树时使用它,那么在实际内容跟踪的“外部”是微不足道的。”

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

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


26
是的,我读了很多关于关键字扩展的电子邮件。Linus的态度几乎足以使我彻底摆脱git的困扰。
Joe Casadonte

15
是的,有时他缺乏礼貌,但通常是正确的,而且他绝对是在关键字扩展这个话题上。
孟买

必须进行版本跟踪。git除了用于纯开发之外,还用于其他许多基础架构中,在纯基础开发中,人们可以读取代码以确定其是否有意义。但是,当使用修订控制系统来跟踪具有任意内容的文件时,则必须具有某种了解正式发行版的方法。git,更不用说git log不在.ini,.conf,.html和其他文件被推送的计算机上了。
rjt

7
Linus仅评论了关键字扩展的一个方面-发布跟踪。但这不是它的唯一目的。那句话清楚地表明了一个人的态度,但没有说关于这个话题的有用信息。一个典型的“以崇高的方式表明明显”的政治手段,而人群却是你的。问题在于,按照定义,人群是愚蠢的,因为人群只有一个大脑。其中用git和关键字扩展清楚地说明了情况。一位白痴说“不”,大家都为之欢呼!
AnrDaemon

1
@AnrDaemon不仅如此,git现在还添加了对$Id$通过ident属性的支持,如此处的另一个答案所述,表明git本身也不是Linus的观点。
Orip

21

正如我写之前

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

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


6
是的,我在发帖之前先读了这本书,这就是为什么我要求针对潜在问题的更一般的解决方案。我认为,希望单个文件具有版本号是合理的,而git无法提供解决方案也是如此。
Joe Casadonte

这不是Git的功能,而是所有分布式SCM的功能。如果您确实想要有意义的版本号,请使用Subversion或CVS或其他一些集中式系统。
孟买

7
仅散列哈希而不是“版本号”怎么了?我希望在内部脚本上使用日志语句,调试网页和“ --version”选项,这些选项可以轻松地告诉我哪个版本在何处运行,因此我可以检出特定的哈希值,并查看其行为方式。它简化了已部署应用程序的管理。
nairbv 2013年

1
你的工作将努力同一个“版本”文件的哈希,只要你可以通过ID查找它的混帐
埃里克Aronesty

2
@Brian-根据OP的编辑,最终用户想知道版本号,但无权访问git或git日志。在这种情况下,哈希是没有意义的数字,而不是版本号。DSCM不协助解决此需求。
Jesse Chisholm

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”,则它将只调用该工具的updateVersion,该工具会自行修改“ MYVERSION”值,然后退出(您可以如果需要的话,还可以更新其他文件)。

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

该脚本只是更改为Git目录的开头,并使用 -updateVersion

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


那么,您的Ruby脚本是否必须称为updateVersion才能拥有 git updateVersion?请提供一些有关其调用方式的示例。
rjt

我在要检查的脚本中添加了一个选项(-updateVersion),该选项称为'updateVersion'函数(在这种情况下,我试图更改脚本本身中的版本号)。然后,我只执行一个oneliner shell命令,该命令使用-updateVersion调用我的脚本,然后在每次检入之前更新自身。
David Ljung Madison Stellar

8

如果拥有$ Keywords $对您来说至关重要,那么也许您可以尝试看看Mercurial吗?它具有一个hgkeyword扩展名,可以实现您想要的内容。无论如何,Mercurial作为DVCS还是很有趣的。


8

使用Git存储库完成的操作是使用tag对象。它可以用来标记带有任何类型字符串的提交,并且可以用来标记版本。您可以使用git tag命令查看存储库中的标签,该命令返回所有标签。

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

git checkout -b v1.1

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

不仅如此,即使标签所在的分支已被删除而没有合并回到主行中,标签仍然保留。


6
那么,有没有办法让git自动将此标签插入文件中?谢谢!
Joe Casadonte,

1
如果用关键字扩展来表示?据我所知。如果您要构建产品,则可以将信息作为构建脚本的一部分获取,并将其插入到构建产品中的某个位置。试试man git-describe,它给出了最新的标签,自该标签以来的提交次数以及当前的哈希值。
阿比森

是的,标签和其他相关信息现在可以通过git的export-subst功能由git自动编辑为文件gitattributes(5)。当然,这需要使用git archive来创建发行版,并且只有在生成的tar文件中,替代编辑才可见。
格雷格·A·伍兹

4

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

首先获取远程源中的更改,但不要将其合并到您的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

1
因此,请勿使用log:git rev-list master..origin / master | wc -l
Dustin

4

如果您只是希望人们能够了解他们过时了多久,Git可以通过几种相当简单的方式告知他们。例如,他们比较主干和您的主干上次提交的日期。他们可以使用git cherry用来查看您的主干中发生了多少次提交,而这些提交中没有这些提交。

如果仅此而已,那么我会寻找一种无需版本号即可提供它的方法。

另外,除非您确定他们愿意,否则我不会打扰任何人。:)


如果可以比较日期,则将DateTImeStamp放入文件中。git除了开发人员还有很多其他用例。现场的IT人员需要了解当前正在进行故障排除的工作站上的.INI或.conf文件是否接近当前的位置。
rjt

仅时间戳就足够了吗?错误的分支可能具有吸引人的时间戳,但仍然不那么正确。
user2066657 '18

4

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

* ident

要查看此效果,您首先需要对文件进行有效检出,例如以任何方式删除或编辑它们。然后使用以下命令还原它们:

git checkout .

并且您应该看到$Id$替换为:

$Id: ea701b0bb744c90c620f315e2438bc6b764cdb87 $

来自man gitattributes

身份

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

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


3

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

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

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

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

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

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


3

如果您想在代码中访问 git commit信息,则必须执行一个预构建步骤才能将其提交到那里。在Bash for C / C ++中,可能看起来像这样:

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_tag或者git_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可以避免这些问题的发烧友会留给读者练习。


1

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

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


2
.INI .conf和.txt通常没有构建工具。
rjt

但是您可以一起破解一个采用当前Git标签并将其写入文件或类似文件的发布脚本。
Marnen Laibow-Koser

1

现在,Git可以通过的export-subst功能将标记名和其他相关信息直接直接自动编辑为文件gitattributes(5)。当然,这需要使用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"

0

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

我偶然碰到了这个问题,几天前我也碰巧遇到了Lively,这是一个Emacs软件包,它允许在文档中包含生动的Emacs Lisp。说实话,我还没有尝试过,但是在读到这篇文章时就想到了。


0

我也来自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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.