git优雅rebase/complete合并到分支
git graceful rebase/complete merge to branch
晚安,
我目前正在尝试弄清楚如何 'overwrite' 一个分支而不使用像 'rebase'.
这样极端的东西
例如:
branch::master
|- dir_a
| |- file_a
| |- file_b
|- file_c
branch::dev
|- dir_a
| |- file_b
|- file_c [edited compared to branch::master]
branch::master [after merge from branch::dev with strategy 'ours' (keep changes from branch::dev)]
|- dir_a
| |- file_a [is also merged, but should be deleted]
| |- file_b
|- file_c [edited version from branch::dev]
要解决上述问题,我可以做一个 'rebase',但这会破坏很多东西,因为 master(head) 分支不是 private/personal。
所以问题是:有没有什么好的方法可以完全overwrite/replacemaster分支的内容,不破坏当前master分支的依赖?
可能影响可能性的信息:
- 两个分支之间的多次提交可能存在差异(数量也未知)
- 最好 'dev' 分支继续存在(也出于依赖原因)。
- master 分支的历史最好保持完整
如果需要任何其他说明,请随时提出。
感谢您的思考!
Is there a good way to completly overwrite/replace the content of the master branch ...
不止一个,但在你选择任何一个之前,你需要清楚你所说的 branch 和 content 是什么意思.
关于分支要记住的是每个分支 name 简单地说 my latest commit is _____ (使用原始提交哈希 ID 填写空白)。 "on" 分支的提交取决于 提交。
每个提交都有一个唯一的哈希 ID。该哈希 ID 表示 that 提交,而不是任何其他提交。一旦提交,它的任何部分都不能更改。所有提交都被完全冻结。提交也大多是永久性的:至多,您可以停止使用提交,并取消查找该提交的方法,我们稍后会看到。
每个提交都有两件事:作为它的主要数据,所有文件的完整快照,以及作为它的元数据——关于关于提交的信息。这包括提交者的姓名和电子邮件地址,以及解释 为什么 他们做出该提交的日志消息。但最重要的是,至少对于 Git,是这样的:当您或任何人进行 new 提交时,Git 记录原始哈希 ID它的直接 parent 提交。该散列 ID 是您刚才提交的散列 ID, 是 分支中的最后一次提交。同样,此数据(快照)或元数据(包括父哈希 ID)的任何部分都不能从现在开始更改 — 因此这意味着 this 提交会记住 previous branch-tip 提交。
因此,"content of a branch" 可以是:
- 分支中最后一次提交的原始哈希ID;或
- 那个哈希 ID 和因此那个提交,加上存储在那个提交中的哈希 ID 和因此之前的提交,加上存储在那个提交中的另一个哈希 ID 和因此另一个之前的提交,加上 ...
如果我们绘制这些提交——这需要对它们的哈希 ID 做出假设;在这里,我将用单个大写字母替换每个实际的哈希 ID——我们得到这样的图片:
I--J <-- master
/
...--F--G--H
\
K--L <-- dev
也就是说,name master
包含提交 J
的原始哈希 ID(不管它是什么)。提交 J
保存提交 I
的原始哈希 ID,它保存提交 H
的哈希 ID。同时,名称 dev
包含提交 L
的哈希 ID,后者包含 K
的哈希 ID,后者包含 H
.
的哈希 ID
请注意,通过 H
的提交在 两个 分支上。
在这种情况下,分支在 H
处汇合,至少从 Git 的角度来看:从 结束 并且向后工作,一次提交一个。
1从技术上讲,索引包含文件的名称、它们的模式(+x 或 -x)以及对 的 引用frozen-format 内容。
树枝通常如何生长
要进行新的提交,您可以从 git checkout <em>name</em>
开始。假设本例中的名称是 master
。这会找到名称指向 的 提交——在本例中为 J
——并将提交的 read-only 内容从提交中复制到 Git 的索引并放入您的 work-tree。 work-tree 是您可以查看和编辑文件的地方。这还将特殊名称 HEAD
附加到名称 master
:
I--J <-- master (HEAD)
/
...--F--G--H
\
K--L <-- dev
Git 现在可以看到 当前分支 是 master
(通过查看 HEAD
的附加位置)和 当前提交是J
(通过查看master
点)。您的 work-tree 现在拥有 您 使用的文件——普通文件,而不是 freeze-dried(压缩和 Git-only)提交的文件——并且在 Git 的索引,J
中有 freeze-dried 个文件的副本 1,准备进入新的提交。
您现在可以随意修改 work-tree。完成后,您可以 运行 git add
处理各种文件。这会将每个添加的文件的 content 复制回 Git 的索引,将它们压缩为 Git 将存储在提交中的 freeze-dried 形式, 替换了之前的副本。或者,您可以 git add
一个新文件,或 git rm
一个现有文件;无论哪种方式,Git 都会相应地更新索引。
那么,你运行git commit
。 Git 简单地打包 index 中的任何内容,添加您的姓名和当前时间,添加您的日志消息,并将其作为新提交写出。新提交的 parent 是 current commit 并且 hash ID 是新提交内容的唯一校验和,永远无法更改。我们将此新提交称为 N
(跳过 M
)。 N
指向 J
:
N
/
I--J <-- master (HEAD)
/
...--F--G--H
\
K--L <-- dev
但现在我们得到了使分支有用的技巧:Git 现在写入新提交的哈希 ID进入分支名称。 所以我们现在有:
I--J--N <-- master (HEAD)
/
...--F--G--H
\
K--L <-- dev
请注意 HEAD
没有改变:它仍然附加到 master
。
现在让我们摆脱 提交N
。它实际上不会消失——它仍将在我们的存储库中——但我们将安排 name master
再次识别提交 J
。为此,我们找到 J
的实际哈希 ID 或它的任何代理,并使用 git reset --hard
:
git reset --hard <hash-of-J>
现在我们有:
N [abandoned]
/
I--J <-- master (HEAD)
/
...--F--G--H
\
K--L <-- dev
如果您在 您的 存储库中执行此操作,而不使用 git push
到 发送 新提交 N
到一些 other Git 存储库,只有您可以提交 N
。没有人会知道你做了这件事!
任意使用git reset
您可以使用 git reset
,将 any 分支移动到 any 提交。但是如果你移动一个分支 "backwards",就像我们刚才做的那样,"fall off the end" 的提交变得很难找到。假设您强制名称 master
指向提交 L
。那么,您将如何找到提交 J
? commit I
怎么样?他们好像不见了!
这可能就是你想要的。但是,如果某些 other Git 存储库 已经 提交 I
和 J
,也许有人正在使用该存储库已构建 new 提交 link 回到 J
。他们不会感激你要求他们忘记所有的提交,以及 I
和 J
.
如果可以让其他人忘记 I
和 J
,您可以将 master
重置为指向 L
。然后你必须使用 git push --force
或等价物来说服一些 other Git 存储库忘记 I
和 J
(可能还有额外的提交) 同样,使用此存储库的克隆的其他人需要确保 他们的 Git 忘记 I
和 J
,等等。
这是使 master
匹配 dev
最快最简单的方法,但它对其他人的干扰也是最大的。 Git "likes" 当分支增长时它,"dislikes" 当它们失去提交时它。
合并的工作原理(缩写)
现在让我们看看如果 运行 git merge dev
(master
再次指向 J
)会发生什么。 Git 对于此合并操作,需要 三个 输入提交。一个是您当前的提交,合并调用 ours
。一个是您选择的任何其他提交,合并调用 theirs
。 第三个——某种意义上说是第一个;至少,一下子就排在第一了——Git发现靠自己。
通过使用 git merge dev
,您选择提交 L
作为 theirs
提交。那是因为名字dev
选择了提交L
。 Git 现在从 both 分支提示向后工作以找到 best shared commit,这在这种情况下,显然是提交 H
。 Git 称其为 合并基础 。
请记住,每次提交都会保存一个快照。所以 commit H
有一个快照,commit J
有一个,commit L
有一个。 Git 可以随时比较任意两个快照 以查看有何不同。为您执行此操作的命令是 git diff
。合并操作想知道有什么不同,所以它 运行s 内部等效于:
git diff --find-renames <hash-of-H> <hash-of-J> # what we changed on master
git diff --find-renames <hash-of-H> <hash-of-L> # what they changed on dev
Git 现在可以合并这两组更改,并将合并的 更改应用于H
(不是 J
,不是 L
,而是 H
:合并基数)。如果我们添加了一个文件而他们没有,Git 会添加该文件。如果我们删除了一个文件而他们没有,Git 会删除该文件。如果他们更改了文件而我们没有,Git 更改文件。
如果一切顺利,结果就是我们的更改和他们的更改的结合。 Git 会将这个结果填回索引中——这是 Git 进行提交的地方——同时也会更新我们的 work-tree 以便我们可以看到它做了什么。然后,由于一切顺利,Git 进行了新的提交。
我们将 merge
称为 M
的新提交没有一个,而是 两个 个父项。 first 父级与往常一样:M
links 回到现有提交 J
,这是 master
的地方刚才。但是 Git 添加了我们选择合并的提交 L
作为 second 父级。所以现在的图片是:2
I--J
/ \
...--F--G--H M <-- master (HEAD)
\ /
K--L <-- dev
提交 M
有快照。它的快照是由Git(而不是你)制作的,组合从共同起点——合并基础——并将组合更改应用到 merge-base 快照。
如果您遇到合并冲突,索引将发挥更大的作用并帮助解决冲突。最终合并提交中的最终快照由您决定,而不是 GitM
。但是如果没有任何合并冲突,Git 通常会自行合并。 通常在这里是一个重要的词。
2提交 N
仍然存在,但无法 找到 它,你看不到它更多,我们不需要费心将它画进去。最终,Git 将完全删除它——通常是在至少 30 天过去后的某个时间。在那之前,如果你想要它,你可以取回它:你只需要找到它的哈希 ID。
这是一个正常的合并;您可能需要覆盖合并
假设您可以告诉 Git:开始合并,但不要进行合并提交 M
。 Git 会像往常一样找到合并基础,像往常一样合并您的更改和它们的更改,并像往常一样更新您的 work-tree 和 Git 的索引...然后停止,不进行提交。
您完全可以做到这一点,使用 git merge --no-commit
。完成后,您可以 将合并结果替换 为您喜欢的任何内容。将您喜欢的任何文件放入 work-tree,然后使用 git add
让 Git 将其复制到索引中。索引副本将进入新的合并提交,现在与您放入 work-tree.
中的任何文件相匹配
您可以在此处完全添加新文件和删除文件。无论您做什么,都取决于 您: 此时您可以控制一切。你可以让合并有任何你想要的内容。
你想要什么——根据你的问题,反正;想想这是否是真的你想要的——是完全忽略从合并基础H
到你的提交J
的差异,而只是从H
到 他们的 comimt L
。当然,这将与提交 L
中的快照完全匹配。
有一个快速简单的方法,使用 git merge -s ours
,告诉 Git 它应该 完全忽略他们分支的更改 并且只使用你的版本一切,即提交 J
。不幸的是,这与您在这里想要的相反:您正在寻求一种方法 完全忽略分支的更改 并仅使用 他们的 版本一切,即提交 L
.
幸运的是,有一个相当神奇的命令3你可以使用它是又快又简单,在这里得到你想要的东西:
git merge --no-commit dev
git read-tree --reset -u dev
这个git read-tree -u
命令告诉Git:用我在这里命名的提交中存储的文件替换索引和我的work-tree内容。 --reset
选项告诉 Git 抛出冲突条目,如果合并生成的合并冲突(如果没有的话应该是无害的)。所以现在索引和您的 work-tree 与 L
中的快照匹配。现在你可以完成合并了:4
git merge --continue
这使得新的合并提交 M
来自索引中存储的内容,这要归功于 git read-tree
命令的 -u
选项 - 您还可以在 work-tree。所以现在你有:
I--J
/ \
...--F--G--H M <-- master (HEAD)
\ /
K--L <-- dev
提交 M
的 content 与提交 L
的 content 完全匹配。 M
的 第一个父 仍然是 J
,第二个仍然是 L
,就像任何其他合并一样。但是您已经 "killed off" 在 I
和 J
中所做的更改,而仅 添加 对 master
的提交。所有其他 Git 将很乐意 添加 提交 M
到 他们的 提交集合。
3这根本不是魔法。这是一个 plumbing 命令,用于脚本,而不是用户。然而,read-tree 命令复杂得惊人:它实现了(部分)合并、子树操作和各种其他聪明之处。这是索引存在的一个重要原因,否则会让人感到痛苦。
4喜欢的话可以运行git commit
代替。在没有 git merge --continue
的旧 Git 版本中,您将不得不使用 git commit
。直接使用 git commit
是可以的:所有 git merge --continue
所做的就是首先检查是否有合并要完成,然后 运行s git commit
为您服务。使用 git merge --continue
,您检查您是否真的完成了合并。
也考虑这个选项
假设您不想要合并。也就是说,假设您想保持进度:
I--J <-- master (HEAD)
/
...--F--G--H
\
K--L <-- dev
原样,但是添加一个新提交 O
到 master
,其 parent 是 J
,但其 content 与 L
在 dev
?
上的匹配
这也很容易做到。刚刚完成 git checkout master
,以便您的索引和 work-tree 匹配 J
,您现在 运行:
git read-tree -u dev
(没有前面的合并,就不会有合并冲突,所以不需要 --reset
)。
这会替换 Git 的索引和您的 work-tree 内容,真的从提交 L
中获取它们,就像我们在合并示例中所做的那样。现在你可以 运行 git commit
来制作 O
:
I--J--O <-- master (HEAD)
/
...--F--G--H
\
K--L <-- dev
提交O
的内容现在匹配L
的内容; history 通过从 master
开始——提交 O
——并向后读取 O
,然后 J
,然后 [=27] =],然后 H
,依此类推。
(你想要哪个选项?由你决定。)
如果您想在不进行真正合并的情况下进行合并,只需通过选择 "ours" 合并策略来加入历史:
git checkout dev
git merge -s ours master
你的历史将记录 dev 到 master 的合并,两个分支的历史将保持不变,但文件内容看起来 master
从未存在过(它们只反映 dev
内容)
晚安,
我目前正在尝试弄清楚如何 'overwrite' 一个分支而不使用像 'rebase'.
这样极端的东西例如:
branch::master
|- dir_a
| |- file_a
| |- file_b
|- file_c
branch::dev
|- dir_a
| |- file_b
|- file_c [edited compared to branch::master]
branch::master [after merge from branch::dev with strategy 'ours' (keep changes from branch::dev)]
|- dir_a
| |- file_a [is also merged, but should be deleted]
| |- file_b
|- file_c [edited version from branch::dev]
要解决上述问题,我可以做一个 'rebase',但这会破坏很多东西,因为 master(head) 分支不是 private/personal。
所以问题是:有没有什么好的方法可以完全overwrite/replacemaster分支的内容,不破坏当前master分支的依赖?
可能影响可能性的信息:
- 两个分支之间的多次提交可能存在差异(数量也未知)
- 最好 'dev' 分支继续存在(也出于依赖原因)。
- master 分支的历史最好保持完整
如果需要任何其他说明,请随时提出。 感谢您的思考!
Is there a good way to completly overwrite/replace the content of the master branch ...
不止一个,但在你选择任何一个之前,你需要清楚你所说的 branch 和 content 是什么意思.
关于分支要记住的是每个分支 name 简单地说 my latest commit is _____ (使用原始提交哈希 ID 填写空白)。 "on" 分支的提交取决于 提交。
每个提交都有一个唯一的哈希 ID。该哈希 ID 表示 that 提交,而不是任何其他提交。一旦提交,它的任何部分都不能更改。所有提交都被完全冻结。提交也大多是永久性的:至多,您可以停止使用提交,并取消查找该提交的方法,我们稍后会看到。
每个提交都有两件事:作为它的主要数据,所有文件的完整快照,以及作为它的元数据——关于关于提交的信息。这包括提交者的姓名和电子邮件地址,以及解释 为什么 他们做出该提交的日志消息。但最重要的是,至少对于 Git,是这样的:当您或任何人进行 new 提交时,Git 记录原始哈希 ID它的直接 parent 提交。该散列 ID 是您刚才提交的散列 ID, 是 分支中的最后一次提交。同样,此数据(快照)或元数据(包括父哈希 ID)的任何部分都不能从现在开始更改 — 因此这意味着 this 提交会记住 previous branch-tip 提交。
因此,"content of a branch" 可以是:
- 分支中最后一次提交的原始哈希ID;或
- 那个哈希 ID 和因此那个提交,加上存储在那个提交中的哈希 ID 和因此之前的提交,加上存储在那个提交中的另一个哈希 ID 和因此另一个之前的提交,加上 ...
如果我们绘制这些提交——这需要对它们的哈希 ID 做出假设;在这里,我将用单个大写字母替换每个实际的哈希 ID——我们得到这样的图片:
I--J <-- master
/
...--F--G--H
\
K--L <-- dev
也就是说,name master
包含提交 J
的原始哈希 ID(不管它是什么)。提交 J
保存提交 I
的原始哈希 ID,它保存提交 H
的哈希 ID。同时,名称 dev
包含提交 L
的哈希 ID,后者包含 K
的哈希 ID,后者包含 H
.
请注意,通过 H
的提交在 两个 分支上。
在这种情况下,分支在 H
处汇合,至少从 Git 的角度来看:从 结束 并且向后工作,一次提交一个。
1从技术上讲,索引包含文件的名称、它们的模式(+x 或 -x)以及对 的 引用frozen-format 内容。
树枝通常如何生长
要进行新的提交,您可以从 git checkout <em>name</em>
开始。假设本例中的名称是 master
。这会找到名称指向 的 提交——在本例中为 J
——并将提交的 read-only 内容从提交中复制到 Git 的索引并放入您的 work-tree。 work-tree 是您可以查看和编辑文件的地方。这还将特殊名称 HEAD
附加到名称 master
:
I--J <-- master (HEAD)
/
...--F--G--H
\
K--L <-- dev
Git 现在可以看到 当前分支 是 master
(通过查看 HEAD
的附加位置)和 当前提交是J
(通过查看master
点)。您的 work-tree 现在拥有 您 使用的文件——普通文件,而不是 freeze-dried(压缩和 Git-only)提交的文件——并且在 Git 的索引,J
中有 freeze-dried 个文件的副本 1,准备进入新的提交。
您现在可以随意修改 work-tree。完成后,您可以 运行 git add
处理各种文件。这会将每个添加的文件的 content 复制回 Git 的索引,将它们压缩为 Git 将存储在提交中的 freeze-dried 形式, 替换了之前的副本。或者,您可以 git add
一个新文件,或 git rm
一个现有文件;无论哪种方式,Git 都会相应地更新索引。
那么,你运行git commit
。 Git 简单地打包 index 中的任何内容,添加您的姓名和当前时间,添加您的日志消息,并将其作为新提交写出。新提交的 parent 是 current commit 并且 hash ID 是新提交内容的唯一校验和,永远无法更改。我们将此新提交称为 N
(跳过 M
)。 N
指向 J
:
N
/
I--J <-- master (HEAD)
/
...--F--G--H
\
K--L <-- dev
但现在我们得到了使分支有用的技巧:Git 现在写入新提交的哈希 ID进入分支名称。 所以我们现在有:
I--J--N <-- master (HEAD)
/
...--F--G--H
\
K--L <-- dev
请注意 HEAD
没有改变:它仍然附加到 master
。
现在让我们摆脱 提交N
。它实际上不会消失——它仍将在我们的存储库中——但我们将安排 name master
再次识别提交 J
。为此,我们找到 J
的实际哈希 ID 或它的任何代理,并使用 git reset --hard
:
git reset --hard <hash-of-J>
现在我们有:
N [abandoned]
/
I--J <-- master (HEAD)
/
...--F--G--H
\
K--L <-- dev
如果您在 您的 存储库中执行此操作,而不使用 git push
到 发送 新提交 N
到一些 other Git 存储库,只有您可以提交 N
。没有人会知道你做了这件事!
任意使用git reset
您可以使用 git reset
,将 any 分支移动到 any 提交。但是如果你移动一个分支 "backwards",就像我们刚才做的那样,"fall off the end" 的提交变得很难找到。假设您强制名称 master
指向提交 L
。那么,您将如何找到提交 J
? commit I
怎么样?他们好像不见了!
这可能就是你想要的。但是,如果某些 other Git 存储库 已经 提交 I
和 J
,也许有人正在使用该存储库已构建 new 提交 link 回到 J
。他们不会感激你要求他们忘记所有的提交,以及 I
和 J
.
如果可以让其他人忘记 I
和 J
,您可以将 master
重置为指向 L
。然后你必须使用 git push --force
或等价物来说服一些 other Git 存储库忘记 I
和 J
(可能还有额外的提交) 同样,使用此存储库的克隆的其他人需要确保 他们的 Git 忘记 I
和 J
,等等。
这是使 master
匹配 dev
最快最简单的方法,但它对其他人的干扰也是最大的。 Git "likes" 当分支增长时它,"dislikes" 当它们失去提交时它。
合并的工作原理(缩写)
现在让我们看看如果 运行 git merge dev
(master
再次指向 J
)会发生什么。 Git 对于此合并操作,需要 三个 输入提交。一个是您当前的提交,合并调用 ours
。一个是您选择的任何其他提交,合并调用 theirs
。 第三个——某种意义上说是第一个;至少,一下子就排在第一了——Git发现靠自己。
通过使用 git merge dev
,您选择提交 L
作为 theirs
提交。那是因为名字dev
选择了提交L
。 Git 现在从 both 分支提示向后工作以找到 best shared commit,这在这种情况下,显然是提交 H
。 Git 称其为 合并基础 。
请记住,每次提交都会保存一个快照。所以 commit H
有一个快照,commit J
有一个,commit L
有一个。 Git 可以随时比较任意两个快照 以查看有何不同。为您执行此操作的命令是 git diff
。合并操作想知道有什么不同,所以它 运行s 内部等效于:
git diff --find-renames <hash-of-H> <hash-of-J> # what we changed on master
git diff --find-renames <hash-of-H> <hash-of-L> # what they changed on dev
Git 现在可以合并这两组更改,并将合并的 更改应用于H
(不是 J
,不是 L
,而是 H
:合并基数)。如果我们添加了一个文件而他们没有,Git 会添加该文件。如果我们删除了一个文件而他们没有,Git 会删除该文件。如果他们更改了文件而我们没有,Git 更改文件。
如果一切顺利,结果就是我们的更改和他们的更改的结合。 Git 会将这个结果填回索引中——这是 Git 进行提交的地方——同时也会更新我们的 work-tree 以便我们可以看到它做了什么。然后,由于一切顺利,Git 进行了新的提交。
我们将 merge
称为 M
的新提交没有一个,而是 两个 个父项。 first 父级与往常一样:M
links 回到现有提交 J
,这是 master
的地方刚才。但是 Git 添加了我们选择合并的提交 L
作为 second 父级。所以现在的图片是:2
I--J
/ \
...--F--G--H M <-- master (HEAD)
\ /
K--L <-- dev
提交 M
有快照。它的快照是由Git(而不是你)制作的,组合从共同起点——合并基础——并将组合更改应用到 merge-base 快照。
如果您遇到合并冲突,索引将发挥更大的作用并帮助解决冲突。最终合并提交中的最终快照由您决定,而不是 GitM
。但是如果没有任何合并冲突,Git 通常会自行合并。 通常在这里是一个重要的词。
2提交 N
仍然存在,但无法 找到 它,你看不到它更多,我们不需要费心将它画进去。最终,Git 将完全删除它——通常是在至少 30 天过去后的某个时间。在那之前,如果你想要它,你可以取回它:你只需要找到它的哈希 ID。
这是一个正常的合并;您可能需要覆盖合并
假设您可以告诉 Git:开始合并,但不要进行合并提交 M
。 Git 会像往常一样找到合并基础,像往常一样合并您的更改和它们的更改,并像往常一样更新您的 work-tree 和 Git 的索引...然后停止,不进行提交。
您完全可以做到这一点,使用 git merge --no-commit
。完成后,您可以 将合并结果替换 为您喜欢的任何内容。将您喜欢的任何文件放入 work-tree,然后使用 git add
让 Git 将其复制到索引中。索引副本将进入新的合并提交,现在与您放入 work-tree.
您可以在此处完全添加新文件和删除文件。无论您做什么,都取决于 您: 此时您可以控制一切。你可以让合并有任何你想要的内容。
你想要什么——根据你的问题,反正;想想这是否是真的你想要的——是完全忽略从合并基础H
到你的提交J
的差异,而只是从H
到 他们的 comimt L
。当然,这将与提交 L
中的快照完全匹配。
有一个快速简单的方法,使用 git merge -s ours
,告诉 Git 它应该 完全忽略他们分支的更改 并且只使用你的版本一切,即提交 J
。不幸的是,这与您在这里想要的相反:您正在寻求一种方法 完全忽略分支的更改 并仅使用 他们的 版本一切,即提交 L
.
幸运的是,有一个相当神奇的命令3你可以使用它是又快又简单,在这里得到你想要的东西:
git merge --no-commit dev
git read-tree --reset -u dev
这个git read-tree -u
命令告诉Git:用我在这里命名的提交中存储的文件替换索引和我的work-tree内容。 --reset
选项告诉 Git 抛出冲突条目,如果合并生成的合并冲突(如果没有的话应该是无害的)。所以现在索引和您的 work-tree 与 L
中的快照匹配。现在你可以完成合并了:4
git merge --continue
这使得新的合并提交 M
来自索引中存储的内容,这要归功于 git read-tree
命令的 -u
选项 - 您还可以在 work-tree。所以现在你有:
I--J
/ \
...--F--G--H M <-- master (HEAD)
\ /
K--L <-- dev
提交 M
的 content 与提交 L
的 content 完全匹配。 M
的 第一个父 仍然是 J
,第二个仍然是 L
,就像任何其他合并一样。但是您已经 "killed off" 在 I
和 J
中所做的更改,而仅 添加 对 master
的提交。所有其他 Git 将很乐意 添加 提交 M
到 他们的 提交集合。
3这根本不是魔法。这是一个 plumbing 命令,用于脚本,而不是用户。然而,read-tree 命令复杂得惊人:它实现了(部分)合并、子树操作和各种其他聪明之处。这是索引存在的一个重要原因,否则会让人感到痛苦。
4喜欢的话可以运行git commit
代替。在没有 git merge --continue
的旧 Git 版本中,您将不得不使用 git commit
。直接使用 git commit
是可以的:所有 git merge --continue
所做的就是首先检查是否有合并要完成,然后 运行s git commit
为您服务。使用 git merge --continue
,您检查您是否真的完成了合并。
也考虑这个选项
假设您不想要合并。也就是说,假设您想保持进度:
I--J <-- master (HEAD)
/
...--F--G--H
\
K--L <-- dev
原样,但是添加一个新提交 O
到 master
,其 parent 是 J
,但其 content 与 L
在 dev
?
这也很容易做到。刚刚完成 git checkout master
,以便您的索引和 work-tree 匹配 J
,您现在 运行:
git read-tree -u dev
(没有前面的合并,就不会有合并冲突,所以不需要 --reset
)。
这会替换 Git 的索引和您的 work-tree 内容,真的从提交 L
中获取它们,就像我们在合并示例中所做的那样。现在你可以 运行 git commit
来制作 O
:
I--J--O <-- master (HEAD)
/
...--F--G--H
\
K--L <-- dev
提交O
的内容现在匹配L
的内容; history 通过从 master
开始——提交 O
——并向后读取 O
,然后 J
,然后 [=27] =],然后 H
,依此类推。
(你想要哪个选项?由你决定。)
如果您想在不进行真正合并的情况下进行合并,只需通过选择 "ours" 合并策略来加入历史:
git checkout dev
git merge -s ours master
你的历史将记录 dev 到 master 的合并,两个分支的历史将保持不变,但文件内容看起来 master
从未存在过(它们只反映 dev
内容)