如何在 Git 裸仓库中取消最近一次提交?

99

考虑到在裸仓库中有几个 git 命令是没有意义的(因为裸仓库不使用索引且没有工作目录),

git reset --hard HEAD^ 

撤销在该版本库中最后一次更改的解决方法不是这样的。

通过互联网搜索,我找到了与该主题相关的所有内容这里,其中介绍了三种方法:
1. "手动更新引用(涉及管道)";
2. "git push -f从非裸库";
3. "git branch -f this $that"。

你认为哪种解决方案更合适或者还有其他什么方法可以实现? 不幸的是,我发现关于Git裸库的文档相当少。


8
不要使用下面的复杂内容。你想将 HEAD 移动到另一个提交,并且这正是 git reset 的用途,即使在裸仓库中也是如此。根据我下面的回答,使用:git reset --soft <commit>使用 --soft,你不会尝试更改不存在的工作树和索引,因此 git 可以让你轻松地进行重置。 - Hazok
4个回答

142

你可以使用 git update-ref 命令。要移除最后一次提交,你可以使用:

$ git update-ref HEAD HEAD^

或者,如果您不在要从中删除最后一个提交的分支中:

$ git update-ref refs/heads/branch-name branch-name^
您也可以传递sha1哈希值,如果您愿意的话:
$ git update-ref refs/heads/branch-name a12d48e2

请查看 git-update-ref 命令的文档。


@Lavinia-Gabriela Dobrovolschi:没错,我对确切的语法不熟悉。 - VonC
@VonC 你可以在 git update-ref <ref> <newvalue> 中指定正确的分支,比如 "refs/heads/master" 而不是 HEAD。希望我没有误解你的问题。 - Lavinia-Gabriela Dobrovolschi
@Sylvain:+1 好的编辑。@Lavinia-Gabriela Dobrovolschi 感谢您提供的细节说明。如果您有直接访问远程服务器的权限,那将更加实用。 - VonC
3
关于 branch-name 参数的示例是误导性的。当使用 update-ref 命令更新“分支”时,必须指定完整的引用名称(即在正常的短分支名称前加上 refs/heads/)。如果您只使用短名称,则会创建/更新 $GIT_DIR/branch-name 而不是 $GIT_DIR/refs/heads/branch-name。存在 branch-namerefs/heads/branch-name 两者都会导致“refname ... is ambiguous”警告。 - Chris Johnsen
这个答案比Zach提出的要复杂得多。而他的解决方案也很好用。 - Krystian

36
如果您在裸库中使用以下内容:
git reset --soft <commit>

那么,使用--hard--mixed选项时,您不会遇到在裸库中使用这些选项时遇到的问题,因为您不试图更改裸库没有的内容(即工作树和索引)。在您的情况下,您需要使用以下命令(从裸库):

git reset --soft HEAD^

要在远程仓库上切换分支,请执行以下操作:

git symbolic-ref HEAD refs/heads/<branch_name>

要查看当前选择的分支,请使用:

git symbolic-ref HEAD

https://mirrors.edge.kernel.org/pub/software/scm/git/docs/git-symbolic-ref.html


3
你如何选择想要移动到的分支?在主分支上你的示例可以正常工作,但在一个裸仓库中,git checkout other_branch无法使用。 - Gauthier
1
嗯...我在想是谁在这个问题上投了反对票。这个问题并没有问如何在远程仓库上切换分支,而是问如何在裸仓库上重置。要更改远程仓库中的默认分支,请使用git symbolic-ref HEAD refs/heads/<branch_name>。 - Hazok

6
git push -f 命令应该可以正常工作:
如果你克隆了裸仓库,并在本地非裸仓库中删除了最后一次提交(如你所述,使用 git reset --hard HEAD^ 命令),然后强制推送回去(使用 -f 参数):
  • 你不会改变任何在你删除的提交之前的 SHA1。
  • 你可以确保推送的是裸仓库减去额外提交的精确内容(因为你刚刚克隆了它)。

@ VonC 嗨 Von,我看到你在 Git 上回答了很多问题,所以想问一下... 我很好奇,为什么在裸仓库中移动 HEAD 时不推荐使用我下面回答中展示的 git reset --soft <sha1> 的方法呢? - Hazok
我猜我提问的另一个原因是,对于裸仓库使用软重置并不是容易获取的信息,许多论坛似乎有不必要复杂的解决方法,而软重置似乎是最佳实践,因为它需要输入最少的内容且出错的可能性最小。 - Hazok
2
@Zach:在裸仓库上直接执行 reset --soft 应该可以解决问题。我怀疑这很少被使用是因为裸仓库通常是一个上游仓库(即你正在向其推送数据的仓库),而大多数情况下,你没有直接访问它的本地权限。但如果你有,那么这肯定是“reset --soft”用法的另一个好例子(如 https://dev59.com/7G435IYBdhLWcg3wyzaa )。所以对你的回答点赞。 - VonC

2
你还可以使用git refspec的表示法,像这样操作:
git push -f origin +你想还原到的提交:<目标头 | 分支名称>
这将强制更新目标分支(由ref表示),将其更新为源提交(由+<object ref>部分表示)。

2
除非分支上有ACL(访问控制列表) - 如果您“需要在裸仓库本身上执行操作”,通常情况下会这样做... - David Schmitt

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