如何在git rebase中覆盖一个新文件?

3
所以我一直在开发一个结账功能分支,我们称之为feature-branch-development。我在这个分支上有相当数量的提交,大约15个左右。最终当我需要将master分支合并到feature-branch-development时,它停在了中途,抱怨我在feature-branch-development上添加了一个新文件,并给出了一个错误,类似于您在此分支上的更改将被覆盖,请提交或存储更改。 当然,我已经提交了所有内容,我的工作树是干净的。就像git“相信”新文件也存在于主分支上。值得注意的是,在这一点上我没有合并冲突。
我通过将feature-branch-development上的所有提交压缩并再次将master进行了合并来解决了这个问题。
因此,我的问题是为什么git会这样做?我对Git有一定的了解,我只是想提出这个问题来增加我的知识量。

我遇到了同样的问题。在阅读了你的问题之后,我尝试了像你一样压缩我的提交记录,结果也解决了我的问题,所以谢谢你。 - undefined
哎呀,这个删除了一个被.gitignore忽略的文件。所以要小心啊。我觉得被忽略的文件才是导致冲突的原因。看看我的答案吧。 - undefined
2个回答

4
这个问题如果有一个具体的例子会更容易回答。但原则上,git rebase 意味着“复制提交记录”,就像使用 git cherry-pick 一样。在这里失败的是复制步骤。
我们从分支 R(重新设置)开始,并选择一些新的目标分支 T(目标)。然后,我们确定要复制的一组提交:这些提交大多数情况下是在 R 分支上但不在 T 分支上的提交。(在稍微有点谎言的情况下,git rebase 文档 表示,这些是由 git log T..Rgit log T..HEAD 列出的提交。这基本上是正确的,除了添加了三个额外的小把戏,或者说“减去”可能是正确的词。)
然后,在列出要复制的提交哈希值之后,Git 对目标分支进行分离 HEAD checkout,以便当前的(HEAD)提交与分支 T 标识的提交相同。请注意,我们没有处于分支 T 上,我们只是在同一个提交上。现在,对于每个保存了哈希 ID 的提交,Git 有效地(有时也确实)运行 git cherry-pick <hash-id>
                    ....... HEAD
                    v
...--o--o--o--o--o--o   <-- T
            \
             A--B--C   <-- R

我们运行git cherry-pick A来复制提交A。如果我们将新副本称为A',则结果如下:
                      A'  <-- HEAD
                     /
...--o--o--o--o--o--o   <-- T
            \
             A--B--C   <-- R

然后我们运行git cherry-pick BB复制到B'

                      A'-B'  <-- HEAD
                     /
...--o--o--o--o--o--o   <-- T
            \
             A--B--C   <-- R

我们重复以上操作,直到所有提交都被复制,然后作为最后一步,Git将“分支标签”R从原始链中“剥离”,并将其粘贴在已复制的链的末尾。
                      A'-B'-C'  <-- R (HEAD)
                     /
...--o--o--o--o--o--o   <-- T
            \
             A--B--C   <-- (only in reflogs and ORIG_HEAD)

冲突和其他问题

每个副本——每个挑选步骤——都是使用Git的合并机制完成的,或者我称之为“合并”动词形式(与名词或形容词形式相反,即合并或合并提交——git merge通常在执行动词形式的合并后生成一个合并提交,而我们只是使用了这个提交的前半部分)。例如,在合并添加新文件的提交时,如果也添加相同的新文件的更改,则会出现增加/增加冲突。

最可能发生这种冲突的地方是从A创建拷贝A',因为挑选操作的合并基础是要复制的提交的父提交。 合并操作——“合并”动词——将此合并基础与两个提交进行比较。 在这种情况下,这两个提交是提交A本身和分支T的最高提交,即T指向的提交。 让我们称其为X,并将合并基础用*标记:

                      ? [merge in progress] HEAD
                     /
...--o--o--*--o--o--X   <-- T
            \
             A--B--C   <-- R

如果提交记录AX相对于*都添加了文件path/to/file.txt,那么就会出现一个add/add冲突。此时cherry-pick会停止,并留下一个合并冲突。你需要解决它并告诉rebase继续执行。
如果A添加了path/to/file.txt,但在提交X中没有该文件,则通常不会有问题:Git只需创建该文件并将其放入新提交中。根本不会出现add/add冲突,只会生成完美的副本A'(然后我们会继续进行rebase的其余部分)。
但是,如果某些原因(例如,作为未跟踪的文件并且可能被忽略)工作目录中存在文件path/to/file.txt,那么Git不能简单地将path/to/file.txt从提交A中复制到工作目录中覆盖掉已有的版本。这时会出现错误消息,就像普通的合并冲突一样。你必须解决这个问题并告诉rebase继续执行。
对于一些在索引/暂存区中的文件(因此可以在各种提交中),但被标记为--assume-unchanged--skip-worktree的文件,这里还有一些额外的情况。在较新版本的Git中,精确的错误消息会标识“将被覆盖”的文件是否真正为未跟踪文件或标记为此类文件。 (这在旧版本的Git中可能也是正确的,但我没有查过。)这就是为什么具体的示例更好:这个特定问题有几个不同的原因。
为了完整起见:我提到的减法
这些也在rebase文档中有所涉及,但值得提到git rebase故意不复制的提交:
  • 不能复制合并操作。使用--preserve参数时,git rebase重新执行合并操作以尝试重建它们,但它无法复制原始的合并操作。因此通常它甚至不会尝试。

  • 省略已有上游等效的合并操作(例如那些已经被cherry-pick的)。在原始图表中(包括T和R),Git会检查ABCgit patch-id值是否与合并基准提交右侧的任何提交的git patch-id值匹配。如果是,则Git会放弃patch ID已经存在于上游的AB和/或C提交。

  • 如果与--fork-point一起使用,则重新定位代码运行git merge-base --fork-point来尝试找到已删除的上游提交。这通常对远程跟踪分支非常有效,但如果您将自己的本地分支设置为自己分支的上游,则通常效果较差。在使用自动机制在上游进行变基时,默认使用--fork-point参数,因此在这里必须谨慎。有关更多信息,请参见Git rebase - commit select in fork-point mode


0
简短回答:在新的目录结构中检查你正在忽略的文件,可以通过.gitignore或者像@torek提到的--assume-unchanged来实现。
我遇到了一个非常类似的问题。在我的情况下,我有一个文件在新的目录结构中被忽略了。显然,被.gitignore忽略的文件存在于工作树中,这就导致了冲突。我在一个新分支上压缩了我的提交,成功地将其rebase到了主分支上,没有发生冲突。然后我注意到一个文件消失了,意识到它是一个被忽略的文件。那个文件现在完全消失了。在进行压缩操作时,某种原因导致它被删除而没有任何警告。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接