[英]Why does git pull not update locally changed files?
据我了解,“ git pull”会进行获取和合并。 对于iOS项目,我使用发布设置配置了设置(证书,配置文件等)。 但是,当在master分支上时,当我执行git pull
和git log
,我看到git提交与远程master分支的最新提交保持最新,但是git diff
仍然显示我的pbxproj文件是不同的(即以及提供的配置文件字符串等(我已在本地配置)。
明确地说,这对我来说是理想的,因为我只希望代码更改,而不必在每次发布分支之前(在发布到生产环境之前)更新分支和设置概要文件。 但我也想了解git合并的方式。 我期望git将合并我在本地更改的文件和/或表明冲突。 但是没有发生这样的事情。
注意:我知道一个“强制更新”,它使我的本地主服务器完全类似于远程主机,但想了解此操作与常规合并之间的区别。
git
会将来自远程的更改与本地提交的更改合并。 它不会尝试与您未提交的更改合并,并且如果同一文件中有远程更改,它将警告您合并将清除您的本地更改并拒绝合并。
更新 -我意识到我并没有真正回答“为什么”:
区分本地提交的更改和未提交的本地更改的原因是,您始终可以取回本地提交的更改。 他们处于提交状态,您可以checkout
该提交,在该位置创建一个新分支,甚至在需要时将当前分支reset
到该位置。 与本地提交的更改合并不会丢失任何内容。
但是您的工作树(未提交的本地更改)没有这种保护。 如果git将更改合并到其中,而结果却不是您想要的,则无法轻松地“退回”到以前的状态。
我们还可以就与未提交的更改合并所涉及的内容进行技术差异,但是,如果需要与未提交的更改合并,则可以克服那些技术原因。 但是,由于这样做可能会导致数据丢失,因此最好避免将更改合并到具有本地未提交更改的任何文件中。
据我了解,“ git pull”会进行获取和合并。
没错 更准确地说, git pull
实际上运行 git fetch
,然后运行git merge
。 或者更确切地说,它曾经是过去的字面意思。 在过去的一年左右的时间里,对Git进行了修改,使其可以通过在内部执行获取和合并步骤来加快git pull
速度,而不是在可能的情况下运行单独的命令-并且您还可以告诉它执行git rebase
而不是git merge
,还有其他一些极端情况。 但从本质上讲,pull =提取+合并。
棘手的部分是这两个步骤分别意味着什么 。 获取和合并都很复杂,也可能很复杂。
取回步骤比较简单:您的Git调用其他Git。 您的Git和他们的Git进行了一段简短的交谈,其中他们的Git告诉您的Git有关他们拥有的任何新提交的信息,而您没有。 您的Git将这些新的提交加载到您的存储库中,以便您现在拥有这些提交; 然后,您的Git会设置您的远程跟踪名称 (如origin/master
类的名称)来记住其 Git的master
。 (当git pull
运行git fetch
,它可能会限制您的Git的获取。在非常旧的版本中,这甚至也禁止您的Git更新您的origin/master
等。ModernGit消除了最后的无用特性。)
只要您自己不使它复杂化(通过添加refspecs或各种获取标志),就可以完成所有操作。 合并部分仍然很棘手。 完成提取后, git pull
现在运行git merge
,这会使事情变得有些复杂。
合并的目标很简单:Git首先假设您一直在工作并进行提交,而其他人也一直在工作并进行提交。 如果我们将这些提交与先前的提交朝左绘制,然后将其向右提交,给它们提供一个简单的单字母名称,而不是它们实际的难以理解的Gitty哈希ID,我们将得到如下所示:
C--D--E <-- branch (HEAD)
/
...--A--B
\
F--G--H <-- origin/branch
E
是您在分支上的最新提交。 其父是提交D
; D
的父母是C
; 等等。 提交H
是他们的最新提交,您刚刚通过git fetch
引入了它。 H
的父母是G
,其父母是F
,其父母是B
我们可以看到,这两条开发线是并行发生的,从提交B
的共同点( 合并基础)开始 。
git merge
在这种情况下所做的实际上是运行两个git diff
命令。 第一个找出您所做的更改:
git diff --find-renames <hash-of-B> <hash-of-E>
第二个差异找出了它们的变化:
git diff --find-renames <hash-of-B> <hash-of-H>
Git可以将这两组不同的更改组合在一起 ,只要它们彼此不冲突即可。 或者说,只要Git不认为它们发生冲突-Git不知道 ,它就对git diff
的逐行差异做出了很多假设。 成功(也许)组合了更改之后,Git将组合的更改应用于合并基础中的快照以生成新的快照,并与两个父级进行合并提交 :
C--D--E
/ \
...--A--B I <-- branch (HEAD)
\ /
F--G--H <-- origin/branch
进行新的合并提交会使分支名称本身branch
指向新的提交,就像进行新的普通提交一样。
这是一个真正的合并:Git必须合并自合并基础以来的更改 ,这意味着它必须首先找到合并基础提交,然后进行两个比较。 这是要合并 (两组更改)的合并的动词部分。 在完成了要合并的动词部分后,Git进行了merge commit ,merge作为形容词或不那么正式地称为merge ,它使用merge作为名词。
假设您运行git fetch
(也许通过git pull
),但什么都没有改变,所以您有:
...--D--E <-- branch (HEAD), origin/branch
也就是说,您在这里有两个不同的名称。 您有一个名称branch
-您的分支-指向您的哈希ID为E
提交(确实是一些难看的Git哈希)。 您还具有远程跟踪名称origin/branch
,记住了它们在 Git的分支branch
,指向您的哈希ID为E
提交,因为两个Git中都存在相同的提交。
于是,成就了没有工作,取得了不提交,运行git fetch
又过去了,只是这一次,他们已经做了一些工作,取得了一定的提交:
...--D--E <-- branch (HEAD)
\
F--G <-- origin/branch
如果现在要求Git将您的工作(或缺少工作)与他们的工作合并,Git会像往常一样找到合并基础,但是会发现合并基础是提交E
本身。 显然,将提交E
与自身进行比较不会发现任何变化。 无论如何,Git 可以继续构建新的合并提交,将无更改与某些更改结合起来以得到:
...--D--E------H <-- branch (HEAD)
\ /
F--G <-- origin/branch
其中H
中的快照与G
中的快照完全匹配。 但是git merge
的默认设置是不打扰 。 合并将其检测为快进操作,并且不执行合并动作, 也不进行形容词合并提交。 不用执行新的提交H
,Git只会将分支名称branch
向前移动:
...--D--E
\
F--G <-- branch (HEAD), origin/branch
并检出提交G
以使G
的快照充满索引和工作树。
Git称这为快速合并,但没有合并! 快进只是对分支名称的操作。 最终没有任何痕迹发生,这就是为什么Git允许您禁止快速前进的非合并“合并”的原因。 但是,对于与远程跟踪名称相对应的本地分支,无论如何实际上实际上是您经常想要的东西,这就是为什么这是Git的默认设置。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.