由于不正确的变基,如何压缩重复提交?

How to squash duplicate commits due to an incorrect rebase?

我有一个有点特殊的、复杂的 Git 情况,因为我早先犯了错误,当时没有解决。我将首先解释我采取的步骤(使用 Git 网络视觉效果)到达我所在的位置。

  1. 我从最初的 master 分支开始。

  1. 我创建了一个 new-catalog 分支并做了一组提交 A

  1. 我用过时的本地 master 重新设置了 new-catalog 的基础,并进入了新的重新设置基础的 new-catalog 分支。 (虽然我当时并不知道,这将是我痛苦的开始)

这创建了第二组提交 A'A 相同,但不同的提交具有新的时间戳。

  1. 我创建了一个 beta/master 分支(仍然关闭过时的本地 master),并将 new-catalog PRed 到 beta/master.

  1. 我在 beta/master 上做了另一组提交 B

  1. 我从 beta/master 创建了一个子分支 beta/bootstrap

现在,既然我在这里,我想用 master 变基 beta/master 以准备 PR 回到 master。我确实尝试过变基,但是,我遇到了一些奇怪的冲突,我怀疑是由于重复的提交集 AA'。用 master 变基 beta/master 最安全的方法是什么?

我考虑过进行交互式变基和 squashing/dropping 初始提交集 A,但我也不想丢失这些提交的时间信息。如果这是最好的选择,我愿意失去时间信息,但我只是想看看有没有更好的方法。

此外,如果我能够成功地变基 beta/master,我还需要变基 beta/bootstrap 吗?还是我可以 PR 到 beta/master 而没有任何冲突?

我发了一个git-practice repository to recreate these mistakes and this是全网

非常感谢!

我不确定我是否理解正确。但我尝试在本地重现问题并修复它。我使用了您的 git-practis repo。

我重置本地 master 分支以提交 fb90356(我模拟中断 master)并创建 'new-catalog-demo' 分支。

这是两次更改后的样子

[u@d git-practice]$ git reset --hard fb90356
HEAD wskazuje teraz fb90356 Add JavaScript
[u@d git-practice]$ git log -1
fb90356 Add JavaScript
[u@d git-practice]$ git checkout -b new-catalog-demo
Przełączono na nową gałąź „new-catalog-demo”
[u@d git-practice]$ git commit -a -m"Add feature 1"
[new-catalog-demo 5c91e3d] Add feature 1
 1 file changed, 2 insertions(+), 1 deletion(-)
[u@d git-practice]$ git commit -a -m"Add feature 2"
[new-catalog-demo ed804d5] Add feature 2
 1 file changed, 2 insertions(+)
[u@d git-practice]$ git log --oneline --graph 
* ed804d5 (HEAD -> new-catalog-demo) Add feature 2
* 5c91e3d Add feature 1
* fb90356 (master) Add JavaScript
* a0f3d5a Add another main feature

然后我确实重新定位到中断主机并从 origin/master 拉出。这是它之后的样子。与此同时,我将 master 重置为另一个提交以模拟来自另一方的一些更改。在第二个日志中我们可以看到 A', B' 提交。

[u@d git-practice]$ git rebase master 
Pomyślnie przestawiono i zaktualizowano refs/heads/new-catalog-demo.
[u@d git-practice]$ git log --oneline --graph 
* c3f9338 (HEAD -> new-catalog-demo) Add feature 2
* 5ddb34c Add feature 1
* b89ef1b (master) Change another feature
* e7278df Change a main feature
* fb90356 Add JavaScript
* a0f3d5a Add another main feature
* 5a7b447 Add main feature
[u@d git-practice]$ git pull origin master
[u@d git-practice]$ git log --oneline --graph 
*   3b9619e (HEAD -> new-catalog-demo) Merge branch 'master' of https://github.com/itsjoshthedeveloper/git-practice into new-catalog-demo
|\  
| * b4bf1c6 (origin/master, origin/HEAD) Update main feature
| * f716c02 Change another main feature #-->> B' ??
| * f4f9669 Change main feature         #-->> A' ??
| * 98acbf4 Reset changed features
* | c3f9338 Add feature 2
* | 5ddb34c Add feature 1
|/  
* b89ef1b (master) Change another feature
* e7278df Change a main feature
* fb90356 Add JavaScript
* a0f3d5a Add another main feature
* 5a7b447 Add main feature
* ec699c4 Update README
* 395a5c6 Add h1
* e1633b3 Add index.html
* f11b51f Initial commit

