繁体   English   中英

是git rebase master总是跟着git pull --rebase然后是git push --force吗?

[英]Is `git rebase master` always followed by `git pull --rebase` and then `git push --force`?

我根据分支重新调整了主分支:

my-branch>> git rebase origin/master

然后我看到状态:

➜  supporTool git:(my-branch) git status
On branch my-branch
Your branch and 'origin/my-branch' have diverged,
and have 12 and 11 different commits each, respectively.
  (use "git pull" to merge the remote branch into yours)
nothing to commit, working tree clean

我了解提交顺序已在本地与远程(我的分支)之间进行了更改

那么这是否意味着我总是必须执行git pull --rebase然后使用git push --force来使远程提交顺序相同?

1)如果我是该分支机构中的唯一开发人员并且我领先于远程服务器,为什么我需要先撤退,然后再依赖master?

2)如果在我本地进行了基准调整之后但在使用git push --force进行git push --force之前,有人将某些内容推送到了遥控器,会发生什么情况?

更改的不是提交顺序 正是提交本身发生了变化。

要真正理解所有这些,您需要了解三件事:

  • 分支名称仅指向提交。 具体来说,任何一个分支名称(例如my-branch )都指向单个提交,Git称之为“分支的尖端 ”或tip commit
  • 分支(我们人类认为是分支)是提交 该链接是通过向后箭头在内部实现的:每个提交都向后指向先前的提交。 使用这些内部的向后箭头收集一连串提交,您将得到一个由分支名称指向的分支提示 ,其中包含一系列提交:

     ...<- o <- o <- o <- o <- o <-- branch1 \\ o <- o <- o <-o <-- branch2 

    (内部箭头全部向后退的事实通常并不十分重要,因此我倾向于将其绘制为:

     o--o--o <-- master 

    更具可读性。)

  • git rebase工作方式是复制提交。 复制提交后,它将更改一个(并且只有一个!)分支名称以指向新的,最尖端的提交。

例如,假设开始条件如下:

...--o--A--B            <-- master, origin/master
         \
          C--D--E--F    <-- my-branch, origin/my-branch

在这里,我们在您的存储库中拥有指向提交B master (一个常规的普通本地分支名称),我在这里给了它们所有一个字母的名称,比40个字符长的散列还更加残酷 -以及origin/master (所谓的远程跟踪分支名称)。 远程跟踪分支名称origin/master也指向提交B

提交CF在分支my-branch ,但不在分支master (这给了您4次此类提交,而git status显示了11次,因此简化了该图。)您的远程跟踪分支名称origin/my-branch也指向了F

