Git 将一个分支移动到另一个具有一个提交历史的分支
Git Moving a Branch to Another One with One Commit History
我正在我的功能分支上开发。开发完成后,我会将所有提交的代码移动到 master 分支。
但指定点是在这个操作之后主分支应该只有一个提交历史。
我会尽量用树来解释
--- BRANCH TREES WITHOUT MERGE OPERATION ---
A--B--C (master-branch)
\ ^--- After the merge operation commit will come after this point.
\
D--E--F--G (feature-branch)
--- AFTER THE MERGE OPERATION EXPECTED BRANCH TREES ---
A--B--C--(H) (master-branch)
\ ^--- 'H' is the commit of all feature branch merge.
\ Include commits D, E, F, G.
\ This should have all source but, will shown one commit message.
\
D--E--F--G (feature-branch)
我怎样才能像这样合并这些分支?
我想你想做的是 'squash commits' 功能。
我只复制原始分支,然后以交互方式对其进行变基,这样我就可以压缩提交。
Git称之为"squash merge",你通过运行宁git merge --squash
获得。与 Git 中的所有新提交一样,新提交将添加到当前分支,因此在这种情况下,您将首先进入 master-branch
,然后是 运行 git merge --squash feature-branch
。
注意事项
有一些重要的事情需要了解。特别是,虽然与结果提交关联的 tree(源或工作树)是通过 merging(动词:动作,"to merge"),它不是合并提交(合并的形容词或名词形式)。
请记住,在 Git 中,提交有两个功能:
他们是历史。
它们 包含 work-tree 快照(加上一些元数据:提交的人、时间和日志消息)。
一个正常的 git merge other
找到一个 合并基础 提交 运行ning git merge-base --all HEAD other
,1 然后:
组合两组差异:从基础到 HEAD
的差异总计 "what you changed",加上从基础到 other
的差异,总计到 "what they changed"。如果您更改了文件 one.txt
并且他们更改了文件 two.txt
,则 Git 会进行两项更改,即您的 one.txt
和他们的 two.txt
。如果您 都更改了文件 three.txt
,Git 会尝试查看您的更改和他们的更改是否重叠,或者完全重复,或者完全独立。如果它们重叠,Git 会给你一个合并冲突。如果它们是独立的,则 Git 接受两个更改。如果它们是完全重复的,Git 将复制一份更改。结果 three.txt
是 three-way merge.
的输出
进行新提交,其 历史记录 说:"this commit is a merge commit, combining the work from the first parent HEAD
and the second parent other
".
A git merge --squash
其他人仍在执行合并操作,包括任何必需的 three-way 合并。然后它——没有明显的原因2——省略了 git commit
步骤,让你 运行 git commit
,当你 做 运行 git commit
,结果是 不是 合并提交:它的历史显示 "this commit is an ordinary commit, and its sole parent is HEAD
"。换句话说,它的提交历史就好像您自己完成了 other
中的所有更改,除了任何重复的更改。
这就是你的要求,但这也是一个陷阱。如果您稍后决定需要在 feature-branch
上进行更多开发并再次合并,Git 将不会有显示现有合并的历史记录,并且不知道它可以使用先前合并的结果开始新的合并过程。
这最终意味着当您打算在 squash-merging 之后立即(或至少很快)删除 功能分支时,压缩合并最有效:
A---B----C <-- main
\
D--E--F--G <-- feature
(现在git checkout main && git merge --squash feature && git commit && git branch -D feature
)
A---B----C--H <-- main
\
D--E--F--G [abandoned]
这样一来,您就无法返回并在 G
之后添加更多使用 D-E-F-G
链的提交,这些提交稍后需要再次合并。如果你确实回去做了这样的提交,然后再次合并,合并基础提交将是 A
,而不是 G
,并且 Git 更有可能是 mis-merge,不小心复制了一些 D-E-F-G
更改。3
1如果git merge-base --all
找到多个合并基础,行为取决于合并策略。如果它发现 no 合并基础,旧版本 Git 和新版本 Git 的行为不同:较新的 Gits 停止并出现错误关于拒绝合并不相关的历史。
2常规合并将也停止,但只有在 --no-commit
特别请求时才会停止。随后的 git commit
将进行合并提交,即 two-parent 提交。所以 git merge --squash
没有理由表现得好像你也指定了 --no-commit
:如果你 真的想要 它停止,你可以 运行 git merge --squash --no-commit
。 (这很可能是最早的 Git 脚本的遗留问题。)
3Git 的合并算法努力尝试只获取每个更改的一个副本,通常只会成功获取一个更改副本。也就是说,从 A
到 G
之后的差异包括所有 D-E-F-G
的变化,以及从 A
到 H
之后的差异也包括所有D-E-F-G
变化:但这就是"take one copy of the changes"意味着要处理的。
问题是当你对这个算法施加太大压力时,它就会失败。通常,结果是您必须手动解决的巨大合并冲突。
方法之一:
git checkout -b tmp feature-branch
git reset A --soft
git commit -m "feature"
#a new commit X is created. It's a squash of D, E, F, G
git checkout master-branch
git cherry-pick X
git branch -D tmp
我正在我的功能分支上开发。开发完成后,我会将所有提交的代码移动到 master 分支。
但指定点是在这个操作之后主分支应该只有一个提交历史。
我会尽量用树来解释
--- BRANCH TREES WITHOUT MERGE OPERATION ---
A--B--C (master-branch)
\ ^--- After the merge operation commit will come after this point.
\
D--E--F--G (feature-branch)
--- AFTER THE MERGE OPERATION EXPECTED BRANCH TREES ---
A--B--C--(H) (master-branch)
\ ^--- 'H' is the commit of all feature branch merge.
\ Include commits D, E, F, G.
\ This should have all source but, will shown one commit message.
\
D--E--F--G (feature-branch)
我怎样才能像这样合并这些分支?
我想你想做的是 'squash commits' 功能。
我只复制原始分支,然后以交互方式对其进行变基,这样我就可以压缩提交。
Git称之为"squash merge",你通过运行宁git merge --squash
获得。与 Git 中的所有新提交一样,新提交将添加到当前分支,因此在这种情况下,您将首先进入 master-branch
,然后是 运行 git merge --squash feature-branch
。
注意事项
有一些重要的事情需要了解。特别是,虽然与结果提交关联的 tree(源或工作树)是通过 merging(动词:动作,"to merge"),它不是合并提交(合并的形容词或名词形式)。
请记住,在 Git 中,提交有两个功能:
他们是历史。
它们 包含 work-tree 快照(加上一些元数据:提交的人、时间和日志消息)。
一个正常的 git merge other
找到一个 合并基础 提交 运行ning git merge-base --all HEAD other
,1 然后:
组合两组差异:从基础到
HEAD
的差异总计 "what you changed",加上从基础到other
的差异,总计到 "what they changed"。如果您更改了文件one.txt
并且他们更改了文件two.txt
,则 Git 会进行两项更改,即您的one.txt
和他们的two.txt
。如果您 都更改了文件three.txt
,Git 会尝试查看您的更改和他们的更改是否重叠,或者完全重复,或者完全独立。如果它们重叠,Git 会给你一个合并冲突。如果它们是独立的,则 Git 接受两个更改。如果它们是完全重复的,Git 将复制一份更改。结果three.txt
是 three-way merge. 的输出
进行新提交,其 历史记录 说:"this commit is a merge commit, combining the work from the first parent
HEAD
and the second parentother
".
A git merge --squash
其他人仍在执行合并操作,包括任何必需的 three-way 合并。然后它——没有明显的原因2——省略了 git commit
步骤,让你 运行 git commit
,当你 做 运行 git commit
,结果是 不是 合并提交:它的历史显示 "this commit is an ordinary commit, and its sole parent is HEAD
"。换句话说,它的提交历史就好像您自己完成了 other
中的所有更改,除了任何重复的更改。
这就是你的要求,但这也是一个陷阱。如果您稍后决定需要在 feature-branch
上进行更多开发并再次合并,Git 将不会有显示现有合并的历史记录,并且不知道它可以使用先前合并的结果开始新的合并过程。
这最终意味着当您打算在 squash-merging 之后立即(或至少很快)删除 功能分支时,压缩合并最有效:
A---B----C <-- main
\
D--E--F--G <-- feature
(现在git checkout main && git merge --squash feature && git commit && git branch -D feature
)
A---B----C--H <-- main
\
D--E--F--G [abandoned]
这样一来,您就无法返回并在 G
之后添加更多使用 D-E-F-G
链的提交,这些提交稍后需要再次合并。如果你确实回去做了这样的提交,然后再次合并,合并基础提交将是 A
,而不是 G
,并且 Git 更有可能是 mis-merge,不小心复制了一些 D-E-F-G
更改。3
1如果git merge-base --all
找到多个合并基础,行为取决于合并策略。如果它发现 no 合并基础,旧版本 Git 和新版本 Git 的行为不同:较新的 Gits 停止并出现错误关于拒绝合并不相关的历史。
2常规合并将也停止,但只有在 --no-commit
特别请求时才会停止。随后的 git commit
将进行合并提交,即 two-parent 提交。所以 git merge --squash
没有理由表现得好像你也指定了 --no-commit
:如果你 真的想要 它停止,你可以 运行 git merge --squash --no-commit
。 (这很可能是最早的 Git 脚本的遗留问题。)
3Git 的合并算法努力尝试只获取每个更改的一个副本,通常只会成功获取一个更改副本。也就是说,从 A
到 G
之后的差异包括所有 D-E-F-G
的变化,以及从 A
到 H
之后的差异也包括所有D-E-F-G
变化:但这就是"take one copy of the changes"意味着要处理的。
问题是当你对这个算法施加太大压力时,它就会失败。通常,结果是您必须手动解决的巨大合并冲突。
方法之一:
git checkout -b tmp feature-branch
git reset A --soft
git commit -m "feature"
#a new commit X is created. It's a squash of D, E, F, G
git checkout master-branch
git cherry-pick X
git branch -D tmp