我想我重现了你的问题。这是我修复它的方法

首先我们必须检查 git reflog 以找到我们有 new-catalog-demo 分支的地方,在不适当的 rebase 之前进行了更改。

[u@d git-practice]$ git reflog 
3b9619e (HEAD -> new-catalog-demo) HEAD@{0}: pull origin master: Merge made by the 'recursive' strategy.
c3f9338 HEAD@{1}: rebase (finish): returning to refs/heads/new-catalog-demo
c3f9338 HEAD@{2}: rebase (pick): Add feature 2
5ddb34c HEAD@{3}: rebase (pick): Add feature 1
ed804d5 HEAD@{8}: commit: Add feature 2
5c91e3d HEAD@{9}: commit: Add feature 1
fb90356 HEAD@{10}: checkout: moving from master to new-catalog-demo
fb90356 HEAD@{11}: reset: moving to fb90356
b4bf1c6 (origin/master, origin/HEAD) HEAD@{12}: clone: from https://github.com/itsjoshthedeveloper/git-practice.git

我发现 new-catalog-demo 上最后一个好的提交是 ed804d5,所以我重置到这一点。

u@d git-practice]$ git reset --hard ed804d5
HEAD wskazuje teraz ed804d5 Add feature 2

现在我有我的 new-catalog-demo 分支,其中包含我的 ole 更改,然后我将其重新设置为中断主控

[u@d git-practice]$ git log --oneline --graph 
* c3f9338 (HEAD -> new-catalog-demo) Add feature 2
* 5ddb34c Add feature 1
* b89ef1b (master) Change another feature

不,我们可以将它发布到 origin/master 上,以确保我们在 master 分支的当前状态下进行。

[u@d git-practice]$ git rebase origin/master 
Pomyślnie przestawiono i zaktualizowano refs/heads/new-catalog-demo.
[u@d git-practice]$ git log --oneline --graph 
* 86a2a81 (HEAD -> new-catalog-demo) Add feature 2
* 3dca49f Add feature 1
* b4bf1c6 (origin/master, origin/HEAD) Update main feature
* f716c02 Change another main feature

终于可以合并new-catalog-demo到本地master了。

[u@d git-practice]$ git checkout master 
Przełączono na gałąź „master”
Twoja gałąź jest za „origin/master” o 4 zapisy i może zostać przewinięta.
  (użyj „git pull”, aby zaktualizować lokalną gałąź)
[u@d git-practice]$ git merge new-catalog-demo 
Aktualizowanie b89ef1b..86a2a81
Fast-forward
 README.md      | 5 ++++-
 web/index.html | 2 +-
 2 files changed, 5 insertions(+), 2 deletions(-)
[u@d git-practice]$ git log --oneline --graph 
* 86a2a81 (HEAD -> master, new-catalog-demo) Add feature 2
* 3dca49f Add feature 1
* b4bf1c6 (origin/master, origin/HEAD) Update main feature
* f716c02 Change another main feature
* f4f9669 Change main feature
* 98acbf4 Reset changed features
* b89ef1b Change another feature
* e7278df Change a main feature
* fb90356 Add JavaScript
* a0f3d5a Add another main feature
* 5a7b447 Add main feature
* ec699c4 Update README
* 395a5c6 Add h1
* e1633b3 Add index.html
* f11b51f Initial commit

现在历史很干净,我们没有遗漏任何更改,也没有重复提交。我希望现在您能看到如何使用 git reflog 来解决此类问题。

没有什么“奇怪的冲突”,只是rebase的效果。甚至不考虑重复的提交,因为已经合并到 beta/master 分支中。正如您所注意到的,您的存储库有点混乱,但您仍然可以轻松(可能不会很快)达到所需的状态。问题是您在将您的更改合并到 master 之前等待了太久,并带来了冗余提交的副作用:除了重复的 new-catalog 分支之外,合并的拉取请求提交实际上是一个快进来自 new-catalog。您可以自己验证您对同一棵树有多个提交:我说的是

| * ec73ee6 Merge pull request #4 from itsjoshthedeveloper/new-catalog
|/| 
| *   84aabf5 Merge branch 'new-catalog' of https://github.com/itsjoshthedeveloper/git-practice into new-catalog
| |\  
| | * b40f340 Add a new JS feature
| | ...
| | ...
| | ...
| * | 4b463f5 Add a new JS feature

