撤销意外提交

58

你正在使用Subversion,但不小心在代码还没准备好时就提交了。例如,我经常会这样操作:a) 提交一些代码,然后 b) 进行一些编辑,接着 c) 按上箭头和回车键来重复之前的命令,但不幸的是那个命令是一个提交。

有没有可能从Subversion服务器中撤销这种意外提交呢?


请参考以下两个链接:如何撤销 SVN 提交?删除 SVN 提交的所有痕迹 - Vadzim
11个回答

72
请参考SVNBook的“撤销更改”部分和反向合并。另一个常见用途是回滚已经提交的更改。假设你正在愉快地在/calc/trunk的工作副本上工作,然后你发现303修订中的更改完全错误。它不应该被提交。您可以使用svn merge在您的工作副本中“撤消”更改,然后将本地修改提交到存储库。您只需要指定反向差异即可:$ svn merge -r 303:302 http://svn.example.com/repos/calc/trunk。请注意,您最初的更改仍将存在于存储库中。但是,您现在已经在以后的修订中撤回了它。也就是说,存储库已捕获了您的所有更改(这确实是您想要的!除非您已经检查了纯文本密码或类似内容!)。

1
这是一个可行的解决方案。谢谢。 - moinudin
8
是否可以使用相同的方法,但我更喜欢使用svn merge -c -303来撤销更改。这似乎更容易被人们理解。 - Jeremy French
@JeremyFrench -c 起作用了,而 -r 没有,不太确定为什么,但还是谢谢!1M! - Justin
哎呀!谢谢,这样就避免了一些头发被拔掉的情况。 - SeanDowney

19

NB: 可能在当前版本的Subversion上不起作用,这是一个坏主意——但我还是把它留在这里供参考。

NB: 通常当你因为错误而check in之后,你应该撤销commit——请参见其他回答。但是,如果你想真正撤消已提交的更改并将仓库恢复为之前的状态,下面有一些解释:

这不是你通常想要的,但如果你真的想从仓库中删除实际提交的版本,则可以对仓库进行粗暴的回滚,具体操作如下(假设$REV设置为要移除的最新修订版本):

  • 首先备份仓库,因为这些更改可能会破坏它(并阅读下面的假设)
  • 将本地副本还原到上一个版本,以免混淆 (svn revert -r $((REV-1)))
  • 从仓库中删除db/revs/$REVdb/revprops/$REV
  • 在仓库中删除db/current和(对于Subversion 1.6或更高版本)db/rep-cache.db,然后运行svnadmin recover .
  • (可能需要)调整db/rep-cache.db的权限,以防止出现attempt to write a readonly database错误

这一切都假设:

  • 你正在使用fsfs类型的仓库
  • Subversion发布版本大于1.5.0 (否则你需要手动编辑db/current并更改修订号,而不是运行svnadmin recover .
  • 没有其他后续版本被提交
  • 你拥有对仓库文件系统的写访问权限
  • 你不害怕其他人在你执行以上操作时尝试访问它

我曾经在将一个巨大的文件提交到仓库时做过这件事,因为我不希望它永久留在历史记录(以及镜像等)中;这绝不是理想或正常的做法...


1
这真的是错误的方法!下面的答案才是正确的做法。 - Martin Serrano
4
因此,上面使用了“nasty”一词!我回答了问题,因为问者询问了实际撤销签入的操作;当然,通常情况下,您希望提交合并的回滚。使用更智能的版本控制系统也使这些选择更容易 :) - David Fraser
当然。这个问题暗示着改变刚刚发生,我不建议这样做,除非您意外提交了机密信息,比如密码。当然,在有更好的选择时,我现在很少使用Subversion :) - David Fraser
1
这种情况之前也发生在我身上,不过我通过chmod重新调整了它的权限。除此以外,一切都非常顺利,你的写作非常清晰易懂,应该不需要进行编辑。 - wtf8_decode
2
使用此方法检测库损坏时会返回-1。 我同意,在某些情况下更改库以丢失数据是必要的,而svn revert不能解决问题。 但是,我们刚刚尝试了这个过程,并使库损坏(svnadmin:E200002:序列化哈希缺少终止符)。 我们发现,由于我们只是删除了最新修订版本,因此对次新版本进行svnadmin转储,然后将其加载到新库中完全可以达到我们想要的效果,并且绝对不会破坏库。 - Dave Gregory
显示剩余6条评论

