如何在CVS中还原一个大的更改?

29

我的一个同事彻底搞乱了我们主要的CVS仓库中一个目录的内容。我需要将整个模块还原到去年年底的状态。请问如何使用CVS命令完成此操作?

他添加和删除了数百个文件,因此简单的“从旧的checkout复制文件并提交”不够。

我已经阅读了文档和搜索了网络,并尝试了以下方法:

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

但这并不起作用-他创建的新文件被删除了,但旧文件和目录没有被恢复。(我没有提交它)。我可能可以为此编写一个shell脚本,但肯定已经有CVS中的这个功能了吧?更新:一些澄清:1.我可以在特定日期获取模块的本地检出。问题是如何将其放回CVS。2.我有备份,但使用像CVS这样的版本控制系统的重点是应该很容易地获得任何历史状态。下一次发生这样的事情,我可能没有足够的运气拥有备份(例如,备份是每天进行的,因此我可能会失去一天的工作)。3.我知道CVS很老了,我们应该转移到更新的东西。但在一个大型团队中,有大量基于CVS的工具(检出和构建脚本、每晚构建服务器等),这样做的时间成本是相当高的。(评估、更新脚本、测试、迁移、培训、失去开发人员的时间、同时维护两个系统,因为CVS仍然需要用于旧分支)。因此,这必须由管理层计划和安排。更新#2:我将在这个问题上开始一个悬赏。为了有资格获得奖励,您必须解释如何使用普通的CVS命令还原,而不是使用hacky shell脚本。

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


我不想失礼,但是你有备份吧? - Keltia
6
@Keltia,我认为这不是备份问题- 这就是源代码控制的设计目的。尽管如此,我同意OP应该备份,但不是为了这个目的。 - Michael Haren
是的,我们有备份。我也考虑过进行还原的想法。我(愚蠢地)认为CVS回滚会更容易。 - user9876
这将是哪个CVS版本?我认为支持补丁集的新版cvs应该足够轻松地完成这个任务... - Stobor
10个回答

28

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


Actually your initial approach was very close to the solution. The problem is, that joining date-based does not handle removed files and directories correctly. You need to set a tag to the code base you want to join first:
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"
请注意,像这样标记您希望加入的代码是行不通的,因为在使用 -D 时,rtag也无法正确处理已删除的文件和目录。
cvs rtag -D "2008-12-30" code_base_2008_12_30 modulename

我不确定你是否需要在两个不同的目录中检出。但这样做也不会有任何损失。 - OliCoder

4

CVS存在几个问题,你正在遇到其中的一个。

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

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

所以,回答你的问题,日期可能是唯一的处理方式,因为你没有使用标签来创建某种简单的变更集版本。

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

我鼓励你——但这是另一个话题——尽快更改版本控制。相信我,我在FreeBSD项目中使用CVS很长时间,非常清楚CVS是多么讨人厌……请参见此处了解一些我对版本控制软件的看法。


2

我认为您的第二个命令也应该是checkout而不是update。虽然在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

1

你可以研究一下cvsps。在谷歌上搜索一下。

此外,使用quilt(或Andrew Morton的patchscripts,这就是quilt的起源)和cvsps,可以得到非常接近变更集的近似值。

请参见http://geocities.com/smcameron/cvs_changesets.html


1

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

# 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。我只是没有意识到有带空格的文件。)

0

你尝试使用-d选项了吗?(构建子目录)

据我所记,它对于cvs co是隐含的,但对于cvs up则不是。


我刚刚尝试在cvs up部分添加-d;不幸的是,我得到了相同的结果。 - user9876

0

这将把旧版本放入我的本地工作副本中,但我随后需要将其提交到存储库中(以消除更改)。不幸的是,提交它很困难 - 如果您使用过时的检出,则CVS不允许您提交。 - user9876
1
你可以从head检出另一个副本,用日期检出的目录替换所需的目录,然后在顶部提交。 - James Van Huis

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

0

如果您或同事熟悉git,您可以使用 git cvsimport 创建mirroring CVS存储库的git存储库。在git中撤消提交/更改集是微不足道的(使用 git revert )。然后,您可以使用 git cvsexportcommit 将还原提交发送到CVS。

所有这些听起来可能过于复杂,但根据我的经验,一旦您设置好所有内容, git cvsimport 和 git cvsexportcommit 即可完美运行。尽管项目仍在使用CVS,但您个人最终拥有了git的所有强大功能。


小心处理,如果你在不知情的情况下使用cvsnt,可能会丢失数据。很多商店使用cvsnt进行LDAP集成。 - Dustin Getz

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 获取正确的时刻。很多条件...


"find ... -print0 | xargs -0 ..." 也可以工作。然而,文件名中的空格非常少见,我直到太晚才意识到需要它。 - user9876

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