如何在Git中将分支中快照(commit)的副本创建到另一个分支?

3
当我开始使用Git时,我学到的第一件事之一是Git不会将信息存储为基于文件的更改(补丁)列表,而是作为快照流。提交是仓库所有内容的一个快照。
假设我们在仓库中有两个分支Branch_A和Branch_B。无论它们之间的关系是多么简单或复杂(与其他分支合并...),只需将它们表示为以下形式:
A <--- ... -- I <----- J <----- K                Branch_A


       M <------ N <----- O <----- P             Branch_B

由于某些原因,我希望将Branch_A的下一次提交完全设置为Branch_B中提交P的状态(快照)。
是否有git命令可以实现这一点?
我不能使用“正常”合并,因为我不想解决P和K之间的冲突。
我不能使用递归合并选项“theirs”,因为它会保留所有在K中不与P冲突的文件,即使它们在P中不存在。
我可以按照以下方式完成:
cd /path/to/myrepo;
git checkout Branch_B
cp -R * /path/to/save_branch_b_state
git checkout Branch_A
rm -rf *
cp -R /path/to/save_branch_b_state . (In fact, I copy everything but the .git directory)
git add *
git commit 

但我想象中有一个 Git 命令可以完成这个任务。
3个回答

3

虽然没有单个git命令可以完成此操作,但是只需要几个命令就可以完成。

首先要问的问题是:您是否希望这至少类似于合并(在这种情况下,它实际上将是一个合并)?也就是说,您希望提交图形看起来像什么-是否应该显示某种链接从branch_B(其中提交P位于):

A <--- ... -- I <----- J <----- K <--- *         Branch_A
                                     /
       M <------ N <----- O <----- P             Branch_B

你的新提交在哪里?或者你想让它完全独立?

低级命令git commit-tree将创建一个具有任意父级年龄(您使用-p提供父ID,消息和现有的“tree”对象)的新提交。它会打印出结果提交对象的ID。因此,要使用低级命令执行此操作:

tree=$(git rev-parse B^{tree})  # or similar to find the tree ID
commit=$(git commit-tree -p ... -m message $tree) # or use -F, or stdin

然后将Branch_A设置为指向$commit(使用git branchgit update-ref)。这可以转化为一行Shell命令,并作为git别名执行,但这有点棘手,可能值得编写一个Shell脚本。选择一个ID作为父提交以使新提交成为常规(非合并)提交,或选择多个ID使其成为合并;如果要进行合并,请根据将来想要--first-parent的工作方式选择父ID排序。
或者,您可以使用稍微高级的git命令完成此操作。例如,如果您不希望它成为(并且类似于)合并,则可以简单地从Branch_A开始,删除所有内容(从索引和工作树中删除整个树),从提交P重新填充所有内容(基于提交P创建新的索引内容和工作树),然后提交。
$ git checkout Branch_A
$ git rm -rf .                # cd to top dir first if needed
$ git checkout Branch_B -- .  # index and work tree = commit P
$ git commit

或者,将上述操作作为真正的合并操作执行:请参见此问题的答案

“git commit-tree”并不是为了随意使用而设计的,因为大多数人在大多数情况下都没有准备好的树对象可用。 :-) 但它在“git filter-branch”中被使用,并且反复提到,所以一旦你做了一些筛选分支,你就会掌握这些东西... - torek

0
在目标分支中: git cherry-pick <提交ID>

2
这将会将 <commit id> 与其父提交 <commit id>^ 进行差异比较,然后将该更改应用于目标分支,这不是 OP 想要的。 - torek

0

在目标分支的工作目录中:

$ rm -rf *
$ git add .
$ git cherry-pick -n -X theirs <COMMIT_ID>
$ git commit --no-edit

对于点(隐藏)文件(.* 除了 .git),可能需要额外运行 rm 命令。因为在典型的 shell 中,通配符模式中的星号 (*) 符号不会扩展到点文件。


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