当Git拉或推到另一个分支中的另一个分支时会发生什么

Cha*_*ari 2 git version-control dvcs

我熟悉GIT并将其用于项目的版本控制。

我有几个想知道的问题。我用谷歌搜索,但没有得到很好的答案。

所以事情是我要拥有master,module1,feature1分支。

master
 ---------- 
         | module1
         ----------
                  | feature1
                  ------------
Run Code Online (Sandbox Code Playgroud)

module1从master分支,feature1从module1分支。

查询1:在feature1分支中,如果我进行少量更改并将其提交并推入module1,该怎么办?

git add .
git commit -m "Changes of feature1"
git push origin module1 //Being in feature1 branch
Run Code Online (Sandbox Code Playgroud)

Feature1到module1分支的代码在这里发生了什么,以及module1分支如何采用它。

我的了解:根据我的理解,feature1的更改将被推送到module1分支。后来我意识到我应该将其推入feature1分支,然后将其推入feature1分支,并将检出到module1分支,并还原我最近推送的代码。

查询2:如果在feature1分支中,我通过以下操作在该分支中提取了module1的代码,该怎么办

git pull origin module1 //Being in feature1 branch
Run Code Online (Sandbox Code Playgroud)

我的理解:module1代码的更改将合并到我的feature1分支中,并且与命令中的以下内容相同

git checkout moduel1
git pull origin module1
git checkout feature1
git merge module1
Run Code Online (Sandbox Code Playgroud)

如果有任何冲突,将显示出来。我需要解决。

任何人都可以帮助我,无论我的理解是否正确。如果没有,请帮助我正确理解这个概念。提前致谢。

tor*_*rek 5

我认为,您对分支名称和的使用有一些普遍的误解git pull。让我将其分为几个部分,并向您提供以下执行摘要概述:

  • push对应的不是pull,而是fetch
  • git pull只需运行git fetch第二个Git命令(通常是)git merge,我相信新Git用户最好避免git pull使用,而应使用两个单独的命令;
  • pushfetch使用转移散列ID的名称,它的提交,通过哈希ID标识,这个问题; 和
  • 对于git mergegit rebase,您当前的分支确实很重要。在推入或获取期间,当前分支无关紧要,但是如果使用git pull,则运行git mergegit rebase现在当前分支很重要。

现在,让我们深入研究细节。

分支名称只是指向(单个)提交的指针

Git与提交有关。从某种意义上说,如果我们只是人类不需要分支名称,而Git一直都在谈论哈希ID的提交,那么Git会“喜欢”它。我可能会问您是否正在使用commit 95ec6b1b3393eb6e26da40c565520a8db9796e9f,并且您会说“是”或“否,但我有那个”或“否,但我还没有听说过那个”。

您提到:

module1从master分支,feature1从module1分支。

但是在吉特(Git)看来,分支不会另一个分支分支。而是,每个提交都链接到先前的提交或提交。您画了这个:

master
 ---------- 
         | module1
         ----------
                  | feature1
                  ------------
Run Code Online (Sandbox Code Playgroud)

这向我暗示您认为提交仅属于(或位于)一个分支。不过,这不是Git看到他们的方式。相反,大多数提交同时在多个分支上。例如,考虑我们可能这样绘制的图形:

          o--o--o   <-- br1
         /
...--o--o--o   <-- master
         \
          o--o   <-- br2
Run Code Online (Sandbox Code Playgroud)

每轮o代表一次提交。所有提交拦腰行上master,但大多数这些提交的是同样br1br2。在最后(最新和最右侧)提交的mastermaster; 其余的也在其他分支上。

这是因为在Git中,分支名称之类的名称master仅指向一次提交。名称指向的提交是最右边的提交,如果我们以这种方式绘制提交图,则从左(较早)向右(较晚)绘制。Git将此称为尖端提交,有时称为分支的(在此注意小写)。为了找到您(或Git)可以从该技巧提交中获得的其余提交,Git将通过其丑陋的哈希ID(例如)查找提交95ec6b1...。同样,它是让Git查找提交的哈希ID。该名称仅允许Git查找此哈希ID!

提交本身会存储一个父提交哈希值,因此Git将通过哈希ID查找父提交,并向后退一步。该提交具有另一个父ID,依此类推。git log例如,我们从父级哈希ID的序列向后浏览,一次提交一次,从后来到更早。

如果您运行:

git checkout br1
Run Code Online (Sandbox Code Playgroud)

然后做一些工作,然后运行:

git add -a && git commit
Run Code Online (Sandbox Code Playgroud)

并进行新的提交(让我们以*代替,o以便我们可以看到它),这就是分支名称发生的情况br1

          o--o--o--*   <-- br1 (HEAD)
         /
...--o--o--o   <-- master
         \
          o--o   <-- br2
Run Code Online (Sandbox Code Playgroud)

我们在添加(HEAD)(注:全部为大写)的同时画出该名称,以记住我们给它起的名字git checkout。该*进去的图表,点向后取其犯br1。同时,Git 更改了名称br1,使其指向我们刚刚进行的新提交。这是分行如何生长,在Git中:我们添加新的提交到图表,和Git更新的名字HEAD连接到。

