为什么Git中没有撤销/重做功能?

10
据我所知,在Git中想要撤销某些操作,你必须明确地找到相应的命令并执行。例如,撤销一个提交并重新提交的方法有很多种,其中一种方法可以参考这里的示例。
$ git commit ...
$ git reset --soft HEAD^
$ edit
$ git add ....
$ git commit -c ORIG_HEAD 

要撤销一个pull操作,您可以按照这里的说明进行操作。
$ git reset --hard

但是,这些命令不一定可以互换使用。Git 为什么不允许简单的撤销和重做命令?这与其背后的哲学有关吗?另外,我没有太多其他版本控制系统的经验,但它们中有没有提供简单的撤销和重做命令?

我不确定,但我认为你可以使用 git rebase 来完成这个任务。请查看 http://gitready.com/intermediate/2009/01/31/intro-to-rebase.html。 - gustavotkg
2
这个想法是,通常情况下你不希望做一些会使你失去历史记录的事情。撤销提交会将其从历史记录中删除,因此通常不建议这样做。 - poke
@poke 举个例子,Adobe Photoshop 保留了完整的历史记录,并且仍然允许您撤销/重做。为什么 Git 不这样做呢? - john
2
@awfullyjohn 好的,Git允许您在存储库中创建分支。为什么Adobe Photoshop不这样做呢? - asjo
@awfullyjohn:比较不恰当。除了源代码控制和图像编辑显然不是同一回事之外... Photoshop 的设计是为了让非技术人员使用,并且撤消历史记录从一开始就被视为要求。为了实现这一点,Photoshop 还需要大量的内存,往往是图像大小的多倍。 - Cascabel
5个回答

8
这种想法存在几个问题:
- 并非所有操作都可逆。有时是因为Git记录的信息不足以推断之前的状态 - 这在一般情况下是极其昂贵的。有时是像git reset --hardgit clean这样的操作,它们会破坏未跟踪的更改。为了撤消它们,必须不断地自动备份。有时是因为撤消的概念是模糊的 - 正如您所指出的,有许多方法可以撤消提交。 - 如果一个操作是可逆的,并且涉及某种历史记录,那么撤消/重做是否也应该在历史记录中,还是应该使其消失?应该通过重置来撤消提交,还是通过还原(创建另一个提交来取消它)? - 如果没有记录每一件事情,你怎么知道最近的操作是什么?比如你添加了一个文件到索引中,并创建了一个分支。没有记录哪个先。
即使一切都清楚地定义了,实现起来也是一项荒谬的工作。你如何决定什么构成一个单一的操作?一个单一的Git命令可能会做很多事情。它应该撤销一个步骤,整个过程?如果你运行了数百万个命令,每个命令都执行一个微小的步骤,你想撤消所有这些步骤怎么办?而且它必须是完美的,完全完美的,因为这是一种将被不熟练用户使用的功能,他们将不知道如何从任何错误中恢复。
因此,就像Git给你提供了做事情的工具一样,它还给你提供了查看你所做的事情,并在需要时自己撤消事情的工具。
此外,关于“重做”,正如您在问题中定义的那样,它是重复一个命令,而不是再次执行原始操作。当您重新提交时,它是不同的提交。重新运行以前的命令是命令行shell被设计来做的事情。Git不需要重新发明它。

5

实际上,您可以使用以下方法执行第一个示例:

$ git commit ...
$ edit
$ git add ...
$ git commit --amend

你的第二个例子应该更像git reset --hard <hash>

回答你的问题是,这理论上是可能的,但正是Git所倡导的哲学使它没有被做到。理论上无法判断您是否通过创建提交或删除其他提交来达到提交状态,但使用reflog可能是可能的...以前没有以那种方式思考过。

我认为“撤消”和“重做”在源代码控制中并不是很常见,但如果我错了,请纠正我。

编辑:您可以使用reflog可能制作一个脚本来实现您想要的操作-不确定是否有足够的信息,但是值得一试。


1

git 实际上是作用于仓库、索引和工作目录的多个小工具,因此它没有任何“核心”部分来进行“撤消”操作。

话虽如此,它确实有各种日志,例如 reflog,供您查看已完成的操作。

最后,按照惯例,许多 git 操作通常被认为是“单向”的,也就是说,您不希望任何人公开地看到它,因此 git 在公共场合会“让您绕路”。如果您仍在本地仓库中,则可以使用各种命令进行备份,但全局撤消不是一个适当的命令选项。


“仅单向”实际上是一个很好的思考方式,因为一旦某些东西进入了公共领域,撤销和重做就变得很危险。 - MattJenko

0

除了其他评论,我建议看一下git stash。很多时候我会把一些工作储存起来,去修复另外的问题,然后再取消储存然后继续工作。或者对于一个Web应用程序,我会储存我的完成但未提交的工作,刷新浏览器,取消储存,打开一个新标签页,然后来回比较两个版本以确保与UI相关的错误修复没有破坏任何其他内容。


0

有时候我会将整个活动源代码树快照到一个压缩文件中(在 Mac 上只需单击一下),然后再做一些让我感到害怕的事情,比如进行大型变基。

因为我将使用的 git 命令只是在本地目录树上操作,所以如果出现可怕的错误,我可以轻松放弃操作并恢复到之前的状态(包括未检入或修改的文件)。

我不需要经常这样做,但这种手动“撤消”已经多次救了我的命。


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