Reset/revert 整个分支到另一个分支状态?
Reset/revert a whole branch to another branches state?
我有一个分支 A 和一个分支 B(以及其他一些分支)。
假设 A 的提交历史看起来像:
- 提交 5
- 提交 4
- 提交 3
- ...
和B的提交历史:
- 一些其他提交
- 提交 4
- 合并分支 C 中的其他内容(进入分支 B)
- 提交 3
- ...
基本上我想要的是 "delete" 提交所做的所有更改 一些其他提交 和 合并来自分支 C[= 的其他内容45=] 到分支 B.
我希望分支 B 的工作树与分支 A 的工作树完全相同。
如何实现?
实现此目的的一种方法是通过 git reset
。在分支 B
上执行
git reset --hard A
此后,分支B
指向A
的head-commit。 --hard
选项重置索引和工作树,以便所有跟踪的文件都重置为分支 A
中的版本。 A
的旧 HEAD 提交 ID 存储在 .git/ORIG_HEAD
中,以便允许撤消更改。
或者 - 当不在分支 B
上时 - 你可以删除分支 B
并像这样重新创建它:
git branch -d B # delete branch B
git branch B A # re-create branch B and let it point to the commit of branch A
除了第一个建议,这将保持索引和工作树不变。
如果您希望您的分支 B
看起来与分支 A
完全一样。你可以做一个 reset --hard
git checkout branch-B
git reset --hard branch-A
请注意,在这种情况下您会丢失提交。您的分支 B 看起来与分支 A 完全一样,无论对分支 B 进行的任何提交,如果分支 A 中不存在,都将 lost。另外如果branch-B与其他人共享,不建议执行此操作。
在这种情况下,您可以尝试在分支 B 中恢复不需要的提交
git revert <sha-of-"some other commit">
git revert <sha-of-"merge of other stuff from branch C (into branch B)">
第二次提交看起来像合并提交,因此您可能还必须传递父提交。
git revert <sha-of-"merge of other stuff from branch C (into branch B)"> -m1
为了完成,让我们添加这个非常简单的方法来实现它:
git branch -f branchB branchA
它利用了 git 中的分支只是指针这一事实。此命令只是将对一个分支的尖端提交的引用替换为另一个分支。无需进行复杂的结构更改即可构建您已有的东西。
(见doc)
我意识到(很晚,我承认)OP 实际上从未要求删除 B 的所有 history,而是 changes,所以我的第一个答案,和上面的大多数其他人一样,确实是实现了预期的工作树,但不幸的是,它 以牺牲 分支 B
的历史为代价,它已经丢失了。
所以让我们在这里建议管道方式既保留完整的历史记录又实现您想要获得的确切树,git commit-tree
(参见doc )
# to be executed from branch B
git reset --hard $(git commit-tree -m "Reset to A" -p $(git rev-parse --abbrev-ref HEAD) $(git rev-parse A)^{tree})
说明
git commit-tree
命令分解:
git commit-tree -m <message> -p <parent> <tree>
<tree>
这里需要做分支A
的树,我们用$(git rev-parse A)^{tree}
.
得到它
<parent>
必须指向 B
的提示:$(git rev-parse --abbrev-ref HEAD)
- 然后上面的两个参数加上消息被命令用来产生一个新的提交,
- 最后
git reset --hard
在 git commit-tree
返回的新提交上设置当前分支 (B
)。
初看起来很复杂,但这是一个很棒的工具。
正如其他人所表明的那样,git reset --hard
确实会使分支 B 看起来与分支 A 完全一样。但是,这将删除 B 的历史记录。另一种避免此问题的方法是创建并应用补丁文件:
git checkout A
git diff B > /tmp/AtoB.patch # Generate changes needed to make B match the current branch A
git checkout B
git apply /tmp/AtoB.patch # Update files to match the state of A
git add -A # Track any deleted or newly created files
git commit -a -m "Make B match A" # Commit the changes
现在我们不是在“改写历史”,所以当你推到原点时就不会有争议了。这种方法的好处是同步是 B 历史记录中的一个离散提交,可以随时恢复。但是请注意,分支 A 的提交历史记录在翻译中丢失了。
BTW: If you get an error about binary files, add the --binary flag to your diff command like this git diff --binary B > /tmp/AtoB.patch
我有一个分支 A 和一个分支 B(以及其他一些分支)。
假设 A 的提交历史看起来像:
- 提交 5
- 提交 4
- 提交 3
- ...
和B的提交历史:
- 一些其他提交
- 提交 4
- 合并分支 C 中的其他内容(进入分支 B)
- 提交 3
- ...
基本上我想要的是 "delete" 提交所做的所有更改 一些其他提交 和 合并来自分支 C[= 的其他内容45=] 到分支 B.
我希望分支 B 的工作树与分支 A 的工作树完全相同。
如何实现?
实现此目的的一种方法是通过 git reset
。在分支 B
上执行
git reset --hard A
此后,分支B
指向A
的head-commit。 --hard
选项重置索引和工作树,以便所有跟踪的文件都重置为分支 A
中的版本。 A
的旧 HEAD 提交 ID 存储在 .git/ORIG_HEAD
中,以便允许撤消更改。
或者 - 当不在分支 B
上时 - 你可以删除分支 B
并像这样重新创建它:
git branch -d B # delete branch B
git branch B A # re-create branch B and let it point to the commit of branch A
除了第一个建议,这将保持索引和工作树不变。
如果您希望您的分支 B
看起来与分支 A
完全一样。你可以做一个 reset --hard
git checkout branch-B
git reset --hard branch-A
请注意,在这种情况下您会丢失提交。您的分支 B 看起来与分支 A 完全一样,无论对分支 B 进行的任何提交,如果分支 A 中不存在,都将 lost。另外如果branch-B与其他人共享,不建议执行此操作。
在这种情况下,您可以尝试在分支 B 中恢复不需要的提交
git revert <sha-of-"some other commit">
git revert <sha-of-"merge of other stuff from branch C (into branch B)">
第二次提交看起来像合并提交,因此您可能还必须传递父提交。
git revert <sha-of-"merge of other stuff from branch C (into branch B)"> -m1
为了完成,让我们添加这个非常简单的方法来实现它:
git branch -f branchB branchA
它利用了 git 中的分支只是指针这一事实。此命令只是将对一个分支的尖端提交的引用替换为另一个分支。无需进行复杂的结构更改即可构建您已有的东西。
(见doc)
我意识到(很晚,我承认)OP 实际上从未要求删除 B 的所有 history,而是 changes,所以我的第一个答案,和上面的大多数其他人一样,确实是实现了预期的工作树,但不幸的是,它 以牺牲 分支 B
的历史为代价,它已经丢失了。
所以让我们在这里建议管道方式既保留完整的历史记录又实现您想要获得的确切树,git commit-tree
(参见doc )
# to be executed from branch B
git reset --hard $(git commit-tree -m "Reset to A" -p $(git rev-parse --abbrev-ref HEAD) $(git rev-parse A)^{tree})
说明
git commit-tree
命令分解:
git commit-tree -m <message> -p <parent> <tree>
<tree>
这里需要做分支A
的树,我们用$(git rev-parse A)^{tree}
. 得到它
<parent>
必须指向B
的提示:$(git rev-parse --abbrev-ref HEAD)
- 然后上面的两个参数加上消息被命令用来产生一个新的提交,
- 最后
git reset --hard
在git commit-tree
返回的新提交上设置当前分支 (B
)。
初看起来很复杂,但这是一个很棒的工具。
正如其他人所表明的那样,git reset --hard
确实会使分支 B 看起来与分支 A 完全一样。但是,这将删除 B 的历史记录。另一种避免此问题的方法是创建并应用补丁文件:
git checkout A
git diff B > /tmp/AtoB.patch # Generate changes needed to make B match the current branch A
git checkout B
git apply /tmp/AtoB.patch # Update files to match the state of A
git add -A # Track any deleted or newly created files
git commit -a -m "Make B match A" # Commit the changes
现在我们不是在“改写历史”,所以当你推到原点时就不会有争议了。这种方法的好处是同步是 B 历史记录中的一个离散提交,可以随时恢复。但是请注意,分支 A 的提交历史记录在翻译中丢失了。
BTW: If you get an error about binary files, add the --binary flag to your diff command like this
git diff --binary B > /tmp/AtoB.patch