28

我的一位同事完全弄乱了我们主要 CVS 存储库中目录的内容。我只需要将整个模块恢复到去年底的状态即可。请问执行此操作的 CVS 命令是什么?

他已经添加和删除了数百个文件,因此简单的“从旧的签出和提交中复制文件”是不够的。

我有 RTFM 和 STFW,我试过这个:

cvs co modulename  # Note no -P option
cvs up -jHEAD -jMAIN:2008-12-30 modulename

但这不起作用 - 他创建的新文件被删除,但旧文件和目录不会复活。(我没有犯)。

我可能可以为此编写一个 shell 脚本,但这个功能肯定已经在 CVS 中了吗?

更新:一些澄清:

  • 我可以在特定日期对模块进行本地结帐。问题是如何将其恢复到 CVS 中。

  • 我确实有备份,但使用像 CVS 这样的修订控制系统的重点是它应该很容易获得任何历史状态。下次发生这种情况时,我可能没有足够的运气进行备份(例如,每天都有备份,所以我可能会失去一天的工作)。

  • 我知道 CVS 已经过时了,我们应该转向更新的东西。但是在拥有大量基于 CVS 的工具(检查和构建脚本、每晚构建服务器等)的大型团队中,这种移动的时间成本是相当可观的。(评估、更新脚本、测试、迁移、培训、浪费开发人员时间、并行维护两个系统,因为旧分支仍然需要 CVS)。因此,这必须由管理层计划和安排。

更新#2:我要开始赏金了。 要获得赏金资格,您必须解释如何使用普通 CVS 命令恢复,而不是使用 hacky shell 脚本。

更新#3:服务器是CVS 1.12.13。访问是通过 pserver。我可以在 Linux PC 上使用相同版本的 CVS,或者在 Windows 上使用CVSNT 2.0.51d客户端。

4

10 回答 10

27

实际上,您最初的方法非常接近解决方案。问题是,基于日期的加入不能正确处理删除的文件和目录。您需要先为要加入的代码库设置一个标签:

mkdir code_base1 && cd code_base1
cvs co -D "2008-12-30" modulename
cvs tag code_base_2008_12_30

现在进行基于标签的连接,减去从现在到 2008-12-30 之间的所有更改:

cd .. && mkdir code_base2 && cd code_base2
cvs co modulename
cvs update -d -j HEAD -j code_base_2008_12_30  # use -d to resurrect deleted directories

比较code_base1 和code_base2 的内容。除了 CVS 元信息之外,它们应该是相同的。最后将 2008 年 12 月 30 日的代码作为新的 HEAD 提交:

cvs commit -m "Revert all changes this year"

请注意,像这样标记您希望加入的代码将不起作用,因为 rtag 在使用 -D 时也无法正确处理已删除的文件和目录:

cvs rtag -D "2008-12-30" code_base_2008_12_30 modulename
于 2009-06-25T04:05:47.177 回答
4

CVS 有几个问题,而您遇到了这样的问题。

  1. CVS 是面向文件的,没有变更集或快照的概念。这意味着您想要还原的更改有点难以处理。提交在给定目录内是原子的,而不是在外部。

  2. 目录没有版本控制。这意味着空目录将被删除(如果您使用 更新 -P)并且您必须指定-d在结帐/更新时创建它们。

因此,要回答您的问题,日期可能是唯一的处理方法,因为您没有使用标签来创建一些穷人版本的变更集。

我对备份的评论是,从备份中恢复整个 repo 可能比尝试纠正 CVS 不擅长的事情更容易。

我会鼓励您——但这是另一个主题——尽快更改版本控制。相信我,我在FreeBSD 项目中与 CVS 打交道已经有很长一段时间了,并且很快就知道 CVS 是多么可恨……请参阅此处了解我对版本控制软件的一些看法。

于 2009-01-12T21:37:45.333 回答
2

我相信您的第二个命令也应该是结帐,而不是更新。我不能用逻辑来证明这一点,因为在 CVS 的世界中没有逻辑,但它对我有用。试试这个:

cvs co -P modulename
cvs co -P -jHEAD -jMAIN:2008-12-30 modulename

