有没有一种简单的方法可以压缩 git 中的许多提交?
Is there an easy way to squash many commits in git?
我有以下问题:
我有很多提交需要压缩(数千个,我们有一个 运行away 脚本)。这些提交在 40 到 200 之间分组。
使用 git rebase -i
是不可行的,因为它会涉及太多的劳动力。
我们有一个工具,可以从分支 HEAD 输出这样一个组相对的第一次和最后一次提交(可用于通过其引用哈希获取实际提交)。
举个例子,我正在寻找可以将 HEAD~400 压缩到 HEAD~200 到单个提交中的东西。然后可以 运行 再次(使用更改参数)将 HEAD~100 压缩到 HEAD~50 到另一个提交中。
编辑 1:
我考虑过创建一个“假”编辑器,它通过对 rebase 文件执行更改来本质上伪造交互性。一个抽象的示例脚本看起来像这样(我可以循环直到所有组都被压扁):
start=$('get start of oldest group')
end=$('get end of oldest group')
git config core.editor "'~/fakeeditor' -start $start -end $end"
git rebase -i
您可以预先为 git rebase -i
生成完整的命令列表,然后将它们粘贴到编辑器中。这个过程看起来像这样:
- 对于要压缩的每批提交,生成完整的提交哈希列表,并在前面加上单词
fixup
;假设您的开始和结束提交在 $start
和 $end
中:git log "$start...$end" --pretty='fixup %h'
- 在每批提交之间,做同样的事情,但
pick
;你可以取一批南瓜的末尾和下一批南瓜开始的父代,然后丢弃第一行:git log "$end...$nextStart^" --pretty='pick %h' | sed -n '2,$p'
- 在您要压缩的第一个提交之前找到提交,然后 运行
git rebase -i
将其作为参数。
- 删除 git 生成的所有行,然后粘贴到您准备好的待办事项列表中。
- 保存并退出编辑器,git 的 rebase 工具将在您的列表中运行。
首先,创建一个脚本来操作 git rebase 的待办事项列表:
squash-it
文件:
#!/bin/sh
first=$(git rev-parse --short "")
last=$(git rev-parse --short "")
todo=""
lines=$(
sed -n "/^pick $first /,/^pick $last/{s/^pick/squash/p}" "$todo" | sed "1s/squash/pick/"
sed "/^pick $first /,/^pick $last/d" "$todo"
)
echo "$lines" > "$todo"
然后像这样使用这个脚本:
GIT_SEQUENCE_EDITOR='sh -c "./squash-it HEAD~100 HEAD~50 "' GIT_EDITOR=cat git rebase -i HEAD~100^
如果需要,您可以将 squash
替换为 fixup
。
您当然可以在脚本本身中生成此行,例如在循环中根据您的喜好替换 HEAD~100
和 HEAD~50
。
我个人最喜欢使用 git reset --soft
。
所以,假设您想压缩从 HEAD~1000
到这一点(HEAD~1000
是最后一个 surviving 不会被压缩的提交) :
git reset --soft HEAD~1000
git commit -m "Squashed a lot of stuff"
就是这样。您可以使用修订 ID 而不是使用 HEAD~n
引用。
我看到你想像分段那样做……所以,比如说……让我们把 HEAD~400 压缩到 HEAD~200……然后从 HEAD~200 压缩到 HEAD~100.. . 然后从 HEAD~100 到 HEAD。因此,让我们创建一个临时分支来完成我们的工作。
git checkout -b temp HEAD~200
git reset --soft HEAD~400
git commit -m "squashing first segment"
# next segment
git checkout the-original-branch~100
git reset --soft temp
git commit -m "Second segment"
git branch -f temp #set temp over here
# final segment
git checkout --detach the-original-branch
git reset --soft temp
git commit -m "Final segment"
git branch -f temp #set temp over here
# if you like the result, feel free to move whatever branch over here
git branch -f whatever-branch
# and delete temp if so you want
git branch -D temp
我觉得很简单。
一天后,我才意识到(通过回答另一个问题)可以用更简单的方式完成:
git branch -f temp $( git commit-tree -p HEAD~400 -m "first squash" HEAD~200^{tree} )
# that set up temp on the first squash
git branch -f temp $( git commit-tree -p temp -m "second squash" HEAD~100^{tree} )
# temp has move to second squash
git branch -f temp $( git commit-tree -p temp -m "final squash" HEAD^{tree} )
现在 temp 已经按照我的示例中请求的方式压缩了提交。随意在那边做一个reset --hard
(使用reset --hard
时通常的警告)。
我有以下问题:
我有很多提交需要压缩(数千个,我们有一个 运行away 脚本)。这些提交在 40 到 200 之间分组。
使用 git rebase -i
是不可行的,因为它会涉及太多的劳动力。
我们有一个工具,可以从分支 HEAD 输出这样一个组相对的第一次和最后一次提交(可用于通过其引用哈希获取实际提交)。
举个例子,我正在寻找可以将 HEAD~400 压缩到 HEAD~200 到单个提交中的东西。然后可以 运行 再次(使用更改参数)将 HEAD~100 压缩到 HEAD~50 到另一个提交中。
编辑 1:
我考虑过创建一个“假”编辑器,它通过对 rebase 文件执行更改来本质上伪造交互性。一个抽象的示例脚本看起来像这样(我可以循环直到所有组都被压扁):
start=$('get start of oldest group')
end=$('get end of oldest group')
git config core.editor "'~/fakeeditor' -start $start -end $end"
git rebase -i
您可以预先为 git rebase -i
生成完整的命令列表,然后将它们粘贴到编辑器中。这个过程看起来像这样:
- 对于要压缩的每批提交,生成完整的提交哈希列表,并在前面加上单词
fixup
;假设您的开始和结束提交在$start
和$end
中:git log "$start...$end" --pretty='fixup %h'
- 在每批提交之间,做同样的事情,但
pick
;你可以取一批南瓜的末尾和下一批南瓜开始的父代,然后丢弃第一行:git log "$end...$nextStart^" --pretty='pick %h' | sed -n '2,$p'
- 在您要压缩的第一个提交之前找到提交,然后 运行
git rebase -i
将其作为参数。 - 删除 git 生成的所有行,然后粘贴到您准备好的待办事项列表中。
- 保存并退出编辑器,git 的 rebase 工具将在您的列表中运行。
首先,创建一个脚本来操作 git rebase 的待办事项列表:
squash-it
文件:
#!/bin/sh
first=$(git rev-parse --short "")
last=$(git rev-parse --short "")
todo=""
lines=$(
sed -n "/^pick $first /,/^pick $last/{s/^pick/squash/p}" "$todo" | sed "1s/squash/pick/"
sed "/^pick $first /,/^pick $last/d" "$todo"
)
echo "$lines" > "$todo"
然后像这样使用这个脚本:
GIT_SEQUENCE_EDITOR='sh -c "./squash-it HEAD~100 HEAD~50 "' GIT_EDITOR=cat git rebase -i HEAD~100^
如果需要,您可以将 squash
替换为 fixup
。
您当然可以在脚本本身中生成此行,例如在循环中根据您的喜好替换 HEAD~100
和 HEAD~50
。
我个人最喜欢使用 git reset --soft
。
所以,假设您想压缩从 HEAD~1000
到这一点(HEAD~1000
是最后一个 surviving 不会被压缩的提交) :
git reset --soft HEAD~1000
git commit -m "Squashed a lot of stuff"
就是这样。您可以使用修订 ID 而不是使用 HEAD~n
引用。
我看到你想像分段那样做……所以,比如说……让我们把 HEAD~400 压缩到 HEAD~200……然后从 HEAD~200 压缩到 HEAD~100.. . 然后从 HEAD~100 到 HEAD。因此,让我们创建一个临时分支来完成我们的工作。
git checkout -b temp HEAD~200
git reset --soft HEAD~400
git commit -m "squashing first segment"
# next segment
git checkout the-original-branch~100
git reset --soft temp
git commit -m "Second segment"
git branch -f temp #set temp over here
# final segment
git checkout --detach the-original-branch
git reset --soft temp
git commit -m "Final segment"
git branch -f temp #set temp over here
# if you like the result, feel free to move whatever branch over here
git branch -f whatever-branch
# and delete temp if so you want
git branch -D temp
我觉得很简单。
一天后,我才意识到(通过回答另一个问题)可以用更简单的方式完成:
git branch -f temp $( git commit-tree -p HEAD~400 -m "first squash" HEAD~200^{tree} )
# that set up temp on the first squash
git branch -f temp $( git commit-tree -p temp -m "second squash" HEAD~100^{tree} )
# temp has move to second squash
git branch -f temp $( git commit-tree -p temp -m "final squash" HEAD^{tree} )
现在 temp 已经按照我的示例中请求的方式压缩了提交。随意在那边做一个reset --hard
(使用reset --hard
时通常的警告)。