在进行 git rebase 时,我经常难以弄清楚在解决冲突时“本地”和“远程”发生了什么。有时我的印象是他们从一个提交到下一个提交交换了双方。
这可能(肯定)是因为我还没有正确理解。
变基时,谁是“本地”,谁是“远程”?
(我使用 P4Merge 来解决冲突)
git checkout A
git rebase B # rebase A on top of B
local
是B
(变基到),remote
是A
和:
git checkout A
git merge B # merge B into A
local
是A
(并入),remote
是B
一个变基切换ours
(变基开始之前的当前分支)和theirs
(要变基的分支)。
kutschkem指出,在 GUI 合并工具上下文中:
ours
”(上游分支)theirs
” - 变基之前的当前分支。请参阅此答案最后一部分中的插图。
混淆可能与rebase的反转ours
和theirs
期间有关。
(相关摘录)
请注意,rebase 合并的工作原理是从分支顶部的工作分支重放每个提交
<upstream>
。
因此,当发生合并冲突时:
ours
' 的一侧是迄今为止重新调整的系列,以 ,<upstream>
开头theirs
' 是工作分支。换句话说,双方交换了。x--x--x--x--x(*) <- current branch B ('*'=HEAD)
\
\
\--y--y--y <- other branch to merge
,我们不会更改当前分支“B”,所以我们所拥有的仍然是我们正在处理的(并且我们从另一个分支合并)
x--x--x--x--x---------o(*) MERGE, still on branch B
\ ^ /
\ ours /
\ /
--y--y--y--/
^
their
但是在 rebase 上,我们切换到一边,因为 rebase 所做的第一件事就是检查上游分支!(在它之上重播当前的提交)
x--x--x--x--x(*) <- current branch B
\
\
\--y--y--y <- upstream branch
Agit rebase upstream
将首先将HEAD
B 更改为上游分支HEAD
(因此与之前的“当前”工作分支相比,“我们的”和“他们的”的切换。)
x--x--x--x--x <- former "current" branch, new "theirs"
\
\
\--y--y--y(*) <- upstream branch with B reset on it,
new "ours", to replay x's on it
,然后变基将在新的“我们的”B 分支上重放“他们的”提交:
x--x..x..x..x <- old "theirs" commits, now "ghosts", available through reflogs
\
\
\--y--y--y--x'--x'--x'(*) <- branch B with HEAD updated ("ours")
^
|
upstream branch
注意:“上游”概念是从其中读取数据或添加/创建新数据的参考数据集(所有 repo 或像这里一样,分支,可以是本地分支)。
local
' 和 ' remote
' vs. ' mine
' 和 ' theirs
'对我来说,问题仍然存在,即“本地”和“远程”谁(因为在 git 中重新定位时不使用“我们的”和“他们的”这两个术语,引用它们似乎会使答案更加混乱) .
kutschkem补充说,这是正确的:
解决冲突时,git 会说:
local: modified file and remote: modified file.
我很确定这个问题针对的是本地和远程的定义。在这一点上,根据我的经验,在我看来:
ours
”(上游分支)theirs
” - 变基之前的当前分支。git mergetool
确实提到了“本地”和“远程”:
Merging:
f.txt
Normal merge conflict for 'f.txt':
{local}: modified file
{remote}: modified file
Hit return to start merge resolution tool (kdiff3):
例如,KDiff3会像这样显示合并分辨率:
使用 git mergetool -t gvimdiff 调用 Vimdiff 作为合并工具。最新版本的 Git 使用以下窗口布局调用 Vimdiff:
+--------------------------------+
| LOCAL | BASE | REMOTE |
+--------------------------------+
| MERGED |
+--------------------------------+
LOCAL
:
包含当前分支上文件内容的临时文件。BASE
:
包含合并通用基础的临时文件。REMOTE
:
包含要合并的文件内容的临时文件。MERGED
:
包含冲突标记的文件。Git 已尽可能多地执行自动冲突解决,并且该文件的状态是两者的结合,
LOCAL
并且REMOTE
带有围绕 Git 无法自行解决的任何内容的冲突标记。
应该将mergetool
解析结果写入此文件。
底线
git rebase
git 合并
换句话说,LOCAL始终是原始的,而REMOTE始终是之前不存在的提交,因为它们正在被合并或重新基于顶部
证明给我看!
当然。不要相信我的话!这是一个简单的实验,您可以自己看看。
首先,确保您正确配置了 git mergetool。(如果你不这样做,你可能无论如何都不会阅读这个问题。)然后找到一个工作目录。
设置您的存储库:
md LocalRemoteTest
cd LocalRemoteTest
创建初始提交(使用空文件):
git init
notepad file.txt (use the text editor of your choice)
(save the file as an empty file)
git add -A
git commit -m "Initial commit."
在非主分支上创建提交:
git checkout -b notmaster
notepad file.txt
(add the text: notmaster)
(save and exit)
git commit -a -m "Add notmaster text."
在主分支上创建提交:
git checkout master
notepad file.txt
(add the text: master)
(save and exit)
git commit -a -m "Add master text."
gitk --all
此时您的存储库应如下所示:
现在进行变基测试:
git checkout notmaster
git rebase master
(you'll get a conflict message)
git mergetool
LOCAL: master
REMOTE: notmaster
现在进行合并测试。关闭您的合并工具而不保存任何更改,然后取消 rebase:
git rebase --abort
然后:
git checkout master
git merge notmaster
git mergetool
LOCAL: master
REMOTE: notmaster
git reset --hard (cancels the merge)
您的结果应该与顶部显示的结果相同。
我也是,迷茫了很久,经常做出错误的决定,不得不重新开始。
免责声明:我不是 git 专家,所以如果这里有任何问题,请纠正我!
我想我已经意识到我的困惑是因为我对 rebase 的描绘与许多人绘制的不同。这里有两张通常用来描述变基的图:
--1--2--3--4--5 \ 6--7--8
进而
--1--2--3--4--5--6--7--8
当然,这是绘制它的一种方式,但我对变基所发生的事情的感觉是:
--1--2--3--4--5 \ 6--7--8
这当然是完全一样的。但从“我们/他们”的角度来看,情况有所不同。在第二种情况下,感觉好像“我们”仍然“在”分支上(“6--7--8”),我们想从“主”那里获取更改。所以在这个世界上,“我们的”仍然是“分支”。这让我感到困惑。
但是在第一个“世界视图”中,我认为这是 Git 的视图,我们移动到 master(我们想要重新定位到的提交),然后从那里我们依次选择分支上的每个提交并应用它们。所以“我们的”最初成为“主人” 5
。6
成功应用后,“我们的”是6
,但实际上6'
是“在”主:
--1--2--3--4--5--6' \ 6--7--8
然后我们继续使用“7”。
因此,在合并中,您“处于”状态8
并将两者组合成一个新的提交,但在 rebase 中,您移动到5
并尝试将分支上的提交中的差异应用为那里的新提交。
因此,rebase 最终结果的“真实”画面应该是:
--1--2--3--4--5--6'--7'--8' \ 6--7--8
在变基之后,你就在8'
. 你的分支也是如此(我想!)。这可以(在我看来)可视化为:
--1--2--3--4--5 \ \ 6--7--8 6'--7'--8'