14

警告:接受的答案(作者为David Fraser)可以用于SVN 1.5仓库,但对于SVN 1.6,您还必须在下一次提交之前删除db/rep-cache.db否则会破坏您的仓库并且可能直到下一次尝试完整检出时才会意识到。我曾看到过后续的完整检出失败,并出现“Malformed representation header”的错误。

你可能会问rep-cache.db是什么?FSFS布局的文档说明指出,如果删除此文件,您将失去"rep-sharing capabilities";然而,它会在您下一次提交时重新创建。表示共享在1.6中添加。


抱歉,你的答案在哪里? - squashed.bugaboo
我现在更倾向于在每次提交之前备份存储库。这样,如果我意识到自己犯了错误,我可以简单地删除整个存储库,并快速从以前的一次备份中恢复它,而无需担心存储库内部。预防胜于治疗... - Pete
谢谢,我已经编辑了我的答案以纳入上述内容,以减少损坏的风险。 - David Fraser

12
使用TortoiseSVN,选择“显示日志”,然后找到要还原的修订版本。从上下文菜单中选择“还原到此修订版本”。这将对您的工作副本执行逆向合并,因此您需要提交您的工作副本以完成操作。
另请参阅:我们如何跟踪我们的工作副本分支? :-)

6
如果你的意思是如何干净地删除一个意外提交的历史记录: 这很困难。
svn不允许你撤销任何东西,因为它将修订版保存为更改集。 但是,有一些工具可以让您在存储库的转储上执行几乎任何操作。 你可以:
1. 转储你的存储库。 2. 使用svn管理工具中的svndumpfilter来摆脱提交。 3. 将其放回到存储库中。
但这可能会完全破坏你的存储库,所以除非你绝对知道自己在做什么并且备份了所有内容,否则永远不要尝试这样做。

我正准备发布这个...你比我快一步,加1。 - rmeador
2
哦,你要找的工具是“svndumpfilter”,如果我没记错的话。 - rmeador

2

您无法删除修订版本 - 这里的一些答案似乎完全误解了您想要的内容。但是,您可以更改签入消息以表示它是无意的。签入不会花费太多时间,因此有偶尔多出来的签入并不是什么大问题。


这是错误的,根本没有解决问题。Brian Agnew和David Fraser上面提出的想法更好。 - Jim Dagg

1
有时需要在服务器上编辑存储库,例如当您意外提交了难以更改的密码时。以下是我认为完全安全的方法(@David Fraser的答案导致我的存储库损坏)。请注意,此方法仅会从存储库末尾删除修订版本,因此如果您立即注意到错误,则最有用。
  1. 通知所有用户该代码仓库即将下线,并且他们需要从服务器上创建一个新的检出。
  2. 关闭代码仓库,备份并将主要代码仓库移动到安全位置,例如reponame_old。
  3. 将仓库转储为单文件表示形式,留下不需要的修订版本:
    • svnadmin dump -r 0:N > reponame.dump
    • 例如:svnadmin dump -r 0:6610 > reponame.dump 将删除6611及以后的版本
    • 请注意,repodump文件可能是你仓库文件夹大小的两倍。
  4. 创建一个新的代码仓库来加载这些修订版本:
    • svnadmin create reponame
  5. 将修剪过的修订版本加载到新的代码仓库中
    • svnadmin load reponame < reponame.dump
  6. 对新的代码仓库应用任何必要的自定义内容(例如钩子),然后重新启用服务。
    • 我们使用VisualSVN服务器,所以必须恢复conf\VisualSVN-WinAuthz.ini文件。
    • 我们还看到一些奇怪的行为,直到我们重新启动了服务器,因此VisualSVN可能会缓存仓库状态;其他托管设置可能会有所不同。
  7. 不要忘记删除带有机密数据的备份,或将其放在安全位置。
  8. 告诉所有用户从代码仓库服务器上进行新的svn checkout

