在合并冲突时,Git允许丢失文件更改的历史记录。

9

我是一个有帮助的助手,可以为您进行文本翻译。

我已经使用Git很长时间了,但最近遇到了一个有趣的技巧,可以在合并期间撤销文件变更历史记录。以下是重现步骤:

我有一个包含两个文件和一个提交的git存储库:

$ git branch   
  * master
$ git log --oneline 
  80c8d5a Initial commit
$ git log --oneline -- README
  80c8d5a Initial commit
$ ls
  README conflict_file

我正在创建新的实验分支并更改README和conflict_file文件:

$ checkout -b experiment 
  Switched to branch 'experiment'
$ vim README 
$ vim conflict_file 
$ git commit -am "Experimental changes"
  [experiment cdbc988] Experimental changes
  2 files changed, 2 insertions(+)
$ git log --oneline -- README
  cdbc988 Experimental changes
  80c8d5a Initial commit

回到主分支并编辑冲突文件(以在合并过程中出现冲突):

$ git checkout master 
  Switched to branch 'master'
$ vim conflict_file 
$ git commit -am "Master changes"
  [master ad8b68e] Master changes
  1 file changed, 1 insertion(+)

以下是我的存储库状态:
$ git log --oneline --decorate --graph --all
  * ad8b68e (HEAD, master) Master changes
  | * cdbc988 (experiment) Experimental changes
  |/  
  * 80c8d5a Initial commit

尝试与实验分支合并:

$ git merge experiment 
  Auto-merging conflict_file
  CONFLICT (content): Merge conflict in conflict_file
  Automatic merge failed; fix conflicts and then commit the result.
$ git mergetool 
  <merging conflict in conflict_file>

这是个小技巧:

$ git reset HEAD README
  Unstaged changes after reset:
  M      README
$ git commit 
  [master 909139f] Merge branch 'experiment'
$ git log --oneline -- README
  80c8d5a Initial commit

我丢失了引入实验性分支中Experimental changes提交中的README文件的更改和历史记录。是否有人能够说明这与Git知道所有更改的思想有何关联?是否有可能避免这种情况发生?这可能会成为我们公司的问题,因为合并是开发人员可以执行的操作,但他们可能会意外地删除别人的更改。


1
尝试使用 git log 命令的 --full-history 选项。同时阅读有关“历史简化”的手册页面:“默认模式...是解释树的最终状态的最简单历史记录。” - chirlu
你为什么会对README文件的更改消失感到惊讶呢?你明确使用了git reset HEAD README来请求这样做。 - Chronial
调用 git log --follow --<file> 帮助了我。 - erkfel
2个回答

8
没有任何历史记录丢失:对实验分支上README的更改仍包含在提交210fdc1中。它不是合并提交909139f的一部分,仅因为您在完成合并之前明确还原了它。
您没有说明您预期会发生什么,所以我只能猜测您在这里感到惊讶的具体原因。我将指出两个可能的原因。
  1. 合并提交不仅限于修改自合并基础以来触及的文件,甚至不仅限于具有冲突更改的文件。实际上,它可以完全不同于其所有父提交。

    当然,“完全不同”并不是人们会推荐的做法,因为它会打破每个人对合并操作的假设。但是,在合并提交中扩展文档文件可能是非常合理的,并且解决某些合并冲突可能需要更改其他文件。

  2. 当您调用git log -- some_path时,会启动一个名为“历史简化”的过程。正如手册所描述的那样,git log将尝试仅显示“足以解释匹配指定路径的文件是如何生成的”提交。在您的情况下,初始提交就足以解释README的当前状态(因为README仍然或再次具有相同的内容),因此它是唯一显示的。

    您可能想要使用--full-history,可能与--simplify-merges或其他一些历史简化选项一起使用。它们以及包括提交的确切规则(比上面暗示的更复杂)在git log手册页中进行了描述,其中还包括一个扩展示例。


我对此有所期望,即每个更改都应该被跟踪并保存在文件修改历史记录中。我的期望是在909139f文件README中有3个更改。如果您使用一个分支,在进行一些更改然后撤消这些更改时,将会有2个不同的提交,而不需要使用--amend,它们将在git log中显示给您。 - erkfel
我们正在尝试在组织中引入Git,开发人员使用IDE进行提交/合并/推送,并使用Gerrit/GitHub通过UI访问代码,我们有Git使用策略,如禁用强制推送、特性分支等。人们开始使用Git,并收到抱怨说很容易丢失更改。 - erkfel
经过调查,我们发现这是由于不正确的合并导致的,就像我的例子一样,当人们在冲突合并期间做错事情并意外重置(然后删除)更改时。当他们提到某些更改消失时,他们会去Gerrit/GitHub,检查文件历史记录,却发现这些更改甚至没有被引入。这非常令人困惑。只有深入调查才能找到不正确的合并。 - erkfel
提交只是某一时刻目录的快照。提交中没有更改,只有在提交之间才会有更改。 - chirlu
最后两段解决了我的困惑,即为什么 git log -- some-file 没有包含我预期的所有提交。 - Kenny Evitt

1
您实际上并没有丢失更改,只是还没有将更改检入到文件README中,所以它们不在您的git log命令显示的任何提交中。
*   9b8ede8 (HEAD, master) Merge branch 'experiment'
|\  
| * 210fdc1 (experiment) Experimental
* | d0c1637 Master changes
|/  
* b558d42 Initial commit

(我的 SHA 当然是不同的),以及:
$ git status
# On branch master
# Changes not staged for commit:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#   modified:   README
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#   conflict_file.orig
no changes added to commit (use "git add" and/or "git commit -a")

如@Chronial在评论中指出的那样,您明确告诉git执行了一个git reset --mixed--mixed是默认值),将文件README重置到当时的HEAD,即(在您的情况下)ad8b68e
如果在合并冲突期间(在运行git mergetool之前)运行git status,您会看到以下内容:
$ git status
# On branch master
# You have unmerged paths.
#   (fix conflicts and run "git commit")
#
# Changes to be committed:
#
#   modified:   README
#
# Unmerged paths:
#   (use "git add <file>..." to mark resolution)
#
#   both modified:      conflict_file
#
git mergetool 步骤解决了冲突,将 conflict_file 移动到与上面的 README 相同的准备提交状态 -- 但在执行 reset --mixed 后,README 由 "要提交的更改" 移动到 "未暂存的更改"。

感谢您的回复,请查看我对“chirlu”答案的评论。 - erkfel

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