如何在使用上游代码覆盖特定的本地冲突提交时git rebase

问题描述 投票:0回答:1

我们分叉和开源项目,并在此基础上开发了功能。这些特征特别是分支

现在我试图用新的上游分支重新设置我们的代码。 rebase在错误消息中失败了许多冲突。我希望覆盖传入提交的所有错误消息,但我找不到如何做到这一点。

根据我的发现,以下命令将覆盖与上游代码的所有冲突

git rebase -Xours upstream/branch

但我希望非常明确地覆盖那些不是代码而只是文档的提交

有没有办法实现这个目标?

git rebase
1个回答
0
投票

注意:这是一个有点修改的问题的答案(参见长评论帖)。这是修改后的问题:

我们在本地存储库中创建了一些分支B,它基于上游存储库及其上游/ U分支,用于识别提交1234567

上游存储库已由其所有者更新,现在upstream/U识别提交89abcde123456789abcde之间有许多承诺和许多变化。

我们已经选择改变我们的分支B,它有很长的提交列表,所以我们所有的提交都将在89abcde之后应用。也就是说,我们现在有:

  ...--o--1234567--o--o--...--o--89abcde   <-- upstream/U
               \
                B1--B2--B3--...--B1000   <-- B

但最终会有:

                B1--B2--B3--...--B1000   [abandoned]
               /
  ...--o--1234567--o--o--...--o--89abcde   <-- upstream/U
                                    \
                                     B1'-B2'-B3'-...--B1000'  <-- B

因此,我们通过以下方式开始此操作:

git checkout B
git rebase upstream/U

然而,在此过程中,我们遇到了很多情况,在将B1提交到B1B2B2等等的同时,我们发生了冲突。

解决这些冲突有时意味着“获取文件的上游版本”。例如,对于所有文档文件都是如此。对于一些唯一冲突的文件名为.gitreview的情况也是如此。

如果我们运行git checkout --ours .gitreview来获取他们的文件版本,并尝试继续这样,Git说:

# git checkout --ours `.gitreview`
# git add .gitreview
# git rebase --continue
Applying: Update .gitreview for stable/mitaka
No changes - did you forget to use 'git add'?
#

这是什么意思,我们应该如何进行?

现在,如问题中的插图所示,git rebase正在做的是复制每个提交,B1B2,...,B1000,用于分支B上不在分支upstream/U上的每个提交。为了复制一个提交,Git基本上运行git cherry-pickgit rebase的一些变体使用它)或git format-patch然后git am

任何一个都将每个提交转换为差异(在版本控制系统术语中的变更集),然后可以将其应用于其他一些提交。当使用git cherry-pick时,这个变更集将通过Git的三向合并机制应用,合并基础是樱桃挑选提交的父级。两个分支提示提交是当前提交,它是左侧或“本地”或--ours提交,我称之为L,并且提交被挑选为右侧或“远程”或--theirs提交,我称之为R.

请注意,这些更改集一次应用一个,之后Git进行新的提交。 Git首先检查提交89abcde,使用Git称之为“分离的HEAD”模式,以便提交89abcde是索引和工作树中的内容,并且根本没有当前分支。然后Git将挑选承诺B1。由于当前提交是89abcde,我们将上游代码称为--ours或本地或L版本。由于现在应用的提交是B1,我们将从B分支引用我们自己的代码作为--theirs或远程或R版本。

假设B1的变更集成功应用于89abcde,Git使用commit B1中的日志消息进行新的提交。这个新的提交现在是B1',我们的超级HEAD现在指向这个新的B1'

                B1--B2--B3--...--B1000   <-- B
               /
  ...--o--1234567--o--o--...--o--89abcde   <-- upstream/U
                                    \
                                     B1'  <-- HEAD (detached)

这个过程现在用B2重复,并且一遍又一遍地重复进行许多提交,因为要复制(在这个例子中为1000),直到我们筋疲力尽并且彻底厌恶Git。 :-)

使用git am时,更改集的应用略有不同,但通常结果相同。关键的区别在于Git没有立即采用三方合并;相反,它首先尝试将diff直接应用于git format-patch输出中指定的文件。如果diff无法应用,那么Git将提取diff无法应用的文件的合并基础版本。 diff将正确应用于merge-base版本,从而生成文件的R版本。 (L版本只是现有工作树中的版本。)

git cherry-pick一样,合并基础版本是该文件的任何版本存储在我们正在复制的提交的父提交中。因此,当退回时,Git将合并基础版本与HEAD版本区分开来。

现在让我们来看看.gitreview会发生什么。假设我们正在复制提交B11。文件.gitreview在提交foo中包含分支B11的名称,但它在我们作为bar的提交中包含不同的分支名称B10'。 Git在B11B10的分歧中确定.gitreview已经改变了,所以它在变化集中。实际上,它是变更集中唯一的文件:B11完全由.gitreview的变化组成。

我们现在选择不接受我们的更改,因此我们运行:1

# git checkout --ours .gitreview
# git add .gitreview
# git rebase --continue

并得到投诉。

我们得到这个投诉的原因是这个修改过的.gitreview文件是唯一的变化。通过使用git checkout --ours .gitreview提取与提交.gitreview相关的B10'版本,而不是与B11一起使用的版本,我们已经使当前的索引和工作树匹配提交B10'

Git注意到,与HEADB10')相比,索引和工作树没有任何变化,并抱怨。

此时的解决方案是运行:

# git rebase --skip

告诉Git,实际上,我们不再需要提交B11:只需将其从提交列表中删除即可复制。

Git将继续承诺B12。这可能会改变.gitreview,或者可能没有。如果没有,我们不会在那里看到任何问题。

如果B12确实改变了.gitreview,我们可能会发生冲突,或者我们可能不会,这取决于:

  1. 我们在做git amgit cherry-pick吗?
  2. 如果我们正在做git am,补丁应该干净利落吗?
  3. 如果由于git cherry-pick我们正在进行三向合并,或者因为补丁在(2)中没有干净地应用,是否存在合并冲突?

假设B12副本没有冲突,我们现在将:

                B1--B2--B3--...--B1000   <-- B
               /
  ...--o--1234567--o--o--...--o--89abcde   <-- upstream/U
                                    \
                                     B1'-...--B10'-B12'  <-- HEAD (detached)

请注意如何简单地跳过B11。 Git将继续复制B13,依此类推。

请记住,在所有情况下,所有Git正在做的是差异的逐行应用。它不知道这些差异意味着什么。它只知道它们可以应用,或者不能应用;或者它们相互冲突(当通过三向合并组合两个差异时),或者不相互冲突。

每次提交,每个文件可以获得一次合并冲突。因此,如果要复制1000个提交,每个提交中有1000个文件,则最多可能有100万个合并冲突。但是,大多数提交最多只能更改几个文件,并且大多数更改都可以干净地应用或没有合并冲突;所以很可能每次合并最多会有几个冲突,最多只有几千个冲突要解决。

尽管如此,除了复制所有1000个(或多个)提交之外,您可能还想使用某些策略。


1这些#提示我稍微关注一下:他们建议你用root用户做这一切。对于非root用户,通常的sh / bash提示符是或以$结尾。通常最明智的做法是尽可能地“生活”为非root用户。

© www.soinside.com 2019 - 2024. All rights reserved.