当然,这不是偶然的,如果我们开始在双方的技巧br1master工作倒退,我们最终会回到一个会议点提交。但是提交的这种汇合并非来自名字无论如何选择从的最短提交br1和的最短提交开始都没关系master;重要的是这两个特定的提交,然后我们在此过程中找到每个提交

换句话说,分支名称使我们从提交图中入手。 最重要的是提交图。 名称只是起点!

相反的pushfetch

以上所有内容都是关于在单个Git存储库中工作的。但是当我们使用Git时,我们将使用多个存储库。特别是,我们有自己的存储库,我们在其中进行自己的工作,但随后我们经常需要与存储在其他计算机上的另一个Git存储库进行对话,例如GitHub或雇主,朋友或同事提供的存储库。

这意味着我们要共享提交。正如我们在上面看到的,Git完全是关于提交和提交图的,但是我们仅仅是人类需要名字来使我们起步。这就是git fetch它的对应项,git push进来。运行任何一个命令都将我们的Git连接到其他Git。

我们的Git拥有我们所有的提交,其中一些可能是我们所做的提交。我们的一些Git提交可能是我们从其他地方获得的提交。同样,他们的Git拥有所有拥有的提交,其中一些与我们拥有的提交相同。有些可能是不同的提交。但是无论如何,所有这些提交都由其唯一的哈希ID标识。如果这些ID 具有相同的提交,那么它们在世界各地的每个 Git中都是相同的。如果他们没有我们的提交(因为我们刚刚创建了它们),那么我们的新提交的ID将不同于他们拥有的每个提交ID!(这看起来像魔术,但这只是加密数学,例如,尽管Git使用的是较弱的散列集,但其中一些类似于比特币背后的知识。)

最后,这意味着这两个Git中的每一个仅通过查看这些哈希ID就能分辨出我们中的一个提交而另一个没有。这就是我们的Git可以给予我们他们没有的承诺的方式git push-或他们的Git可以给予我们他们没有的Git承诺:git fetch

一旦他们发送了提交对象(以及完成这些提交所需的其他相关Git对象),那么两个Git就需要为任何新的技巧提交设置名称。这是分支名称开始重要的第二位。

提取方向更简单。您的Git会调用其他Git。通常,我们使用该名称origin来代表某个URL,而另一个Git在该URL上监听呼叫,因此我们运行git fetch origin。您的Git调用该Git,并询问:您的分支提示名称是什么?这些是什么哈希ID? 他们的Git告诉您的Git它的分支提示和哈希ID,而您的Git要么说:啊,我有哈希IDHmm,我没有哈希ID,请发送给我那个提交,以及它的含义是什么父哈希ID,因为也许我也需要该ID,依此类推。

最终,您的Git拥有了他们建议的所有提交和其他哈希对象。现在,您的Git会像它们一样获取它们的分支名称master,并保存这些名称。但是您有自己的分支机构。使用Git不能将那些名字保存为您的分支机构。它重命名所有这些名称。它们的分支,例如master,成为您的Git的远程跟踪名称,例如origin/master。请注意,您的Git只是origin在分支名称之前使用缩写名称,加上斜线。

