Git 子分支删除父分支的更新
Git child branch removes updates of parent branch
好的,我觉得 git 有一个奇怪的问题( 使用 BitBucket)。
我有以下(例子)
development-branch
child-branch
现在在child-branch
我做了很多返工,但在同一时期我也修复了一些bug,修复了来自另一个子分支的pull requests在development-branch
。现在,当我从 child-branch
向 development-branch
发出拉取请求时,它想要删除对development-branch
。但是当我想在 child-branch
中合并 development-branch
时,对 development-branch
没有合并到child-branch
.
我是不是在流程中做错了什么?有没有办法在不手动检查每个文件的差异的情况下解决这个问题?
TL;DR
分支名称与您想象的不同。
鉴于你想做什么,你可能想要 rebase-oriented work-flow,如 。请记住,rebase 意味着 将一些提交复制到 new-and-improved 提交,然后移动分支名称以使用 new-and-improved 提交,放弃现在的 old-and-lousy 提交。这意味着 每个使用重新设置分支的人 必须准备好放弃旧的提交。
长
Branches,或更准确地说,分支 names,没有 parent/child 关系。分支名称只是标签,附加到(或指向)特定的 commits.
提交,但是,确实有parent/child关系。特别是,用于进行 new 提交的命令序列如下所示:
git checkout somebranch
[edit various files]
git add file1.ext file2.ext
git commit
git checkout
步骤意味着 select 命名分支,通过这样做,select 提交分支名称 selects。此 复制该提交的内容 — 在 Git 中,它是您所有文件的完整快照,存储在特殊的 read-only、Git-only,压缩格式——进入 index aka staging area 和 work-tree 或 工作树.1
编辑步骤当然适用于 work-tree 中的那些文件。
git add
步骤将更新后的 work-tree 文件——您已进行更改的地方——复制回 索引 / staging-area,最后一步 git commit
从索引中的副本创建一个新快照,其中包含文件的 frozen-for-all-time 副本。您没有用git add
覆盖的任何文件仍然与您之前git checkout
的文件相同,因此新快照包含所有没有改变,任何更新的文件都改变了,视情况而定。
但是,新提交是 previously-labeled 提交的 child。之前的提交没有改变——没有任何东西,甚至 Git 本身,一旦提交就可以改变它——但它现在有一个 child 提交。刚刚完成的 child 提交记录了其 parent 提交的原始哈希 ID。所以链接只有一种方式,向后,从 child 到 parent。由于 child 是一个新的提交,它获得了一个新的、唯一的哈希 ID。
git commit
最后的关键步骤是 将新的提交哈希 ID 写入分支名称 。也就是说,标签不再指向之前的 parent 提交,现在指向 child 提交。
这意味着我们从一系列提交开始,每个提交都有一个唯一(又大又丑)的哈希 ID,我将在这里用一个大写字母替换它:
... <-F <-G <-H <-- develop
名称 develop
当前 select 提交 H
.
为此,您添加一个新名称 feature
, 也 select 提交 H
:
...--F--G--H <-- develop, feature
我们现在需要一种方法来记住您正在使用的分支名称。为此,我们将特殊名称 HEAD
附加到一个分支。如果您 git checkout develop
,我们将姓名 HEAD
附加到 develop
:
...--F--G--H <-- develop (HEAD), feature
如果你git checkout feature
,我们得到:
...--F--G--H <-- develop, feature (HEAD)
无论哪种方式,您都使用提交H
,但您是通过附加到/指向提交[=30=的两个名称之一来完成的]. (同时提交 H
指向提交 G
,因为它的 parent,并且 G
指向 F
,依此类推。)
现在您修改 work-tree 中的一些文件,git add
将它们复制回索引 / staging-area,git commit
进行新提交 I
。如果您使用 feature
:
,会发生以下情况
...--F--G--H <-- develop
\
I <-- feature (HEAD)
名称 HEAD
仍附加到名称 feature
,但 feature
现在指向新提交 I
。新提交 I
指向提交 H
——feature
刚才指向的位置——因此 feature
包含通过并包括 I
和 develop
包含通过并包括 H
的提交。请注意大多数提交仍然在两个分支上。
如果您将提交添加到 develop
,它们只会添加到 develop
:
...--F--G--H--K <-- develop (HEAD)
\
I--J <-- feature
名称 develop
和 feature
之间没有 parent/child 关系。它们只是 标签 ,标识特定的提交。
这就是 Git 中分支和标签名称的工作方式:它们标识提交。分支名和标签名的区别是:
分支名称 随时间移动。事实上,它们会自动移动!标记名称永远不能移动。2
分支名称本地于一个特定的存储库。其他存储库可以看到它们,但是当您从 获得提交 时,您的 Git 将复制他们的 branch 名称,例如 master
,到你的remote-tracking名字,比如origin/master
。标签名称很多ore global:如果他们——无论他们是谁——有一个 v1.2
,并且你从他们那里得到你的提交,你的 Git 可能会创建你的 v1.2
来匹配。3
当然,分支名称是分支名称,标签名称是标签名称: 有一堆较小的 knock-on 效果,但以上两点是重要的区别。
1work-tree 包含实际文件,采用常规普通格式,您和计算机上的所有其他 non-Git 程序都可以使用。 Git 未使用这些实际文件。它使用压缩的 read-only、Git-only 提交文件,并使用索引/临时区域进行 new 提交,如上所示。
2可以强制移动标签,也可以删除标签然后重新创建标签。但是,其他软件可能希望标签 not 移动。例如,Go 模块系统可以缓存标签的哈希 ID,如果您稍后移动标签,不会 更新。移动标签通常是一个坏主意:如果可以的话,请避免它。
3这一切都在你的掌控之中,这里的默认规则有些复杂,但如果你不做任何特殊的事情,它们就等于你得到了他们的标签。
使用变基
假设你有这个:
...--F--G--H--K <-- develop
\
I--J <-- feature (HEAD)
但你想要这个:
?--? <-- feature (HEAD)
/
...--F--G--H--K <-- develop
您不能更改现有提交。没有什么可以。现有提交 I
和 J
卡在原处。但是你 可以 复制 现有的提交到 new-and-improved 版本。特别是,您可以将提交 I
复制到一个新的改进的提交 - 我们将其称为 I'
以指示它是 I
的副本,尽管在 Git 中它只是得到一些又大又丑的 random-looking 哈希 ID,它与 I
的哈希 ID 没有任何关系。 I
和 I'
之间的区别是:
I
的parent是H
,但是I'
的parent会是K
.
I
的快照和I'
的快照可能不匹配,但是通过比较H
得到的差异 -vs-I
将是 same 作为 difference 你通过比较 K
-vs-I'
.
将I
复制到I'
后,我们希望Git将J
复制到J'
。结果如下所示:
I'-J'
/
...--F--G--H--K <-- develop
\
I--J
请注意,我已经删除了标签 feature
并完全省略了名称 HEAD
,此时,我们已准备好移动 feature
并制作 HEAD
附新,移feature
。执行这个 "build new commits" 的过程实际上使用了 Git 所谓的 分离的 HEAD 所以现在,实际情况是:
I'-J' <-- HEAD
/
...--F--G--H--K <-- develop
\
I--J <-- feature
请注意,特殊名称 HEAD
没有 附加到任何分支名称。这就是为什么这是一个 分离的 HEAD。同时名称 feature
仍然 select 现有提交 J
。
git rebase
命令的最后一幕,即完成变基,是将名称 feature
从其现有提交 (J
) 中剥离,并使其指向与HEAD
指向 (J'
):
I'-J' <-- feature, HEAD
/
...--F--G--H--K <-- develop
\
I--J [abandoned]
移动标签后,将 re-attaches HEAD
变基到(移动的)标签,使一切看起来都像我们想要的那样。而且,由于 Git finds 提交的方式是从 labels 开始并向后工作,我们不能 see 提交 I
和 J
了。我们看到的是:
I'-J' <-- feature (HEAD)
/
...--F--G--H--K <-- develop
如果我们不记得提交 I
和 J
的哈希 ID,我们会认为 Git 以某种方式移动了旧提交。它没有——旧的提交仍然存在,每个拥有存储库克隆和旧提交的人,仍然有旧的提交。不过我们只看到我们的新。
正如 Nikos C. 指出的那样,如果您有一个使用现有提交 I-J
的活动拉取请求,您可能需要使用 git push --force
来更新拉取请求。你必须得到任何 Git 存储库持有他们的提交副本 I
和 J
放弃他们的提交支持新的和改进的 I'-J'
链。说服其他 Git 从旧提交切换到新提交的精确方法取决于 在 其他 Git 设置的控件。 Git集线器允许简单的 git push --force
来完成任务。
好的,我觉得 git 有一个奇怪的问题( 使用 BitBucket)。
我有以下(例子)
development-branch
child-branch
现在在child-branch
我做了很多返工,但在同一时期我也修复了一些bug,修复了来自另一个子分支的pull requests在development-branch
。现在,当我从 child-branch
向 development-branch
发出拉取请求时,它想要删除对development-branch
。但是当我想在 child-branch
中合并 development-branch
时,对 development-branch
没有合并到child-branch
.
我是不是在流程中做错了什么?有没有办法在不手动检查每个文件的差异的情况下解决这个问题?
TL;DR
分支名称与您想象的不同。
鉴于你想做什么,你可能想要 rebase-oriented work-flow,如
长
Branches,或更准确地说,分支 names,没有 parent/child 关系。分支名称只是标签,附加到(或指向)特定的 commits.
提交,但是,确实有parent/child关系。特别是,用于进行 new 提交的命令序列如下所示:
git checkout somebranch
[edit various files]
git add file1.ext file2.ext
git commit
git checkout
步骤意味着 select 命名分支,通过这样做,select 提交分支名称 selects。此 复制该提交的内容 — 在 Git 中,它是您所有文件的完整快照,存储在特殊的 read-only、Git-only,压缩格式——进入 index aka staging area 和 work-tree 或 工作树.1
编辑步骤当然适用于 work-tree 中的那些文件。
git add
步骤将更新后的 work-tree 文件——您已进行更改的地方——复制回 索引 / staging-area,最后一步 git commit
从索引中的副本创建一个新快照,其中包含文件的 frozen-for-all-time 副本。您没有用git add
覆盖的任何文件仍然与您之前git checkout
的文件相同,因此新快照包含所有没有改变,任何更新的文件都改变了,视情况而定。
但是,新提交是 previously-labeled 提交的 child。之前的提交没有改变——没有任何东西,甚至 Git 本身,一旦提交就可以改变它——但它现在有一个 child 提交。刚刚完成的 child 提交记录了其 parent 提交的原始哈希 ID。所以链接只有一种方式,向后,从 child 到 parent。由于 child 是一个新的提交,它获得了一个新的、唯一的哈希 ID。
git commit
最后的关键步骤是 将新的提交哈希 ID 写入分支名称 。也就是说,标签不再指向之前的 parent 提交,现在指向 child 提交。
这意味着我们从一系列提交开始,每个提交都有一个唯一(又大又丑)的哈希 ID,我将在这里用一个大写字母替换它:
... <-F <-G <-H <-- develop
名称 develop
当前 select 提交 H
.
为此,您添加一个新名称 feature
, 也 select 提交 H
:
...--F--G--H <-- develop, feature
我们现在需要一种方法来记住您正在使用的分支名称。为此,我们将特殊名称 HEAD
附加到一个分支。如果您 git checkout develop
,我们将姓名 HEAD
附加到 develop
:
...--F--G--H <-- develop (HEAD), feature
如果你git checkout feature
,我们得到:
...--F--G--H <-- develop, feature (HEAD)
无论哪种方式,您都使用提交H
,但您是通过附加到/指向提交[=30=的两个名称之一来完成的]. (同时提交 H
指向提交 G
,因为它的 parent,并且 G
指向 F
,依此类推。)
现在您修改 work-tree 中的一些文件,git add
将它们复制回索引 / staging-area,git commit
进行新提交 I
。如果您使用 feature
:
...--F--G--H <-- develop
\
I <-- feature (HEAD)
名称 HEAD
仍附加到名称 feature
,但 feature
现在指向新提交 I
。新提交 I
指向提交 H
——feature
刚才指向的位置——因此 feature
包含通过并包括 I
和 develop
包含通过并包括 H
的提交。请注意大多数提交仍然在两个分支上。
如果您将提交添加到 develop
,它们只会添加到 develop
:
...--F--G--H--K <-- develop (HEAD)
\
I--J <-- feature
名称 develop
和 feature
之间没有 parent/child 关系。它们只是 标签 ,标识特定的提交。
这就是 Git 中分支和标签名称的工作方式:它们标识提交。分支名和标签名的区别是:
分支名称 随时间移动。事实上,它们会自动移动!标记名称永远不能移动。2
分支名称本地于一个特定的存储库。其他存储库可以看到它们,但是当您从 获得提交 时,您的 Git 将复制他们的 branch 名称,例如
master
,到你的remote-tracking名字,比如origin/master
。标签名称很多ore global:如果他们——无论他们是谁——有一个v1.2
,并且你从他们那里得到你的提交,你的 Git 可能会创建你的v1.2
来匹配。3当然,分支名称是分支名称,标签名称是标签名称: 有一堆较小的 knock-on 效果,但以上两点是重要的区别。
1work-tree 包含实际文件,采用常规普通格式,您和计算机上的所有其他 non-Git 程序都可以使用。 Git 未使用这些实际文件。它使用压缩的 read-only、Git-only 提交文件,并使用索引/临时区域进行 new 提交,如上所示。
2可以强制移动标签,也可以删除标签然后重新创建标签。但是,其他软件可能希望标签 not 移动。例如,Go 模块系统可以缓存标签的哈希 ID,如果您稍后移动标签,不会 更新。移动标签通常是一个坏主意:如果可以的话,请避免它。
3这一切都在你的掌控之中,这里的默认规则有些复杂,但如果你不做任何特殊的事情,它们就等于你得到了他们的标签。
使用变基
假设你有这个:
...--F--G--H--K <-- develop
\
I--J <-- feature (HEAD)
但你想要这个:
?--? <-- feature (HEAD)
/
...--F--G--H--K <-- develop
您不能更改现有提交。没有什么可以。现有提交 I
和 J
卡在原处。但是你 可以 复制 现有的提交到 new-and-improved 版本。特别是,您可以将提交 I
复制到一个新的改进的提交 - 我们将其称为 I'
以指示它是 I
的副本,尽管在 Git 中它只是得到一些又大又丑的 random-looking 哈希 ID,它与 I
的哈希 ID 没有任何关系。 I
和 I'
之间的区别是:
I
的parent是H
,但是I'
的parent会是K
.I
的快照和I'
的快照可能不匹配,但是通过比较H
得到的差异 -vs-I
将是 same 作为 difference 你通过比较K
-vs-I'
.
将I
复制到I'
后,我们希望Git将J
复制到J'
。结果如下所示:
I'-J'
/
...--F--G--H--K <-- develop
\
I--J
请注意,我已经删除了标签 feature
并完全省略了名称 HEAD
,此时,我们已准备好移动 feature
并制作 HEAD
附新,移feature
。执行这个 "build new commits" 的过程实际上使用了 Git 所谓的 分离的 HEAD 所以现在,实际情况是:
I'-J' <-- HEAD
/
...--F--G--H--K <-- develop
\
I--J <-- feature
请注意,特殊名称 HEAD
没有 附加到任何分支名称。这就是为什么这是一个 分离的 HEAD。同时名称 feature
仍然 select 现有提交 J
。
git rebase
命令的最后一幕,即完成变基,是将名称 feature
从其现有提交 (J
) 中剥离,并使其指向与HEAD
指向 (J'
):
I'-J' <-- feature, HEAD
/
...--F--G--H--K <-- develop
\
I--J [abandoned]
移动标签后,将 re-attaches HEAD
变基到(移动的)标签,使一切看起来都像我们想要的那样。而且,由于 Git finds 提交的方式是从 labels 开始并向后工作,我们不能 see 提交 I
和 J
了。我们看到的是:
I'-J' <-- feature (HEAD)
/
...--F--G--H--K <-- develop
如果我们不记得提交 I
和 J
的哈希 ID,我们会认为 Git 以某种方式移动了旧提交。它没有——旧的提交仍然存在,每个拥有存储库克隆和旧提交的人,仍然有旧的提交。不过我们只看到我们的新。
正如 Nikos C. 指出的那样,如果您有一个使用现有提交 I-J
的活动拉取请求,您可能需要使用 git push --force
来更新拉取请求。你必须得到任何 Git 存储库持有他们的提交副本 I
和 J
放弃他们的提交支持新的和改进的 I'-J'
链。说服其他 Git 从旧提交切换到新提交的精确方法取决于 在 其他 Git 设置的控件。 Git集线器允许简单的 git push --force
来完成任务。