PS:你应该定期备份你的代码库。 - Dave Gregory
顺便说一句,你应该使用 Git ;) - Dave Gregory

1

是的,这确实是Subversion的用途。

你需要做的就是在SVN仓库中用之前的版本替换你的副本。

有几个选项:

  1. 使用版本号替换
  2. 使用URL替换
  3. 从仓库获取最新版本(但在你的情况下,你已经有了最新版本)
  4. 使用分支替换

但我强烈建议在替换本地副本之前执行以下操作:

  1. 进行“与仓库/版本/URL比较”。

那只是更改本地副本。我想从服务器撤销该次提交。 - moinudin
如果你真的不想留下任何历史记录,你可以删除整个代码库并重新创建它。 - uuɐɯǝʃǝs

0
评论一下:这是我在一个代码库上执行的一系列命令,将其从版本2还原回版本1。不过最后你也需要提交更改。
Last login: Mon Apr 13 16:01:34 on ttys004
[wlynch@orange ~] cd /tmp
[wlynch@orange /tmp] svnadmin create foo
[wlynch@orange /tmp] svn co file:///tmp/foo foo-repo
Checked out revision 0.
[wlynch@orange /tmp] cd foo-repo/
[wlynch@orange foo-repo] ls
[wlynch@orange foo-repo] touch blah
[wlynch@orange foo-repo] touch repl
[wlynch@orange foo-repo] touch bar
[wlynch@orange foo-repo] svn add *
A         bar
A         blah
A         repl
[wlynch@orange foo-repo] svn ci
Adding         bar
Adding         blah
Adding         repl
Transmitting file data ...
Committed revision 1.
[wlynch@orange foo-repo] echo "hi" > bar
[wlynch@orange foo-repo] echo "oh no" > blah
[wlynch@orange foo-repo] svn ci
Sending        bar
Sending        blah
Transmitting file data ..
Committed revision 2.
[wlynch@orange older-foo] svn diff -r 1:2 file:///tmp/foo
Index: bar
===================================================================
--- bar (revision 1)
+++ bar (revision 2)
@@ -0,0 +1 @@
+hi
Index: blah
===================================================================
--- blah    (revision 1)
+++ blah    (revision 2)
@@ -0,0 +1 @@
+oh no

[wlynch@orange foo-repo] svn diff -r 1:2 file:///tmp/foo | patch -R
patching file bar
patching file blah    

0
我会怀疑这种说法。源代码管理的主要理念之一是存储库不会丢失任何历史记录。你不能删除历史记录。你能做的最好的事情就是获取旧版本,并用它覆盖当前版本。但是历史日志仍将显示您的错误。
(离题:您在使用什么样的IDE?)

1
嘿,使用vim +单独的shell进行签入。 - moinudin
建议:使用集成SVN支持的IDE。这样会更容易。 :) - Vilx-
伴随强大的力量,必有巨大的责任。又一个快乐使用VIM和独立Shell的用户到这里来了。我很高兴为了VIM和Bash提供的强大和速度而偶尔遭受这种“意外”。 - dotancohen
这个时代我是TortoiseGit的坚定支持者。:)虽然Visual Studio确实有基本的Git支持,但它仍然无法接近Tortoise所能做到的。而且,是的,我知道-命令行甚至更加强大(但99%的日常工作我仍然更喜欢Tortoise)。:) - Vilx-

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