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)
如果有任何冲突,将显示出来。我需要解决。
任何人都可以帮助我,无论我的理解是否正确。如果没有,请帮助我正确理解这个概念。提前致谢。
我认为,您对分支名称和的使用有一些普遍的误解git pull
。让我将其分为几个部分,并向您提供以下执行摘要概述:
push
对应的不是pull
,而是fetch
;git pull
只需运行git fetch
第二个Git命令(通常是)git merge
,我相信新Git用户最好避免git pull
使用,而应使用两个单独的命令;push
和fetch
使用转移散列ID的名称,它的提交,通过哈希ID标识,这个问题; 和git merge
或git rebase
,您当前的分支确实很重要。在推入或获取期间,当前分支无关紧要,但是如果使用git pull
,则运行git merge
或git rebase
,现在当前分支很重要。现在,让我们深入研究细节。
Git与提交有关。从某种意义上说,如果我们只是人类不需要分支名称,而Git一直都在谈论哈希ID的提交,那么Git会“喜欢”它。我可能会问您是否正在使用commit 95ec6b1b3393eb6e26da40c565520a8db9796e9f
,并且您会说“是”或“否,但我有那个”或“否,但我还没有听说过那个”。
您提到:
module1从master分支,feature1从module1分支。
但是在吉特(Git)看来,分支不会从另一个分支分支。而是,每个提交都链接到先前的提交或父提交。您画了这个:
Run Code Online (Sandbox Code Playgroud)master ---------- | module1 ---------- | feature1 ------------
这向我暗示您认为提交仅属于(或位于)一个分支。不过,这不是Git看到他们的方式。相反,大多数提交同时在多个分支上。例如,考虑我们可能这样绘制的图形:
o--o--o <-- br1
/
...--o--o--o <-- master
\
o--o <-- br2
Run Code Online (Sandbox Code Playgroud)
每轮o
代表一次提交。所有提交拦腰行上master
,但大多数这些提交的是同样的br1
和br2
。在最后(最新和最右侧)提交的master
是只对master
; 其余的也在其他分支上。
这是因为在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
连接到。
当然,这不是偶然的,如果我们开始在双方的技巧br1
和master
工作倒退,我们最终会回到一个会议点提交。但是提交的这种汇合并非来自名字。无论如何选择从的最短提交br1
和的最短提交开始都没关系master
;重要的是这两个特定的提交,然后我们在此过程中找到每个提交。
换句话说,分支名称使我们从提交图中入手。 最重要的是提交图。 名称只是起点!
push
是fetch
以上所有内容都是关于在单个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要么说:啊,我有哈希ID或Hmm,我没有哈希ID,请发送给我那个提交,以及它的含义是什么父哈希ID,因为也许我也需要该ID,依此类推。
最终,您的Git拥有了他们建议的所有提交和其他哈希对象。现在,您的Git会像它们一样获取它们的分支名称master
,并保存这些名称。但是您有自己的分支机构。使用Git不能将那些名字保存为您的分支机构。它重命名所有这些名称。它们的分支,例如master
,成为您的Git的远程跟踪名称,例如origin/master
。请注意,您的Git只是origin
在分支名称之前使用缩写名称,加上斜线。
一旦git fetch
完成,你的Git现在还记得其中的 Git的枝梢是,使用您的 origin/*
远程跟踪名称。您拥有它们的提交以及所有必需的关联内容,以便您可以签出这些提交并获取与它们一起保存的文件,但是只有通过这些远程跟踪名称才能找到任何新的提交。如果他们有,作为分支的顶端提交,一些你的旧的提交,你可能已经有其他的方法来找到它们。
的对应项git fetch
是git push
,但并非完全对称。例如,要进行git push
to操作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,然后要求他们设置分支机构,master
或module1
或者feature1
,该散列ID。如果他们还没有该提交ID,他们会让您的Git发送提交对象(以及所有需要的父提交和其他对象,全部由哈希ID标识),然后他们自己评估是否允许您设置他们的分支名。有时他们会,并且您的推动成功;有时他们会找到一条规则,说“不允许这样做”,这样您的推送就会失败。
请注意,他们的规则取决于他们!您发送请求(“请设置module1
为a9fc312...
”);他们可以查看其当前module1
分支哈希ID,以及您发送的提交(如果是新提交),并选择是否接受请求。您可以使用--force
它作为命令发送,但是即使您这样做,他们仍然可以选择是否遵守命令。您所能做的就是发出请求(或强制命令),然后查看他们是否接受该请求。但是有一个标准规则,大多数Gits大部分时间都在使用:如果请求仅执行添加新commits的请求,则允许该请求。
看看在br1
上面添加提交时发生了什么。现有的提交都可以从原始技巧提交中访问,而仍然可以从新的技巧提交中访问。新的提交“增加了分支”。它没有更改任何现有的提交-没有新的提交可以更改任何现有的提交-但新提交的父级是旧的提示提交。如果我们像Git一样从新的技巧开始并向后工作,我们就会到达旧的技巧。
相同的规则也适用git push
:如果您发送给另一个Git的请求将使新分支提示中所有现有的提交都可访问,则另一个Git可能会允许该请求。
请注意,我们一直在使用git push origin module1:module1
,但是您建议我们运行:
Run Code Online (Sandbox Code Playgroud)git push origin module1 //Being in feature1 branch
不要紧,我们是哪个部门在(或者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 pull
被git fetch
接着是第二GIT中命令然后您询问了以下内容:
Run Code Online (Sandbox Code Playgroud)git pull origin module1 //Being in feature1 branch
是什么git pull
做的是简单的描述:
它git fetch
与您传递的大多数参数一起运行:
git fetch origin module1
Run Code Online (Sandbox Code Playgroud)如果可行,它将运行第二个Git命令,您可以预先选择。要运行的默认命令是git merge
。第二个命令的确切参数稍微取决于期间的输入git fetch
,但是您仍然必须先选择git merge
vs,git rebase
然后才能查看输入的内容。
我们之前提到过,我们通常只运行git fetch origin
,它会带来所有名称origin
has,然后您的Git重命名。如果您运行:
git fetch origin module1
Run Code Online (Sandbox Code Playgroud)
这仅限制了获取:它找出它们的内容module1
,并在必要时通过ID提交提交,然后设置your origin/module1
。2 它忽略所有其他名称。在module1
这里,就像是很多git push
,但如果你不使用两个名字,如果你不写module1:module1
-您的Git只会更新远程跟踪名称。(在这里,您很少使用两个用冒号分隔的名称。它确实可以工作并且可以用于某些目的,但是您将需要了解更多详细信息。)
在中git fetch
,签出哪个分支都没有关系。但第二的Git命令或者是git merge
或git rebase
,并为这两个命令,它确实不管你已经检查哪个分支出来。
git pull
运行的第二个命令在不更改当前分支的情况下运行。假设第二个分支为git merge
,则参数为:
这很像运行,尽管消息有所不同。git merge origin/name
什么git merge
有这样做本身就是一个有点复杂,因为git merge
有时什么也不做,有时做了快进,而不是合并,有时做一个真正的合并,而且有时有合并冲突,使合并停止在中间,并从你的帮助。我将所有这些留给其他答案。
让我们在这里总结一下:
您git push
和git 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
,则不会犯此特定错误!