我知道这对很多场景来说都是一个坏主意.我正在学习Git并进行实验.本练习中不会损害任何代码.
我创建了一个这样的结构:
* [cf0149e] (HEAD, branch_2) more editing
* [8fcc106] some edit
|
| * [59e643e] (branch_2b) branch 2b
| /
|/
| * [0f4c880] (branch_2_a) branch 2a
| /
|/
* [a74eb2a] checkout 1
* [9a8dd6a] added branch_2 line
|
|
| * [bb903de] (branch_3) branch 3
|/
|
| * [674e08c] (branch_1) commit 1
| * [7d3db01] added branch_1 line
|/
* [328454f] (0.0.0) test
Run Code Online (Sandbox Code Playgroud)
现在我想通过这个图并重命名各种提交,以便它们有意义.
例如:
* | [a74eb2a] checkout 1
* | [9a8dd6a] added branch_2 line
renamed to:
* | [a74eb2a] branch 2 commit 2
* | [9a8dd6a] branch 2 commit 1
Run Code Online (Sandbox Code Playgroud)
注意:
[cf0149e] (HEAD, branch_2) more editing
[59e643e] (branch_2b) branch 2b
[0f4c880] (branch_2_a) branch 2a
Run Code Online (Sandbox Code Playgroud)
都是分支出来的:
[a74eb2a] checkout 1
Run Code Online (Sandbox Code Playgroud)
我试过了
git rebase -i 328454f
Run Code Online (Sandbox Code Playgroud)
然后将"选择"更改为"编辑"我想要修改并随后运行的提交
git commit --amend -m "the new message"
Run Code Online (Sandbox Code Playgroud)
随着rebase过程的继续.
这种方法的问题在于,在最后一次git rebase --continue
我最终在分支上进行了两次新的提交(我要重命名的两个重复),我碰巧在.例如,如果我在HEAD位于"branch_2"时运行了rebase,那么图形可能如下所示:
* [cf0149e] (HEAD, branch_2) more editing
* [8fcc106] some edit
* [3ff23f0] branch 2 commit 2
* [2f287a1] branch 2 commit 1
|
| * [59e643e] (branch_2b) branch 2b
| /
| /
| | * [0f4c880] (branch_2_a) branch 2a
| | /
| |/
| * [a74eb2a] checkout 1
| * [9a8dd6a] added branch_2 line
|/
|
| * [bb903de] (branch_3) branch 3
|/
|
| * [674e08c] (branch_1) commit 1
| * [7d3db01] added branch_1 line
|/
* [328454f] (0.0.0) test
Run Code Online (Sandbox Code Playgroud)
换句话说,我现在有两组提交代表完全相同的代码状态.
我想做的就是更改提交消息.
我还想将初始消息从"test"重命名为"Initial version".我似乎无法做到这一点,git commit --amend -m "Initial version"
因为如果我结帐那个提交,我会以无头模式结束.
我究竟做错了什么?当然不会那么难.
编辑:
这是我尝试过的方法.当然,它重写了历史.所以,在非常特殊的情况下,这是一个坏主意.以下是步骤:
签出要修改的分支.创建补丁文件:
git format-patch HEAD~x // Where x is how far back from HEAD you need to patch
Run Code Online (Sandbox Code Playgroud)
编辑补丁文件以更改提交消息.现在重置头部.
git reset --hard HEAD~x // Same x as before
Run Code Online (Sandbox Code Playgroud)
应用补丁:
git am 000*
Run Code Online (Sandbox Code Playgroud)
将使用新的SHA1创建新提交.如果任何分支现在需要使用更正的消息引用新提交,则必须使用git rebase
它们来移动它们.
要使用我自己的示例,在应用修补程序后,我最终得到了:
* [7761415] (HEAD, branch_2) branch 2 commit 4
* [286e1b5] branch 2 commit 3
* [53d638c] branch 2 commit 2
* [52f82f7] branch 2 commit 1
| * [bb903de] (branch_3) branch 3
|/
| * [59e643e] (branch_2b) branch 2b
| | * [0f4c880] (branch_2_a) branch 2a
| |/
| * [a74eb2a] checkout 1
| * [9a8dd6a] added branch_2 line
|/
| * [674e08c] (branch_1) commit 1
| * [7d3db01] added branch_1 line
|/
* [328454f] (0.0.0) test
Run Code Online (Sandbox Code Playgroud)
所以,我的branch_2提交很好地标记了.现在我想移动branch_2a以便它分支出来[53d638c] branch 2 commit 2
结帐branch_2a
git checkout branch_2a
Run Code Online (Sandbox Code Playgroud)
重新启动它
git rebase 53d638c
Run Code Online (Sandbox Code Playgroud)
我现在有:
* [fb4d1c5] (HEAD, branch_2a) branch 2a
| * [7761415] (branch_2) branch 2 commit 4
| * [286e1b5] branch 2 commit 3
|/
* [53d638c] branch 2 commit 2
* [52f82f7] branch 2 commit 1
| * [bb903de] (branch_3) branch 3
|/
| * [59e643e] (branch_2b) branch 2b
| * [a74eb2a] checkout 1
| * [9a8dd6a] added branch_2 line
|/
| * [674e08c] (branch_1) commit 1
| * [7d3db01] added branch_1 line
|/
* [328454f] (0.0.0) test
Run Code Online (Sandbox Code Playgroud)
与branch_2b相同的过程导致:
* [ca9ff6c] (HEAD, branch_2b) branch 2b
| * [fb4d1c5] (branch_2a) branch 2a
|/
| * [7761415] (branch_2) branch 2 commit 4
| * [286e1b5] branch 2 commit 3
|/
* [53d638c] branch 2 commit 2
* [52f82f7] branch 2 commit 1
| * [bb903de] (branch_3) branch 3
|/
| * [674e08c] (branch_1) commit 1
| * [7d3db01] added branch_1 line
|/
* [328454f] (0.0.0) test
Run Code Online (Sandbox Code Playgroud)
这正是我想要的.不太乱.同样,在非常特殊的情况下,你不想做的事情.在我的情况下,我只是在玩Git,所以上面并没有真正影响真正的代码库.很高兴知道你可以做到这一点,如果你不得不这样做.
现在重命名第一个提交.
tor*_*rek 18
您实际上无法更改提交.如果您在任何地方进行最小的单位更改,从电子邮件行中的名称拼写到提交时间戳的确切秒,您将获得一个新的,不同的提交,使用新的不同SHA1(SHA1是"true"在git数据库中为每个"对象"命名",其中提交是四种类型的对象之一".
提交的一个不可变的部分是它的"父提交",它从最近提交到最旧提交向后构建链.
因此,git rebase -i
创建新的提交链是什么,链中的每个提交都具有与原始提交相同的内容/效果加上或减去您在交互期间所做的任何更改.完成所有操作后,它会从旧提交链的末尾删除标签(粘滞便笺),并将其粘贴到新提交链的末尾.它首先制作要修改/重新定位的最旧提交的副本.这个具有重新定位的父级(可以是与原始链中相同的父级提交,或者是不同的父级:无论哪种方式都可以,因为它是新的提交).然后它会复制旧链中的下一个,但指向新链.它一直重复到旧链的末尾.
这就是为什么你所有的其他分支现在都独立于你的重新分支.他们必须是,因为他们使用旧的提交ID.如果您希望它们分支您的新重新分支,您必须进入每个分支并重新定义它们.
有一个强大的Swiss-Army-chainsaw-ish命令,git filter-branch
你可以用来做一大堆"重做大量的提交,使所有新的提交(大部分)与原始内容相同",有点像git rebase
就类固醇而言,您可以将其用于此目的.(运行它--all
以影响所有分支.)当然,因为它实际上重做了所有提交,所以最终会得到一个与原始仓库基本无关的仓库.
重写初始提交(并非不可能)很难,因为它没有父级,所以常规rebase
只是不会这样做.1 (filter-branch
可以.)因为这些更改了SHA1 ID,而且克隆了你的repo的任何人正在使用/依赖这些,所以它通常是一个你不能摆弄这样的提交的功能.当你知道没有其他人依赖于某些特定的提交时,那么你可以rebase
随心所欲,但你不会回到初始提交,因为那将在repo的共享部分.这是非常罕见的(虽然当然不是"永远"),整个事情一直回到"初始提交"是你自己的私人东西.
1自从我写这篇文章以来,我git rebase
学会了像初始提交一样复制root提交.2 使用传统的git rebase
语法,你就必须命名父提交的根源,当然还有就是没有父(这是什么使得它在图中的"根").所以rebase
使用--root
作为参数来弥补这一情况.
2可能有多个根; 使用git checkout --orphan
,例如,其次git commit
,创建一个新的根提交.虽然git源本身保存在具有多个根的git repo中,但它有点不寻常.