为了消除疑惑,您可以很容易地找到哪个是 rebase 过程开始使用 git log $(git merge-base master beta/master) -n 1 --format="%h %s" 的公共根,哪个 returns

f4f9669 Change main feature

当您在 master 上变基 beta/master 时出现的冲突来自您在 web/index.html 上对 beta/master 分支所做的更改。最后一个文件在原始 master 中也已更改,这是您冲突的原因,而不是重复的 new-catalog。那么,一般应该怎么办? 这是困境:git merge 获取分支的提示和最近的公共根,它尝试将它们合并在一起并创建提交(除非指定 --no-commit),除非不存在冲突,“简单”那样。另一方面,使用 git rebase 你可能有更多的冲突需要解决,这是由于每个提交的投影(cherry-pick)从一个新的基础开始,但以线性历史结束.我建议你只有在经常集成你的变更时才选择第二种方式,以尽可能避免多个分支之间的冲突。


关于 beta/bootstrap 分支,查看您在那里所做的更改,beta/mastermaster 上的变基不应产生任何冲突。但是,首先在 beta/master 上变基 beta/bootstrap,然后在 master 上变基(或之前进行合并)beta/bootstrap 绝对更方便。通过这种方式,只需一次变基,您就可以将他们的两个提示都向前推进。

甚至考虑 rerere 的使用:看起来你处境艰难,因为你有太多的冲突需要解决。 git rerere 是一个非常有趣的功能,它包含缓存您如何解决特定冲突。假设你完成了一次合并,但最后你决定要进行变基并从零开始:一些冲突会自动解决,因为它们与之前遇到的相同。


编辑: 来自评论:

I tried rebasing beta/bootstrap on beta/master first, but after I rebased beta/master on master, I had to rebase beta/bootstrap on beta/master again

你当然有,你每次只能变基一个分支,即使它们确实共享了它们的大部分历史记录。为避免 rebase --onto,您可以将 beta/bootstrap 重新设置为 beta/master,然后将 beta/bootstrap 设置为 master。此时 beta/bootstrap 分支同时具有 master 分支和 beta/master 提交作为其祖先。要将 beta/master 分支移动到 master 主线上​​的新提交,你必须用 git branch -f beta/master beta/bootstrap~2.

之类的东西强制它

What is the safest way to rebase beta/master with master?

我发现进行交互式变基并删除初始提交集 A 是简化一切的安全方法。与我之前的想法相反,变基保留了作者时间戳(如使用 git log 时所见)并且仅替换了提交时间步长(如 GitHub 中所见)。

如何在 master

上变基 beta/master
  1. git checkout beta/master

    检查 beta/master 分支。

  2. git rebase -i master

    master 上交互式变基 beta/master

  3. 删除原始提交集A

    在文本编辑器中为原始集合 A 中的每个提交将 pick 替换为 d。对于我的 git-practice 存储库,这将是提交 3e8c537642dc347e2fa44b40f340.

    然后,保存并退出编辑器。

  4. 处理任何合并冲突

    Git 然后将 beta/master 中的每个提交(从 branch/deepest 父提交的底部开始)与 master.[=54= 的 HEAD 进行比较]

    如果您遇到任何合并冲突,最上面的更改将是 master 分支(您要变基到的分支)的 HEAD 中的当前代码,而底部的更改将是对应的代码提交其更改与 HEAD 冲突。您可以选择上变、下变、两者都变或两者都不变。

    按照您的意愿获取文件后,请确保保存文件并git add它以暂存更改。然后,git rebase --continue继续。

    如果你合并所有冲突,它应该成功变基并显示Successfully rebased and updated refs/heads/beta/master.

  5. git push --force origin beta/master

    这将推送您的本地 beta/master 并覆盖您的远程 origin/beta/master

Do I need to rebase beta/bootstrap as well? Or will I just be able to PR into beta/master without any conflicts?

您可以 PR(合并提交)到 beta/master,但是变基会大量清理提交历史。

如何在 beta/master

上变基 beta/bootstrap
  1. git checkout beta/bootstrap

    检查 beta/bootstrap 分支。

  2. git rebase --onto beta/master beta/master@{1} beta/bootstrap

    beta/master 上重新设置 beta/bootstrap 基线。

  3. git push --force origin beta/bootstrap

    这将推送您的本地 beta/bootstrap 并覆盖您的远程 origin/beta/bootstrap