有没有一种快速的方法可以将长期提交到 master 分支的历史变基?
Is there a fast way to rebase a long history of commits to master branch?
过去两周我一直在研究一项功能,并为它创建了一个单独的分支。问题是这个任务花了我很长时间,而且我没有定期将我的分支重新设置为 master 。我最终得到了 100 多个提交,现在当我尝试使用 git rebase master
将分支重新设置为 master 时,它会永远持续下去,因为我需要检查每个提交的冲突,修改它,git add
然后 git rebase --continue
。
有更简单的方法吗?
这在一定程度上取决于您完成 100 多次提交的方式以及同时发生的情况。四种策略可能有用:
减少 rebase 的提交次数
如果你有 100 次提交的美丽清晰的流,从不修改相同的代码两次,对不起,但你被卡住了。
但是,从两周内的 100 次提交来看,我猜您的提交历史看起来不像那样。也许您在提交历史中修改了函数 X 5 次,而这些提交中的每一次都会导致冲突。如果您只有一个修改函数 X 的提交,那么您将有 20% 的工作要做。
所以这里的答案是在变基到 master 之前整理你的提交历史。这是 'git rebase' 的另一个用途。将其变基到其 现有 基础上,但使用 git rebase -i
以交互方式压缩在一起并重新排序提交。确保你有最少数量的提交,并且每一个都做一些独立的事情。您也可以借此机会更改您的提交日志。关键备份分支 (git branch
) 和 git diff
到每次提交后的分支,以检查您没有更改任何内容。
特别是,如果您有内部合并提交,将它们展平会很有帮助。
不可避免地,当我这样做时,我发现我做错了,或者可以做得更好,即使它是 'only' 代码格式或提交消息。您可以借此机会解决这些问题。我猜你应该在完成后减少到 10 或 20 次提交 - 更易于管理。
分阶段变基
如果您已经这样做了,您可能不得不咬紧牙关。但是,如果同事在您工作的同时进行了重构,那么有时您最好的做法如下。想象一下这棵树看起来有点像这样:
A --- B --- C --- D --- E --- F -- .... -- X --- Y --- Z (master)
\
\----- 1 --- 2 --- 3 --- 4 .... 97 --- 98 --- 99 --- 100 (you)
你最终想做的是变基到 Z。然而,这会给你带来很多冲突。假设提交 D 和 E 是同事重构您的代码正在处理的内容的两个大型提交。有时,更容易:
- Rebase 到 C(这应该没什么工作)
- 深吸一口气,重新调整到 E
- Rebase 到 master 上(这应该没什么工作)
避免 'master' 方面的复杂性
有时,您尝试合并到的树上的事情变得非常复杂。一个典型的案例是(使用上图),提交 G 恢复提交 C,然后提交 H 重做(或几乎重做)它,同时有合并(特别是你可能已经合并的东西) .谢谢同事,这让 rebase 变得非常简单。其他造成困难的事情是复杂的合并提交和讨厌的重命名。每次提交都会无缘无故地一次又一次地给您带来相同的冲突。 git rebase
啊啊。
这里的一种技术是展平 master 树的副本,即 master 分支(本地),然后将其变基,将整个事物压缩成从 A 到 Z 的单个提交。你也可以先展平你的东西(看上面)。然后变基到扁平化的母版上。现在您已经有了 'right' 组提交,您可以轻松地将其变基到 master 上(嗯,到 'Z' 上,因为 master 可能会更改)因为代码完全相同。
如果一切都失败了
有时git rebase
似乎进入了矛盾的神游状态,是时候打破git format-patch
和git am
整体或部分地重新应用它了。如果有人重命名了一个文件(因为你可以在编辑器的补丁中修复文件名)或重命名一个常见的 class/variable/whatever(你可以在补丁中 find/replace),这将非常有用。
从错误中吸取教训
下次,更频繁地变基到 master 上。边做边做,痛苦会小一些。
另外,如果你有两个人在同一块代码上工作并且产生了一堆冲突,可能是你的代码抽象得不够好,或者你是在互相顶层工作,可能会分散你的代码工作得更好?
通过合并变基
让我描述一下我找到的方法。
我有同样的 rebase 痛苦,没有“只是合并”选项。我知道我们有 rebase 标志 -X theirs,它会自动解决所有与我们的更改的冲突,但它会机械地工作,我们正在丢失外部更改。
但是如果我们可以添加一个额外的提交来从临时合并中恢复项目状态呢?我找到了简单的方法来做到这一点。
首先,检查临时分支并开始合并
git checkout -b temp
git merge origin/master
你将不得不解决冲突,但只有一次,而且是真正的冲突。
然后暂存所有文件并完成合并。
git commit -m "Merge branch 'origin/master' into 'temp'"
然后 return 到你的分支(让它成为 alpha)并开始变基自动解决任何冲突。
git checkout alpha
git rebase origin/master -X theirs
分支已变基,但项目可能处于无效状态。没关系,我们还有最后一步。我们只需要恢复项目状态,所以它应该就像我们在分支 'temp' 上一样。从技术上讲,我们只需要通过低级命令 git commit-tree 复制它的 tree(文件夹状态)。加上合并到当前分支刚刚创建的提交。
git merge --ff $(git commit-tree temp^{tree} -m "Fix after rebase" -p HEAD)
并删除临时分支
git branch -D temp
就是这样。我们通过隐藏合并进行了硬变基。
另外我开发了一个脚本,所以可以用对话的方式来完成,你可以找here。
我只是从 master 创建了一个新分支,然后将你的功能分支压缩并合并到那个新分支上。现在你有一个分支,所有代码都在一次提交中。
我看到了很多 post 和这样的例子,但是当你有 100 多个提交时 none 适合,其中大多数你必须解决冲突。我早些时候 merged/rebased 所以我的分支基本上是一团糟。所以看起来很难做简单的 squash/rebase.
这就是我最终所做的。将我的冲突解决方案从大约 30 次减少到仅 2 次 :)。
我认为你也可以将你的分支硬重置为 master 而不是下面的第 2 步 (git rebase -i
) 并且具有相同的效果(只有 1 个冲突解决)
git reset --hard master
- 备份您的项目文件夹。
git rebase -i mainbranch #it will open a editor.
- 放弃所有提交留下拳头选择:将 'pick' 的其余部分替换为 'd'
pick abas121 first commit
d abas122 sec commit
d abas123 third commit
d abas124 fourth commit
- 解决任何冲突(仅一次),并提交(仅提交,无push/pull)。
- 从备份项目文件夹中复制您的更改(除 .git 之外的所有更改,或者如果更改很小,则仅复制)。
- 做 git 差异并解决冲突的更改。将您的更改调整为 master 的最新版本。
- 承诺。查看提交历史并确保它是好的。
- 强制推送到您的远程分支。
git push --force.
过去两周我一直在研究一项功能,并为它创建了一个单独的分支。问题是这个任务花了我很长时间,而且我没有定期将我的分支重新设置为 master 。我最终得到了 100 多个提交,现在当我尝试使用 git rebase master
将分支重新设置为 master 时,它会永远持续下去,因为我需要检查每个提交的冲突,修改它,git add
然后 git rebase --continue
。
有更简单的方法吗?
这在一定程度上取决于您完成 100 多次提交的方式以及同时发生的情况。四种策略可能有用:
减少 rebase 的提交次数
如果你有 100 次提交的美丽清晰的流,从不修改相同的代码两次,对不起,但你被卡住了。
但是,从两周内的 100 次提交来看,我猜您的提交历史看起来不像那样。也许您在提交历史中修改了函数 X 5 次,而这些提交中的每一次都会导致冲突。如果您只有一个修改函数 X 的提交,那么您将有 20% 的工作要做。
所以这里的答案是在变基到 master 之前整理你的提交历史。这是 'git rebase' 的另一个用途。将其变基到其 现有 基础上,但使用 git rebase -i
以交互方式压缩在一起并重新排序提交。确保你有最少数量的提交,并且每一个都做一些独立的事情。您也可以借此机会更改您的提交日志。关键备份分支 (git branch
) 和 git diff
到每次提交后的分支,以检查您没有更改任何内容。
特别是,如果您有内部合并提交,将它们展平会很有帮助。
不可避免地,当我这样做时,我发现我做错了,或者可以做得更好,即使它是 'only' 代码格式或提交消息。您可以借此机会解决这些问题。我猜你应该在完成后减少到 10 或 20 次提交 - 更易于管理。
分阶段变基
如果您已经这样做了,您可能不得不咬紧牙关。但是,如果同事在您工作的同时进行了重构,那么有时您最好的做法如下。想象一下这棵树看起来有点像这样:
A --- B --- C --- D --- E --- F -- .... -- X --- Y --- Z (master)
\
\----- 1 --- 2 --- 3 --- 4 .... 97 --- 98 --- 99 --- 100 (you)
你最终想做的是变基到 Z。然而,这会给你带来很多冲突。假设提交 D 和 E 是同事重构您的代码正在处理的内容的两个大型提交。有时,更容易:
- Rebase 到 C(这应该没什么工作)
- 深吸一口气,重新调整到 E
- Rebase 到 master 上(这应该没什么工作)
避免 'master' 方面的复杂性
有时,您尝试合并到的树上的事情变得非常复杂。一个典型的案例是(使用上图),提交 G 恢复提交 C,然后提交 H 重做(或几乎重做)它,同时有合并(特别是你可能已经合并的东西) .谢谢同事,这让 rebase 变得非常简单。其他造成困难的事情是复杂的合并提交和讨厌的重命名。每次提交都会无缘无故地一次又一次地给您带来相同的冲突。 git rebase
啊啊。
这里的一种技术是展平 master 树的副本,即 master 分支(本地),然后将其变基,将整个事物压缩成从 A 到 Z 的单个提交。你也可以先展平你的东西(看上面)。然后变基到扁平化的母版上。现在您已经有了 'right' 组提交,您可以轻松地将其变基到 master 上(嗯,到 'Z' 上,因为 master 可能会更改)因为代码完全相同。
如果一切都失败了
有时git rebase
似乎进入了矛盾的神游状态,是时候打破git format-patch
和git am
整体或部分地重新应用它了。如果有人重命名了一个文件(因为你可以在编辑器的补丁中修复文件名)或重命名一个常见的 class/variable/whatever(你可以在补丁中 find/replace),这将非常有用。
从错误中吸取教训
下次,更频繁地变基到 master 上。边做边做,痛苦会小一些。
另外,如果你有两个人在同一块代码上工作并且产生了一堆冲突,可能是你的代码抽象得不够好,或者你是在互相顶层工作,可能会分散你的代码工作得更好?
通过合并变基
让我描述一下我找到的方法。
我有同样的 rebase 痛苦,没有“只是合并”选项。我知道我们有 rebase 标志 -X theirs,它会自动解决所有与我们的更改的冲突,但它会机械地工作,我们正在丢失外部更改。
但是如果我们可以添加一个额外的提交来从临时合并中恢复项目状态呢?我找到了简单的方法来做到这一点。
首先,检查临时分支并开始合并
git checkout -b temp
git merge origin/master
你将不得不解决冲突,但只有一次,而且是真正的冲突。 然后暂存所有文件并完成合并。
git commit -m "Merge branch 'origin/master' into 'temp'"
然后 return 到你的分支(让它成为 alpha)并开始变基自动解决任何冲突。
git checkout alpha
git rebase origin/master -X theirs
分支已变基,但项目可能处于无效状态。没关系,我们还有最后一步。我们只需要恢复项目状态,所以它应该就像我们在分支 'temp' 上一样。从技术上讲,我们只需要通过低级命令 git commit-tree 复制它的 tree(文件夹状态)。加上合并到当前分支刚刚创建的提交。
git merge --ff $(git commit-tree temp^{tree} -m "Fix after rebase" -p HEAD)
并删除临时分支
git branch -D temp
就是这样。我们通过隐藏合并进行了硬变基。
另外我开发了一个脚本,所以可以用对话的方式来完成,你可以找here。
我只是从 master 创建了一个新分支,然后将你的功能分支压缩并合并到那个新分支上。现在你有一个分支,所有代码都在一次提交中。
我看到了很多 post 和这样的例子,但是当你有 100 多个提交时 none 适合,其中大多数你必须解决冲突。我早些时候 merged/rebased 所以我的分支基本上是一团糟。所以看起来很难做简单的 squash/rebase.
这就是我最终所做的。将我的冲突解决方案从大约 30 次减少到仅 2 次 :)。
我认为你也可以将你的分支硬重置为 master 而不是下面的第 2 步 (git rebase -i
) 并且具有相同的效果(只有 1 个冲突解决)
git reset --hard master
- 备份您的项目文件夹。
git rebase -i mainbranch #it will open a editor.
- 放弃所有提交留下拳头选择:将 'pick' 的其余部分替换为 'd'
pick abas121 first commit
d abas122 sec commit
d abas123 third commit
d abas124 fourth commit
- 解决任何冲突(仅一次),并提交(仅提交,无push/pull)。
- 从备份项目文件夹中复制您的更改(除 .git 之外的所有更改,或者如果更改很小,则仅复制)。
- 做 git 差异并解决冲突的更改。将您的更改调整为 master 的最新版本。
- 承诺。查看提交历史并确保它是好的。
- 强制推送到您的远程分支。
git push --force.