Git rebase:使用上游代码覆盖本地冲突提交的子集 [英] Git rebase: override subset of local conflicting commits with upstream code

查看:84
本文介绍了Git rebase:使用上游代码覆盖本地冲突提交的子集的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我们分叉了一个开源项目,并在特定分支的顶部开发了功能.现在我正在尝试使用新的上游分支重新设置我们的代码.但是,rebase 会提供许多冲突错误消息.我希望覆盖与传入提交的冲突子集,但我找不到如何做到这一点.

We forked an opensource project and developed features on top in a particular branch. Now I am trying to rebase our code with the new upstream branch. However, the rebase gives many conflict error messages. I wish to override a subset of conflicts with the incoming commits but I could not find how to do that.

以下命令将覆盖与上游代码的所有冲突:

The following command would override all conflicts with upstream code:

git rebase -Xours upstream/branch

但我只想覆盖那些与文档相关的提交(而不是与代码相关的提交).

But I only want to override those commits related to documentation (not those related to code) .

有什么方法可以实现吗?

Any way to achieve this ?

推荐答案

注意:这是对稍微修改过的问题的回答(请参阅长评论线程).这是修改后的问题:

Note: this is an answer to a somewhat modified question (see the long comment thread). Here's the modified question:

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

We have some branch B we made in a local repository, based on an upstream repository and its branch upstream/U, which used to identify commit 1234567.

上游存储库已由其所有者更新,现在 upstream/U 标识提交 89abcde.123456789abcde 之间有很多提交和很多变化.

The upstream repository has been updated by its owner, and now upstream/U identifies commit 89abcde. There are many commits and many changes between 1234567 and 89abcde.

我们选择了重新定位我们的分支B,它有很长的提交列表,这样我们所有的提交都将在89abcde 之后应用.也就是说,我们现在有:

We have chosen to rebase our branch B, which has a long list of commits, so that all of our commits will be applied after 89abcde. That is, we now have:

  ...--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

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

Therefore, we started this operation by doing:

git checkout B
git rebase upstream/U

但是,在此过程中,我们遇到了很多情况,在将提交 B1 复制到 B1,将 B2 复制到 B2,等等,我们就会发生冲突.

Along the way, though, we hit a lot of cases where, while copying commit B1 to B1, B2 to B2, and so on, we get a conflict.

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

Resolving these conflicts sometimes means "take the upstream version of the file". This is true for all the documentation files, for instance. It's also true for some cases where the only file conflicting is named .gitreview.

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

If we run git checkout --ours .gitreview to take their version of the file, and attempt to continue like this, Git says:

# 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'?
#

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

What does this mean and how should we proceed?

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

Now, as shown in the illustration within the question, what git rebase is doing is copying each commit, B1, B2, ..., B1000, for every commit that is on branch B that is not on branch upstream/U. To copy a commit, Git essentially runs either git cherry-pick (some variants of git rebase use this) or git format-patch followed by git am.

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

Either one turns each commit into a diff (a changeset, in Version Control System terms) that can then be applied to some other commit. When using git cherry-pick this changeset will be applied through Git's three-way merge machinery, with the merge base being the parent of the cherry-picked commit. The two branch tip commits are the current commit, which is the left side or "local" or --ours commit that I call L, and the commit being cherry-picked as the right side or "remote" or --theirs commit that I call R.

请注意,这些变更集一次应用一个,之后 Git 进行新的提交.Git 首先检查提交 89abcde,使用 Git 所谓的分离 HEAD"模式,因此提交 89abcde 是索引和工作树中的内容,并且有根本没有当前的分支.然后 Git 会挑选提交 B1.由于 current 提交是 89abcde,我们将上游代码称为 --ourslocalL 版本.由于现在应用的提交是 B1,我们将从分支 B 引用我们自己的代码作为 --theirsremoteR 版本.

Note that these changesets are applied one at a time, after which Git makes a new commit. Git starts by checking out commit 89abcde, using what Git calls its "detached HEAD" mode, so that commit 89abcde is what is in the index and work-tree and there is no current branch at all. Git will then cherry-pick commit B1. Since the current commit is 89abcde, we will refer to the upstream code as --ours or the local or L version. Since the commit being applied now is B1, we will refer to our own code from branch B as --theirs or the remote or R version.

假设来自 B1 的变更集成功应用于 89abcde,Git 使用来自提交 B1 的日志消息进行新的提交.这个新提交现在是 B1',我们分离的 HEAD 现在指向这个新的 B1':

Assuming the changeset from B1 is successfully applied to 89abcde, Git makes a new commit, using the log message from commit B1. This new commit is now B1', and our detached HEAD now points to this new B1':

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

这个过程现在用 B2 重复,并且一遍又一遍地重复尽可能多的提交(在本例中为 1000),直到我们筋疲力尽并彻底厌倦 Git.:-)

The process now repeats with B2, and repeats over and over again for as many commits as there are to copy (1000, in this example), until we are exhausted and thoroughly sick of Git. :-)

当使用 git am 时,更改集的应用略有不同,但通常结果是相同的.关键的区别在于 Git 不会立即求助于三向合并;相反,它首先尝试将差异直接应用于 git format-patch 输出中命名的文件.如果差异应用失败, Git 会提取差异应用失败的文件的合并基础版本.差异将正确应用于合并基础版本,生成文件的 R 版本.(L 版本只是现有工作树中的一个.)

