我修复了一个文件中的正常合并冲突(拉动后)。我更改了文件,添加了git add并提交以便推送。
然后我发现,当我推送时,其他5个文件用我更改的文件推送。
发生了什么?如果我用gitk检查我的提交只有那个文件,
如果我在github web界面中检查,我会看到6个文件而不是1个(所以当我拉的时候我也下载了其他5个文件,但我没有触摸也没有添加)。
如果我git show --stat commitID
,我会在本地看到网页。
请问,任何人都可以告诉我为什么合并添加文件并且没有默默合并?我认为pull命令合并了它们,然后只有我修改的快照(1个文件)才会被推送。为什么gitk会给出一件事并且git show stat另一件事? (也许是一些gitk params?)
答案 0 :(得分:3)
要解决您在评论中添加的内容:
我担心的是......当我提交某些内容时,我会在舞台上提交文件。
那是对的。但是:
要在舞台中添加文件,我需要" git add"它们。
这不太对。我们马上回过头来看看。
当我拉(这是一个fetch + merge)时,是否有一些合并的部分将文件添加到舞台上?
是的,有。
我可以这样解释。我的错是我没有做过" git status"在添加我的修复之前,我认为舞台是空的。
现在让我们仔细看看。究竟 这个临时区域到底是什么?请注意,它有三个不同的名称:临时区域,这是您一直使用的名称; 索引;有时候缓存(例如,您可以看到git rm --cached
和git diff --no-index
或git diff --cached
来控制索引/缓存/是否临时区域参与其中)。我在这里使用 index 这个词,以避免过多地暗示它:名称临时区域(这在很多方面更好)意味着如何index用于暂存文件,而名称 cache 则表示如何使用索引来加速各种操作。
我喜欢定义索引的方式是,您可以使用构建下一个提交的提交。这是指它在暂存文件中的作用,但也提醒你为什么你也要暂存文件:因为你打算进行新的提交。如果它们与旧的完全相同,那么制作新的没有任何意义。因此,在某种程度上,我们知道我们正在更改存储在索引中的数据,然后我们会使用git commit
将其转换为新的快照。
关于索引的第一个奇怪的事情是它永远不会 1 为空。相反,索引通常匹配当前提交。此时您必须运行git commit --allow-empty
来创建新提交。那不是因为索引本身是空的;相反,它是因为索引和当前提交之间的差异是空的。
稍后,您将git checkout
一些提交(可能通过分支名称),或运行git commit
来创建一个新提交。在此点,索引和当前提交将相互匹配。索引中的内容也是什么。
当您运行git add
时,您正在做的是将工作树文件复制到索引中。如果它是新文件,那么它现在就在索引中,而不是之前的那个。如果它是已更改的文件,您刚才所做的就是用新数据替换索引中存储的数据。无论哪种方式,索引版本现在都与工作树版本匹配。 (练习:索引版本现在是否与提交的版本匹配?如果工作树版本与提交的版本匹配会怎样?)
关于指数的第二个奇怪的事情是在git merge
期间发生的事情。 git merge
有多种情况需要处理,有些情况很容易,并且不会这样做;但其中一个是真正的合并。在真正的合并中,Git有两个特定的提交 - 我喜欢称它们为 L 和 R ,左右,或本地和远程,或--ours
和--theirs
- 共享一个共同的合并基础提交 B 。 Git通过实际运行来查找自公共基数以来发生的事情:
git diff --find-renames B L # what did we change in --ours?
git diff --find-renames B R # what did they change in --theirs?
然后Git将组合更改,实际上是通过每个文件的三个版本填充索引:一个来自 B ,一个来自 L ,一个来自 R 。这三个进入"插槽"在每个文件的索引条目中。通常,在不合并的中间情况下,仅占用时隙零。当我们合并时,插槽零被启动,三个版本进入插槽1-3。
然后,Git可以轻松地比较这三个版本,并决定采用哪一个版本(如果只有--ours
vs --theirs
中的一个更改了文件与基础)或者如何组合这些更改(如果两者都有) --ours
和--theirs
改变了它。 如果 Git可以合并更改 - 如果认为它可以 - 它会这样做,并完全丢弃这些编号较高的插槽条目:组合版本进入插槽零像往常一样,该文件已解决。如果Git并不认为它可以组合这些更改,它会将最好的猜测(带有冲突标记)写入工作树,然后将更高编号的插槽留在索引中 。 2 然后你必须自己解决(使用工作树文件,或三个索引槽条目,或者你喜欢的任何东西),并git add
正确的结果,无论那是什么。然后git add
步骤将清除插槽1-3条目并写入插槽零条目。
在所有这一切结束时,我们回到更正常的情况。索引现在在每个文件的正常槽零(无冲突)条目中包含每个文件的副本,因为它应该出现在新的合并提交中。所以现在我们或者Git可以运行git commit
来提交最终结果。
如果我们正在进行真正的合并,并且 R 中的文件既不在 L 中,也不在合并库 B 中,该文件的合并结果是"取新文件"。所以这种情况很简单,git merge
将文件添加到插槽零而没有真正提及它。
1 好吧,有一个明显的例子,索引是完全空的,就是根本没有文件的情况。例如,这是一个新的,完全空的新存储库中的初始状态。请注意,在这种情况下,您也没有任何分支。这是一个特殊的状态:您在分支master
上但分支master
尚不存在!索引真的是空的,根本没有提交。
2 这个一般性描述并不完全是内部发生的,它都经过优化,可以简化案例的速度。但这是合乎逻辑的过程。有关优化过程,请参阅the git read-tree
documentation。