Kai*_*ugo 3 git bitbucket git-rebase
在其他任何事情之前,我只是git分支的新手.我不知道每个功能分支都应该分支出来,master并且只使用与下一个功能分支有关的先决条件功能分支.
我有三个分支.master,feature-1以及feature-2所有推一到位桶储存库(启用跟踪问题).事情是提交M4并且M5是关键提交,所有分支应该在提交合并之前对其进行重新定义(任务git rebase)
M1 -- M2 -- M3 -- M4 -- M5 [master]
\ /
A1 --- A2 [feature-1]
\
B1 -- B2 -- B3 [feature-2]
Run Code Online (Sandbox Code Playgroud)
开发feature-2已经完成,现在需要合并到master.这是我为feature-2改变M4和M5提交而完成的任务的优先顺序feature-2.
git push - 发展 feature-2git checkout feature-2git rebase mastergit pull执行完这些步骤后,我注意到了git status.我不得不再次推所有提交(feature-2,M4,M5和冲突提交).好吧,我所要做的只是做git push并启动拉取请求,我已经完成了,对吧?但这会为问题跟踪器添加另一个git commit注释.
有没有办法来重订feature-2到master,而无需再次推的提交feature-2,M4并M5与git log应包含冲突提交.
更新
pok*_*oke 11
由于您已经推送了两个功能分支,因此根本不应该进行变基.强烈建议重新定位已发布的分支,因为它会破坏其他开发人员的存储库.原因是rebase只是对提交的完全重写.您正在重新设置的提交将被重新创建,内容已更改,最重要的是 - 使用不同的哈希值.这会导致新提交与旧提交不兼容,因此拥有旧提交的人最终会与替换它们的新提交冲突.
对此的正确解决方案是简单地合并更改.虽然这可能看起来不那么漂亮,但它是一种非破坏性的动作,其中没有改变现有的提交.所有这一切都是添加提交,这将导致推或拉时没有问题.
话虽这么说,你可以改变并仍然发布已更改的分支.但要做到这一点,你需要强制推动分支机构和其他开发人员拉动这些更改将需要将其分支重置为新版本.
将下面的一些评论包含在答案中:重要的是要理解在Git中,分支只是提交的指针.整个历史 - 没有分支 - 是一个很大的非循环图,其中提交只指向其父提交.所以从问题中拿出你的例子,这是历史,没有任何分支指针:
A -- B -- C -- D -- E
\ /
F --- G
\
H -- I -- J
Run Code Online (Sandbox Code Playgroud)
每个字母代表一个提交,一个连接到左侧的所有提交都是其父级.因此,例如F,父母是A,并且C是与父母B和G.的合并提交.
现在,如果我们向该可视化添加分支,那么我们只需添加指向某些提交的指针.它真的没什么别的(分支实际上只是一个包含提交哈希的文件):
master
?
A -- B -- C -- D -- E
\ /
F --- G ? feature-1
\
H -- I -- J
?
feature-2
Run Code Online (Sandbox Code Playgroud)
现在,想象一下我们对feature-2分支进行提交.我们将该提交添加到树中......
\
H -- I -- J -- K
?
feature-2
Run Code Online (Sandbox Code Playgroud)
...然后我们将分支指针向前移动:
\
H -- I -- J -- K
?
feature-2
Run Code Online (Sandbox Code Playgroud)
现在,为了理解推送过程中发生的事情,我们需要记住远程分支也只是分支,因此只是另一组指针.所以它看起来像这样:
\
H -- I -- J ----------- K
? ?
origin/feature-2 feature-2
Run Code Online (Sandbox Code Playgroud)
我想你可以想象现在推送过程中会发生什么:我们告诉远程存储库更新其指向的分支指针K.但是服务器只有J,所以我们需要给服务器一些东西来构造可访问的树K(所以其中的任何其他提交,以及这些提交的所有实际内容).但是当然我们不需要物理推动J,或者H甚至A(尽管那些技术上都是在feature-2分支上,因为你可以到达它们); Git足够聪明,可以找出实际丢失的对象(你可以看到Git在你开始推送时计算它).
因此,一旦我们将丢失的对象传输到远程存储库,我们就告诉远程存储库更新其feature-1指针,因此它也将指向K.如果成功,我们也会更新我们的远程分支(origin/feature-2)以指向它(只是为了同步).
现在,合并的情况确实如此.试想一下,我们合并master到feature-2(使用git merge master而上feature-2):
master
?
A -- B -- C -- D -- E -----
\ / \
F --- G ? feature-1 \
\ \
H -- I -- J -- K -- L
?
feature-2
Run Code Online (Sandbox Code Playgroud)
现在,如果我们想要推送feature-2,我们再次需要为远程存储库提供它没有的所有对象.既然我们现在正在合并提交,我们需要检查所有父母:所以如果服务器没有,K我们需要推送K; 但是,如果它没有E,我们将不得不推动E.当然,我们需要再次关注这些父母,以确保遥控器上存在所有对象.一旦完成,我们再次告诉遥控器更新分支指针.
总而言之:分支包含通过在非循环树中导航其提交的父级而以某种方式可访问的所有提交.但即使这意味着分支通常非常"大"(历史长度),Git只会将这些对象传输到它没有的远程存储库.因此,虽然合并可以向分支添加更多提交,但是如果远程已经从另一个分支知道它们,则不一定必须传输这些提交.
最后,关于变基的最后几个词:上面我们做git merge master了将master分支合并到feature-2.如果我们git rebase master改为,完整的树现在看起来像这样:
master feature-2
? ?
A -- B -- C -- D -- E -- H' -- I' -- J' -- K'
\ /
F --- G ? feature-1
\
H -- I -- J -- K
?
origin/feature-2
Run Code Online (Sandbox Code Playgroud)
正如你所看到的,也有新的提交H',I',J'和K'.这些是重写的提交,因此它们从E(在master指向rebase时指向)开始而不是G.由于我们重新定位,因此没有合并提交L.如上所述,原始提交仍然存在.只是没有指针指向它们; 所以他们"迷失"并最终将被垃圾收集.
那么现在推的问题是什么?远程分支仍指向原始分支K,但我们希望它指向K'现在.因此,我们开始为远程存储库提供所需的所有对象,就像之前一样.但是当我们告诉它设置更新分支指针时,它将拒绝这样做.其原因是,通过设置指针,K'那就得"回去的历史",而忽略提交的存在H来K.它不知道我们已经重新定义了那些,并且重写的和原始的之间也没有联系.因此,为了防止意外数据丢失,远程将拒绝更新分支指针.
现在,你可以强制推动分支.这将告诉远程存储库更新分支指针,即使这样做会抛出那些原始提交.所以你这样做,情况将如下所示:
origin/feature-2
master feature-2
? ?
A -- B -- C -- D -- E -- H' -- I' -- J' -- K'
\ /
F --- G ? feature-1
Run Code Online (Sandbox Code Playgroud)
到目前为止,一切都很顺利:你决定改变分支,你告诉远程存储库接受它而不用质疑它.但现在想象一下我想拉那个; 而我的分支仍指着I.因此,run pull与反向推送相同:遥控器为我提供了完成历史记录所需的所有对象,然后它告诉我在哪里设置分支指针.在那时,我的本地Git拒绝这样做,原因与远程存储库之前做到的相同.
通过之前的推动,我们知道我们想要替换原始提交; 但是我们没有那个,所以我们现在需要调查或询问我们是否应该只更换我们的本地分支,或者是否实际上有一些故障.如果我们在当地做了一些我们想要合并的工作,情况会变得更糟.
这些问题发生在每次获取原始提交的人身上.一般来说,你想完全避免这种混乱,所以规则是永远不要改变你已经发布的东西.只要没有其他人得到那些原始提交,你就可以进行改造,但是一旦不再是这种情况,对于所有参与者来说都会是一团糟.所以合并绝对是首选.