如果您要恢复 HEAD 以外的分支,例如 X,请在两个命令中传递 -rX 参数:

cvs co -P -rX modulename
cvs co -P -rX -jHEAD -jMAIN:2008-12-30 modulename
于 2009-01-14T18:34:58.557 回答
1

我仍然有兴趣知道是否有更简单的方法。(肯定有更简单的方法)。我最终做的是,在使用 bash 的 Linux PC 上:

# Get woking copy we're going to change
cd ~/work
rm -rf modulename
cvs up -dP modulename
cd modulename

# Remove all files
find . -name CVS -prune -o -type f -print | xargs cvs rm -f

# Get the old revision
cd ~
mkdir scratch
cd scratch
cvs -q co -D 2008-12-31 modulename
cd modulename

# Copy everything to the working dir and do "cvs add" on it
find . -name CVS -prune -o -type f -print | \
    xargs tar c | \
    (cd ~/work/modulename && tar xv | \
    xargs cvs add)

# Check everything is OK before we commit
cd ~/work/modulename
cvs -nq up

# it gave me an error on readme.txt because I'd deleted and then added it, so:
mv readme.txt x # save good rev
cvs add readme.txt # resurrect the bad rev
mv x readme.txt # clobber file with good rev

# Commit it
cvs commit -m "Revert all changes this year"

# Delete now-empty directories
cvs -q up -dP

# Double-check everything is back how it was
diff -ur -xCVS ~/scratch/modulename ~/work/modulename

然后我发现仍然存在差异 - 我的同事添加了包含空格的文件名,而上述过程没有删除这些文件名。我不得不单独删除这些。(我应该使用find ... -print0而不是-print,并将-0参数传递给xargs。我只是没有意识到有带空格的文件。)

于 2009-01-12T13:59:15.417 回答
1

你可以看看cvsps。去谷歌上查询。

此外,使用 quilt(或 Andrew Morton 的补丁脚本,这是 quilt 最初的样子)和 cvsps,可以得到非常接近的变更集。

http://geocities.com/smcameron/cvs_changesets.html

于 2009-06-28T02:18:09.743 回答
0

您是否尝试过使用该-d选项?(建立子目录)

据我记得,它是隐含的 for cvs co,但不是 for cvs up

于 2009-01-12T13:15:15.783 回答
0

根据http://www.astro.ku.dk/~aake/MHD/docs/CVS.html,您需要以下内容:

cvs update -D "30 Dec 2008 23:59"
于 2009-01-12T13:29:22.323 回答
0

如果您有存储库的备份(服务器上的实际 RCS 文件,例如在磁带上),您可以将 CVS 服务器上的文件夹恢复到之前的状态。在执行此操作之前不要忘记停止 CVS 服务器(然后重新启动它)。

于 2009-01-12T22:40:41.493 回答
0

大问题,没有完整的答案,只是关于处理文件名中空格的脚本的提示。

代替

find ... | xargs tar c - | ...

试着把

find ... | perl -e '@names = <>;' -e 'chomp @names;' -e 'system( "tar", "c", "-", @names);' | ...

这样,您的存档创建(或类似操作)将不会受到名称中空格的影响,在调用 tar 之前会跳过 shell argv 解析。

还有一件事,如果它确实有效:如果有一个 CVS 到 SVN 实用程序,请使用它(我假设这样的实用程序会从“CVS 阁楼”中提取已删除的文件),如果它及时保存每一刻作为项目级别的检查点(因为 SVN 这样做,与 CVS 不同),使用 SVN 来获取正确的时间。很多如果...

于 2009-01-14T01:56:34.047 回答
0

如果您或您的同事对 git 感到满意,您可以使用它git cvsimport来创建一个镜像 CVS 存储库的 git 存储库。在 git 中恢复提交/变更集是微不足道的(使用git revert)。然后,您可以使用git cvsexportcommit将还原提交发送到 CVS。

这听起来可能过于复杂,但根据我的经验git cvsimportgit cvsexportcommit一旦你完成了所有设置,工作就会非常好。即使项目仍在使用 CVS,您最终也将获得 git 的所有功能。

于 2011-08-19T17:34:50.450 回答