但是随后您运行了git rebase master 这将复制它需要复制的每个提交(在这种情况下为CDEF Git的通过枚举所有对你当前分支的提交获得这个名单my-branch ,使上分支master 请注意, 这两个分支都包含commit A和任何更早的提交,因此这些是git rebase从列表中抛出的my-branch可以到达的提交。 这种可到达性的想法取决于那些向后的箭头。 这就是箭头指向后方的事实真正重要的地方。

因此,让我们看看如果git rebase成功复制了这些提交会发生什么。 我们得到:

...--o--A--B              <-- master, origin/master
         \  \
          \  C'-D'-E'-F'  <-- my-branch
           \
            C--D--E--F    <-- origin/my-branch

提交C (原始提交)与提交C' (副本)之间的区别是C'构建在B之上。 它在AB之间有什么不同,以及从AC的变化。 1同样, D'建立在C'之上,依此类推。

重新设置完成后,它将当前分支名称移至新的最尖端提交F' 但是它没有移动任何其他名称。 特别是,它不动origin/my-branch哪位使得很多的意义,因为origin/my-branch是为了记住的Git中看到的origin的混帐原产的my-branch ,当你的Git最后谈通过git fetchgit push与他们的Git。

请记住,所有这些操作已在您的存储库中进行。 据推测, origin存储库都保持不变。 这种“大概” 可能会给我们带来麻烦,而这正是“带租约”之类的东西出现的地方,但是在我们深入研究之前,请再看一下上面的图表。 计算在my-branch但不在origin/my-branch上的提交数。 然后,看看有多少是提交关于origin/my-branch ,但不是my-branch

请记住,为了进行计数,我们使用可达性 :我们向后跟随内部箭头。 提交A两个分支上的第一个。 更新后的my-branch包括A--B--C'-D'-E'-F' :它具有A 旧的origin/my-branch包括ACDEF :它也有A 因此,我们不再指望打A ,这意味着my-branch现在有五个(不是四个!)提交,而origin/my-branch缺少,而origin/my-branch有四个提交, my-branch缺少。 (将其与您的git status进行比较:您在my-branch上先进行了11次提交,而这些提交又不在master ,并且以相同的方式又获得了1次提交,因此您有了12-vs-11。)

当然,所有有关origin/my-branch提交都是我们在重新定基时复制的。 my-branch上的额外对象只是我们在master :commit B


1如果从变化AC最终替换 ,而不是扩增,从变化AB ,然后与相关联的 C'树相匹配C 但是,父ID仍然不同: C的父是A ,而C'的父是B 仅此一项就足以使两个提交不同:它们将具有不同的40个字符的SHA-1哈希。


用力或不用力推动,并可能租赁

至此,当您有了这个新图时,您决定使用git push将部分或全部新提交复制到origin上的存储库中。

git push的最基本部分(它始终会执行的部分)包括通过某个URL与另一个Git联系,并接受您的一些提交并将其移交给另一个Git。 该URL通常来自远程名称origin 首先,遥控器只是完整URL的简称。 因此,剩下的有趣部分,实际上是更有趣的部分,是此移交过程。

要移交一些提交,通常通过给Git一个分支名称(例如my-branch ),告诉Git查找特定的提示提交。 要告诉您的Git告诉其他 Git的内容,您还给Git另一个分支名称。 您用冒号分隔这两个分支名称,如下所示:

git push origin my-branch:my-branch

左侧的名称是您如何告诉Git推动哪个尖端提交的。 右边的名称是如何告诉Git您希望 Git在末端进行更改的分支。

当然,通常它们是相同的,因此您的Git可以让您省略重复的重复重复:my-branch (这句话是由冗余部给您带来的。)因此,您只需运行:

git push origin my-branch

或者,对于新版(2.0或更高版本)的Git,则2更简单:

git push

(这可以从您当前分支的上游设置中找出正确的内容)。 但要记住重要的是,这“是指” git push origin my-branch:my-branch ,即用 my-branch找到尖端提交,有的Git的手自己的Git是承诺,并让您的Git问他们的Git来设置他们的 my-branch (这是为什么以及为什么可以在存储库和存储库中使用不同的分支名称的原因,这不是您经常需要的,但是当您确实需要它时,事实证明这非常方便。)

当您的Git与他们的Git联系时,您的Git首先会交出这些提示提交ID。 (您一次可以推送一个以上的分支,因此“提示ID”是复数。)然后,他们的Git会检查它们是否已经提交(通过其唯一的40个字符的哈希ID);如果没有,则有Git发送提交的实际内容,包括提交的父ID。 他们的Git要求根据需要提供更多的对象内容。 对话一直进行到您的Git给Git赋予他们可以识别的东西:他们已经拥有的哈希ID,例如在我们的图形中提交B

至此,您的Git已经交出了您必须提供给他们的所有提交(加上任何其他内部Git对象(树和Blob),所有这些都有自己的唯一哈希ID),现在他们的Git开始评估过程。


2这里的关键项是push.default的配置设置,在Git 2.0及更高版本中,如果不进行更改,它就是simple 在早期版本的Git中,默认情况下将其设置为matching


强迫还是不强迫

Git是基于添加新内容的想法构建的。

具体来说,每当您进行新的提交时,只需将其添加到现有的提交链中即可。 无论您现在在哪个分支上,都添加一个新提交,并将分支标签向前移动以容纳它:

before:
...--C--D--E   <-- my-branch

after:
...--C--D--E--F   <-- my-branch

因此,Git 希望分支标签像这样移动:添加新的提交。

因此,对于没有--force的普通git push ,如果结果仅添加了新的commit ,则Git将允许该push。 推送的复杂程度无关紧要:

before:
...--o--o--o    <-- branch

after:       o-------o
            /         \
...--o--o--o--o--o--o--o---o   <-- branch
               \          /
                o--o--o--o

关键问题是:它仅添加还是删除某些内容? 上面是允许的,但下面是不允许的:

before:
...--o--o--o--o    <-- branch

after:
...--o--o--x--x
         \
          o--o    <-- branch

分支标签仅指向一个提交,因此,如果Git允许进行此推送,则顶行(现在标记为x--x )的最后两个提交将被“忘记”。

因此,现在回头看看您的基准图。 如果您按下新的my-branch ,则所有“ 原始提交”(您复制的副本,然后将其放置在master提示之后)都将被“遗忘”。

当然,这正是您想要的。 您放弃了原件,转而使用新副本。 您也希望origin也放弃它们。

这是--force出现的地方: git push --force设置“ force flag”,您的Git传递给另一个Git。 他们的Git根本不需要遵守强制标志,但是如果这样做,它只会告诉他们:“是的,继续前进,忘记一些提交。”

强制标志强大了

现在, 只要上游origin Git存储库在这里实际上没有更改,使用--force或等效的方法就可以了。 您复制了CDEF提交链,并且正在推送副本,因此,如果它们“忘记”了原始CDEF ,那就很好了。

如果my-branch确实对您公开,那么我们就完成了。 我们无需担心其他可能性。 但是,如果不是这样呢? 如果其他可能会说“哦,哇, my-branch有很多东西,让我添加commit G并推送”,然后他们这样做了,那么Git origin现在拥有CDEFG链:

...--o--A--B                <-- master
         \  \
          \  C'-D'-E'-F'    [proposed replacement]
           \
            C--D--E--F--G   <-- my-branch

注意缺乏origin/在这里:这是信息库的绘图origin ; 因此它本身没有名称origin/ 这也是为什么F'是拟议替代my-branch

如果他们的Git接受了建议的替换,他们的Git将失去提交G 没有提交G ,因此您没有将其复制到新的G'

强制租赁

--force-with-lease选项背后的想法实际上是从多处理计算机的“比较和交换”或CAS指令中获取的,在内部,它实际上称为“ cas”选项。 它的工作方式是您的Git告诉他们的Git:“我相信my-branch指向F ,并且我希望您将其强制更改为F' 。”

实际上,如果他们的Git的my-branch 确实指向F ,则--force-with-lease--force-with-lease推推成功,替换他们的my-branch ,使其现在指向您给他们的F'

另一方面,如果他们的Git的my-branch更新了,则--force-with-lease推送失败。 您现在可以运行git fetch带来了什么新的提交,并找出他们my-branch标签实际上了点。 请注意,您的Git得到它在那里“他们”的分支点(“我相信你的观点my-branch是...”) origin/my-branch 有一些带有租赁的强制变式,可以让您更好地控制它,但是您不太可能需要它们。

这就是它的全部功能:它只是将强大的“力量”修改为稍弱的“力量”,但前提是……。 但是,如果您尝试与其他人合作,则在像这样重新建立基础或重写并通过某个中央服务器推送的分支上工作,“强制租用”可能会很有帮助。

这是基本规则: 始终在变基之前先拉。

在你衍合你的工作,对集成分支的顶部(无论是masterdevelop ),你需要确保你有最新的承诺,人们穿上了集成分支。 所以这是你应该做的:

  1. 保存您的工作。 提交要保存在功能分支( my-branch )中的所有内容,并确保git status报告没有未提交的更改。

  2. 检出集成分支(例如git checkout master )和git pull以获得最新版本。 此时,您的本地分支master与远程跟踪分支origin/master完全相同。

  3. git checkout -出您的功能分支(快捷方式: git checkout -以“跳回到上一个分支”)。

  4. git rebase mastergit rebase master将您的功能分支置于最新的master提交之上。

  5. 发布: git push -u --force-with-lease origin my-branch:my-branch (第一次,为您的功能分支正确设置跟踪)和git push --force-with-lease (每隔一次) 。

这一系列步骤将确保您的功能分支始终清晰地位于master节点之上,并且在需要时易于合并。

git rebase不适用于公共分支(如果遵循功能或修补程序工作流,则为master或development分支)

  1. 因为重新定基会改变历史
  2. 您将在哪里准确调整基准?

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM