【问题标题】:How can a new file be owerwritten at a git rebase?如何在 git rebase 上重写新文件?
【发布时间】:2017-11-18 08:16:36
【问题描述】:

所以我一直在研究结帐功能分支,我们称之为feature-branch-development。我最终在这个分支上提交了大量的提交,大约 15 个。当我终于到了将master 分支重新设置为 feature-branch-development 的时候了,它中途停止抱怨我在feature-branch-development 上添加的一个新文件,并给了我一个错误,说类似Your changes on this branch will be overwritten, please commit or stash them. 我当然有已经提交了所有内容,我的工作树很干净。好像 git “相信” 新文件也存在于 master 上。值得注意的是,此时我没有合并冲突。

我通过压缩feature-branch-development 上的所有提交并再次重新设置master 解决了这个问题。

因此,我的问题是为什么 git 会这样?我对 Git 有中级知识,我只是问这个问题来提高我的知识水平。

【问题讨论】:

  • 我遇到了同样的问题。阅读您的问题后,我尝试像您一样压缩我的提交,它也为我修复了它,所以谢谢。
  • 糟糕,删除了一个被 .gitignore 忽略的文件。所以要小心。我认为被忽略的文件首先是导致冲突的原因。看我的回答。

标签: git


【解决方案1】:

用一个具体的例子来回答这个问题会更容易。 原则上很简单:git rebase 表示:复制提交,就像 git cherry-pick 一样。这里失败的是复制步骤。

我们从分支 R(用于变基)开始并选择一些新的目标分支 T(用于 Target)。然后我们确定一些要复制的提交:这些提交大部分是在 R 上而不是在 T 上的提交。 (在一个善意的谎言中,the git rebase documentation 暗示这些是由git log T..Rgit log T..HEAD 列出的提交。这基本上是正确的,除了添加了三个额外的小提琴,或者可能是“减去”这个词.)

然后,在列出要复制的提交哈希后,Git 对目标分支进行 detached-HEAD 签出,以便当前 (HEAD) 提交与分支 T 标识的提交相同.请注意,我们不是 on 分支 T,我们只是在同一个 commit 上。现在,对于我们保存其哈希 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 的 merge 机制 完成的,或者我称之为 to merge 的“动词形式”(与名词或形容词形式,a mergemerge commit——git merge 通常在做动词形式 to 之后进行 merge commit合并,我们只使用了前半部分)。例如,当您将添加新文件的提交与也添加相同新文件的更改合并时,您会遇到添加/添加冲突。

最有可能发生这种冲突的地方是在从A 复制A' 时,因为cherry-pick 操作的合并基础 是被复制提交的父提交.合并操作 - to merge 动词 - 将此合并基础与 两个 提交进行比较。在这种情况下,两个提交是提交A 本身,以及分支T 的最尖端提交,即T 指向的提交。让我们称之为X,并用*标记合并基础:

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

如果同时提交AX,与* 相比,添加文件path/to/file.txt,您会遇到添加/添加冲突。樱桃挑选停止,留下正在进行的合并冲突。你必须解决它并告诉 rebase 恢复。

如果A 添加path/to/file.txt 而它在提交X不是 怎么办?通常,这没有问题:Git 只会创建文件并将其放入新的提交中。根本不会有添加/添加冲突,只是一个完美的副本A'(我们会继续做其余的变基)。

但也许文件path/to/file.txt由于某种原因存在于工作树中(例如,作为一个未跟踪文件,也可能被忽略)。在这种情况下,Git 不能只是从提交 A 中复制 path/to/file.txt,从而覆盖工作树版本。您会在此处收到错误消息,就像常规合并冲突一样。你必须解决问题并告诉 rebase 恢复。

对于位于索引/暂存区域(因此可以在各种提交中)但标记为 --assume-unchanged--skip-worktree 的文件,这里还有一些其他情况。在较新版本的 Git 中,精确的错误消息会识别“将被覆盖”的文件是否真正未跟踪,或者像这样标记。 (这在旧版本的 Git 中可能也是如此,但我不记得手头也没有检查过。)这就是为什么一个具体的例子更好:这个特定问题有几个不同的原因。

为了完整性:我提到的减法

这些也(在某种程度上)在 rebase 文档中有所涉及,但值得一提的是提交 git rebase 故意复制:

  • 不能复制任何合并。当使用--preserve 时,git rebase重新执行合并以尝试重建它们,但它无法复制原始合并。所以通常它甚至不会尝试。

  • 它忽略了具有上游等效项的合并(例如,那些已经精心挑选的合并)。在原始图表中(使用TR),Git 检查ABCthe git patch-id value 是否与@987654372 上任何提交的git patch-id 值匹配@ 在合并基础提交的右侧。如果是这样,Git 会丢弃其补丁 ID 已在上游出现的 AB 和/或 C 提交。

  • 如果与--fork-point 一起使用,rebase 代码将运行git merge-base --fork-point 以尝试查找故意丢弃的上游提交。这通常适用于远程跟踪分支,如果您将自己的本地分支设置为您自己分支的上游,则通常效果较差。当使用自动机制基于上游重新定位时,使用--fork-point默认,因此在这里必须小心。更多信息请参见Git rebase - commit select in fork-point mode

【讨论】:

    【解决方案2】:

    简答:通过.gitignore 或@torek 提到的--assume-unchanged 在新目录结构中检查您忽略的文件。

    我遇到了一个非常相似的问题。就我而言,我有一个文件在新目录结构的某处被忽略。显然 .gitignore 文件存在于工作树中,这就是导致冲突的原因。我在一个新分支中压缩了我的提交,并且成功地重新基于 master 而没有冲突。然后我注意到一个文件丢失了,并意识到它是一个被忽略的文件。该文件现在完全消失了。做壁球的一些事情在没有警告的情况下摆脱了它。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-07-22
      • 2021-06-27
      • 1970-01-01
      • 2017-10-06
      • 2018-11-05
      相关资源
      最近更新 更多