我似乎无法找到一个很好的解释.
我知道git pull会做什么:
1)获取,即来自服务器的所有额外提交被复制到本地存储中,并且origin/master分支指针移动到提交链的末尾
2)合并所述的原点/主分支到主支路,该主支指针移动到新创建的提交,而原点/主指针留在原地.
我认为git push做的事情非常相似,但我不确定.我相信它会做其中一个,或类似的东西,或其他东西(?):
要么
我目前正在使用git进行基本操作,所以我做得很好,但我想完全理解这些内部结构.
tor*_*rek 65
假设你已经理解了git的"对象"模型(你的提交和文件等等都只是"git数据库中的对象",带有"松散"的对象 - 那些没有打包以节省空间存储.git/objects/12/34567...的对象等). .
你是对的:git fetch检索对象"他们"(origin在这种情况下)你没有,并在其上贴上标签:origin/master等等.更具体地说,您的git在Internet电话(或任何其他合适的传输)上调用它们并询问:您拥有哪些分支,以及哪些提交ID?他们拥有master和ID 1234567...,所以你的git要求1234567...和你还没有的任何其他对象,并origin/master指出提交对象1234567....
这里的部分git push是对称的:你的git像往常一样在同一个网络电话上调用他们的git,但这一次,你的git告诉他们关于你的分支和你的 git存储库对象,而不是只询问他们的分支.,然后说:"那我给你设置你来?"master56789ab...
他们的git会看一下你发送的对象(新的提交56789ab...以及你没有的其他任何对象,他们需要接受它).他们的混帐然后考虑到设置请求他们 master来56789ab....
正如Chris K已经回答的那样,这里没有合并:你的git只是建议他们的git master用这个新的commit-ID 覆盖他们.决定是否允许这一点取决于他们的git.
如果"他们"(无论他们是谁)没有设置任何特殊规则,git在这里使用的默认规则非常简单:如果更改是"快进",则允许覆盖.它还有一个附加功能:如果使用"force"标志设置完成更改,也允许覆盖.在这里设置强制标志通常不是一个好主意,因为默认规则"只有快进",通常是正确的规则.
这里显而易见的问题是:快进的究竟是什么?我们马上就会谈到这一点; 首先,我需要在标签上进行扩展,或者"引用"更加正式.
在git,分支,或标签,甚至像藏匿的东西,HEAD都是引用.其中大多数都位于.git/refs/git存储库的子目录中.(一些顶级引用,包括HEAD,.git本身就是正确的.)所有引用都是包含SHA-1 ID 的文件17452b4b5786778d5d87f5c90a94fab8936502e20.SHA-1 ID很麻烦,人们不可能记住,所以我们使用名称,比如v2.1.0(在这种情况下是一个标签,版本2.1.0的git本身)来保存它们.
一些参考文献是 - 或者至少是完全静态的.标签v2.1.0永远不应该引用上面的SHA-1 ID以外的东西.但有些参考文献更具活力.具体来说,您自己的本地分支机构master就是移动目标.一个特例,HEAD甚至不是它自己的目标:它通常包含移动目标分支的名称.所以"间接"引用有一个例外:HEAD通常包含字符串ref: refs/heads/master,或者ref: refs/heads/branch沿着这些行的某些东西; 并且git不会(也不能)强制执行"永不更改"的引用规则.特别是分支机构变化很大.
你怎么知道引用是否应该改变?好吧,其中很多只是按照惯例:分支移动而标签不移动.但是你应该问:你怎么知道引用是分支,标记还是什么?
refs/heads/,refs/tags/,等.除了特殊的顶级引用之外,所有git的引用都在refs/我们上面已经提到过.但是,在refs/目录(或"文件夹",如果您在Windows或Mac上),我们可以拥有一整套子目录.Git目前有四个明确定义的子目录:refs/heads/包含所有分支,refs/tags/包含所有标记,refs/remotes/包含所有"远程跟踪分支",并refs/notes/包含git的"注释"(我会在这里忽略它们复杂).
由于所有分支都在refs/heads/,git可以告诉我们应该允许这些更改,并且由于所有标记都在refs/tags/,git可以告诉它们不应该.
当你进行新的提交,并且在分支上时master,git会自动移动引用.您的新提交是通过其"父提交"作为前一个分支提示创建的,一旦您的新提交被安全地保存起来,git将更master改为包含新提交的ID .换句话说,它确保分支名称(heads子目录中的引用)始终指向最尖端的提交.
(事实上,作为存储在存储库中的提交图的一部分的提交集合意义上的分支是由存储库中的提交构成的数据结构.它与分支名称的唯一连接是分支本身的提示提交存储在具有该名称的引用标签中.这在以后很重要,如果更改或删除分支名称,因为存储库会增加更多提交.现在只需记住:有"分支提示"(即"分支名称"指向的位置)与分支作为提交子集的DAG之间的区别.有点不幸的是,git倾向于将这些不同的概念归结为单个名称,"分支".)
通常你会在合并的上下文中看到"快进",通常将合并作为第二步完成git pull.但实际上,"快速转发"实际上是标签移动的属性.
让我们画一点提交图.小o节点表示提交,每个节点都有一个箭头指向左,左 - 上,或左 - 下(或在一种情况下,两个箭头)到其父(或父).为了能够通过名称引用三个,我将给它们大写字母而不是o.此外,这个基于角色的艺术作品没有箭头,所以你必须想象它们; 只要记住它们都指向左或左,就像三个名字一样.
o - A <-- name1
/
o - o - o - o - B <-- name2
\ /
o - C <-- name3
Run Code Online (Sandbox Code Playgroud)
当你要求git更改引用时,你只需要它将新的提交ID粘贴到标签中.在这种情况下,这些标签存在refs/heads/并因此成为分支名称,因此它们应该能够采用新值.
如果我们让Git把B成name1,我们得到这样的:
o - A
/
o - o - o - o - B <-- name1, name2
\ /
o - C <-- name3
Run Code Online (Sandbox Code Playgroud)
请注意,提交A现在没有名称,o只有通过查找A... 才能找到它左侧,因为A没有名称.提交A已被放弃,这两个提交已经有资格进行"垃圾回收".(在git中,"reflog"中留下了一个"幽灵名称",这使得分支A一般保持30天左右.但这完全是另一个主题.)
大约GIT中告诉什么把B成name3?如果我们接下来这样做,我们得到这个:
o - A
/
o - o - o - o - B <-- name1, name2, name3
\ /
o - C
Run Code Online (Sandbox Code Playgroud)
在这里,提交C仍然有一种方法可以找到它:从B下一个和左下方开始,到其他(第二个)父提交,然后你找到提交C.因此,提交C的不放弃.
name1像这样更新不是快进,但更新name3 是.
更具体地说,当且仅当对象 - 通常是提交 - 参考用于指向的提交 - 仍然可以通过从新位置开始并向后工作,沿着所有可能的向后路径到达时,引用更改是"快进"..在图形术语中,如果旧节点是新节点的祖先,则它是快进的.
push是快进,通过合并当您做的唯一事情是添加新提交时,会发生分支名称快进; 但是,当你添加了新的提交时,你也合并了其他人添加的新提交.也就是说,在你做了一个新的提交之后,假设你的repo中有这个:
o <-- master
/
...- o - o <-- origin/master
Run Code Online (Sandbox Code Playgroud)
在这一点上,origin/master"向上和向右" 移动将是一个快进.但是,有其他人出现并更新了other(origin)repo,所以你做了一个git fetch并从中获取新的提交.你的git移动你的origin/master标签(在你的仓库的快进操作中,当它发生):
o <-- master
/
...- o - o - o <-- origin/master
Run Code Online (Sandbox Code Playgroud)
在这一点上,移动origin/master到master会不会是快进,因为这将放弃一个新的提交.
但是,您可以使用两个父提交ID 执行git merge origin/master操作以对您 进行新提交master.让我们标记这个M(用于合并):
o - M <-- master
/ /
...- o - o - o <-- origin/master
Run Code Online (Sandbox Code Playgroud)
你现在可以git push回复origin并要求他们设置他们 master - 你正在调用 - origin/master等于你的(新)M,因为对他们来说,这是一个快速的操作!
请注意,您也可以执行此操作git rebase,但让我们将其留给不同的stackoverflow发布.:-)
1实际上,git引用总是从各个子目录中的单个文件开始,但如果引用很长时间没有更新,它往往会被"打包"(以及所有其他大多数静态引用)到一个包含完整打包引用的文件中.这只是一个节省时间的优化,这里的关键不依赖于确切的实现,而是使用git rev-parse和update-ref命令从引用中提取当前的SHA-1,或者更新引用以包含新的SHA- 1.
我最简单的描述是, push 只需执行以下操作:(假设您执行git push origin master)
但是,它将检查您的本地/主服务器是否基于源/主服务器。从概念上讲,这意味着在 git 图中,从 local/master 可以直接返回到 origin/master(不是本地 git 的 origin/master,而是远程仓库上的 master),只需“向下”移动,这意味着没有在您推送之前对远程仓库进行了修改。否则推送将被拒绝
| 归档时间: |
|
| 查看次数: |
20953 次 |
| 最近记录: |