我如何知道在 Git 提交中发生了什么,两个父项没有合并第二个父项的更改?

How can I tell what happened in a Git commit with two parents that did not merge in the changes from the second parent?

在 Gitk 我可以看到团队成员的提交 (X) 有两个父项,第一个父项是他自己之前的提交 (A),另一个父项包含很多其他人的提交 (1通过 5).在他合并之后,其他人(1 到 5 和其他人)所做的所有更改都不再存在于 X、B、C 等...

A------------
              \
               X - B - C 
              /
1--2--3--4--5
           /
e--r--j--k
     /
l--m

如果我将提交 X 与提交 A 进行比较,它不会显示任何差异,如果我将提交 X 与提交 5 进行比较,它会显示所有缺失的更改。此外,在提交 X、B 或 C 时,git 日志不显示提交 1 到 5 中对文件所做的更改。但是,如果我这样做 git log --full-history 那么 history 会显示在 1 到 5 中所做的更改,但这些更改仍不存在于实际文件中,并且历史记录不显示它们被撤消。所以 git log --full-history 似乎与当前文件内容相矛盾。

我与提交 X 的用户进行了交谈。他说他没有进行重置或变基,并且他说他在相关时间内没有还原任何提交。然而,他说他有时会做一个 pull origin master,导致其他人的更改被放入他的索引或工作树中,就好像他做了这些更改,而不是这些更改的实际作者。他说,当发生这种情况时,他做了一个新的克隆,并没有将任何东西从本地仓库推送到 master,因为他认为 Git 做错了什么。

这两件事是否相关(错误的拉取和错误的合并)?

我怎样才能准确地说出发生了什么,以便我们以后可以避免这种情况?

是什么导致 Git 有时将从 origin master 拉取的更改放置在本地工作目录或索引中,就好像它们是本地更改一样?

有几种方法可以获取索引中事物的行为。拉是先获取然后合并。该合并可能会导致冲突,该冲突看起来就像您描述的那样与其他人在您的索引中所做的更改。不了解冲突管理的用户可能会造成很大的损失,结果可能是糟糕的合并。

否则,他们必须将额外的标志传递给 git pull,例如 --no-commit,以使其按照他们描述的方式运行。

以下是我的调查方式...

用户因不报告所有信息而臭名昭著。当问题发生时,我会确切地找出他们在做什么,并要求他们在问题发生时复制他们的终端历史记录。他们的 shell 历史或 reflog 可能也很有趣。

检查他们的配置。我会看看他们的~/.gitconfigproject/.git/configenv | grep GIT,看看有没有什么好笑的。

我还会查明他们是否在命令行或某些工具上使用 git,该工具可能会导致问题。

查明他们使用的 git 版本,也许是旧版本或错误版本(尽管我还没有遇到由 git 错误引起的情况)。

检查他们的遥控器,他们可能以某种方式混合了一些其他存储库。

存储库有挂钩吗?如果是这样,他们是否使用了可能无法在用户计算机上按预期工作的实用程序?

However, he says that he does sometimes do a pull origin master that results in everyone else's changes getting put in his index or working tree as if he had made those changes and not the actual authors of those changes.

听起来他遇到了合并冲突,但不明白它们是什么。这是一个非常的常见问题,不幸的是,我们不知道避免它的好方法(例如,切换回 SVN 并不能避免它)。

这到底是怎么发生的?

让我们称呼您的开发人员 Alice 和 Bob。 Alice 提交了 1-5,Bob 提交了 A 和 X。这是一个似是而非的历史。

  1. Bob 提交 A.

  2. Alice 进行 1-5 次提交,并将它们推送到中央存储库。

  3. Bob 尝试推送 A,但无法推送,因为他的存储库已过期。

    $ git push
     ! [rejected]        master -> master (non-fast-forward)
    
  4. 鲍勃然后他按照你告诉他的去做:他先拉。但是,他遇到了合并冲突,因为提交 A 和提交 1-5 触及了一些相同的代码。

    $ git pull
    Auto-merging file.txt
    CONFLICT (content): Merge conflict in file.txt
    Automatic merge failed; fix conflicts and then commit the result.
    
  5. Bob 在他的工作目录中看到其他人所做的更改,但不明白为什么会有这些更改。

    $ git status
        both modified:   file.txt
    
  6. 他认为 Git 做错了什么,而实际上 Git 要求他解决合并冲突。他尝试签出新副本,但出现错误:

    $ git checkout HEAD file.txt  
    error: path 'file.txt' is unmerged
    
  7. 既然不行,他试一下-f:

    $ git checkout -f HEAD file.txt
    warning: path 'file.txt' is unmerged
    
  8. 成功!他承诺并推动。

    $ git commit
    $ git push
    

变难的部分

那里有很多 git 工具。严重地。 Visual Studio 和 Xcode 都带有 Git 集成,还有其他几个 GUI,甚至还有多个命令行客户端。人们描述他们如何使用 Git 的方式也很马虎,而且大多数开发人员对 Git 在 "pull commit push" 工作流程之外的工作方式不太满意。

不久前有一篇关于这个主题的优秀论文(我很难找到它)。一些结论是(原谅我的记忆):

  • 大多数开发人员并不真正知道如何使用源代码管理,除了一些非常简单的命令(提交、推送)。

  • 当源代码控制未按开发人员预期的方式运行时,他们会采取一些策略,例如将一些他们不太了解的命令复制粘贴到 "fix things",添加 -f 标志,或擦除存储库并使用干净的副本重新开始。

  • 在开发团队中,通常只有首席开发人员才真正知道存储库中发生了什么。

所以这真的是一个教育挑战。

我认为 Bob 需要学习的关键教训是 git pull 实际上只是 git fetchgit merge,并且您可能会遇到合并冲突,并且您需要在解决合并时,以一种非常认真和有目的的方式行事。即使没有报告的冲突,这也适用......但现在让 Bob 不要太过担心!

这里的另一个重要教训是,首席开发人员需要花时间确保团队中的每个人都能正确使用源代码管理,并了解拉取、推送、分支和合并之间的关系。这是午餐时间讲座的好机会:整理一些幻灯片,买披萨,然后讨论 Git 的工作原理。