一旦git fetch完成,你的Git现在还记得其中 Git的枝梢是,使用您的 origin/*远程跟踪名称。您拥有它们的提交以及所有必需的关联内容,以便您可以签出这些提交并获取与它们一起保存的文件,但是只有通过这些远程跟踪名称才能找到任何新的提交。如果他们有,作为分支的顶端提交,一些你的旧的提交,你可能已经有其他的方法来找到它们。

的对应项git fetchgit push,但并非完全对称。例如,要进行git pushto操作origin,您的Git像以前一样调用了他们的Git,但是这次,您想他们发送东西。发送操作的第一部分是移交您拥有,不需要,需要的所有提交。您可以通过git push接受一些额外的参数来识别这些提交:

git push origin module1:module1
Run Code Online (Sandbox Code Playgroud)

请注意,我在这里输入了两次相同的名称。左边的名称module1:,是要查找要发送给他们的特定提交哈希。那就是提示提交您的module1。右边的名称(即:module1部分)是您希望他们使用名称。(这些名称不必相同!但是,如果您在每侧使用不同的名称,则会变得很棘手,因此请尽可能避免这种情况。)

当您运行时git fetch origin,您通常希望拿起他们没有的所有东西。这是非常安全的,因为无论他们提交了什么提示master,您的Git都会将其称为origin/master。无论他们有什么小费承诺module1,您的Git都会称您为origin/module1。您的远程跟踪名称是您自己的私有条目,全部保留给一个名为的远程对象origin,对它进行即时更新无害,甚至是一件好事。1个

但是git push这种方式行不通。你给他们一个承诺哈希ID,然后要求他们设置分支机构mastermodule1或者feature1,该散列ID。如果他们还没有该提交ID,他们会让您的Git发送提交对象(以及所有需要的父提交和其他对象,全部由哈希ID标识),然后他们自己评估是否允许您设置他们的分支名。有时他们会,并且您的推动成功;有时他们会找到一条规则,说“不允许这样做”,这样您的推送就会失败。

请注意,他们的规则取决于他们!您发送请求(“请设置module1a9fc312...”);他们可以查看其当前module1分支哈希ID,以及您发送的提交(如果是新提交),并选择是否接受请求。您可以使用--force它作为命令发送,但是即使您这样做,他们仍然可以选择是否遵守命令。您所能做的就是发出请求(或强制命令),然后查看他们是否接受该请求。但是有一个标准规则,大多数Gits大部分时间都在使用:如果请求仅执行添加新commits的请求,则允许该请求。

看看在br1上面添加提交时发生了什么。现有的提交都可以从原始技巧提交中访问,而仍然可以从新的技巧提交中访问。新的提交“增加了分支”。它没有更改任何现有的提交-没有新的提交可以更改任何现有的提交-但新提交的父级是旧的提示提交。如果我们像Git一样从新的技巧开始并向后工作,我们就会到达旧的技巧。

相同的规则也适用git push:如果您发送给另一个Git的请求将使新分支提示中所有现有的提交都可访问,则另一个Git可能会允许该请求。

请注意,我们一直在使用git push origin module1:module1,但是您建议我们运行:

git push origin module1 //Being in feature1 branch
Run Code Online (Sandbox Code Playgroud)

不要紧,我们是哪个部门(或者on,如git status会说on branch feature1)。这里重要的是命令的两个module1:module1部分git push。这告诉我们的Git:用我们的名字module1,找到承诺推动,并要求他们的Git来设置自己的 module1名字。

我们没有给出两个部分。我们只给出了一部分。对于git push,如果只给出一个部分,则Git只是假设您的意思是“两次使用相同的名称”。所以git push origin module1意味着git push origin module1:module1,毕竟。

git pullgit fetch接着是第二GIT中命令

然后您询问了以下内容:

git pull origin module1 //Being in feature1 branch
Run Code Online (Sandbox Code Playgroud)

是什么git pull做的是简单的描述:

  1. git fetch与您传递的大多数参数一起运行:

    git fetch origin module1
    
    Run Code Online (Sandbox Code Playgroud)
  2. 如果可行,它将运行第二个Git命令,您可以预先选择。要运行的默认命令是git merge。第二个命令的确切参数稍微取决于期间的输入git fetch,但是您仍然必须先选择git mergevs,git rebase 然后才能查看输入的内容。

我们之前提到过,我们通常只运行git fetch origin,它会带来所有名称originhas,然后您的Git重命名。如果您运行:

git fetch origin module1
Run Code Online (Sandbox Code Playgroud)

这仅限制了获取:它找出它们的内容module1,并在必要时通过ID提交提交,然后设置your origin/module12 它忽略所有其他名称。在module1这里,就像是很多git push,但如果你不使用两个名字,如果你不写module1:module1-您的Git只会更新远程跟踪名称。(在这里,您很少使用两个用冒号分隔的名称。它确实可以工作并且可以用于某些目的,但是您将需要了解更多详细信息。)

在中git fetch,签出哪个分支都没有关系。但第二的Git命令或者是git mergegit rebase,并为这两个命令,它确实不管你已经检查哪个分支出来。

git pull运行的第二个命令在不更改当前分支的情况下运行。假设第二个分支为git merge,则参数为:

  • 从远程Git获得的分支提示哈希ID 3,以及
  • 消息“ URL的合并分支' 名称 ' ”

这很像运行,尽管消息有所不同。git merge origin/name

什么git merge有这样做本身就是一个有点复杂,因为git merge有时什么也不做,有时做了快进,而不是合并,有时做一个真正的合并,而且有时有合并冲突,使合并停止在中间,并从你的帮助。我将所有这些留给其他答案。

让我们在这里总结一下:

  • git pushgit fetch操作始终会传输整个提交。它们从不处理单个文件:它们将提交从一个Git存储库复制到另一个。复制提交的过程通常涉及在接收Git中设置一些名称,以便记住任何已调整的提示提交。在大多数情况下,这些都不关心您的当前分支(尽管git push可以告诉您默认情况下推送当前分支:请参见push.default)。

  • git pull只需运行两个单独的Git命令。如果您还不太熟悉这两个命令,建议您分别运行每个命令。

  • 您的git merge命令是最复杂的命令,应该是一个单独的问题。如果您不认为自己执行了git merge命令,请参见git pull上文。


1获得一切的一个缺点是,如果有很多“一切”并且您的网络连接很慢,则可能要花费很长时间。另一方面,如果您今天得到所有东西,明天将几乎没有任何东西可以得到,因此下一个 git fetch将会很快。如果您有选择地今天只得到一点点,也许明天git fetch会很慢。

2这假设您的Git版本至少为1.8.4。在1.8.4之前的Git中,git fetch origin module1带来了其哈希ID,但随后扔掉了名称,无法更新自己的名称origin/module1

3如果运行git pull origin name1 name2,则Git会将多个哈希ID传递到git merge,从而产生Git所谓的章鱼合并。这几乎永远不是您想要的,因此请避免这种情况。如果您避免git pull,则不会犯此特定错误!