从 CVS 迁移到 Git:$Id$ 等效吗?
我读了一堆关于简单源代码控制工具的问题,Git 似乎是一个合理的选择。 我已经启动并运行了它,到目前为止效果很好。 我喜欢 CVS 的一方面是版本号的自动递增。
我知道这在分布式存储库中没有多大意义,但作为开发人员,我想要/需要这样的东西。 让我解释一下原因:
我使用 Emacs。 我会定期检查并寻找第三方软件包的 Lisp 源文件的新版本。 假设我有一个文件 foo.el,根据标头,该文件的版本是 1.3; 如果我查找最新版本并看到它是 1.143 或 2.6 或其他版本,我知道我已经落后很多了。
相反,如果我看到几个 40 个字符的哈希值,我将不知道哪个晚了,也不知道晚了多少。 如果我必须手动检查 ChangeLogs 只是为了了解我有多过时,我绝对会讨厌它。
作为一名开发人员,我想将这种礼貌延伸到使用我的输出的人(也许我在自欺欺人地认为任何人都是如此,但让我们暂时将其放在一边)。 我不想每次都必须记住自己增加该死的数字,或者时间戳或类似的东西。 这是真正的 PITA,我从经验中知道这一点。
那么我还有什么选择呢? 如果我无法获得 $Id:$ 等价物,我还能如何提供我正在寻找的东西?
我应该提到的是,我的期望是最终用户不会安装 Git,即使安装了,也不会拥有本地存储库(事实上,我希望不要以这种方式提供它)。
I read through a bunch of questions asking about simple source code control tools and Git seemed like a reasonable choice. I have it up and running, and it works well so far. One aspect that I like about CVS is the automatic incrementation of a version number.
I understand that this makes less sense in a distributed repository, but as a developer, I want/need something like this. Let me explain why:
I use Emacs. Periodically I go through and look for new versions of the Lisp source files for third-party packages. Say I've got a file, foo.el, which, according to the header, is version 1.3; if I look up the latest version and see it's 1.143 or 2.6 or whatever, I know I'm pretty far behind.
If instead I see a couple of 40-character hashes, I won't know which is later or get any idea of how much later it is. I would absolutely hate it if I had to manually check ChangeLogs just to get an idea of how out of date I am.
As a developer, I want to extend this courtesy, as I see it, to the people that use my output (and maybe I'm kidding myself that anyone is, but let's leave that aside for a moment). I don't want to have to remember to increment the damn number myself every time, or a timestamp or something like that. That's a real PITA, and I know that from experience.
So what alternatives do I have? If I can't get an $Id:$ equivalent, how else can I provide what I'm looking for?
I should mention that my expectation is that the end user will NOT have Git installed and even if they do, will not have a local repository (indeed, I expect not to make it available that way).
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(19)
SHA 只是版本的一种表示(尽管是规范的)。
git describe
命令提供了其他命令,并且做得非常好。例如,当我在 Java memcached 的 master 分支中运行
gitdescribe
时client 源,我明白了:这说明了两件重要的事情:
举例来说,您将一个
version
文件与源代码一起打包(甚至重写了所有内容以进行分发)以显示该数字。 假设打包版本是 2.2-12-g6c4ae7a(不是发行版,而是有效版本)。您现在可以准确地看到您落后了多远(4 次提交), 您可以准确地看到哪 4 次提交:
The SHA is just one representation of a version (albeit canonical). The
git describe
command offers others and does so quite well.For example, when I run
git describe
in my master branch of my Java memcached client source, I get this:That says two important things:
Let's say, for example, you packaged a
version
file with the source (or even rewrote all the content for distribution) to show that number. Let's say that packaged version was2.2-12-g6c4ae7a
(not a release, but a valid version).You can now see exactly how far behind you are (4 commits), and you can see exactly which 4 commits:
现在 Git 中已经支持 $Id:$ 了。 要为文件 README 启用它,您可以将“README ident”放入 .gitattributes 中。 支持文件名通配符。 有关详细信息,请参阅 man gitattributes。
By now there is support for $Id:$ in Git. To enable it for file README you would put "README ident" into .gitattributes. Wildcards on file names are supported. See man gitattributes for details.
这并不是OP的无理要求。
我的用例是:
/usr/local/bin
。我使用三台独立的机器,上面有相同的 Git 存储库。 很高兴知道我当前在”。
/usr/local/bin
中的文件的“版本”,而无需手动执行“diff -u对于那些持消极态度的人,请记住还有其他用例。 并非每个人都使用 Git 进行协作工作,Git 存储库中的文件是其“最终”位置。
不管怎样,我这样做的方法是在存储库中创建一个属性文件,如下所示:
然后将 $Id$ 放在文件中的某个位置(我喜欢将其放在 shebang 之后)。
提交。 请注意,这不会像我预期的那样自动进行扩展。 例如,您必须重新编译该文件,
然后您将看到扩展,例如:
如何为 Git 存储库启用 ident 字符串?。
This isn't an unreasonable request from the OP.
My use-case is:
/usr/local/bin
when they are ready.I use three separate machines with the same Git repository on it. It would be nice to know what "version" of the file I have currently in
/usr/local/bin
without having to do a manual "diff -u <repo version> <version in /usr/local/bin>".To those of you being negative, remember there are other use cases out there. Not everyone uses Git for collaborative work with the files in the Git repository being their "final" location.
Anyway, the way I did it was to create an attributes file in the repository like this:
Then put $Id$ somewhere in the file (I like to put it after the shebang).
The commit. Note that this doesn't automatically do the expansion like I expected. You have to re-co the file, for example,
And then you will see the expansion, for example:
Some good information is in How do I enable the ident string for a Git repository?.
不确定这是否会出现在 Git 中。 引用 Linus:
不过,检查日志非常容易 - 如果您正在跟踪 foo.el 的稳定分支,您可以看到稳定分支日志中哪些新提交是不存在的如果你想模拟 CVS 的内部版本号,你可以比较最后一次提交的时间戳。
编辑:你应该为此编写或使用其他人的脚本,当然,不要手动执行此操作。
Not sure this will ever be in Git. To quote Linus:
It's pretty easy to check the log, though - if you're tracking foo.el's stable branch, you can see what new commits are in the stable branch's log that aren't in your local copy. If you want to simulate CVS's internal version number, you can compare the timestamp of the last commit.
Edit: you should write or use someone else's scripts for this, of course, not do this manually.
正如我之前所写:
As I’ve written before:
我有同样的问题。 我需要一个比哈希字符串更简单的版本,并且可供使用该工具的人使用,而无需连接到存储库。
我使用 Git 预提交挂钩来完成此操作,并更改了我的脚本以能够自动更新自身。
我根据完成的提交数量来确定版本。 这是一个轻微的竞争条件,因为两个人可以同时提交,并且都认为他们正在提交相同的版本号,但我们在这个项目上没有很多开发人员。
举个例子,我有一个用 Ruby 签入的脚本,我向其中添加了这段代码 - 这是非常简单的代码,因此如果您用不同的语言签入某些内容,很容易移植到不同的语言(尽管显然这无法轻松处理不可运行的签入(例如文本文件)。 我添加了:
然后我向脚本添加了一个命令行选项(-updateVersion),因此如果我将其称为“工具-updateVersion”,那么它只会调用工具的 updateVersion 来修改“MYVERSION”值本身,并且然后退出(如果您愿意,您也可以让它更新其他打开的文件)。
设置完成后,我会转到 Git 头并在
.git/hooks/pre-commit
中创建一个可执行的单行 bash 脚本。该脚本只是更改为 Git 目录的头部并使用
-updateVersion
调用我的脚本。每次我签入时,都会运行预提交脚本,该脚本会使用 -updateVersion 运行我的脚本,然后根据提交次数更新 MYVERSION 变量。 魔法!
I had the same problem. I needed to have a version that was simpler than a hash string and available for people using the tool without needing to connect to the repository.
I did it with a Git pre-commit hook and changed my script to be able to automatically update itself.
I base the version off of the number of commits done. This is a slight race condition because two people could commit at the same time and both think they are committing the same version number, but we don't have many developers on this project.
As an example, I have a script that I checkin that is in Ruby, and I add this code to it - it's pretty simple code so it's easy to port to different languages if you are checking in something in a different language (though obviously this won't easily work with non-runnable checkins such as text files). I've added:
And then I add a command-line option (-updateVersion) to the script so if I call it as "tool -updateVersion" then it just calls updateVersion for the tool which modifies the "MYVERSION" value in itself and then exits (you could have it also update other files if they are opened as well if you wanted).
Once that's setup, I go to the Git head and create an executable one-line bash script in
.git/hooks/pre-commit
.The script simply changes to the head of the Git directory and calls my script with
-updateVersion
.Every time I check in the pre-commit script is run which runs my script with -updateVersion, and then the MYVERSION variable is updated based on what the number of commits will be. Magic!
如果 $Keywords$ 对您来说至关重要,那么也许您可以尝试查看 Mercurial ? 它有一个 hgkeyword 扩展来实现你想要的。 无论如何,Mercurial 作为 DVCS 很有趣。
If having $Keywords$ is essential for you, then maybe you could try to look at Mercurial instead? It has a hgkeyword extension that implement what you want. Mercurial is interesting as a DVCS anyway.
使用 Git 存储库完成的事情是使用
tag
对象。 这可用于用任何类型的字符串标记提交,并可用于标记版本。 您可以使用 git tag 命令查看存储库中的标签,该命令会返回所有标签。检查标签很容易。 例如,如果有一个标签
v1.1
您可以将该标签签出到分支,如下所示:由于它是顶级对象,因此您还将看到该提交的整个历史记录能够运行差异、进行更改和合并。
不仅如此,即使标签所在的分支已被删除而没有合并回主线,标签仍然存在。
Something that is done with Git repositories is to use the
tag
object. This can be used to tag a commit with any kind of string and can be used to mark versions. You can see that tags in a repository with thegit tag
command, which returns all the tags.It's easy to check out a tag. For example, if there is a tag
v1.1
you can check that tag out to a branch like this:As it's a top level object, you'll see the whole history to that commit, as well as be able to run diffs, make changes, and merges.
Not only that, but a tag persists, even if the branch that it was on has been deleted without being merged back into the main line.
要将扩展应用到存储库中所有子目录中的所有文件,请将
.gitattributes
文件添加到存储库中的顶级目录(即通常放置.gitignore 的位置)
文件)包含:要查看其效果,您需要首先对文件进行有效的签出,例如以任何方式删除或编辑它们。 然后使用以下命令恢复它们:
您应该会看到
$Id$
替换为以下内容:来自
man gitattributes
:每次提交新版本的文件时,此 ID 都会更改。
To apply the expansion to all files in all sub-directories in the repository, add a
.gitattributes
file to the top level directory in the repository (i.e. where you'd normally put the.gitignore
file) containing:To see this in effect, you'll need to do an effective checkout of the file(s) first, such as deleting or editing them in any way. Then restore them with:
And you should see
$Id$
replaced with something like:From
man gitattributes
:This ID will change every time a new version of the file is committed.
现在,Git 可以通过
gitattributes(5)
的export-subst
功能将标签名称和其他相关信息直接自动编辑到文件中。 这当然需要使用 git archive 来创建版本,并且只有在生成的 tar 文件中,替换编辑才可见。例如,在
.gitattributes
文件中添加以下行:然后在源文件中,您可以添加如下行:
在由例如
git 存档 v1.2.0.90
:Tag names and other related information can now be edited directly into files automatically by Git through the
export-subst
feature ofgitattributes(5)
. This of course requires use ofgit archive
to create releases, and only in the resulting tar file will the substitution edits be visible.For example in the
.gitattributes
file put the following line:Then in source files you can add a line like this:
And it will expand to look like this in a release created by, for example,
git archive v1.2.0.90
:如果我理解正确的话,本质上,您想知道自上次更新以来给定文件发生了多少次提交。
首先获取远程源中的更改,但不要将它们合并到您的
master
分支中:然后获取
master
之间给定文件上发生的更改的日志> 分支和远程origin/master
。这将为您提供自您上次将
origin/master
合并到master
以来远程存储库中发生的所有提交的日志消息。如果您只想要更改的计数,请将其通过管道传输到
wc
。 说吧,像这样:If I understand correctly, essentially, you want to know how many commits have happened on a given file since you last updated.
First get the changes in the remote origin, but don't merge them into your
master
branch:Then get a log of the changes that have happened on a given file between your
master
branch and the remoteorigin/master
.This gives you the log messages of all the commits that have happened in the remote repository since you last merged
origin/master
into yourmaster
.If you just want a count of the changes, pipe it to
wc
. Say, like this:如果您只是希望人们能够了解他们的内容已经过时了多少,Git 可以通过几种相当简单的方式告知他们这一点。 例如,他们会比较他们的主干和您的主干上最后一次提交的日期。 他们可以使用
gitcherry
来查看有多少提交发生在你的行李箱中但不存在于他们的行李箱中。如果这就是您想要的,我会寻找一种无需版本号即可提供它的方法。
另外,除非你确定他们想要,否则我不会向任何人表示礼貌。 :)
If you're just wanting people to be able to get an idea how far out of date they are, Git can inform them of that in several fairly easy ways. They compare the dates of the last commit on their trunk and your trunk, for example. They can use
git cherry
to see how many commits have occurred in your trunk that are not present in theirs.If that's all you want this for, I'd look for a way to provide it without a version number.
Also, I wouldn't bother extending the courtesy to anyone unless you're sure they want it. :)
如果您希望 git 提交信息可访问到您的代码中,那么您必须执行预构建步骤以将其获取到那里。 在 C/C++ 的 bash 中,它可能看起来像这样:
prebuild.sh
和
version.h
看起来像:然后,无论你在代码中需要它
#包含“version.h”
并根据需要引用git_tag
或git_commit
。您的
Makefile
可能包含如下内容:这样做的好处是:
无论分支、合并、樱桃采摘等。
prepublish.sh
的这种实现有以下缺点:git_tag
/git_commit
没有更改,也会强制重新编译。可以避免这些问题的更高级的
prebuild.sh
留给读者作为练习。If you want the git commit information accessible into your code, then you have to do a pre-build step to get it there. In bash for C/C++ it might look something like this:
prebuild.sh
with
version.h
looking like:Then, wherever you need it in your code
#include "version.h"
and referencegit_tag
orgit_commit
as needed.And your
Makefile
might have something like this:This has the benefit of:
regardless of branching, merging cherry-picking and such.
This implementation of
prepublish.sh
has the drawbacks of:git_tag
/git_commit
didn't change.git describe --tags --always --dirty
to catch that use-case.A fancier
prebuild.sh
that could avoid these issues is left as an exercise for the reader.RCS ID 对于单文件项目很有用,但对于任何其他项目,$Id$ 没有说明该项目(除非您对虚拟版本文件进行强制虚拟签入)。
还有人可能感兴趣如何在每个文件级别或提交级别获取 $Author$、$Date$、$Revision$、$RCSfile$ 等的等效项(如何将它们放在某些关键字所在的位置是另一回事)问题)。 我对这些没有答案,但看到更新这些的要求,特别是当文件(现在在 Git 中)源自 RCS 兼容系统 (CVS) 时。
如果源代码与任何 Git 存储库分开分发(这也是我所做的),那么这样的关键字可能会很有趣。 我的解决方案是这样的:
每个项目都有自己的目录,在项目根目录中我有一个名为
.version
的文本文件,其内容描述了当前版本(导出时将使用的名称)来源)。在开发下一个版本时,脚本会提取
.version
编号、一些 Git 版本描述符(例如gitdescribe
)以及.build(加上主机和日期)到链接到最终程序的自动生成的源文件,这样您就可以找出源代码以及构建时间。
我在单独的分支中开发新功能,我做的第一件事是将
n
(表示“next”)添加到.version
字符串(源自同一根的多个分支)将使用相同的临时.version
编号)。 在发布之前,我决定要合并哪些分支(希望所有分支都具有相同的.version
)。 在提交合并之前,我将.version
更新为下一个数字(主要或次要更新,具体取决于合并的功能)。RCS IDs are nice for single-file projects, but for any other the $Id$ says nothing about the project (unless you do forced dummy check-ins to a dummy version file).
Still one might be interested how to get the equivalents of $Author$, $Date$, $Revision$, $RCSfile$, etc. on a per file level or at the commit level (how to put them where some keywords are is another question). I don't have an answer on these, but see the requirement to update those, especially when the files (now in Git) originated from RCS-compatible systems (CVS).
Such keywords may be interesting if the sources are distributed separately from any Git repository (that's what I also do). My solution is like this:
Every project has a directory of its own, and in the project root I have a text file named
.version
which content describes the current version (the name that will be used when exporting the sources).While working for the next release a script extracts that
.version
number, some Git version descriptor (likegit describe
) and a monotonic build number in.build
(plus host and date) to an auto-generated source file that is linked to the final program, so you can find out from what source and when it was built.I develop new features in separate branches, and the first thing I do is add
n
(for "next") to the.version
string (multiple branches originating from the same root would use the same temporary.version
number). Before release I decide which branches to merge (hopefully all having the same.version
). Before committing the merge, I update.version
to the next number (major or minor update, depending on the merged features).我同意那些认为令牌替换属于构建工具而不是版本控制工具的人。
您应该有一些自动发布工具来在标记版本时在源中设置版本 ID。
I agree with those who think that token replacement belongs to build tools rather than to version control tools.
You should have some automated release tool to set the version IDs in your sources at the time the release is being tagged.
我也来自 SCCS、RCS 和 CVS (
%W% %G% %U%
)。我也遇到过类似的挑战。 我想知道一段代码在运行它的任何系统上是什么版本。 该系统可能连接也可能不连接到任何网络。 系统可能安装也可能没有安装 Git。 系统可能安装也可能没有安装 GitHub 存储库。
我想要针对多种类型的代码(.sh、.go、.yml、.xml 等)使用相同的解决方案。 我希望任何不了解 Git 或 GitHub 的人都能够回答这个问题“您正在运行什么版本?”
因此,我围绕一些 Git 命令编写了所谓的包装器。 我用它来标记一个文件的版本号和一些信息。 它解决了我的挑战。 它可能对你有帮助。
https://github.com/BradleyA/markit
I also came from SCCS, RCS, and CVS (
%W% %G% %U%
).I had a similar challenge. I wanted to know what version a piece of code was on any system running it. The system may or may not be connected to any network. The system may or may not have Git installed. The system may or may not have the GitHub repository installed on it.
I wanted the same solution for several types of code (.sh, .go, .yml, .xml, etc). I wanted any person without knowledge of Git or GitHub to be able to answer the question "What version are you running?"
So, I wrote what I call a wrapper around a few Git commands. I use it to mark a file with a version number and some information. It solves my challenge. It may help you.
https://github.com/BradleyA/markit
如果像OP一样,您使用Emacs,则可以使用它的
时间戳
函数来获取您想要的自动属性更新。
让您的编辑进行更新的优点是
文件内容更新时时间戳
更改,而不是当某人仅仅将文件提交到
版本控制或重建。
$Id$
模板的时间戳等效为Time-stamp: <>
并且,与 $Id$ 不同,它必须出现在文件的前 8 行内。
您需要在 Emacs init 文件中启用自动时间戳,
您可以这样做:
选中
时间戳
框并选择应用并保存
。If, like the OP, you use Emacs, you can use its
time-stamp
function to get the automatic attribute updates you want.
Having your editor do the update has the advantage that
the time stamp gets updated when the file contents
change, and not when someone merely commits the file to
version control or rebuilds.
The time-stamp equivalent of the
$Id$
template isTime-stamp: <>
and, unlike $Id$, it must occur within the first 8 lines of the file.
You'll want to enable automatic time-stamping in your Emacs init file,
which you can do like this:
Check the
time-stamp
box and selectApply and Save
.既然你使用 Emacs,你可能会很幸运:)
我碰巧遇到了这个问题,也碰巧我来到了 Lively 几天前,一个 Emacs 包,允许在文档中包含生动的 Emacs Lisp 片段。 老实说,我没有尝试过,但读到这篇文章时我就想到了这一点。
Since you use Emacs, you might be lucky :)
I've came across this question by coincidence, and also by coincidence I've came by Lively few days ago, an Emacs package which allows having lively pieces of Emacs Lisp in your document. I've not tried it to be honest, but it came to my mind when reading this.
为了自己解决这个问题,我创建了一个小的“hack”作为提交后钩子:
更详细的记录在 我的博客上的这篇文章。
To resolve this issue for myself, I created small "hack" as post-commit hook:
In more detail documented in this post on my blog.