详细的原因为什么远程git rebase是如此邪恶
所以我来自一个集中的VCS背景,并试图确定我们在Git(新公司,年轻代码库)中的工作流程。 有一个问题我找不到一个简单而又详细的答案,就是远程分支上的rebase究竟是干什么的。 我知道它会重写历史,一般应该只限于当地的分支机构。
我目前试图审核的工作流涉及远程协作分支,每个开发者为了共享代码而“拥有”一个。 (在可预见的未来,拥有2个开发人员和最多3个开发人员,每个项目和功能需求的功能分支似乎过多,而且开销超过获得的收益。)
然后我遇到了这个答案并且尝试了它,并且完成了我想要的 - 开发人员经常向他自己的协作分支提交并推送,当他知道什么是批准发布的时候,他可以远程重新分配(或者挤压在合并之前进行开发。
输入原始问题 - 如果远程分支是为了合作的目的,其他人必然迟早会进行合作。 如果这个过程/培训问题没有“客人开发者”提交给该协作分支,那么分支所有者实际上发生了什么事情使该远程分支发生了变更?
这不是非常邪恶的,这是实施和期望的问题。
我们从一堆事实开始:
每个Git散列代表一些独特的对象。 为了我们的目的,我们只需要考虑提交对象。 每个散列是将加密散列函数(对于Git,特别是SHA-1)应用到对象的内容的结果。 对于提交,其内容包括源代码树的ID; 作者和提交者的姓名和电子邮件地址以及时间/日期戳; 提交消息; 在这里最关键的是父提交的ID。
即使只改变内容中的一个位,也会产生一个新的,非常不同的哈希ID。 散列函数的加密属性(用于验证和验证每个提交(或其他对象))也意味着无法让某个不同的对象具有相同的散列ID。 Git也希望能在库之间传输对象。
通过将提交复制到新提交,Rebase有效(必然)。 即使没有其他更改 - 通常,与新副本相关联的源代码与原始源代码不同 - 整个重设点的重点是重新保留某个提交链。 例如,我们可能从以下开始:
...--o--*--o--o--o <-- develop
o--o <-- feature
其中分支feature
从分支分离develop
的承诺*
,但现在我们想feature
从尖端下降犯下的develop
,所以我们重订了。 结果是:
...--o--*--o--o--o <-- develop
@--@ <-- feature
o--o abandoned [used to be feature, now left-overs]
其中两个@
是原始两个提交的副本。
分支名称,如develop
,只是指向(单个)提交的指针。 我们倾向于认为是“分支”的东西,就像两个提交@--@
,是通过从每次提交到其父代的后退而形成的。
分支预计会增加新的提交。 发现develop
或master
有一些新的提交被添加,这是非常正常的,所以现在这个名称指向一个提交 - 或许多提交的最后一个 - 指向名称用来指向的地方。
每当你让你的Git与其他一些Git和其他仓库同步(无论何种程度)你的仓库,你的Git和他们的Git都有一个ID交换 - 具体来说就是哈希ID。 具体哪些ID取决于传输方向,以及您要求Git使用的任何分支名称。
远程跟踪分支实际上是您的Git存储的实体,与您的存储库相关联。 实际上,您的远程追踪分支的origin/master
是Git的地方,可以记住“Git在origin
说他的master
是什么,这是我们最后一次交谈的地方。”
所以,现在我们拿这七个项目,看看git fetch
是如何工作的。 例如,你可以运行git fetch origin
。 在这一点上,你的Git在origin
上调用Git并询问它的分支。 他们说master = 1234567
和branch = 89abcde
(尽管散列值全部是40个字符长,而不是这7个字符)。
你的Git可能已经有了这些提交对象。 如果是这样,我们快完成了! 如果没有,它会要求他们的Git发送这些提交对象,以及Git需要的其他对象来理解它们。 额外的对象是任何与这些提交一起提交的文件,以及那些提交的那些提交,以及父母的父母,等等,直到我们找到一些提交对象你确实有。 这将为您提供所有新的历史记录所需的所有提交和文件
一旦Git将所有对象安全地存储起来,Git就会使用新ID更新您的远程跟踪分支。 他们的Git刚刚告诉你他们的master
是1234567
,所以现在你的origin/master
设置为1234567
。 他们的branch
:它成为你的origin/branch
,你的Git保存89abcde
哈希。
如果你现在使用git checkout branch
,你的Git使用origin/branch
创建一个新的本地标签,指向89abcde
。 我们来画这个:
...--o--*--o--1 <-- master, origin/master
o--8 <-- branch, origin/branch
(我在这里缩短了1234567
到1
,而89abcde
到了8
,让他们更好地适应。)
为了让事情变得非常有趣,让我们在branch
上进行自己的新提交。 假设它被编号为aaaaaaa...
:
...--o--*--o--1 <-- master, origin/master
o--8 <-- origin/branch
A <-- branch
(我缩短了aaaaaaa...
只是A
)。
那么有趣的问题是,如果他们 - 你从中获取Git - 重新定义一些东西,会发生什么。 例如,假设他们将branch
分配给master
。 这复制了一些数量的提交。 现在你运行git fetch
,你的Git看到他们说branch = fedcba9
。 你的Git会检查你是否有这个对象; 如果没有,你得到它(及其文件)及其父文件(以及该提交文件)等等,直到我们达到某个共同点 - 事实上,这将是提交1234567
。
现在你有这个:
...--o--*--o--1 <-- master, origin/master
o--F <-- origin/branch
o--8--A <-- branch
在这里我已经写了F
来提交fedcba9
,现在一个origin/branch
指向。
如果你后来遇到这种情况,却没有意识到上游人员重新启动了他们的branch
(你的origin/branch
),你可能会看到这一点,并认为你必须在o--8--A
链中写下所有三个提交,因为他们'重新在你的branch
而不再在origin/branch
。 但是他们不在origin/branch
原因在于上游放弃了他们而转向新的副本。 要说这些新副本实际上是副本,而且你也应该放弃这些提交,这有点难。
1如果分支以“正常”,“预期”的方式增长,那么Git和他们的Git很容易确定你的Git需要从他们那里提交哪些内容:你的origin/master
告诉你上次看到master
,现在他们的master
进一步下降了一个更长的链。 你需要的提交正是那些在他们的master
身上发出的,来自你的origin/master
的提示。
如果分支机构以不太典型的方式进行洗牌,则有点困难。 在最常见的情况下,他们只需要使用散列ID枚举所有对象,直到您的Git告诉他们已达到您已有的对象。 浅层克隆的具体细节变得更加复杂。
这不是不可能的
这并不是不可能的,因为Git 2.0版本以后,现在有一些内置的工具可以让Git为你解决问题。 (具体来说, git merge-base --fork-point
,由git rebase --fork-point
调用,使用你的reflog作为origin/branch
来确定o--8
链过去是在origin/branch
上一点,这只适用于保留这些reflog条目的时间段,但默认为至少30天,让您有一个月的时间赶上,这是您的时间表中的30天:从您的时间起30天运行git fetch
,而不管上游做了多久的rebase。)
这真正归结为,如果你和你的上游事先同意某些特定的分支集合被重新分配,那么你可以安排在你每次执行这些操作时在你的存储库中做任何需要的事情。 然而,对于一个更典型的开发过程,你不会指望它们发生变形,如果它们不是 - 如果它们从不“放弃”你已发布的已发布的提交 - 那么就没有什么需要恢复的。
对已发布(远程)分支进行重新布局(或重写历史记录)的主要问题是,基于它们重新整合工作变得困难。 因此,如果这些遥控器仅仅是用于评论而没有提交,即使是合并,也会在通常不会遇到许多问题的情况下制作。 否则,合并和解决冲突可能很快成为主要的烦恼。
链接地址: http://www.djcxy.com/p/49041.html