git - Git合并过程忽略文件
问题描述
有两个分支:branch A
带有一些文件,branch B
例如文件夹名为FolderB
. 当我将分支 B 合并到分支 A 时,提交的暂存文件中没有“FolderB”。这对我来说是一个意外的行为。我记得几天前在此合并之前,我进行了另一次合并,该合并完成了我通过选择 option 解决的冲突keep ours
。也许这个选项保存在某个地方,现在它确实有副作用。如果是,我在哪里可以删除这些设置?
解决方案
我记得几天前在此合并之前,我进行了另一次合并,该合并完成了我通过选择 option 解决的冲突
keep ours
。也许这个选项保存在某个地方,现在它确实有副作用。
我不能与其他界面(可以做这种事情)交谈,但命令行 Git 不保存合并策略和策略选项。然而,你已经合并的事实意味着你不是从你认为你开始的地方开始的。
首先,让我们做一个简单的说明:“合并”并不意味着“相同”。如果是这样,我们根本不会为分支而烦恼。假设你和我都从v1.1
软件开始,我对我的副本进行了一些更改,而你对你的进行了一些更改。如果我从你那里得到你的提交并运行:
$ git merge yours
这用你所做的代替了我所做的,我可能对此不太高兴!
因此,要做git merge
的就是找到一些共同的起点——Git 称之为合并基础提交——并找出你和他们,无论“他们”是谁,从那时起都做了什么。但这意味着你必须了解Git如何找到这个共同的起点。
合并基地
让我们看一下正常的提交过程。我们首先克隆一些现有的存储库:
$ git clone <url>
我们开始工作并做出承诺。究竟什么是提交?正如任何一本优秀的 Git 书籍都会告诉你的,提交是你制作快照时所有源代码的快照,以及一些元数据:谁制作了快照,何时制作,为什么制作,以及——这对 Git 的内部操作至关重要—就在您制作快照之前,您正在使用哪个提交。
每个提交都有自己唯一的哈希 ID,例如b7bd9486b055c3f967a870311e704e3bb0654e4f
. 这些十六进制数字串很大、很丑陋,而且人类无法使用。所以我们让 Git为我们记住它们。每个提交不仅有自己唯一的哈希 ID,每个提交还记得我们之前签出的提交的哈希 ID 。我们和 Git 使用分支名称来记住最终提交。
当我们进行新的提交时,我们的新提交会获得一个新的又大又丑的 ID,但它也会记住哪个提交是分支上的最后一个提交。然后 Git 将新提交的大丑 ID 写入分支名称,以便名称记住新的而不是旧的。
结果是我们从一些以提示提交结束的提交链开始,如下所示:
... <-F <-G <-H <--master
其中名称 master
记住了 commit 的 ID H
。CommitH
记住了 commit 的 ID G
,它记住了 commit 的 ID F
,以此类推。这些内部链接,我们可以像这样画成向后的箭头,总是向后,从子提交到父提交:提交知道他们的祖先,但不知道他们的后代。一旦提交,就无法更改任何提交,因此无法更改提交以记住其子级,但子级也永远不会忘记其父级。
所以,假设你克隆了存储库,我克隆了存储库,我们都有:
...--F--G--H <-- master
现在您进行一两次新的提交:
...--F--G--H--I <-- master
同时其他人做出一两次提交:
...--F--G--H--I <-- master
\
J--K <-- other
(每个提交都有自己唯一的 ID,这些大写字母只是代表真实的 ID。在 26 次提交后我们将用完字母。这就是为什么 Git 使用更大更丑的东西。)
如果我们现在——我们中的任何一个人——去合并我们的工作,我们首先要获取其他人的提交。结果是一个看起来像上面的图表。然后我们git checkout
一个分支,例如master
,并运行一个合并命令,例如git merge other
。
Git 现在通过图形返回——使用从每个分支尖端开始的向后箭头——以找到合并基础。在这种情况下,这很清楚:它是 commit H
。然后,Git 实际上运行了两个 git diff
命令:
git diff --find-renames <hash-of-H> <hash-of-I> # what we did on master
git diff --find-renames <hash-of-H> <hash-of-K> # what they did on other
Git 现在的工作是组合这些更改,将它们应用到 中的快照H
,并根据结果进行新的合并提交。如果一切顺利,Git 会自行完成。如果没有,您会遇到合并冲突,您必须正确解决它们。你告诉 Git 你已经这样做了,然后 Git 进行新的合并提交:
...--F--G--H--I---L <-- master (HEAD)
\ /
J--K <-- other
这个合并提交的特别之处L
在于它有两个父级,而不仅仅是一个。Commit L
,合并提交,记住这是将所有“从到”与“从到”结合起来的正确方法。H
I
H
K
合并基地,再次
现在假设你和我都继续工作。我在之后做出新的提交K
,而你在之后做出新的提交L
:
...--F--G--H--I---L--M--N <-- master (HEAD)
\ /
J--K--O--P--Q <-- other
如果你现在git merge other
再次运行,Git 必须找到你最近一次提交N
与我最近一次提交的合并基础Q
。这次的合并基地不是 H
!我们从两者开始,N
然后Q
向后工作以找到最近的共享提交。从 开始Q
,只有一条向后的路径,通过P
、O
、K
、J
、H
等等。
但是,从 开始N
,我们可以在提交时双向使用L
:N
, M
, L
,然后是 I
和 K
。1 所以现在是第一个共享提交K
!
git merge
这次要做的是针对两个分支提示提交进行差异提交K
,N
以及Q
. 同时,我们之前的合并 commitL
是我们告诉 Git处理与我们工作结合的正确方法。K
那么,假设这些新文件被添加到 commitsJ
或K
中,但L
因为我们选择了“保留我们的”选项而不在其中。K
从to的差异N
会说:删除这些此处的文件。K
从到 的差异Q
可能会或可能不会修改这些新文件。我们可以猜测,在这种情况下,它不会修改它们,因为如果它修改了,我们会遇到新的合并冲突,Git 告诉我们我们 删除了文件,并且它们 改变了它们。如果他们没有更改它们,Git 会假设我们的“删除”是正确的答案,并保持它们被删除。
1请记住,内部箭头总是向后指向——就像我在这里绘制它们的方式一样向左。我们可以从L
到K
,但不能从K
到L
。
该怎么办
如果这是正在发生的事情(根据我的经验,确实如此),真正的问题是你之前的合并——我们在L
上面绘制的提交——是错误的:它删除了文件。但是我们已经注意到,您不能更改任何较早的提交。
有两种解决问题的策略。第一个,通常也是最好的,是承认错误并将其留在原处:将错误的合并留在原处,但现在添加一个新的提交来存储更正的快照。我们可以通过多种方式做到这一点。所有这些都涉及大量工作。在大多数情况下,我首选的方法是重新执行合并,但这次要正确执行:在不正确的合并之前创建一个指向提交的新分支:
............<-- rework (HEAD)
.
...--F--G--H--I---L--M--N <-- master
\ /
J--K--O--P--Q <-- other
然后运行git merge <hash of other commit>
,在这种情况下将是 commit 的哈希值K
。您将得到与上次相同的合并冲突,这次您可以正确解决它。
正确解决并提交后,您可以运行git diff <hash-of-original-merge> <hash-of-corrected-merge>
以查看现在需要添加哪些更改以更正问题。例如:
$ git diff <hash-of-L> <hash-of-new-merge> > /tmp/patch
$ git checkout master
$ git apply /tmp/patch
或者,如果您确定要为除新文件之外的所有内容“保留我们的”,您可以将额外的文件复制进去,git add
然后提交。
处理问题的第二种策略是让它全部消失:丢弃提交L
(错误的合并)和每个后续提交。这显然会让你重新做一堆工作。如果您将这些提交(无论他们的哈希 ID 是什么)提供给其他任何人,它还有另一个可怕的副作用:其他人仍然拥有这些提交。您也必须让他们丢弃这些提交的副本。否则他们会不断回来。
要使用第二种(在大多数情况下效果不佳)策略,您可以使用git reset
命令。由于这通常是处理问题的错误方法,因此我将在此停止,但请参阅如何将 Git 存储库恢复为先前的提交。
推荐阅读
- swift - 限制列表中的 API 请求
- node.js - React App 无法启动并给出错误
- java - 如何使用 appium 从内部存储中获取文件列表
- java - Spring Boot App 无法连接到 PostgreSQL 容器
- r - 从变量中整理信息
- python - Python csv 列表
- spring-boot - 线程“OkHttp Dispatcher”中的异常 java.lang.NoSuchMethodError: com.google.gson.JsonParser.parseString(Ljava/lang/String;)
- javascript - 输入字段的 API 参考函数
- android - Android - 如何解决“错误膨胀类(在 DexPathList 上找不到类)”?
- python - 用一行代码将对象数组转换为python中的字典