从CVS转到Git:$ Id:$等价?

时间:2008-12-21 04:45:46

标签: git version-control cvs keyword-substitution

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

据我所知,这在分布式存储库中没有多大意义,但作为开发人员,我希望/需要这样的东西。让我解释一下原因:

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

如果相反我看到了几个40个字符的哈希值,我不知道哪个是后来的,或者不知道它后来有多少。如果我不得不手动检查ChangeLogs以了解我的过时情况,我会非常讨厌它。

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

那么我有什么替代品?如果我不能得到$ Id:$等价物,我还能提供我想要的东西吗?

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

18 个答案:

答案 0 :(得分:64)

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

例如,当我在Java memcached client来源的主分支中运行git describe时,我明白了:

2.2-16-gc0cd61a

这说了两件重要的事情:

  1. 自2.2以来,这棵树中确实有16次提交
  2. 完全源代码树可以显示在任何其他人的克隆上。
  3. 例如,假设您使用源打包了一个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 :(得分:51)

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

答案 2 :(得分:31)

这不是OP的无理要求。

我的用例是:

  1. 我使用Git作为我自己的个人代码,因此没有与他人合作。
  2. 我将系统Bash脚本保留在那里,当它们准备就绪时可能会进入/usr/local/bin
  3. 我使用三台具有相同Git存储库的独立机器。很高兴知道我当前在/usr/local/bin中的文件的“版本”,而无需在“/ usr / local / bin>”中进行手册“diff -u< repo version>< version”。

    对于那些消极的人,请记住还有其他用例。并非所有人都使用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 $
    

    一些好的信息在 How do I enable the ident string for a Git repository?

答案 3 :(得分:23)

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

  

“关键字替换的整个概念完全是愚蠢的。它是   如果你愿意,可以做实际内容跟踪的“外部”琐碎   在将树木作为焦油球等发布树木时有它。“

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

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

答案 4 :(得分:20)

正如我写的before

  

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

     

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

答案 5 :(得分:9)

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

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

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

我的是Ruby,但它并不是非常复杂的代码。 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。

最后,我转到Git头并在.git/hooks/pre-commit中创建一个可执行脚本。

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

每次登记时,MYVERSION变量都会根据提交次数进行更新。

答案 6 :(得分:8)

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

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

git checkout -b v1.1

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

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

答案 7 :(得分:8)

如果拥有$ Keywords $对您来说很重要,那么也许您可以尝试查看Mercurial而不是?它有一个hgkeyword扩展,可以实现你想要的。无论如何,Mercurial作为DVCS很有意思。

答案 8 :(得分:4)

如果您只是希望人们能够了解它们的过时程度,Git可以通过几种相当简单的方式告知它们。例如,他们比较最后一次提交他们的行李箱和行李箱的日期。他们可以使用git cherry来查看您的主干中发生了多少次未提交的提交。

如果这就是您想要的全部内容,我会寻找一种方法来提供它而没有版本号。

另外,除非你确定他们想要,否则我不会费心向任何人伸出礼貌。 :)

答案 9 :(得分:3)

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

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

答案 10 :(得分:3)

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

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

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

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

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

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

答案 11 :(得分:1)

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

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

答案 12 :(得分:1)

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

* ident

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

git checkout .

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

$Id: ea701b0bb744c90c620f315e2438bc6b764cdb87 $

来自man gitattributes

  

身份

     

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

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

答案 13 :(得分:1)

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

例如,在git archive文件中,添加以下行:

.gitattributes

然后在源文件中可以添加如下一行:

* export-subst

在由#ident "@(#)PROJECTNAME:FILENAME:$Format:%D:%ci:%cN:%h$" 创建的发行版中,它将扩展为如下所示:

git archive v1.2.0.90

答案 14 :(得分:0)

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

我巧合地遇到了这个问题,而且巧合的是我几天前来过Lively,这是一个Emacs软件包,允许在你的文档中包含生动的Emacs Lisp。我没有试过说实话,但在读这篇文章时我想到了。

答案 15 :(得分:0)

我也来自SCCS,RCS和CVS("SHOW MASTER STATUS")。

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

我想为几种类型的代码(.sh,.go,.yml,.xml等)提供相同的解决方案。我希望任何不了解Git或GitHub的人能够回答问题"你在运行什么版本?"

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

https://github.com/BradleyA/markit

%W% %G% %U%

答案 16 :(得分:0)

如果您想让git commit信息可访问到您的代码中,那么您必须进行预构建步骤以将其提交到那里。在用于C / C ++的bash中,可能看起来像这样:

new_Data <- data[ , grepl( "correct|time" , names( data ) ) ]

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留给读者练习。

答案 17 :(得分:0)

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

echo | tee --append *
git checkout *

this post on my blog中有更详细的记录。