我有一个我从未遇到的git问题。
我有2个分支,有2个文件不同。
如果我将一个合并到另一个(任何一个方向),它会说“已经是最新的”。
为什么它认为它们是最新的,我怎样才能让它合并?
我读过这篇文章git merge does not merge(和其他人),他们都说“如果它说得最新,那么你已经正确合并了。”
我意识到git 认为它们被合并了,但它们不是 - 我在2个分支中的文件不同。
即使我进入并进行更改并且我合并它仍然说'已经是最新的'。
我可以让git“忘记”我已合并它们并再次合并吗?
我不知道在这种状态下发生了什么,但这是一场噩梦。必须有一些方法来解决这个问题。
用我所做的更新:
我是唯一一个在此工作的人。我被要求添加一些新功能,所以我从master,new_features创建了一个分支,并开始研究它们。然后我被告知将它放在一边并开发更高优先级的新功能,所以我在master,new_features2上创建了另一个分支,然后我开始研究这些功能。然后我被告知我们需要一些bug_fixes,然后我做了另一个分支,bug_fixes。我修复了bug并将bug_fixes合并到master中。然后我回到工作new_features2,让那些东西工作,将master合并到new_features2,测试,然后将new_features2合并到master。到目前为止都很好。
现在我回到了new_features工作。我完成了这些工作,并将master合并到new_features中。它合并了一些东西。我测试,我发现new_features2的东西和bug_fixes都没有正常工作。我看一下合并的代码,似乎new_features2和bug_fixes的一些变化都在new_features中,但不是全部。我尝试再次合并 - 它说“已经是最新的。”
答案 0 :(得分:5)
我把rebase部分放在顶部,但你可能想先阅读其余部分。
git中最有用的东西之一就是挑选一个完整的" branchlet" (以分支提示结尾的提交字符串)实际上移动它们。也就是说,假设您现在对采摘樱桃感到满意(如果需要,请参见下文),让我们假设您在提交图中有类似的内容:
o--o <-- fix-bug-1234
/ \
...--o--o--o---o--o--o <-- develop
\
o--o--o--o <-- feature
你刚刚开始feature
工作,然后不得不对develop
进行一些紧急更改,并且现在已经合并了一个紧急错误修复程序。现在是时候回到feature
上工作了。
但是,确定feature
的{{1}}版本从develop
中的最新好东西开始,确实会很不错。
所以,让我们做到这一点。我们首先根据develop
的提示检查新的功能分支:
git checkout -b new-feature develop
给我们这个:
o--o <-- fix-bug-1234
/ \
...--o--o--o---o--o--o <-- develop, HEAD->new-feature
\
o--o--o--o <-- feature
现在我们只是挑选所有四个feature
提交。我们可以编写feature~4..feature
,但这需要我们计算提交,所以既然我们知道所有关于git set符号, 1 我们使用develop..feature
来找到同一组提交。这允许我们使用第二个git命令:
git cherry-pick develop..feature
复制这四个提交:
o--o <-- fix-bug-1234
/ \
...--o--o--o---o--o--o <-- develop
\ \
\ o--o--o--o <-- HEAD->new-feature
\
o--o--o--o <-- feature
现在我们将feature
重命名为old-feature
,然后将new-feature
重命名为feature
:
git branch -m feature old-feature
git branch -m new-feature feature
或者我们使用git rebase
以简单的方式完成所有这些操作。我们不必从git checkout -b
开始,然后是git cherry-pick
和两个重命名步骤,我们只需执行一个git checkout
后跟一个git rebase
:
git checkout feature
git rebase develop
rebase
命令使用我们的一个参数develop
来:
develop..feature
develop
rebase
有更好的形式让我们将提交列表与樱桃选择分开,但对于典型情况,我们不需要这些
请注意,当我们执行rebase时,rebase命令通常忽略复制中的合并提交,但副本提交&#34;后面&#34;合并提交,除非它们被设置符号(develop..feature
东西)排除。当序列中有合并提交时,这会产生令人惊讶的结果。
此外,因为重新定位副本提交 - 实际上是一系列的挑选操作 - 如果你<
1 如果你没有知道这套符号,你就会遗漏一些使git生活更轻松的东西。
你错过了合并的重点 - 或者说合并点缺少你想做的事情会更准确。
让我们首先看看合并点。
假设你和Alice正在做两件事。你应该让小部件左右切片,而不是仅向右切片。爱丽丝应该让横杆保持更好。
你们两个都是从一个公共基础开始的,其中小部件只向右切片,而且交叉开关会中断。
本周你设法让小部件向左切片,并且Alice修复了横杆。所以你(或爱丽丝)现在想要把你的工作和她的工作结合起来:
$ git checkout larry-widget-fixes
$ git merge alice-crossbar-fixes
如果这使得您的所有文件都与Alice的文件完全匹配,那么将从本周撤消您的所有工作。
因此,git merge
不会使事情匹配。相反,它会返回公共基础以查看(1)您更改的内容,以及(2)Alice更改的内容。然后,它尝试将这两组更改合并为一个组合(合并)更改。
在这种情况下,它通过将她的更改添加到您的身份来实现(因为我们将她的工作合并到您的工作中,而不是将您的工作合并到她的工作中)。
请注意,合并基础 - 共同的起点 - 是此处的关键项目之一。
我们假设你不想结合一些变化。你想要使一些文件匹配。
有很多方法可以做到这一点,具体取决于你想要的结果。让我们看看其中三种方式。
通过提取其他版本,只需将一个文件匹配。
让我们说在修理小部件时,你不小心打破了垫片。 Alice的分支有shims.data
的好副本(就合并基础而言,就此而言,让我们只使用Alice这里):
git checkout alice-crossbar-fixes -- shims.data
这会从Alice的分支提示中提取文件shims.data
的版本,现在您拥有了所需的文件。
只需通过提交ID取消一次提交。
让我们说你意识到你在提交badf00d
中打破了垫片,你在那里做的所有其他改变也应该撤消,或者撤销它们也没关系。然后你可以运行:
git revert badf00d
git revert
命令将给定的提交转换为补丁,然后反向应用补丁,以便取消该提交的效果,并从结果中进行新的提交。
你想完全放弃一切。你意识到你本周所做的一切,虽然它使小部件向左和向右切片,但这是一个错误,因为小部件根本不应该向右切片。您可以在此处使用git reset
。
我不会举一个例子,因为这(a)这可能不是你想要的; (b)git reset
过于复杂(git人们将大约五到八个逻辑上不同的操作塞进一个命令中); (c)StackOverflow上已经有很多很好的例子。 (对于步骤2中的git revert
也是如此,但git revert
更加简单,所以我把它留在了。)
这更复杂。
请记住,当我们在第一个例子中进行合并时,我们有一个共同的起点,在您开始之前的一周开始,Alice开始了不同的任务:
...--o--*--o--o--o <-- larry-widget-fixes
\
o-o-o-o-o <-- alice-crossbar-fixes
公共合并基础是提交*
。
但是,一旦完成合并,我们就有了这个(我将标记为合并提交M1
以便于查看):
...--o--o--o--o--o--M1 <-- larry-widget-fixes
\ /
o-o-o-o-o <-- alice-crossbar-fixes
现在假设Alice再做一些修复,让你再做一次修改:
...--o--o--o--o--o--M1-o <-- larry-widget-fixes
\ /
o-o-o-o-*--o--o <-- alice-crossbar-fixes
现在你再次合并来选择这些:
git merge alice-crossbar-fixes
和git找到公共合并库。这是两个分支上最近的提交,我再次用*
标记。 Git会找出自*
以来Alice更改的内容,以及自*
以来您更改的内容,并尝试合并这些更改。你已经完成了Alice之前所有的更改 - 他们已经在你的分支中,M1
- 所以git不会再看那些是好事。 Git会尝试将Alice自*
以来的新更改添加到自*
以来的更改(您的Alice版本更改)。
但是,如果你弄错了怎么办?如果您的Alice版本在M1
中的更改是一堆错误的文件会怎么样?
此时您必须选择是否重试合并,或者使用git的许多其他工具来解决问题。
要重试合并,您必须备份到合并发生前的某个点。例如,假设我们在M1
之前的提交中绘制一个新箭头,如下所示:
..........<-- retry
.
...--o--o--o--o--o--M1-o <-- larry-widget-fixes
\ /
o-o-o-o-o--o--o <-- alice-crossbar-fixes
(想象一下它是一个箭头,而不仅仅是一些蹩脚的ASCII点,无论如何)。我们可以将现有的分支标签留在那里,指向它们的位置,我们只需添加这个新的retry
:
git branch retry <commit-id> && git checkout retry
或:
git checkout -b retry <commit-id>
(这些基本上都做同样的事情,让我们retry
指向所需的提交。现在,如果我们这样做:
git merge alice-crossbar-fixes
git会尝试将M1
指向的提交前 - retry
与alice-crossbar-fixes
的(新的,当前的)提示合并。 Git将再次找到一个合并库,这是这两个分支上最近的提交,这是我们原来的合并基础。
换句话说,我们将重新进行合并,忽略错误的合并M1
,但也忽略了从那时起所做的新工作。
我们可以git checkout -b <commit>
在任何特定提交上返回该状态,然后开始执行git cherry-pick
之类的操作以重新添加特定提交。
让我们说我们喜欢commit-before - M1
很多(它看起来非常接近),所以我们让新的分支 - 让它称之为{{1这点指向:
retry
现在,我们可以使用git checkout -b rework <commit-ID>
从其他提交中进行选择,只提取所做的更改。我们为它提供了更多的提交ID,更多的git cherry-pick
字符串,并将该提交之前的状态与该提交的状态进行比较。结果是一个补丁,但事实上,它比常规的diff-only补丁更好一些,原因有两个:
省略第二个优势并将其视为补丁,197a3c2...
做的是将补丁应用于当前提交,然后进行新提交,重新使用原始提交消息。
如果你想以这种方式重新申请多次提交,你可以提交git cherry-pick
多次提交,它会全部完成:
git cherry-pick
这适用于git cherry-pick xbranch~3..xbranch
上的每个提交,除了从xbranch
及更早开始的提交,即我们沿xbranch~3
计算三个步骤(假设没有合并...)和应用剩下的三个提交:
xbranch
(并参见顶部的变基)