我可以将推送的提交从一个分支移动到另一个现有分支吗?

Can I move pushed commits from one branch to another existing branch?

我有 3 个分支:developfeature-1feature-2。我在 feature-1 上工作,然后切换到 feature-2 上工作。然后我完成了 feature-2 的工作,提交并推送到存储库,然后为该功能创建了一个合并请求。然后我看到 feature-2 包含一些应该在 feature-1 中的提交。我可以在本地将它们从 feature-2 移动到 feature-1,然后在 repo 中反映相同的移动吗?

git cherry-pick <commit-id>是你亲爱的朋友

  1. 继续 feature-2 分支
  2. 复制commit-id(s)你想从feature-2移动到feature-1
  3. 切换到 feature-1 分支
  4. 现在挑选您在上面第 2 步中复制的所有提交。

    git cherry-pick <commit-id>

    如果您有多个提交要移至 feature-1,则将所有提交 ID 按其提交顺序排列 date/time.

    例如:

    git cherry-pick <commit-id-1> <commit-id-2> . . <commit-id-n>

你的问题说有问题的提交属于 in feature-1 而不是 feature-2 branch。已接受的答案无法做到这一点。

cherry-pick 是一种在一个分支上创建新提交的好方法,它复制另一个分支上的提交所做的更改;但它不会改变另一个分支。

例如,如果您从

开始
x <--(master)
|\
| A1 -- A2 <--(feature_1)
 \
  B1 -- A3 -- B2 <--(feature_2)

其中 A3 用于 feature_1,在执行 cherry-pick 程序后,您将获得

x <--(master)
|\
| A1 -- A2 -- A3' <--(feature_1)
 \
  B1 -- A3 -- B2 <--(feature_2)

所以 feature_1 可能看起来像您现在想要的,但是 feature_2 仍然有一些可能不应该有的变化。当您将两个功能合并到 master 中时,这可能会造成一些麻烦。 (我的意思是,可能存在没有多大意义的合并冲突。git 确实尝试提供帮助,但它不可能是完美的。)

在您的问题中,您注意到提交已经被推送,并且作为对 cherry-pick 答案的回应,您询问更改是否 push 干净;所以我假设您已经 运行 遇到(至少部分)在您推送的分支上重写历史的问题。

虽然 cherry-pick 的结果确实会 push 干净,但那是因为 cherry-pick 没有做出您要求的所有更改(如上所述). feature-2 的修复确实导致了 push.

的问题

一个解决方案是使用cherry-pick修复feature-1,然后使用revert修复feature-2。在我们的示例中,使用 feature-2~1 作为将提交标识为 "moved" 的表达式(您通常可以使用提交 ID,或适合您实际情况的表达式):

git checkout feature-1
git cherry-pick feature-2~1
git checkout feautre-2
git revert feature-2~1

给你

x <--(master)
|\
| A1 -- A2 -- A3' <--(feature_1)
 \
  B1 -- A3 -- B2 -- !A3 <--(feature_2)

这仍然 push 干净(因为没有从任何分支的历史记录中删除任何提交)并且两个分支现在都会有正确的更改。从好的方面来说,这使您不太可能遇到任何合并问题;不利的一面是,某些 rebase 操作现在可能会出现问题。您可能会采取一些步骤来缓解这种情况,但如果您担心只进行彻底 push 的更改,那么无论如何您都不会做那种 rebase

如果您想要 "move" 提交很多,那么您可能希望将 cherry-pick 替换为交互式 rebase,如下所示:

git checkout feature-2
git branch temp
git rebase -i feature-1 temp

将打开一个编辑器,显示 rebase 的 "todo" 列表。列表中的每个条目都是关于如何处理提交的说明。删除您想要 "leave behind" 的提交行。 (对于如何在 feature-1 上表示提交,您还可以进行其他调整;但这是一个很长的兔子洞,因为到目前为止您所问的只是如何 "move" 提交,所以只有这些步骤将保留它们 "as is"。)

当您保存并退出编辑器时,rebase 将开始,重写所有选定的提交。通过

完成对feature-1的更新
git checkout feature-1
git merge temp
git branch -d temp

您仍然需要前往 feature-2 来恢复不应该存在的提交;您可能希望在带有 -n 标志的单个命令中执行此操作,以便您可以只创建一个还原提交。 (这会降低我上面提到的某些 rebase 操作在您执行 revert 之后发生的风险;但是我希望您不会再做这些类型的操作rebase 无论如何考虑到这个问题隐含的限制。)

现在有些人不喜欢对这种事情使用 revert,因为历史显示了发生的事情 - 包括进行更改和后来删除更改,这可能看起来很烦人,因为更改永远不应该已制作。

避免这种情况的唯一方法是 "rewrite the history" of feature-2,无论你怎么做,都不会 push 干净。你可以得到它到push,但这样做你会为拥有feature-2副本的所有其他开发者创造工作——如果他们有做错了,它会毁掉你的工作。因此,如果您想重写历史记录,您必须与使用该存储库的其他所有人协调。有关详细信息,请参阅 "recovering from upstream rebase" 下的 git rebase 文档。