git - 变基前压缩
git - squash before rebasing
我有两个分支:master
和 test-branch
(master
的分支)。我的工作看起来像下面这样:
git checkout master
git checkout -b test-branch
- 进行大量更改并提交它们
- 进行更多更改并再次提交
git checkout master
git pull
-> 出其他人已经修改master
git checkout test-branch
git rebase -i master
- 将交互式控制台中除第一个
pick
以外的所有内容更改为 s
- 我必须解决两个合并冲突,一个用于第一次提交,另一个用于第二次提交
我想做的是在变基之前压缩 test-branch
上的所有提交,这样我只需要解决一次合并冲突。这可能吗?如果是,怎么做?
当然可以,yoiu只需要输入:git rebase -i HEAD~<# of commits to squash>
-i 用于交互式变基。执行此操作后,您将看到 vi,其中包含每次提交的下一步操作说明。
有关它的详细信息 post 可以在此处找到 Rewriting history:
这是可能的,甚至很容易。 Git 就是这样,有很多不同的方法可以做到。
按照你最初的建议去做:
... squash all of the commits on test-branch before rebasing
如果你在之前运行宁git merge
在分支master
上这样做是最简单的。 (我知道您没有将 git merge
列为命令,但您在第 6 步中将 运行 git merge
列为命令:
git pull
-> out other people have made changes master
因为 git pull
只是 git fetch
后跟 git merge
。)但之后仍然很容易;我们只需要定位正确的提交 ID。
让我们绘制您在第 4 步中获得的提交图:
...<- o <- * <-- master, origin/master
\
A <- B <-- HEAD=test-branch
这张图显示的是有两个标签1指向我标记为*
的提交,即master
和origin/master
.提交 *
指向提交 o
,后者指向更多提交:这就是分支 master
.
的历史
您创建的分支的标签(现在已经打开,因此 HEAD=
部分)指向提交 B
。提交 B
然后指向提交 A
,后者指向提交 *
。这就是分支 test-branch
的历史:您在 *
时创建了它,然后添加了 A
,然后添加了 B
.
此时您可以通过以下两种方式轻松压缩提交 A
和 B
:
git rebase -i master
这为您提供了一个交互式编辑会话,您可以在其中 "pick" 第一个提交然后 "squash" 第二个,它会将两个提交消息聚集在一起并让您编辑结果,在通常的方式。然后它进行一个(单个)新提交,其树是提交 B
.
git reset --soft master; git commit
这不会为rebase打开一个交互式编辑会话:它只是防止staging-area-and-tree提交B
(那是[= git reset --soft
的 44=] 部分),将标签 test-branch
移回指向直接提交 *
(即 git reset --soft
的 git reset
部分),并使像往常一样新提交 (git commit
).
缺点是您必须编写新的提交消息,但您可以通过多种方式从提交 A
或 B
中恢复提交消息。例如,您可以使用 -c
或 -C
标志来 git commit
(您必须确定提交 A
或 B
,例如,使用 @{1}
或 @{1}^
或 @{yesterday}
或其他一些 reflog 说明符)。或者,在执行 git reset --soft
之前,您可以使用 git log
并将日志消息保存在文件或其他任何文件中。
(当您有 42 个左右的 squash 而不是只有两个提交时,第二种方法会大放异彩。)
这两种方法实际上做同样的事情:它们添加一个 new 提交(我们称之为 AB
),留下 A
和 B
后面有点灰了,我真的画不对:
AB <-- HEAD=test-branch
/
...<- o <- * <-- master, origin/master
\
A <- B [ghost version of test-branch]
你的分支的幽灵版本对大多数正常使用是不可见的,最终(默认情况下 30 天左右)被垃圾收集掉。 (在那之前它仍然在你的存储库和你的引用日志中,这样你就可以找到原始提交 A
和 B
以备不时之需。)
如果您已经完成了第 6 步怎么办?在那种情况下,您仍然必须确定提交 *
。你可以在我写这篇文章的时候 ,或者你可以用 git merge-base
:
找到它
$ mergebase=$(git merge-base HEAD master)
# then pick just ONE of the next two
$ git rebase -i $mergebase
$ git reset --soft $mergebase; git commit
这是它的工作原理。在 git checkout master; git fetch; git merge; git checkout test-branch
(步骤 5 和 6,或多或少)之后,您的提交图现在看起来更像这样:
...<- o <- * <- o <-- master, origin/master
\
A <- B <-- HEAD=test-branch
master
和 origin/master
指向的新 o
提交——或者它可能是整个提交链——是 "in the way",但是 [= test-branch
(你现在所在的位置)和 master
的 158=] 是提交 *
:两个分支分歧之前最近的共享提交。
然后我们简单地在该提交中定位 rebase
或 reset --soft
。当我们完成并有一个新的 AB
提交时,它看起来像这样:
AB <-- HEAD=test-branch
/
...<- o <- * <- o <-- master, origin/master
\
A <- B [ghost version of test-branch]
一旦你压缩了 AB
提交,你就可以 git rebase
以通常的方式把它放到 master
上。
请注意,另一个答案做的是完全相同的事情,它只是通过计算提交来识别提交 *
。如果您知道 test-branch
的提示和 "interesting" 提交 *
之间有两个提交,那么 HEAD~2
标识与 $(git merge-base HEAD master)
相同的提交。使用 git merge-base
只是让你避免计数。
1"References" 是实际的通用 git 术语。在这种情况下,它们是分支名称,我使用 "label" 一词来将它们与该提交图形成的分支历史区分开来。 git 中的 "branch" 一词至少用来指代这两个不同的事物,这让人很困惑。
我有两个分支:master
和 test-branch
(master
的分支)。我的工作看起来像下面这样:
git checkout master
git checkout -b test-branch
- 进行大量更改并提交它们
- 进行更多更改并再次提交
git checkout master
git pull
-> 出其他人已经修改mastergit checkout test-branch
git rebase -i master
- 将交互式控制台中除第一个
pick
以外的所有内容更改为s
- 我必须解决两个合并冲突,一个用于第一次提交,另一个用于第二次提交
我想做的是在变基之前压缩 test-branch
上的所有提交,这样我只需要解决一次合并冲突。这可能吗?如果是,怎么做?
当然可以,yoiu只需要输入:git rebase -i HEAD~<# of commits to squash>
-i 用于交互式变基。执行此操作后,您将看到 vi,其中包含每次提交的下一步操作说明。
有关它的详细信息 post 可以在此处找到 Rewriting history:
这是可能的,甚至很容易。 Git 就是这样,有很多不同的方法可以做到。
按照你最初的建议去做:
... squash all of the commits on test-branch before rebasing
如果你在之前运行宁git merge
在分支master
上这样做是最简单的。 (我知道您没有将 git merge
列为命令,但您在第 6 步中将 运行 git merge
列为命令:
git pull
-> out other people have made changes master
因为 git pull
只是 git fetch
后跟 git merge
。)但之后仍然很容易;我们只需要定位正确的提交 ID。
让我们绘制您在第 4 步中获得的提交图:
...<- o <- * <-- master, origin/master
\
A <- B <-- HEAD=test-branch
这张图显示的是有两个标签1指向我标记为*
的提交,即master
和origin/master
.提交 *
指向提交 o
,后者指向更多提交:这就是分支 master
.
您创建的分支的标签(现在已经打开,因此 HEAD=
部分)指向提交 B
。提交 B
然后指向提交 A
,后者指向提交 *
。这就是分支 test-branch
的历史:您在 *
时创建了它,然后添加了 A
,然后添加了 B
.
此时您可以通过以下两种方式轻松压缩提交 A
和 B
:
git rebase -i master
这为您提供了一个交互式编辑会话,您可以在其中 "pick" 第一个提交然后 "squash" 第二个,它会将两个提交消息聚集在一起并让您编辑结果,在通常的方式。然后它进行一个(单个)新提交,其树是提交
B
.git reset --soft master; git commit
这不会为rebase打开一个交互式编辑会话:它只是防止staging-area-and-tree提交
B
(那是[=git reset --soft
的 44=] 部分),将标签test-branch
移回指向直接提交*
(即git reset --soft
的git reset
部分),并使像往常一样新提交 (git commit
).缺点是您必须编写新的提交消息,但您可以通过多种方式从提交
A
或B
中恢复提交消息。例如,您可以使用-c
或-C
标志来git commit
(您必须确定提交A
或B
,例如,使用@{1}
或@{1}^
或@{yesterday}
或其他一些 reflog 说明符)。或者,在执行git reset --soft
之前,您可以使用git log
并将日志消息保存在文件或其他任何文件中。(当您有 42 个左右的 squash 而不是只有两个提交时,第二种方法会大放异彩。)
这两种方法实际上做同样的事情:它们添加一个 new 提交(我们称之为 AB
),留下 A
和 B
后面有点灰了,我真的画不对:
AB <-- HEAD=test-branch
/
...<- o <- * <-- master, origin/master
\
A <- B [ghost version of test-branch]
你的分支的幽灵版本对大多数正常使用是不可见的,最终(默认情况下 30 天左右)被垃圾收集掉。 (在那之前它仍然在你的存储库和你的引用日志中,这样你就可以找到原始提交 A
和 B
以备不时之需。)
如果您已经完成了第 6 步怎么办?在那种情况下,您仍然必须确定提交 *
。你可以在我写这篇文章的时候 git merge-base
:
$ mergebase=$(git merge-base HEAD master)
# then pick just ONE of the next two
$ git rebase -i $mergebase
$ git reset --soft $mergebase; git commit
这是它的工作原理。在 git checkout master; git fetch; git merge; git checkout test-branch
(步骤 5 和 6,或多或少)之后,您的提交图现在看起来更像这样:
...<- o <- * <- o <-- master, origin/master
\
A <- B <-- HEAD=test-branch
master
和 origin/master
指向的新 o
提交——或者它可能是整个提交链——是 "in the way",但是 [= test-branch
(你现在所在的位置)和 master
的 158=] 是提交 *
:两个分支分歧之前最近的共享提交。
然后我们简单地在该提交中定位 rebase
或 reset --soft
。当我们完成并有一个新的 AB
提交时,它看起来像这样:
AB <-- HEAD=test-branch
/
...<- o <- * <- o <-- master, origin/master
\
A <- B [ghost version of test-branch]
一旦你压缩了 AB
提交,你就可以 git rebase
以通常的方式把它放到 master
上。
请注意,另一个答案做的是完全相同的事情,它只是通过计算提交来识别提交 *
。如果您知道 test-branch
的提示和 "interesting" 提交 *
之间有两个提交,那么 HEAD~2
标识与 $(git merge-base HEAD master)
相同的提交。使用 git merge-base
只是让你避免计数。
1"References" 是实际的通用 git 术语。在这种情况下,它们是分支名称,我使用 "label" 一词来将它们与该提交图形成的分支历史区分开来。 git 中的 "branch" 一词至少用来指代这两个不同的事物,这让人很困惑。