git rebase --onto失败

时间:2017-03-18 11:20:54

标签: git git-rebase git-merge-conflict

我创建了一个用于学习git的本地存储库,因此丢失数据不是问题。

当前树:

*   006f7ab - (2 days ago) Merge branch 'hotfix' idc what will heppen :( - stav alfi (master
|\
| * 0f028e8 - (2 days ago) good - stav alfi
* | fc040d3 - (2 days ago) good - stav alfi
* | ed29b30 - (2 days ago) good - stav alfi
|/
* a7c5bb3 - (2 days ago) good branch - stav alfi
* 9d804c2 - (2 days ago) new.txt changed in 16:35 - stav alfi
* 6ada3b7 - (2 days ago) new.txt changed in 16:32 - stav alfi (oldDad)
* f6497fc - (2 days ago) this is the nest commit! - stav alfi (oldDad1)
* b1b3e25 - (2 days ago) omg - stav alfi
* 74656b3 - (2 days ago) new1234 - stav alfi
* e8977d3 - (2 days ago) fast commit - stav alfi
* 114b46c - (3 days ago) good - Stav Alfi
* 8212c78 - (3 days ago) good - Stav Alfi
* 23dfc61 - (3 days ago) removed-something - Stav Alfi
* 184178d - (3 days ago) shortcut - Stav Alfi
* f1e606f - (3 days ago) good-commit - Stav Alfi
* 5ae787b - (3 days ago) initial-project-version1 - stav alfi
* 1321cba - (3 days ago) initial-project-version1 - stav alfi
* eae3e1c - (3 days ago) initial-project-version - stav alfi
* d3c3e93 - (3 days ago) initial-project-version - Stav Alfi
* db309e9 - (3 days ago) initial-project-version - Stav Alfi (HEAD -> newDad)

所需的树:(我要做的事)

*   006f7ab - (2 days ago) Merge branch 'hotfix' idc what will heppen :( - stav alfi (HEAD -> master)
|\
| * 0f028e8 - (2 days ago) good - stav alfi
* | fc040d3 - (2 days ago) good - stav alfi
* | ed29b30 - (2 days ago) good - stav alfi
|/
* a7c5bb3 - (2 days ago) good branch - stav alfi
* 9d804c2 - (2 days ago) new.txt changed in 16:35 - stav alfi
* 6ada3b7 - (2 days ago) new.txt changed in 16:32 - stav alfi (oldDad)
(....I want to remove all of this....)
* db309e9 - (3 days ago) initial-project-version - Stav Alfi (newDad)

我使用的命令+错误:

$ git rebase --onto newDad oldDad1
First, rewinding head to replay your work on top of it...
Applying: new.txt changed in 16:32
error: Failed to merge in the changes.
Using index info to reconstruct a base tree...
A       new.txt
Falling back to patching base and 3-way merge...
CONFLICT (modify/delete): new.txt deleted in HEAD and modified in new.txt changed in 16:32. Version new.txt changed in 16:32 of new.txt left in tree.
Patch failed at 0001 new.txt changed in 16:32
The copy of the patch that failed is found in: .git/rebase-apply/patch

When you have resolved this problem, run "git rebase --continue".
If you prefer to skip this patch, run "git rebase --skip" instead.
To check out the original branch and stop rebasing, run "git rebase --abort".

git status(运行git rebase --onto newDad oldDad1后)

$ git status
rebase in progress; onto db309e9
You are currently rebasing branch 'master' on 'db309e9'.
  (fix conflicts and then run "git rebase --continue")
  (use "git rebase --skip" to skip this patch)
  (use "git rebase --abort" to check out the original branch)

Unmerged paths:
  (use "git reset HEAD <file>..." to unstage)
  (use "git add/rm <file>..." as appropriate to mark resolution)

        deleted by us:   new.txt

no changes added to commit (use "git add" and/or "git commit -a")

我的问题:

我无法理解我得到的错误以及以下文字: 倒带,重放,应用。 我也不知道如何解决冲突。

我很想知道我做错了什么以及如何解决它。

非常感谢!

1 个答案:

答案 0 :(得分:1)

好的,深呼吸: - )

Git&#39; rebase 副本提交

git rebase使用的基本技巧是挑选操作,复制提交。我们现在要了解复制提交的机制,但考虑一个简单的,普通的git cherry-pick,其中我们git checkout一些分支名称 - 我将创建一个指向一个特定的新名称在这里提交 - 然后告诉Git复制一些其他提交,通常是我们新分支上尚未提交的提交。

git checkout -b newbranch 23dfc61

这使23dfc61成为当前提交,但为其提供了新的分支名称newbranch。现在我们可以进行新的提交,这将添加到新的分支中,所以现在我们运行,例如:

git cherry-pick 9d804c2

复制提交9d8042c

结果,如果这有效 - 如果没有合并冲突,或者在清除任何合并冲突(如果有的话)之后 - 是父提交为23dfc61的新提交,其源树23dfc61类似,但与9d804c2相比,6ada3b7... * 9d804c2 - (2 days ago) new.txt changed in 16:35 - stav alfi * 6ada3b7 - (2 days ago) new.txt changed in 16:32 - stav alfi (oldDad) * f6497fc - (2 days ago) this is the nest commit! - stav alfi (oldDad1) * b1b3e25 - (2 days ago) omg - stav alfi * 74656b3 - (2 days ago) new1234 - stav alfi * e8977d3 - (2 days ago) fast commit - stav alfi * 114b46c - (3 days ago) good - Stav Alfi * 8212c78 - (3 days ago) good - Stav Alfi | * NNNNNNN - (now) new.txt changed in 16:35 - stav alfi (HEAD -> newbranch) |/ * 23dfc61 - (3 days ago) removed-something - Stav Alfi * 184178d - (3 days ago) shortcut - Stav Alfi ... 相比,您添加了它:

NNNNNNN

我们不知道新的哈希值是什么,所以我输入了9d804c2。但是新提交与旧提交具有相同的日志消息,并且与旧提交进行相同的更改

提交包含快照,而不是更改

在提交时,每个提交都附加了完整源。这与许多其他版本控制系统不同:大多数都倾向于将每个提交存储为来自它们之前的提交的更改,或者是它们之后的提交。这意味着,为了复制提交,Git首先必须找出更改的内容。

找出方法是将提交与其提交进行比较。 6ada3b7的父提交是git diff 6ada3b7 9d804c2 ,因此Git会:

new.txt

查看已更改的内容。假设日志消息准确无误,您在23dfc61中更改了一些内容,这就是Git会找到的内容。那么,当Git尝试修改为NNNNNNN保存的快照以便为23dfc61提供新快照时,Git会尝试做什么。

如果成功的话,Git会提交结果,并且会成功完成挑选。

永远不会更改

通过获取每个提交的确切内容来构造不可发布的哈希ID 6ada3b7badf00d以及bedfacegit rebase等等。如果您尝试更改 任何任何提交,Git会构建一个新提交;如果在任何地方甚至有一个不同的位,你会得到一个新的,不同的哈希,所以你得到一个新的,不同的提交。

进入此部分的部分包括所有来源以及父ID,因为每次提交&#34;指向&#34; (包含其父ID的ID)。 (还有一些时间戳,所以除非你在同一秒内进行两次相同的提交,你仍然会得到两个不同的ID,即使它们的其余部分相同也是如此。)因此,要改变任何东西 - 无论它是&#39 ; s源,或者只是父ID-Git 必须复制提交。

这就是rebase副本提交的原因:必须。您正在进行一些提交,将每个提交转换为更改,然后从具有不同父ID的不同提交开始应用这些更改,即使它具有相同的源树。所以你给--onto的内容基本上是两个信息块:

  • 应该复制哪些提交?
  • 这些副本应该放在哪里?

如果您使用X..Y,那么复制的地方很容易,因为那个地方!但是,提交 copy 的提交比较棘手。

选择提交

Git提供了一个范围符号master,它看起来就意味着&#34;在X和Y之间提交&#34; - 它确实如此。但不完全!实际上,Git使用我们称之为 reachability 的东西,遵循提交中的父链接。我们已经注意到每个提交都有一个父ID存储在其中。这就是Git如何找到你的提交:你告诉它从一个分支提示开始,使用分支名称,如master,它找到了通过其哈希ID进行的特定提交,Git在名称X..Y内为您记住。

该提交中还有另一个哈希ID:这是提交的父级。 Git使用该哈希ID来查找 提交。父母还有另一个哈希ID,Git一直在寻找越来越多的父母。只要它可能,它就会一直持续到你做过的第一次提交。

提交太多了,所以我们告诉Git 停止在某些时候回来。那是&#34; Y&#34; ...--D--E--F--G--H <-- branch 的一部分:这告诉Git 从Y开始并向后工作,标记提交&#34;绿色&#34;暂时带走他们。但是,与此同时,从X开始并向后工作,标记提交&#34;红色&#34;暂时避免带他们

我喜欢用提交的单字母名称来绘制所有这些,而不是大丑陋的哈希ID,连接左边有较旧提交的行,右边是较新提交:

H

此处提交G是分支的提示HF的父级,G是{{ 1}}的父母,依此类推。如果我们写E..H,那就画E(和D并且背面)&#34;红色&#34;:停止,不要采取这些!然后它绘制H绿色,然后GF,然后我们点击红色的E并停止。这样选择提交F-G-HE自然被排除在外。

但是当我们有分支和合并时,事情会变得棘手:

          F--G--H
         /       \
...--D--E         K--L
         \       /
          I-----J

提交K合并提交。合并提交是指具有两个(或更多,但不会去那里)父母的合并提交。如果我们坚持使用红色和绿色的油漆类比,E..L表示&#34;油漆E和背面红色并在背面绿色油漆L&#34;:当我们点击{{ 1}},我们将两个 K H绿色绘制成绿色,然后返回此分支的双方 /合并。

如果我们说J,请查看其工作原理:我们将G..L设为红色,然后设为G,然后设为FE,依此类推。我们从不画D,因为I不是向后:我们只能在向后移动,而不是向前移动这个流程。然后我们绘制F绿色和L,然后绘制KHJ已经是红色的,所以我们停止那个方面,但继续前进,绘制G绿色。然后我们回到I,但它是红色的,所以我们停下来。所以选择EI,以及JHK(按某种顺序)。

L副本:合并是一个问题

当Git选择提交给 copy 时,它会使用你的另一个(不是 - git rebase)参数作为&#34;红色油漆&#34;停止项目的一部分,以及当前提交作为&#34;绿色涂料&#34;部分。如果您不使用--onto,则目标是与红色选择器相同的。所有--onto都可以:它可以让您选择不同的&#34;停止&#34;红色油漆选择器比目标。

但是如果这里有合并 - 在你的情况下,我们有一个问题,或者真的,两个问题。一个是rebase 无法复制合并,所以它甚至都没有尝试。它只是完全从合并提交中删除合并。另一个是我们遵循分支和合并的双腿,但除非我们使用交互式(--onto,否则我们无法控制订单 )rebase。

你在-i并且跑了:

master

所以选择:

git rebase --onto newDad oldDad1

作为要复制的提交,但抛出所有合并,并将提交的其余部分线性化。这意味着你从:

开始
oldDad1..master

但最终得到:

*   006f7ab - (2 days ago) Merge branch 'hotfix' idc what will heppen :( - stav alfi (master
|\
| * 0f028e8 - (2 days ago) good - stav alfi
* | fc040d3 - (2 days ago) good - stav alfi
* | ed29b30 - (2 days ago) good - stav alfi
|/
* a7c5bb3 - (2 days ago) good branch - stav alfi
* 9d804c2 - (2 days ago) new.txt changed in 16:35 - stav alfi
* 6ada3b7 - (2 days ago) new.txt changed in 16:32 - stav alfi (oldDad)

或 - 因为我们无法控制订单:

* 0f028e8 - (2 days ago) good - stav alfi
* fc040d3 - (2 days ago) good - stav alfi
* ed29b30 - (2 days ago) good - stav alfi
* a7c5bb3 - (2 days ago) good branch - stav alfi
* 9d804c2 - (2 days ago) new.txt changed in 16:35 - stav alfi
* 6ada3b7 - (2 days ago) new.txt changed in 16:32 - stav alfi (oldDad)

(我在这里所做的就是换掉两条腿)。 Git会将提交* fc040d3 - (2 days ago) good - stav alfi * ed29b30 - (2 days ago) good - stav alfi * 0f028e8 - (2 days ago) good - stav alfi * a7c5bb3 - (2 days ago) good branch - stav alfi * 9d804c2 - (2 days ago) new.txt changed in 16:35 - stav alfi * 6ada3b7 - (2 days ago) new.txt changed in 16:32 - stav alfi (oldDad) (newDad,您的db309e9)作为临时分支检出,然后开始挑选每个提交,将--onto转换为更改,方法是将其与{{1}进行比较}}。但这立即失败了:

6ada3b7

这里的问题是提交f6497fcerror: Failed to merge in the changes. Using index info to reconstruct a base tree... A new.txt Falling back to patching base and 3-way merge... CONFLICT (modify/delete): new.txt deleted in HEAD and modified in new.txt changed in 16:32. Version new.txt changed in 16:32 of new.txt left in tree. 不存在。 Git不知道如何结合并对new.txt&#34;做出轻微改动。与&#34;根本没有db309e9&#34;。

现在,您可以通过决定如何在最终快照中显示new.txt来解决此冲突。编辑或删除工作树中的文件,完成后,new.txt结果并运行new.txt,Git将继续尝试挑选下一个提交。

重复此过程直到git add复制了所有要复制的提交。一旦完成,git rebase --continue告诉Git&#34;剥离&#34;原始分支标签(git rebase)并将其粘贴到刚刚进行的最后一次提交。所以现在git rebase分支将命名最新的提交,它将指向其父级,依此类推。原始提交 - 你复制的那些 - 仍然存在于存储库中一段时间​​,但它们现在被放弃了#34;来自这个分支:他们没有名称master可供查找。

但是现有的分支名称仍然可以找到现有的提交

名称mastermaster仍指向此处的一些原始(未复制)提交。这些名称仍然会找到那些原始提交。如果有更多的名称记住了一些复制的提交,那些名称仍然会记住原件。因此,复制的提交不仅没有消失,有时它们仍然可见,具体取决于分支名称。

请注意,您的最终合并已经消失

由于oldDad甚至尝试来复制合并,因此您的合并提交将完全省略。但是,因为两条腿都是&#34;如果适当地解决了任何冲突,那么合并得到(按某种顺序),最终的源树将匹配。这将是多么艰难或容易取决于首先完成哪条腿以及两条腿是否相互影响。

oldDad1标志

一种让git rebase尝试保留合并的方法。但它不能实际保留它们。相反,它的作用是复制前叉的每一条腿,但这一次,通过分叉两条腿;然后当它到达合并提交时,它会运行一个 new --preserve-merges来进行 new 合并--Git希望 - &#34;同样好& #34;作为原作。

在这种特殊情况下,git rebase无法帮助解决当前问题,因为这发生在分支和重新合并序列之前。这个git merge文件在第一次提交中被修改,但是你的起点不存在,在分支和合并序列之前发生。 --preserve-merges对你有用,我不知道。