git-reset的--merge和--keep标志的典型用例是什么?

19

最近的一篇答案中,torek详细介绍了git-reset的三个最常用选项(--hard--mixed--soft)的典型用例,在此过程中,他提到git-reset还提供了两个相对晦涩的标志,称为--merge--keepgit-reset手册页如下所述:

--merge
           
   Resets the index and updates the files in the working tree
   that are different between <commit> and HEAD, but keeps
   those which are different between the index and working tree
   (i.e. which have changes which have not been added). If a
   file that is different between <commit> and the index has
   unstaged changes, reset is aborted.

   In other words, --merge does something like a git read-tree
   -u -m <commit>, but carries forward unmerged index entries.

--keep
    Resets index entries and updates files in the working tree
    that are different between <commit> and HEAD. If a file that
    is different between <commit> and HEAD has local changes,
    reset is aborted.
我完全理解何时使用--hard--mixed--soft,但在阅读torek的回答时才知道--merge--keep也存在,我想不到这两个标志的实际用途...你通常在什么情况下使用这两个标志?我主要寻求一个简明易懂的解释。请以VonC的这个回答为模型,其中详细说明了git reset --soft的典型用例:

[...] 每当:

  • 你对最终结果感到满意(工作树和索引方面)
  • 你对获取到这里的所有提交都不满意:

git reset --soft就是答案。

不过,我也不反对尝试一下那些标志,类似于我在我的这个回答中发表的愚蠢购物清单示例
4个回答

8
坦白地说,我对此并不确定;我从未使用过--merge--keep模式。然而,发行说明表明git reset --merge是在git版本1.6.2中添加的,附有以下注释:
  • git reset --merge是一种新模式,类似于git checkout切换分支的方式,同时将本地更改应用到另一个提交上。
--keep是在1.7.1中添加的:
  • git reset学习了--keep选项,可以在保留本地更改的情况下丢弃靠近末尾的提交,类似于git checkout branch的方式。
然后,在1.7.9中:
  • git checkout -B <current branch> <elsewhere>是一种更直观的拼写方式,与git reset --keep <elsewhere>相似。
这告诉我们--keep的想法是,你已经开始在某个分支上工作,然后你意识到:哦,这个分支应该从其他点分叉(可能是另一个分支的顶端)。例如,您可能会:
$ git checkout devel
$ git checkout -b fix-bug-1234

然后需要进行一些工作,为下一个发布版本修复bug 1234;但是,某人说:“嘿,我们需要为旧版本修复bug 1234!”所以现在您希望fix-bug-1234release分支出来,而不是devel。同时,您还没有提交任何内容。所以你需要执行以下操作:

$ git checkout -B fix-bug-1234 release

将其从“正在开发”移动到“即将发布”,在1.7.9或更高版本中有效,但在1.7.1到1.7.8中,您需要拼写git reset --keep

这可能解释了--keep,但--merge仍然有点神秘。


在您的示例中,如果您正在编辑buggy-file.c,并且当您从git reset --keep fix-bug-1234切换时,如果分支devel和分支fix-buf-1234中的buggy-file.c不同,则重置将失败。因此,需要使用--merge选项。 - Dietrich Epp
@DietrichEpp: 但是根据--merge的描述,如果情况是这样的:buggy-file.c在索引和<commit>之间是不同的(在devel中与release不同),并且它有未暂存的更改(我们修改了工作树),因此git reset --merge也将中止重置。我感觉手册描述有些不对。源代码表明,在冲突合并的中间允许使用--merge,而--soft--keep则不行。 - torek
@torek 谢谢你。虽然这些标志对我来说仍然很陌生。如果我有需要使用它们的时候,我可能会回到这里并发布自己的答案。 - jub0bs

2
我同意,一开始看那些标志似乎很奇怪。然而,我花了几个小时才理解它们,不过它们的区别非常明显:--keep 取消暂存索引,而 --merge 完全丢弃索引。
回到典型的用例:假设你有一个特定于本地环境的配置文件,例如包含访问本地数据库的凭据。当你要进行“硬”重置时,肯定不想丢失本地更改。在这种情况下。
git reset --keep <commit>

完美地完成工作。这表明,更多情况下您希望使用--keep标志而不是--hard标志。

--merge标志比--keep标志更加激进,因为它完全丢弃了索引(请注意,在这种情况下,与--keep相反,您可能会丢失工作),但是在我看来,两者的实际用例相同。

最初,git reset --merge用于中止合并,但现在更喜欢使用git merge --abort(第一个选项在1.6.2版本中引入,而第二个选项在1.7.4版本中引入)。


--keep不会取消暂存索引的操作。根据文档: --keep将重置索引条目... - avenir

1

https://git-scm.com/docs/git-reset

reset --keep 用于在保留工作目录中的更改的同时,删除当前分支中的最后一些提交。如果要删除的提交中的更改与要保留的工作目录中的更改之间存在冲突,则不允许重置。这就是为什么如果工作树和HEAD之间以及HEAD和目标之间都有更改,则会禁止重置。为了安全起见,在存在未合并条目时也会禁止。

我可以想象一个场景,其中有一些本地提交:Commit0、Commit1、Commit2、...、CommitX、CommitY、...、CommitN

我想丢弃 Commit1..CommitX 中的更改,但保留 CommitY..CommitN 中的更改

为此,我可以使用 git reset --soft CommitX,然后使用 git reset --keep Commit0

我猜没有 --keep 的话,某人需要使用 stash 来完成此操作。


0

试试这个。

如果你从一个分支拉取代码后,发现有多个冲突。然后突然想起自己从错误的分支拉取了代码,那么撤销操作会有所帮助。

git reset --merge HEAD@{1}

谢谢,但这个回答太简洁了。你能描述一下这个命令是做什么的以及它为什么有用吗? - jub0bs

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