When using git am, the change-set is applied slightly differently, but usually the result is the same. The key difference is that Git doesn't immediately resort to the three-way merge; instead, it first attempts to apply the diff directly, to the file named in the git format-patch output. If the diff fails to apply, then Git extracts a merge-base version of the file to which the diff failed to apply. The diff will apply correctly to the merge-base version, producing the R version of the file. (The L version is simply the one in the existing work-tree.)

git cherry-pick 一样,merge base 版本是该文件的任何版本存储在提交的 提交中我们现在正在复制.因此,当回退时,Git 会将合并基础版本与 HEAD 版本进行比较.

As with git cherry-pick, the merge base version is whatever version of that file is stored in the parent commit of the commit we're copying right now. So when falling back, Git diffs the merge base version against the HEAD version.

现在让我们看看 .gitreview 会发生什么.假设我们正在复制提交 B11.文件 .gitreview 包含提交 B11 中的分支 foo 的名称,但它包含不同的分支名称 bar> 在我们作为 B10' 进行的提交中.在将 B11B10 进行比较时,Git 已将 .gitreview 标识为已更改,因此它位于变更集中.事实上,它是变更集中的唯一文件:B11 完全由对 .gitreview 的更改组成.

Let's look now at what happens with .gitreview. Suppose we're copying commit B11. The file .gitreview contains the name of a branch foo in commit B11, but it contains a different branch name bar in the commit we made as B10'. Git has, in diffing B11 against B10, identified .gitreview as changed, so it's in the changeset. In fact, it's the only file in the changeset: B11 consists entirely of a change to .gitreview.

我们现在选择不改变,所以我们运行:1

We now choose not to take our change, so we run:1

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

并收到投诉.

我们收到此投诉的原因是此修改后的 .gitreview 文件是唯一的更改.通过使用 git checkout --ours .gitreview 提取与提交 B10' 一起使用的 .gitreview 版本,而不是使用的版本使用 B11,我们使当前的索引和工作树匹配提交 B10'.

The reason we get this complaint is that this modified .gitreview file was the only change. By using git checkout --ours .gitreview to extract the version of .gitreview that goes with commit B10', rather than the version that goes with B11, we've made the current index-and-work-tree match commit B10' exactly.

Git 注意到与 HEAD(即 B10')相比,索引和工作树没有任何变化,并抱怨.

Git notices that there's nothing changed in the index and work-tree as compared with HEAD (which is B10'), and complains.

此时的解决方案是运行:

The solution at this point is to run:

# git rebase --skip

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

which tells Git, in effect, we no longer want commit B11 after all: just drop it from the list of commits to copy.

Git 将继续提交 B12.这可能会更改 .gitreview,也可能不会.如果没有,我们不会在那里看到任何问题.

Git will go on to commit B12. This may have a change to .gitreview, or it may not. If it doesn't, we won't see any problem there.

如果 B12 确实.gitreview 进行了更改,我们可能会遇到冲突,也可能不会,具体取决于:

If B12 does have a change to .gitreview, we may get a conflict, or we may not, depending on:

  1. 我们是在做 git am 还是 git cherry-pick?
  2. 如果我们在做 git am,补丁是否干净地应用了?
  3. 如果我们因为 git cherry-pick 而进行三向合并,或者因为补丁在 (2) 中没有完全应用,是否存在合并冲突,还是不?
  1. Are we doing git am or git cherry-pick?
  2. If we're doing git am, did the patch apply cleanly, or not?
  3. If we're doing a three-way merge because of git cherry-pick, or because the patch didn't apply cleanly in (2), is there a merge conflict, or not?

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

Assuming B12 copies without conflict, we'll now have:

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

注意 B11 是如何被简单地跳过的.Git 将继续复制 B13,依此类推.

Note how B11 is simply skipped. Git will proceed with copying B13, and so on.

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

Remember, in all cases, all Git is doing is a line-by-line application of diffs. It has no idea what these diffs mean. It just knows that they can be applied, or cannot; or that they conflict with each other (when combining two diffs via three-way merge), or do not.

您可以在每次提交的每个文件中获得一个合并冲突.因此,如果有 1000 个提交要复制,并且每个提交中有 1000 个文件,您可能会遇到多达 100 万个合并冲突.有可能大多数提交最多更改几个文件,并且大多数更改应用干净或没有合并冲突;所以很可能每次合并最多会有几个冲突,最多有几千个需要解决.

You can get one merge conflict per file, per commit. So if there are 1000 commits to copy, and 1000 files in each commit, you could have up to 1 million merge conflicts. Chances are that most commits change at most a few files, though, and most changes apply cleanly or have no merge conflicts; so it's pretty likely that there will be at most a few conflicts per merge, for at most a few thousand of these to resolve.

尽管如此,您可能希望使用一些策略而不是复制所有 1000 个(或多个)提交.

Nonetheless, you may want to use some strategy other than copying all 1000 (or however many) commits.

1这些 # 提示让我有点担心:它们建议您以 root 用户身份执行所有这些操作.对于非 root 用户,通常的 sh/bash 提示是以 $ 或以 $ 结尾.与 root 以外的人一样,尽可能多地活"起来通常是最明智的.

1These # prompts concern me slightly: they suggest you're doing all this as user root. The usual sh/bash prompt is or ends with $ for non-root users. It's generally wisest to "live" as much as possible as someone other than root.

这篇关于Git rebase:使用上游代码覆盖本地冲突提交的子集的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