繁体   English   中英

为什么git pull不更新本地更改的文件?

[英]Why does git pull not update locally changed files?

据我了解,“ git pull”会进行获取和合并。 对于iOS项目,我使用发布设置配置了设置(证书,配置文件等)。 但是,当在master分支上时,当我执行git pullgit 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM