Tay*_*ley 4 git version-control
我在一个大型团队中工作,最近我们从TFS转到了Git。
我们已经有一些工作被覆盖的问题,并且认为这git pull --rebase是我们想要做的,因此与其直接合并,不如直接合并,它会在本地分支上重播拉出的提交。
但是,我们一直在阅读黄金法则,但仍不确定是否可以使用rebase。
我们的理解是,当您使用重定基向远程分支进行推送时,黄金法则就会发挥作用。来自https://medium.freecodecamp.org/git-rebase-and-the-golden-rule-explained-70715eccc372
您可能遇到了该规则,或者用了不同的措词。对于那些没有的人,这个规则很简单。永不,永不,永不重新建立共享分支的基础。共享分支是指位于远程存储库中的分支,您团队中的其他人可以拉。
所以这很糟糕:
git pull --rebase与远程同步git pull --rebase与远程同步没关系:
pull origin support --rebasepull origin feature-a --rebase--rebase和合并到master从本质上讲,我们不想--rebase在每次推送时都使用它,对吗?
实际上,没有任何一种简单,通用的规则适用于每种情况的每个人。“从不让任何事情变基”是一个简单的规则,确实可以,但是并不适合所有人。有一个更复杂的规则始终有效:只要拥有原始提交的每个人都愿意切换到新副本,Rebase就可以了。
您以及与您一起工作的每个人所需要知道的是,Git对待承诺非常宝贵,并且努力工作以确保您永远不会丢失任何东西。但是分支名称不那么重要。分支名称充当指向特定提交的指针。分支名称使Git 可以找到提交,但重要的是提交。使用时git rebase,您告诉Git 将一些现有(旧)提交复制到一些新提交。新的将获得新的“真实名称”(请参见下文)。有很多理由来老提交复制到新的,包括事实,没有提交能不断改变。制作完成后,它(通常)是永久的且不可更改。
您可以做一件事使提交似乎消失:您可以完全停止使用它。一旦没有人使用了很长时间,并且它没有名称,也找不到它,它的确会消失。但是提交是很宝贵的,因此需要一段时间。(这背后的实际机制是reflog,您可以在其他地方阅读更多信息。)
现在,提交具有丑陋的哈希ID作为其“真实名称”。这些名称很难使用:例如,d35688db19c9ea97e9e2ce751dc7b47aee21636bto 的相对关系是4d7268b888d7bb6d675340ec676e4239739d0f6d什么?谁知道,它们只是个丑陋的数字,看似随机。这些是Git真正关心的名称,但不是我们仅人类使用的名称:我们使用诸如master或的名称v2.14。
这就是分支(和标记)名称的来源。Git让我们选择一个分支名称(如)master用于识别d35688db19c9ea97e9e2ce751dc7b47aee21636b一段时间。最终,我们向master分支添加了一个新的提交,并且新的提交获得了一个新的,又大又丑陋的真实名称-Git 更改了master将其映射到新名称的名称。这是一个简单的示例,在一个仅以三个提交开始的存储库中:
A <-B <-C <--master
Run Code Online (Sandbox Code Playgroud)
名称master“指向”提交C(无论如何,C代表着c0193fa8...某个大的丑陋哈希ID)。分支名称保留当前的分支提示哈希ID,作为Git自动执行的服务。我们之所以说“指向”,是因为Git将在名称之后跟随ID以获取提交。Commit C依次指向(存储)commit的哈希ID B,commit B指向commit A。由于commit A是有史以来的第一个提交,因此它不会指向任何地方:它是一个根commit。这就是Git知道在哪里停止的方式。
要立即添加新的提交,请在存储库工作树中工作,编辑文件git add,和git commit。Git现在编写一个新的提交(我们称之为它)D,它指向C,并更改名称master以指向您的最新提交:
A--B--C--D <-- master
Run Code Online (Sandbox Code Playgroud)
(内部箭头始终指向后方,较新的对象则指向较旧的对象,因此没有真正的必要继续绘制它们。很难在更复杂的图中绘制它们。)
只要您做的唯一一件事就是添加新的提交,这就会很好地工作,因为Git是围绕添加新的提交的整个想法而构建的。如在您的示例中,如果Charles和Taylor都向某个分支添加了新的提交,然后其中一个或任何第三方去获取您的新提交,则Git能够添加您的提交及其提交一起。如果添加简单明了:
A--B--C--D--E--F <-- master
Run Code Online (Sandbox Code Playgroud)
然后名称master会一直向右移动,因为新提交会向右添加。如果添加是并行进行的,则Git必须添加“合并提交”以记录两个较新的提交。举例来说,说查尔斯(Charles)E和泰勒(Taylor)添加F,两者的工作速度都不比另一个快,所以我们得到了这一点:
E
/
A--B--C--D
\
F
Run Code Online (Sandbox Code Playgroud)
现在我们有一个问题:master只能指向一个提交。如果我们选择E,我们将无法处理F。如果我们选择F,我们就会输E。合并工作流程通过在E和之上添加F一个新的提交G来处理这点,该提交指向这两个 E 和 F:
E
/ \
A--B--C--D G <-- master
\ /
F
Run Code Online (Sandbox Code Playgroud)
这很好用,但是随着时间的流逝,您会得到很多合并气泡,这些气泡仅用于记录人们并行工作的事实。尽管这是该项目的正确,真实的历史记录,但它充满了无用的细节。这是重新部署的地方。让我们看一下它是如何工作的。
Rebase 复制一些提交,然后,至少在理想情况下,您和其他拥有原始提交的所有人都停止使用原始提交。相反,您和其他所有人都使用了闪亮的新提交。
例如,假设我们有:
E <-- origin/master
/
A--B--C--D
\
F <-- master
Run Code Online (Sandbox Code Playgroud)
对于提交的名称E是origin/master,对于提交的名称F是master,在泰勒的存储库。(泰勒,记住,是制造的F。)
泰勒是唯一有承诺的程序员F。
Taylor现在可以选择(这是一个选择,而不是一个要求),可以将提交复制F到更新的,更明亮的版本,F'至少有一个区别:F'将不指向D,而是指向E。
(泰勒的新人F'至少应该有另外一个区别:他也应该承担查尔斯所做的任何工作E。)
如果泰勒要跑:
git rebase origin/master
Run Code Online (Sandbox Code Playgroud)
他的Git会找到要复制的提交列表,该列表仅列出commit F。然后它将复制F到F'build atop E:
F' <-- [temporary]
/
E <-- origin/master
/
A--B--C--D
\
F <-- master
Run Code Online (Sandbox Code Playgroud)
现在到了棘手的部分:Taylor的Git将移动 Taylor的master以指向复制的提交,而放弃原始提交F。结果是相同的图,但名称已移动:
F' <-- master
/
E <-- origin/master
/
A--B--C--D
\
F [abandoned]
Run Code Online (Sandbox Code Playgroud)
由于F现在已废弃-它没有明显的名称-Git停止显示它。提交仍然存在,因为提交是(大多数)永久性的,并且(完全)不变,但是最终它将消失,因为泰勒是唯一拥有提交的人,并且他更改了master名字,因此master不再找到它。从开始master,他的Git先找到F',然后E,然后D,然后C等等,再也看不到原始内容F了。
如果我们F完全停止绘制,而是停止git push新提交,F'以便其他人现在都可以看到它,那么我们得到:
A--B--C--D--E--F' <-- master, origin/master
Run Code Online (Sandbox Code Playgroud)
(我们已经更新了origin/master,因为我们推F'所以现在origin的master还分F'和泰勒的Git的知道这一点,已经调整了origin/master相应的)。
但是现在假设泰勒F在他重新设基之前就开始努力。假设他F在branch上feature而不是on上进行创建master,这样他在重新设置基准之前就拥有了:
E <-- origin/master
/
A--B--C--D <-- master
\
F <-- feature
Run Code Online (Sandbox Code Playgroud)
通过F继续feature操作origin,Taylor origin在另一个Git中创建了一个分支。泰勒(Taylor)自己的Git通过改名来记住这一点,该名字origin/feature也指向F:
E <-- origin/master
/
A--B--C--D <-- master
\
F <-- feature, origin/feature
Run Code Online (Sandbox Code Playgroud)
现在,如果泰勒做了同样的底垫和以前一样,他得到了相同的F'和以前一样,但这次是一个名字F:
F' <-- feature
/
E <-- origin/master
/
A--B--C--D <-- master
\
F <-- origin/feature
Run Code Online (Sandbox Code Playgroud)
由于位于的其他Git存储库origin具有feature指向的名称F,因此其他任何人(例如Charles)都可以选择commit F。
现在,请记住,提交非常宝贵。分支名称可以更改,但是提交是永久且不变的,因此,如果Charles进行了更改,而FTaylor进行了Taylor的推送F',则Charles仍然具有commit F。
如果查尔斯知道自己在做什么,并且知道什么泰勒做太多,查尔斯可以有自己的Git忘记F,通过移动任何分支姓名(或名称)给他访问F。一旦Charles放弃了F,Taylor放弃了F,而其他所有人都F放弃了F,那么-直到那时- F最终才能真正走开。
因此,使用的规则git rebase实际上是这样的:只要拥有原始文档的每个人都愿意切换到闪亮的新提交,Rebase就可以了。 如果您(或泰勒)是唯一拥有原件的人,那会容易得多。如果您已将这些原始提交复制到其他存储库中,那么会有更多的人不得不告诉他们的Git切换。
合并更容易,因为合并意味着“添加合并的提交”。您可能需要在合并动作(“谓词合并”)中做一些工作,但是一旦完成,它就完成了;Git进行了一个合并提交,该提交不仅记录了一个先前的提交,而且还记录了两个提交,并且新的提交被添加到其中,每个Git都会自动知道如何处理。
但这会使您的历史变得无用。调整基础是更多的工作-可能复制许多提交,并让拥有旧提交的每个人都切换到新提交。在此过程中有更多出错的机会。更糟糕的是,每个复制步骤实际上都是另一个动词合并。但是,一旦完成所有操作,您将拥有光鲜,外观清晰的历史,就好像您刚开始时就确切知道要去的地方,并且从未做过任何错误的举动。看到发生的事情要容易得多,或者更确切地说,是您希望人们认为发生的事情,这与实际发生的事情不同。
由您(和每个人)决定要做什么。有时重新定标既简单又好。有时候,这是艰难而美好的。有时候,这太难了,或者不是一个好主意,因为它掩盖了确实或可能会造成影响的太多真实历史。您必须立即做出判断:将来会不会很重要?如果现在改头换面,明天,下个月或明年的工作会更容易吗?还是会更困难?
黄金法则说的是,你不能改变其他人都会看到的提交历史,其他人的工作可能基于这些历史,因为这将改变每个人已经在工作的所有共享代码,并且可能真的会把每个人都搞砸。 。您的团队的“始终变基”规则是指仅对本地未推送的提交进行变基,这样当您推送时,您就永远不会推送合并提交。这是两件截然不同的事情,但却是完全一致的。
请记住,大部分 git 文档都是针对高度分布式的开源模型,而在企业环境中,通常有一个中央存储库。当事物非常分散时,共享内容和上游内容的概念比在公司环境中更加流动。
有许多网络资源解释了 rebase 或不 rebase 的原因(并且经常在双方激烈争论)。你最好去网上冲浪,而不是我在这里总结一下。但我要说的是,对于一个非常大的团队,当一天可能有一百次推送到一个分支时,如果允许/鼓励合并,历史记录可能会变得笨拙且难以理解,因此总是需要变基可能很有意义。
| 归档时间: |
|
| 查看次数: |
899 次 |
| 最近记录: |