0

我创建了一个本地存储库来学习 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 状态(运行后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")

我的问题:

我无法理解我得到的错误以及以下词语的含义:倒带、重播、应用。我也不知道如何解决冲突。

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

非常感谢!

4

1 回答 1

2

好的,深呼吸:-)

Git 的rebase 副本提交

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

git checkout -b newbranch 23dfc61

这使得23dfc61当前提交,但给它一个新的分支名称, newbranch。现在我们可以进行新的提交,添加到新的分支,所以现在我们运行,例如:

git cherry-pick 9d804c2

复制提交9d8042c

结果,如果这行得通——如果没有合并冲突,或者在你清理任何合并冲突(如果有的话)之后——是一个新的提交,它的父级是23dfc61,并且它的源树是这样的,但与你相比,23dfc61你改变了什么,添加到它:9d804c26ada3b7

...
* 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. 但是新提交与旧提交具有相同的日志消息,并且与旧提交进行相同的更改

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

每个提交都附有该提交时的完整源代码。这与许多其他版本控制系统不同:大多数人倾向于将每个提交存储为它们之前的提交或之后的提交的更改。这意味着为了复制提交,Git 首先必须找出发生了什么变化。

找出方法是将提交与其父提交进行比较。9d804c2is的父提交6ada3b7,所以 Git 会:

git diff 6ada3b7 9d804c2

看看有什么变化。假设日志消息是准确的,您更改了 中的某些new.txt内容,这就是 Git 将找到的内容。那么,这也是Git 在尝试修改保存的23dfc61快照以提供NNNNNNN.

如果成功,Git 将提交结果,并且成功地进行了挑选。

任何提交不能更改

通过获取每个提交的确切内容来构造不可发音的哈希 ID23dfc616ada3b7badf00d等等。bedface如果您尝试更改 任何提交的任何内容,Git 会构建一个新提交;如果任何地方甚至有一点不同,你就会得到一个新的、不同的哈希,所以你会得到一个新的、不同的提交。

进入此部分的部分包括所有源以及父 ID,因为每个提交“指向”(包含其 ID)其父级。(还有一些时间戳,所以除非您在同一秒内进行两次相同的提交,否则您仍然会得到两个不同的 ID,即使它们的其余部分相同。)因此,要更改任何内容——无论它是源,或者只是一个父 ID——Git必须复制提交。

这就是 rebase 复制提交的原因:它必须. 您正在进行一组提交,将每个提交转换为更改,然后从某个不同的提交开始应用这些更改,该提交具有不同的父 ID,即使它具有相同的源树也是如此。因此,您提供git rebase的基本上是两块信息:

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

如果您使用,复制的地方很容易--onto,因为那就是地方!但是,要复制的一组提交比较棘手。

选择提交

Git 提供了一个范围表示法,X..Y看起来它的意思是“X 和 Y 之间的提交”——确实如此。但不完全是!事实上,Git 使用我们称之为可达性的东西,在提交中遵循父链接。我们已经注意到每个提交都存储了一个父 ID。这就是Git如何找到你的提交:你告诉它从一个分支 tip开始,使用一个分支名称mastermaster

该提交中有另一个哈希 ID:这是提交的父级。Git 使用该哈希 ID 来查找提交。父节点还有另一个哈希 ID,Git 不断寻找越来越多的父节点。这种情况会一直持续下去,一直持续到您所做的第一次提交。

提交太多了,所以我们告诉 Git在某个时候停止返回。那是 的“Y”部分X..Y:这告诉 Git从 Y 开始并向后工作,暂时将提交标记为“绿色”以接受它们。但是,与此同时,从 X 开始并向后工作,将提交标记为临时“红色”以避免将它们带走

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

...--D--E--F--G--H   <-- branch

这里 commitH是分支的尖端G,是H的父级,FG的父级,以此类推。如果我们写,那会(和背面)E..H涂上“红色”:停下,不要拿这些!然后它涂成绿色,然后是and ,然后我们撞到红色并停下来。这样就选择了commits 。 这里自然被排除在外。EDHGFEF-G-HE

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

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

提交K合并提交。合并提交是具有两个(或更多,但我们不要去那里)父母的提交。如果我们坚持使用红色和绿色油漆的类比,E..L则意味着“E在背面涂上红色并L在背面涂上绿色”:当我们点击 时,我们同时K涂上绿色,并在此分支/合并的两侧重新工作。 H J

如果我们说G..L,看看它是如何工作的:我们画G红色,然后F,然后E等等D。我们从不画画I,因为那不是倒退:在这个过程中,F我们只能后退,不能前进。然后我们画L绿色, and K,然后两个Hand JG已经是红色了,所以我们停止了那一边,但继续往前走,涂上I绿色。然后我们回到E,但它是红色的,所以我们停下来。所以会选择Iand J, and also H, and Kand L(以某种顺序)。

什么git rebase副本:合并是个问题

当 Git 选择要复制的提交时,它使用您的另一个(非--onto)参数作为停止项的“红色油漆”部分,并将您当前的提交用作“绿色油漆”部分。如果不使用--onto,则 on 目标与 red-paint 选择器相同。就是--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)

或者——因为我们不控制顺序:

* 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)

(我在这里所做的只是交换两条腿)。Git 会将提交 db309e9(newDad, your --onto) 作为临时分支签出,然后开始挑选每个提交,6ada3b7通过将其与f6497fc. 但这立即失败了:

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.

这里的问题是commitnew.txt 中不存在db309e9。Git 不知道如何将“稍微改变一下new.txt”与“根本没有”结合起来new.txt

现在你的工作就是解决这个冲突,决定如何new.txt出现在最终快照中。编辑或删除工作树中的文件,完成后,git add结果并运行git rebase --continue,Git 将继续尝试挑选下一个提交。

这一直重复,直到git rebase复制了所有要复制的提交。完成后,git rebase告诉 Git “剥离”原始分支标签 ( master) 并将其粘贴到它刚刚提交的最后一次提交中。所以现在master分支将命名最新的提交,它将指向它的父级,依此类推。原始提交(您复制的那些)暂时仍在存储库中,但现在它们已从该分支“放弃”:它们没有master可用的名称来找到它们。

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

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

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

因为git rebase甚至不尝试复制合并,所以您的合并提交将被完全省略。但是,由于合并的两个“分支”都被应用(以某种顺序),最终的源代码树将匹配,只要您适当地解决任何冲突。这将是多么困难或容易取决于先完成哪条腿以及两条腿是否相互影响。

一面--preserve-merges旗帜_

一种方法可以git rebase尝试保留合并。但它实际上无法保存它们。相反,它的作用是像以前一样复制叉子的每条腿,但这次是通过分叉两条腿;然后当它到达合并提交时,它会运行一个 git merge的合并来进行一个的合并——Git 希望它与原来的“一样好”。

在这种特殊情况下,--preserve-merges不会帮助解决眼前的问题,因为这发生在分支和重新合并序列之前。该new.txt文件在您挑选的第一次提交中被修改,但在您的起点中不存在,发生在分支和合并序列之前。对你--preserve-merges有没有用,我不知道。

于 2017-03-18T12:42:36.067 回答