git rebase squash 打乱了分支历史
git rebase squash messes up branches history
我有一个git
的仓库,历史悠久,分支众多,已经合并到master
。但是,第一个提交只是从第一个提交开始,没有任何分支。
我想将其中的一些第一次提交压缩在一起。为此,我做到了
git rebase -i --root
然后在编辑器中我选择了要压缩的提交,然后更改了压缩提交的提交消息。 git rebase
似乎工作正常,但令我惊讶的是,git log
显示历史已经搞砸了,丢失了分支合并的历史:现在有几个提交重复,并且分支未合并。
虽然很难复制完整的问题,但我在这里提供了一个显示类似问题的最小示例。考虑通过以下命令创建的 git
本地存储库
git init .
touch foo.txt
git add foo.txt
for (( i=1; i<4; i++ )); do echo hello $i >>foo.txt ; git commit -a -m $i; done
git checkout -B test
for (( i=1; i<4; i++ )); do echo hello $i >>foo.txt; git commit -a -m test$i; done
git checkout master
git merge test
for (( i=4; i<5; i++ )); do echo hello $i >>foo.txt ; git commit -a -m $i; done
这会创建一个存储库,在一些人提交一个新分支 test
之后,然后将其合并到 master
(简单快进),最后在 master
上进行最后一次提交.
存储库的日志已删除(Author/Date)
git log --graph --all
* commit ba326e1ada9525fd2b1b275c597ad189b7cf3ddf (HEAD -> master)
| Author:
| Date:
|
| 4
|
* commit b739356170f946f3d1b3c3cb3299f4379212ecc6 (test)
| Author:
| Date:
|
| test3
|
* commit 303e95fd23beb9c5a96c631667dfd2be7e924390
| Author:
| Date:
|
| test2
|
* commit dd67e3ca5f12e35c736483bcc8335347dc78da87
| Author:
| Date:
|
| test1
|
* commit 2420498ae794fa900dd9fee8296b7561e08028b8
| Author:
| Date:
|
| 3
|
* commit 2d43d89d2b75566f37a99b7cce294cddcd3fbf7a
| Author:
| Date:
|
| 2
|
* commit 487e5158e461479f848eb7313dc87090f197f4a7
Author:
Date:
1
现在,我将提交“2”和“3”压缩为一个。我愿意
git rebase -i --root
然后在编辑器中输入
pick 487e515 1
pick 2d43d89 2
squash 2420498 3
pick dd67e3c test1
pick 303e95f test2
pick b739356 test3
pick ba326e1 4
在压缩提交的提交消息中,我输入了 "squash 2 3"。
git rebase
似乎完成得很好:
[detached HEAD b5d1682] squash 2 3
Date: Mon May 13 12:01:19 2019 +0200
1 file changed, 2 insertions(+)
Successfully rebased and updated refs/heads/master.
然而,令我惊讶的是,现在的历史是一团糟
git log --graph --all
* commit 463cbb99f553d555fadbe05a3cc90aeb46b0bbce (HEAD -> master)
| Author:
| Date:
|
| 4
|
* commit 194e78fc74d4eaea62a8b903baa6379dfc8578d3
| Author:
| Date:
|
| test3
|
* commit 8f5e0be6dd8288790b2893e9b3fa97e5e1021134
| Author:
| Date:
|
| test2
|
* commit 1060aea0f3da5dfee8e37adc68d82d77e7c02ba4
| Author:
| Date:
|
| test1
|
* commit b5d168283be820fed3b9862576c44c054402ab50
| Author:
| Date:
|
| squash 2 3
|
| * commit b739356170f946f3d1b3c3cb3299f4379212ecc6 (test)
| | Author:
| | Date:
| |
| | test3
| |
| * commit 303e95fd23beb9c5a96c631667dfd2be7e924390
| | Author:
| | Date:
| |
| | test2
| |
| * commit dd67e3ca5f12e35c736483bcc8335347dc78da87
| | Author:
| | Date:
| |
| | test1
| |
| * commit 2420498ae794fa900dd9fee8296b7561e08028b8
| | Author:
| | Date:
| |
| | 3
| |
| * commit 2d43d89d2b75566f37a99b7cce294cddcd3fbf7a
|/ Author:
| Date:
|
| 2
|
* commit 487e5158e461479f848eb7313dc87090f197f4a7
Author:
Date:
1
git rebase
再次拆分了合并的 test
分支,而 master
分支保留了历史记录。但是,我也丢失了test
被合并到master
的信息。
我期待这样的历史:
git log --graph --all
* commit ba326e1ada9525fd2b1b275c597ad189b7cf3ddf (HEAD -> master)
| Author:
| Date:
|
| 4
|
* commit b739356170f946f3d1b3c3cb3299f4379212ecc6 (test)
| Author:
| Date:
|
| test3
|
* commit 303e95fd23beb9c5a96c631667dfd2be7e924390
| Author:
| Date:
|
| test2
|
* commit dd67e3ca5f12e35c736483bcc8335347dc78da87
| Author:
| Date:
|
| test1
|
* commit 2420498ae794fa900dd9fee8296b7561e08028b8
| Author:
| Date:
|
| squash 2 3
|
* commit 487e5158e461479f848eb7313dc87090f197f4a7
Author:
Date:
1
这是 git rebase
的错误吗?我正在使用 git 2.19.2
如果我尝试不使用分支 test
或者如果我在变基之前删除分支 test
,它工作正常并产生所需的历史记录,但我仍然丢失了 [=26] 上的信息=]分支.
此外,在更复杂的情况下,test
在没有简单快进的情况下合并,删除 test
分支之前用 git rebase
压缩显然会完全丢失合并的分支历史。
下面的例子说明了这一点。
git init .
touch foo.txt
git add foo.txt
for (( i=1; i<4; i++ )); do echo hello $i >>foo.txt ; git commit -a -m $i; done
git checkout -B test
for (( i=1; i<4; i++ )); do echo hello $i >>foo.txt; git commit -a -m test$i; done
git checkout master
touch foo2.txt
git add foo2.txt
git commit -a -m 4
git merge test
git branch -D test
现在日志历史是
git log --graph --all
* commit e905116fed7f4d52c65da46ab6172ae7a08e824a (HEAD -> master)
|\ Merge: 77cc483 584a037
| | Author:
| | Date:
| |
| | Merge branch 'test'
| |
| * commit 584a03768822ebd92d4feee20ebe238fffd89c25
| | Author:
| | Date:
| |
| | test3
| |
| * commit 7a2ad09b5c39e5076cd22fa957c2d539e37c0861
| | Author:
| | Date:
| |
| | test2
| |
| * commit 11ef4fea5ba207a637a85d8e8456f48d0c7bd7ab
| | Author:
| | Date:
| |
| | test1
| |
* | commit 77cc4833cf5aac84aca9737945fd79a7632019ac
|/ Author:
| Date:
|
| 4
|
* commit 081792ccf9b4714ab4bce23e4e7b126647eeead8
| Author:
| Date:
|
| 3
|
* commit 971f217200f7e485308b861033b5b31b7ae69d1a
| Author:
| Date:
|
| 2
|
* commit cef186ae9ad0e316d82c62c2082381747f25a443
Author:
Date:
1
如上所述将提交 2 和 3 合并在一起,生成具有以下日志的存储库
git log --graph --all
* commit 3a4010551d50a47a9db6d53a4597770fa2517d92 (HEAD -> master)
| Author:
| Date:
|
| test3
|
* commit bbbc747780c847b3d40ed7557ed514e5e4dd9fc2
| Author:
| Date:
|
| test2
|
* commit 6f54fd90555fbe6730fcfe7d85761b6477380214
| Author:
| Date:
|
| test1
|
* commit 71fa7371198a0cbbf4793dc27ffb27ac65d15096
| Author:
| Date:
|
| 4
|
* commit 3032069c375ef37b42af75c97759d7a821f1139f
| Author:
| Date:
|
| s 2 3
|
* commit cef186ae9ad0e316d82c62c2082381747f25a443
Author:
Date:
1
日志显示我丢失了 test
分支的分支和合并历史记录。如果在 test
分支合并到 master
后没有删除它,我得到与第一个示例类似的结果,带有拆分分支 test
.
tl;dr:rebase
仅变基一个分支。
变基只在当前分支(或参数指定的分支)中执行。因此,您的结果正如预期的那样,因为您只重新设置 master
分支,而 test
分支保持不变,即它的 HEAD
指向您的旧提交。
如果你仔细阅读git rebase doc,你会发现它总是在谈论the current branch
。
如果您希望其他分支指向变基提交,您将必须 reset
它们。在这种情况下,检查 test
分支并使用 git reset --hard 194e78fc74d4eaea62a8b903baa6379dfc8578d3
将导致您预期的结果。 (一如既往,小心使用 reset --hard
,因为它会删除未提交的更改。)
我不明白你害怕泄露什么信息,因为分支 test
的所有更改都存在于 master
的变基历史中。
在意识到 git rebase
不是正确的工具后,我设计了一个基于 git filter-branch
的解决方案。这个想法是通过补丁更改提交“2”添加提交“3”的内容。然后,提交“3”变为空,因此可以将其删除。
考虑第二个例子。
git diff 971f217200f7e485308b861033b5b31b7ae69d1a \
081792ccf9b4714ab4bce23e4e7b126647eeead8 \
>patch.txt
git filter-branch --tree-filter '\
if [ "$GIT_COMMIT" == "971f217200f7e485308b861033b5b31b7ae69d1a" ]
then
git apply /localdirectory/patch.txt
fi' \
--prune-empty -- --all
由于 git filter-branch
在临时子目录 .git-rewrite/t
中工作,git apply
命令需要补丁文件的完整路径。
这留下了一些旧的参考。检查 git log
正确后,可以进行清理
git update-ref -d refs/original/refs/heads/master
这给出了一个带有日志的存储库
git log --graph --all
* commit e905116fed7f4d52c65da46ab6172ae7a08e824a (HEAD -> master)
|\ Merge: 77cc483 584a037
| | Author:
| | Date:
| |
| | Merge branch 'test'
| |
| * commit 584a03768822ebd92d4feee20ebe238fffd89c25
| | Author:
| | Date:
| |
| | test3
| |
| * commit 7a2ad09b5c39e5076cd22fa957c2d539e37c0861
| | Author:
| | Date:
| |
| | test2
| |
| * commit 11ef4fea5ba207a637a85d8e8456f48d0c7bd7ab
| | Author:
| | Date:
| |
| | test1
| |
* | commit 77cc4833cf5aac84aca9737945fd79a7632019ac
|/ Author:
| Date:
|
| 4
|
* commit 971f217200f7e485308b861033b5b31b7ae69d1a
| Author:
| Date:
|
| 2
|
* commit cef186ae9ad0e316d82c62c2082381747f25a443
Author:
Date:
1
至此任务基本完成。尽管如此,更改提交“2”的消息可能还是有用的。这可以通过 msg-filter 来完成,例如:
git filter-branch --msg-filter 'sed "s/^2/2 and 3 together/"' -- --all
git update-ref -d refs/original/refs/heads/master
如果有更多的分支,就像第一个例子,还需要消除相应的旧引用,例如:
git update-ref -d refs/original/refs/heads/test
我有一个git
的仓库,历史悠久,分支众多,已经合并到master
。但是,第一个提交只是从第一个提交开始,没有任何分支。
我想将其中的一些第一次提交压缩在一起。为此,我做到了
git rebase -i --root
然后在编辑器中我选择了要压缩的提交,然后更改了压缩提交的提交消息。 git rebase
似乎工作正常,但令我惊讶的是,git log
显示历史已经搞砸了,丢失了分支合并的历史:现在有几个提交重复,并且分支未合并。
虽然很难复制完整的问题,但我在这里提供了一个显示类似问题的最小示例。考虑通过以下命令创建的 git
本地存储库
git init .
touch foo.txt
git add foo.txt
for (( i=1; i<4; i++ )); do echo hello $i >>foo.txt ; git commit -a -m $i; done
git checkout -B test
for (( i=1; i<4; i++ )); do echo hello $i >>foo.txt; git commit -a -m test$i; done
git checkout master
git merge test
for (( i=4; i<5; i++ )); do echo hello $i >>foo.txt ; git commit -a -m $i; done
这会创建一个存储库,在一些人提交一个新分支 test
之后,然后将其合并到 master
(简单快进),最后在 master
上进行最后一次提交.
存储库的日志已删除(Author/Date)
git log --graph --all
* commit ba326e1ada9525fd2b1b275c597ad189b7cf3ddf (HEAD -> master)
| Author:
| Date:
|
| 4
|
* commit b739356170f946f3d1b3c3cb3299f4379212ecc6 (test)
| Author:
| Date:
|
| test3
|
* commit 303e95fd23beb9c5a96c631667dfd2be7e924390
| Author:
| Date:
|
| test2
|
* commit dd67e3ca5f12e35c736483bcc8335347dc78da87
| Author:
| Date:
|
| test1
|
* commit 2420498ae794fa900dd9fee8296b7561e08028b8
| Author:
| Date:
|
| 3
|
* commit 2d43d89d2b75566f37a99b7cce294cddcd3fbf7a
| Author:
| Date:
|
| 2
|
* commit 487e5158e461479f848eb7313dc87090f197f4a7
Author:
Date:
1
现在,我将提交“2”和“3”压缩为一个。我愿意
git rebase -i --root
然后在编辑器中输入
pick 487e515 1
pick 2d43d89 2
squash 2420498 3
pick dd67e3c test1
pick 303e95f test2
pick b739356 test3
pick ba326e1 4
在压缩提交的提交消息中,我输入了 "squash 2 3"。
git rebase
似乎完成得很好:
[detached HEAD b5d1682] squash 2 3
Date: Mon May 13 12:01:19 2019 +0200
1 file changed, 2 insertions(+)
Successfully rebased and updated refs/heads/master.
然而,令我惊讶的是,现在的历史是一团糟
git log --graph --all
* commit 463cbb99f553d555fadbe05a3cc90aeb46b0bbce (HEAD -> master)
| Author:
| Date:
|
| 4
|
* commit 194e78fc74d4eaea62a8b903baa6379dfc8578d3
| Author:
| Date:
|
| test3
|
* commit 8f5e0be6dd8288790b2893e9b3fa97e5e1021134
| Author:
| Date:
|
| test2
|
* commit 1060aea0f3da5dfee8e37adc68d82d77e7c02ba4
| Author:
| Date:
|
| test1
|
* commit b5d168283be820fed3b9862576c44c054402ab50
| Author:
| Date:
|
| squash 2 3
|
| * commit b739356170f946f3d1b3c3cb3299f4379212ecc6 (test)
| | Author:
| | Date:
| |
| | test3
| |
| * commit 303e95fd23beb9c5a96c631667dfd2be7e924390
| | Author:
| | Date:
| |
| | test2
| |
| * commit dd67e3ca5f12e35c736483bcc8335347dc78da87
| | Author:
| | Date:
| |
| | test1
| |
| * commit 2420498ae794fa900dd9fee8296b7561e08028b8
| | Author:
| | Date:
| |
| | 3
| |
| * commit 2d43d89d2b75566f37a99b7cce294cddcd3fbf7a
|/ Author:
| Date:
|
| 2
|
* commit 487e5158e461479f848eb7313dc87090f197f4a7
Author:
Date:
1
git rebase
再次拆分了合并的 test
分支,而 master
分支保留了历史记录。但是,我也丢失了test
被合并到master
的信息。
我期待这样的历史:
git log --graph --all
* commit ba326e1ada9525fd2b1b275c597ad189b7cf3ddf (HEAD -> master)
| Author:
| Date:
|
| 4
|
* commit b739356170f946f3d1b3c3cb3299f4379212ecc6 (test)
| Author:
| Date:
|
| test3
|
* commit 303e95fd23beb9c5a96c631667dfd2be7e924390
| Author:
| Date:
|
| test2
|
* commit dd67e3ca5f12e35c736483bcc8335347dc78da87
| Author:
| Date:
|
| test1
|
* commit 2420498ae794fa900dd9fee8296b7561e08028b8
| Author:
| Date:
|
| squash 2 3
|
* commit 487e5158e461479f848eb7313dc87090f197f4a7
Author:
Date:
1
这是 git rebase
的错误吗?我正在使用 git 2.19.2
如果我尝试不使用分支 test
或者如果我在变基之前删除分支 test
,它工作正常并产生所需的历史记录,但我仍然丢失了 [=26] 上的信息=]分支.
此外,在更复杂的情况下,test
在没有简单快进的情况下合并,删除 test
分支之前用 git rebase
压缩显然会完全丢失合并的分支历史。
下面的例子说明了这一点。
git init .
touch foo.txt
git add foo.txt
for (( i=1; i<4; i++ )); do echo hello $i >>foo.txt ; git commit -a -m $i; done
git checkout -B test
for (( i=1; i<4; i++ )); do echo hello $i >>foo.txt; git commit -a -m test$i; done
git checkout master
touch foo2.txt
git add foo2.txt
git commit -a -m 4
git merge test
git branch -D test
现在日志历史是
git log --graph --all
* commit e905116fed7f4d52c65da46ab6172ae7a08e824a (HEAD -> master)
|\ Merge: 77cc483 584a037
| | Author:
| | Date:
| |
| | Merge branch 'test'
| |
| * commit 584a03768822ebd92d4feee20ebe238fffd89c25
| | Author:
| | Date:
| |
| | test3
| |
| * commit 7a2ad09b5c39e5076cd22fa957c2d539e37c0861
| | Author:
| | Date:
| |
| | test2
| |
| * commit 11ef4fea5ba207a637a85d8e8456f48d0c7bd7ab
| | Author:
| | Date:
| |
| | test1
| |
* | commit 77cc4833cf5aac84aca9737945fd79a7632019ac
|/ Author:
| Date:
|
| 4
|
* commit 081792ccf9b4714ab4bce23e4e7b126647eeead8
| Author:
| Date:
|
| 3
|
* commit 971f217200f7e485308b861033b5b31b7ae69d1a
| Author:
| Date:
|
| 2
|
* commit cef186ae9ad0e316d82c62c2082381747f25a443
Author:
Date:
1
如上所述将提交 2 和 3 合并在一起,生成具有以下日志的存储库
git log --graph --all
* commit 3a4010551d50a47a9db6d53a4597770fa2517d92 (HEAD -> master)
| Author:
| Date:
|
| test3
|
* commit bbbc747780c847b3d40ed7557ed514e5e4dd9fc2
| Author:
| Date:
|
| test2
|
* commit 6f54fd90555fbe6730fcfe7d85761b6477380214
| Author:
| Date:
|
| test1
|
* commit 71fa7371198a0cbbf4793dc27ffb27ac65d15096
| Author:
| Date:
|
| 4
|
* commit 3032069c375ef37b42af75c97759d7a821f1139f
| Author:
| Date:
|
| s 2 3
|
* commit cef186ae9ad0e316d82c62c2082381747f25a443
Author:
Date:
1
日志显示我丢失了 test
分支的分支和合并历史记录。如果在 test
分支合并到 master
后没有删除它,我得到与第一个示例类似的结果,带有拆分分支 test
.
tl;dr:rebase
仅变基一个分支。
变基只在当前分支(或参数指定的分支)中执行。因此,您的结果正如预期的那样,因为您只重新设置 master
分支,而 test
分支保持不变,即它的 HEAD
指向您的旧提交。
如果你仔细阅读git rebase doc,你会发现它总是在谈论the current branch
。
如果您希望其他分支指向变基提交,您将必须 reset
它们。在这种情况下,检查 test
分支并使用 git reset --hard 194e78fc74d4eaea62a8b903baa6379dfc8578d3
将导致您预期的结果。 (一如既往,小心使用 reset --hard
,因为它会删除未提交的更改。)
我不明白你害怕泄露什么信息,因为分支 test
的所有更改都存在于 master
的变基历史中。
在意识到 git rebase
不是正确的工具后,我设计了一个基于 git filter-branch
的解决方案。这个想法是通过补丁更改提交“2”添加提交“3”的内容。然后,提交“3”变为空,因此可以将其删除。
考虑第二个例子。
git diff 971f217200f7e485308b861033b5b31b7ae69d1a \
081792ccf9b4714ab4bce23e4e7b126647eeead8 \
>patch.txt
git filter-branch --tree-filter '\
if [ "$GIT_COMMIT" == "971f217200f7e485308b861033b5b31b7ae69d1a" ]
then
git apply /localdirectory/patch.txt
fi' \
--prune-empty -- --all
由于 git filter-branch
在临时子目录 .git-rewrite/t
中工作,git apply
命令需要补丁文件的完整路径。
这留下了一些旧的参考。检查 git log
正确后,可以进行清理
git update-ref -d refs/original/refs/heads/master
这给出了一个带有日志的存储库
git log --graph --all
* commit e905116fed7f4d52c65da46ab6172ae7a08e824a (HEAD -> master)
|\ Merge: 77cc483 584a037
| | Author:
| | Date:
| |
| | Merge branch 'test'
| |
| * commit 584a03768822ebd92d4feee20ebe238fffd89c25
| | Author:
| | Date:
| |
| | test3
| |
| * commit 7a2ad09b5c39e5076cd22fa957c2d539e37c0861
| | Author:
| | Date:
| |
| | test2
| |
| * commit 11ef4fea5ba207a637a85d8e8456f48d0c7bd7ab
| | Author:
| | Date:
| |
| | test1
| |
* | commit 77cc4833cf5aac84aca9737945fd79a7632019ac
|/ Author:
| Date:
|
| 4
|
* commit 971f217200f7e485308b861033b5b31b7ae69d1a
| Author:
| Date:
|
| 2
|
* commit cef186ae9ad0e316d82c62c2082381747f25a443
Author:
Date:
1
至此任务基本完成。尽管如此,更改提交“2”的消息可能还是有用的。这可以通过 msg-filter 来完成,例如:
git filter-branch --msg-filter 'sed "s/^2/2 and 3 together/"' -- --all
git update-ref -d refs/original/refs/heads/master
如果有更多的分支,就像第一个例子,还需要消除相应的旧引用,例如:
git update-ref -d refs/original/refs/heads/test