GIT - Rebase - 如何处理冲突

时间:2017-02-23 19:23:30

标签: git conflict rebase

我正在开发一个有两个分支的项目:masterfeature

feature分支是在不久前创建的,并且有很多提交。

自创建feature分支以来,master已提交了几个提交

此时,当我从rebase离开master时,我发生了冲突。我解决了它们然后rebase --continue。然后我再次发生冲突,并再次解决并rebase --continue。这种情况一再发生,很多时候看起来它们都是出现的冲突。

在我看来,这里正在发生的事情:

master(commits)->a->b feature(commits)->c->d->e->f->g

featuremaster->a分支,然后创建了所有提交。

当我rebase时,它会回到feature的开头,mastermaster->b开始应用feature->c,然后开始应用master指出它有冲突。我解决(接受feature->d更改)并继续。现在它尝试应用continue并发现相同的冲突。我必须再次解决并master->a <div id="foo"> master->b <div id="bar"> feature->c <div id="fubar"> feature->d //Nothing has changed, inherited from feature->c <div id="fubar"> 。这种情况一遍又一遍地发生。

例如,以下是更改:

feature->c

我假设当它达到foo时,它会将fubar更改为foo,然后它会注意到bar已更改为bar。我决定feature->d,然后应用rebase --continue

执行相同的逻辑

我的两个问题:

1)我对git如何工作/处理提交/冲突/变基的理解是否正确?

2)我怎样才能避免不得不一遍又一遍地解决同样的冲突?我正在考虑压缩功能分支上的所有提交,以便只有一个可以处理。我不确定这是一个好主意还是确切地说是在场景中挤压的最佳方式。

注意,这是一个非常简化的例子。实际上,我有很多提交,并且在每个众多文件中都存在大量冲突。其中一些在整个commit流程中看起来是相同的,有些是function uniteUnique(arr) { var args = Array.prototype.slice.call(arguments); var result = []; var helper; for (var i=0; i<args.length; i++){ console.log(args[i]); } args.reduce(function(acc, arg){ console.log(arg + ' is the arg'); helper = arg.map(function(val){ if (result.indexOf(val) < 0){ result.push(val); } return result; }); return acc + helper; }, result); } 新的。

我的最终目标是尽可能简单地清理这个项目(让功能分支从当前主服务器中重新定位)。我不关心提交的历史。

3 个答案:

答案 0 :(得分:10)

你的心理形象很接近,但让我们确切地说:

...--o--*--A--B          <-- master
         \
          C--D--...--Z   <-- feature

这就是您现在所拥有的:名称master指向提示 B(每个单字母或o或{{1}表示提交)。每个提交都返回(left-ish)到之前的提交。两个提交*A仅在B上。一堆提交仅在master上,即featureC。提交Z和所有之前的(进一步左)提交都在两个分支上。

*做的是通过提交git rebase以及*的提示向后工作来找到提交Z。然后它知道它需要复制提交C-Z。副本将在主人之后立即开始。如果我们使用masterC'的副本命名CD'的副本,依此类推,最终的将会显示像这样:

D

我认为您已经意识到,通过将每个提交(这是一个完整的快照)转换为一组更改,每个副本一次进行一次提交。要获得 C'-D'-...-Z' <-- feature / ...--o--*--A--B <-- master \ C--D--...--Z [abandoned] 已更改的内容,Git会:

C

然后Git尝试将此更改应用于git diff <hash-of-*> <hash-of-C> 中的快照,但它并不容易应用,因此Git尝试进行合并:它将B*进行比较并且认为应该将B更改为<div id="foo">,根据这一点;但根据<div id="bar"> - vs - <div id="foo">,它应该更改<div id="fubar"> *。这是第一次合并冲突。

所以,你解决这个并提交(好吧,C提交):

git rebase --continue

然后Git继续复制 C' <-- HEAD (rebase in progress) / ...--o--*--A--B <-- master \ C--D--...--Z <-- feature ,将DD区分开来以获得补丁。

这次,应该不会C改为<div id="foo">,因此不应该在此处获得合并冲突。要确定,请尝试<div id="fubar"> - 提交git show,同样将其与D进行比较,从而产生差异。

然而,您可能会被白色空间变化或类似情况所困扰。值得仔细查看每个C输出,并检查诸如行尾属性之类的内容(如果您正在使用这些属性)(CRLF转换)。行尾结束通常会影响每个文件中的每一行,但您可以获得单行问题,例如,取决于谁使用了哪个编辑器。

我怀疑更可能出现问题:git diff正在错误的行上进行同步。它找到一些匹配的微不足道的东西,例如读取git diff的行,并使用它们来决定两个文件重新同步,然后选择错误的更改&#34;。如果是这种情况,那么治愈 - 如果有的话 - 是指示Git使用更聪明的差异。特别是,} diff尝试不在普通行上同步,而是仅在重要(即非重复)行上同步。这可能会或可能不会实际帮助 - 在您的提交中运行patience(或git diff --diff-algorithm=patience使用相同的参数)可能会告诉您。是否可以让rebase使用不同的diff算法取决于你的Git版本。

除了为什么Git似乎在重复这一变化之谜,当git show - vs - C中只有其他更改时,你有一件事< em>可以提供帮助就是使用git rerere让Git 重新 - 使用 re 有线重新解决方案(因此得名)。基本上,当Git遇到合并冲突时,如果启用了D,Git会将冲突的部分写入其数据库,然后当您rerere解析文件时,Git会写入解决方案(与原始冲突配对) )。然后,下次Git遇到合并冲突时,它会检查保存的rerere数据:如果冲突是针对同一个补丁,它会拉出记录的分辨率,并在不与您交互的情况下使用它。

您确实可以通过在git add上提交feature(使用交互式rebase)来压缩部分或全部功能提交。由于这些补丁不会与自己发生冲突 - 至少只要它们被回放&#34;顺序&#34; - 这可以让你减少需要解决的提交数量。无论你应该做到这一点取决于你:一般来说,如果你正在变基础,你无论如何都会得到新的替代提交,所以你也可以做出新的&#34;尽可能的&#34; 34;用于将来的调试或其他代码探索。显然,最好有五个&#34;做一些有趣的事情,但几乎是独立的&#34;提交而不是50&#34;做一件事的片段,做另一个片段,修复前一个片段,做另一个片段,确定已完成的事情转移到下一件事,oops修复微小的错误,...&#34;因此,这些是非常适合挤压的候选人。但是,通常最好有五个&#34;做一件事&#34;承诺不是一个&#34;做五件事&#34;提交:这意味着如果五个中的一个中存在错误,您可以修复那个,而不必担心其他四个。

答案 1 :(得分:1)

我遇到了类似的问题,结果发现当我应该使用--ours时,我正在解决与--theirs的冲突。

原因是--ours {/ 1}} merge {/ 1}} rebase --theirs提交并重播它们的副本。

因此,当您使用rebase来解决与您的版本的冲突时:

git checkout --theirs some.file git add some.file

但在使用merge解决与您的版本的冲突时:

git checkout --ours some.file git add some.file

答案 2 :(得分:0)

当你做

git pull --rebase origin/upstream master

它会检查其他人是否有最近的代码合并。如果是这样的话,你必须让你当前的工作树与上游的工作树保持一致。

您可以通过

查看已更改的文件
git status

解决后,

git add 

git commit然后 git push -f origin <branch-name>会让你开心。