Git:分支混乱 - 如何清理

3

我曾在两个分支上工作并混淆了一切。一些旨在应用于A分支的更改实际上被应用到了B分支,反之亦然。有些提交同时包含对正确分支和错误分支的更改。我进行了几次这种混合的提交。

后来我在正确的分支上再次进行了更改。所以我最终得到了所有的更改都在正确的分支中,但是每个分支上还有属于另一个分支的旧更改。然后我将A分支推送了出去,而B分支则已经发起了拉取请求。

问题1:如何从每个分支中删除不属于该分支的更改?我清楚地知道需要将哪些文件还原为原始状态。

问题2:如何更改已经发起的拉取请求?


接收者是否已经合并了该拉取请求? - quetzalcoatl
没有,他们没有合并它。 - Jac Jac
如果他们还没有合并,那么拉取请求就没有意义。它本质上只是记住了一条“请将我的公共存储库分支FOOBAR合并到你的BARFOO中”的“注释”。当您清理好并发布/推送这些清理时,拉取请求...不会改变。它仍然会说:“请将我的foobar合并到你的barfoo中”。但是,现在,显然,您的foobar将在清理之后=>因此,您可以认为“拉取请求”是自动更新的。当然,只要它们还没有合并,这很方便。 - quetzalcoatl
2个回答

1

为了以后的参考,推送前清理代码会更加安全。此时你有两个选择:

  • 你可以创建新的分支(使用新名称),并将所需更改迁移到每个分支中。现有的分支最终将被废弃。

  • 你可以“重写历史”来修复现有的分支。任何已经获取/拉取现有分支的人都必须执行恢复过程(请参见“git rebase”文档中的“从上游rebase恢复”的一般讨论)。

我将假设第一个选项较为安全,并编写此过程。如果你想使用历史记录重写,请注意如何调整此过程。我将演示如何处理branchA,而branchB的过程基本相同。我将假设该分支是从master创建的(因此如果不是,请将所有引用master的内容替换为适当的“父分支”)。

检出现有分支。

git checkout branchA

假设您不希望新分支移动到master的末端,您需要找到分支点。为了简化说明,让我们在该点创建一个临时标签(尽管从技术上讲,您可以使用此提交的SHA1 ID代替)。
所以您有:
x --- A --- x <--(master)
       \
        A1 --- B1 --- AB1 --- A2 <--(branchA)

你需要找到A。如果你知道在branchAmaster分离后有(在此示例中)4个提交,你可以说

git tag rootA branchA~4

如果您不知道这个数字,您可以从GUI前端或者从git log --graph master branchA(如果提交历史记录足够小,这是可行的)来估算它,否则:
git rev-list master..branchA

输出的最后一行是你分支上第一个提交的SHA1哈希值,假设它是31337c0d3,那么你可以
git tag rootA 31337c0d3^

(注意末尾的^)。

无论如何,现在你应该已经拥有了

   [rootA]
      |
x --- A --- x <--(master)
       \
        A1 --- B1 --- AB1 --- A2 <--(branchA)

现在创建新分支(如果使用“历史重写”选项,则可以跳过此步骤)。
git checkout -b new_branchA

现在开始一个交互式的变基会话。
git rebase -i --onto rootA master

您将获得一个文本编辑器,其中包含一个“待办事项”列表,每行表示一个提交。
对于属于branchA的提交,请保留该行不变(使用pick命令)。
对于属于branchB的提交-假设您已经在branchB上进行了这些更改-您只需将命令从pick更改为drop
如果任何提交具有混合更改(一些属于branchA,另一些属于branchB),请将命令从pick更改为edit
保存并退出文本编辑器,然后git将开始重新设置基础。 如果您标记了任何提交以进行edit,则git将暂停并提示您进行编辑,然后告诉它恢复重新设置基础。
重新设置基础完成后,您将拥有:
   [rootA]
      |
x --- A --- x <--(master)
      |\
      | A1 --- B1 --- AB1 --- A2 <--(branchA)
      \
       A1' --- Ab1' --- A2' <--(new_branchA)

(其中Ab1'是对AB1进行编辑替换的结果,只保留了针对BranchA的更改)。

如果您将其作为历史重写,则branchA将指向A2',当然也不会有new_branchA

您可以清除临时标记。

git tag --delete rootA

如果你没有进行历史重写,那么你不再需要旧的branchA

git branch --delete branchA

最后,您可以将其推送到远程。
git push

(如果进行历史重写,则需要使用-f选项。)

谢谢,这非常清晰。我们的一个分支中有不止一个功能,使用您的解决方案,我认为可以为每个功能创建一个新分支,从 RootA 开始,然后仅选择属于该功能的更改。然后为第二个功能创建一个分支,从 RootA 开始,再次只选择属于 feature2 的更改。如果所有功能都被拆分,请删除 branchA。这样正确吗? - Jac Jac

0

如果您还没有推送这些分支,您可以:

  • 创建分支tmpAtmpB(例如从origin/Aorigin/B
  • git cherry-pickAtmpA,从BtmpB中选择所需的提交;如果需要编辑提交,请使用选项--no-commit,然后使用git reset清除索引,接着使用git add -p仅添加所需更改,最后提交结果
  • 将这些本地分支A重置为tmpAgit branch -f A tmpA),对于B也是如此。
  • 删除tmpAtmpB

Tkx,使用cherry pick可以将一个提交分成几个部分吗?在一个提交中,有时会对brancheA和nrancheB进行更改。我认为cherry pick会采取整个提交? - Jac Jac
@JacJac 是的,但是你可以使用带有 --no-commit 选项的 git cherry-pick。然后执行 git reset,现在你可以使用 git add -p 只添加你需要的内容。一旦你添加了所需的文件(或文件的部分),就进行提交,并执行 git clean -fd - VonC

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接