如何修剪git reflog歷史
背景:
vscode插件git-graph可以方便查看git-commit-graph,效果很好,關鍵是交互性很好。
點選任意commit即可預覽提交內容,實在是太方便了,比我之前用命令行上git log --graph --oneline強太多了。
但同時帶來的困擾是能看到的信息(commit歷史)太多了,讓我眼花繚繞。
例如,為了修復一個issue,前后進行了10次git commit --amend。也就是一共11次提及歷史記錄。
git graph大概長這樣

初衷:
實際上當我合并這次的修改之后,我只想保留最后一次的記錄在reflog里,其他的10次提交歷史都不要了。這就涉及到了git reflog修剪了。
實現:
首先,git 是通過HEAD找commit hash ID,然后每個commit都有parent commit,如此組成一條鏈式結構。
commit是描繪git-graph的主要依據,其實只要刪掉一個commit就能改變git-graph的結果。
每一次提交都會在.git/objects目錄下生成至少一個commit類型的文件,其完整的文件路徑為.git/objects/12/34567xx... (這里假設這個commit hash id 是1234567xx...)
git cat-file -t可以查看.git/objects目錄下的文件是tree、commit還是blob類型。
例如:
注意:不能刪除當前分支上可達的commit,不然鏈就斷了,git就無法正常工作了。前面提到的"其他的10次提交歷史" 因為在當前分支已經不可達,所以可以刪除
例如我想從git-graph刪除一個hash為 1234567的commit
那么步驟為:
- 找到.git/objects/12/34567xx...
- 刪除或者移動它 (建議移動到一個目錄下,萬一想要查看的時候還能還原)
因為git commit hash有縮寫形式、引用形式、完整形式,但是.git/objects/下的文件名都是完整形式,這種事情當然要寫個腳本來一勞永逸了。
#!/bin/bash
function zlipd() {
printf "\x1f\x8b\x08\x00\x00\x00\x00\x00" | cat - $@ | gzip - dc 2> /dev/null
}
function move_intermediate_obj() {
local dst_path
dst_path=$1; shift
while [ -n "$1" ]; do
if [ -f .git /objects/ "${1:0:2}" / "${1:2}" ]; then
mv - v .git /objects/ "${1:0:2}" / "${1:2}" "$dst_path/$1"
fi
shift
done
}
# function migrate_intermediate_obj() {
# [ ! -d ./.git ] && { echo ".git dir not exist"; return; }
# [ ! -d ./intermediate_obj ] && mkdir -p intermediate_obj
# for f in $(git rev-list -n "${2:-1}" "${1:?params not enough}")
# do
# # echo "$f"
# move_intermediate_obj "$f"
# done
# }
migrate_intermediate_obj ()
{
[ ! -d ./.git ] && {
echo ".git dir not exist" ;
return
};
local dst_dir
[ -d . /output ] && dst_dir=. /output/intermediate_obj ;
[ -z $dst_dir ] && dst_dir=. /intermediate_obj ;
mkdir -p $dst_dir
if [ $ # -eq 1 ]
then
move_intermediate_obj $dst_dir "$1" ;
elif expr $2 + 0 > /dev/null 2>&1 # test $2 whether is number otherwise $? neq 0/1 if $2 non-integer argument
then
for f in $(git rev-list -n "${2:-1}" "${1:?params not enough}" );
do
move_intermediate_obj $dst_dir "$f" ;
done
else
move_intermediate_obj $dst_dir "$@" ;
fi
}
腳本使用方法:
??source script.sh
??migrate_intermediate_obj 1234567
腳本將會在當前目錄下創建一個文件intermediate_obj,并將commit文件移動進去。
PS:在git gc的時候有些commit會被打包到.git/objects/pack文件夾下的pack后綴的文件里,這樣的話在.git/objects/下就找不到這些commit文件了。
解決辦法是使用git unpack-objects < .git/*.pack文件解壓出來。(pack文件和index文件要事先從.git/objects/pack移出去該命令才會有效果)
PS2:git verify-pack -v .git/objects/pack/pack-xx.pack可以查看哪個pack文件包含你要的commit

浙公網安備 33010602011771號