5.7 KiB
5.7 KiB
彻底修复 Change-Id 重复问题
问题
即使使用了 SHA1,仍然有重复的 Change-Id。这可能是因为 filter-branch 的处理方式有问题。
最可靠的解决方案:使用 git rebase + commit-msg hook
这是 Gerrit 推荐的标准方法,commit-msg hook 会基于提交内容自动生成唯一的 Change-Id。
步骤 1:清理 filter-branch 的备份引用
cd /d/zhini/zhini_im
# 清理所有 filter-branch 创建的备份引用
git for-each-ref --format="%(refname)" refs/original/ | xargs -n 1 git update-ref -d 2>/dev/null || true
步骤 2:确保 commit-msg hook 已安装
mkdir -p .git/hooks
curl -o .git/hooks/commit-msg http://101.43.95.130:8080/tools/hooks/commit-msg
chmod +x .git/hooks/commit-msg
步骤 3:使用 git rebase 让 hook 自动生成 Change-Id
# 创建备份
git branch backup-before-rebase-fix
# 从第一个提交开始交互式 rebase
git rebase -i --root
在编辑器中:
- 将所有
pick改为reword(或简写r) - 保存并关闭(
:wq)
对每个提交:
- 提交信息编辑器会自动打开
- 不要修改任何内容,直接保存退出(
:wq) - commit-msg hook 会自动为这个提交生成唯一的 Change-Id
- 继续下一个提交
这个过程会自动完成,每个提交都会获得基于其内容的唯一 Change-Id。
步骤 4:验证并推送
# 验证所有 Change-Id 都是唯一的
git log --format='%H' | while read commit; do
git log -1 --format='%B' "$commit" | grep "^Change-Id:" | head -1 | cut -d' ' -f2
done | sort | uniq -d
# 如果没有输出,说明所有 Change-Id 都是唯一的
# 推送
git push gerrit HEAD:refs/for/master
自动化脚本(如果提交太多,手动 rebase 太麻烦)
如果提交太多,可以使用这个脚本自动化处理:
cd /d/zhini/zhini_im
# 1. 清理备份
git for-each-ref --format="%(refname)" refs/original/ | xargs -n 1 git update-ref -d 2>/dev/null || true
# 2. 确保 hook 已安装
mkdir -p .git/hooks
curl -o .git/hooks/commit-msg http://101.43.95.130:8080/tools/hooks/commit-msg
chmod +x .git/hooks/commit-msg
# 3. 创建备份
git branch backup-before-auto-fix
# 4. 使用 filter-branch,但这次真正运行 commit-msg hook
# 我们需要模拟提交过程,让 hook 生成 Change-Id
git filter-branch -f --msg-filter '
# 读取原始提交信息
ORIGINAL=$(cat)
# 移除旧的 Change-Id
CLEAN=$(echo "$ORIGINAL" | grep -v "^Change-Id:")
# 将清理后的提交信息保存到临时文件
echo "$CLEAN" > /tmp/commit_msg.txt
# 使用 commit-msg hook 生成 Change-Id
# hook 需要环境变量 GIT_AUTHOR_NAME, GIT_AUTHOR_EMAIL 等
export GIT_AUTHOR_NAME=$(git log -1 --format="%an" HEAD)
export GIT_AUTHOR_EMAIL=$(git log -1 --format="%ae" HEAD)
export GIT_AUTHOR_DATE=$(git log -1 --format="%ad" HEAD)
export GIT_COMMITTER_NAME=$(git log -1 --format="%cn" HEAD)
export GIT_COMMITTER_EMAIL=$(git log -1 --format="%ce" HEAD)
export GIT_COMMITTER_DATE=$(git log -1 --format="%cd" HEAD)
# 运行 commit-msg hook
if [ -f .git/hooks/commit-msg ]; then
cat /tmp/commit_msg.txt | .git/hooks/commit-msg
else
cat /tmp/commit_msg.txt
# 如果 hook 不存在,使用 SHA1 作为后备
echo ""
echo "Change-Id: I$(git rev-parse HEAD)"
fi
rm -f /tmp/commit_msg.txt
' --tag-name-filter cat -- --branches --tags
# 5. 推送
git push gerrit HEAD:refs/for/master
最简单的解决方案(推荐)
如果上面的方法还是有问题,使用这个最简单直接的方法:
cd /d/zhini/zhini_im
# 1. 清理所有备份
git for-each-ref --format="%(refname)" refs/original/ | xargs -n 1 git update-ref -d 2>/dev/null || true
# 2. 确保 hook 已安装
mkdir -p .git/hooks
curl -o .git/hooks/commit-msg http://101.43.95.130:8080/tools/hooks/commit-msg
chmod +x .git/hooks/commit-msg
# 3. 创建备份
git branch backup-final-fix
# 4. 使用 filter-branch,为每个提交生成基于其完整信息的唯一 Change-Id
git filter-branch -f --msg-filter '
# 读取原始提交信息
ORIGINAL=$(cat)
# 移除旧的 Change-Id
CLEAN=$(echo "$ORIGINAL" | grep -v "^Change-Id:" | sed -e :a -e "/^\$/{\$!N;ba}" -e "s/\n\$//")
# 输出清理后的提交信息
echo "$CLEAN"
# 生成基于提交完整信息的唯一 Change-Id
# 使用:提交 SHA1 + 树对象 SHA1 + 父提交 SHA1 + 作者信息 + 提交者信息 + 时间戳
COMMIT_SHA=$(git rev-parse HEAD)
TREE_SHA=$(git rev-parse HEAD^{tree})
PARENT_SHA=$(git rev-parse HEAD^ 2>/dev/null || echo "root")
AUTHOR_NAME=$(git log -1 --format="%an" HEAD)
AUTHOR_EMAIL=$(git log -1 --format="%ae" HEAD)
AUTHOR_DATE=$(git log -1 --format="%at" HEAD)
COMMITTER_NAME=$(git log -1 --format="%cn" HEAD)
COMMITTER_EMAIL=$(git log -1 --format="%ce" HEAD)
COMMITTER_DATE=$(git log -1 --format="%ct" HEAD)
# 组合所有信息
UNIQUE_STRING="${COMMIT_SHA}${TREE_SHA}${PARENT_SHA}${AUTHOR_NAME}${AUTHOR_EMAIL}${AUTHOR_DATE}${COMMITTER_NAME}${COMMITTER_EMAIL}${COMMITTER_DATE}"
# 生成 40 位十六进制字符串(使用 git hash-object)
CHANGE_ID_BASE=$(echo -n "$UNIQUE_STRING" | git hash-object --stdin | cut -c1-40)
CHANGE_ID="I${CHANGE_ID_BASE}"
echo ""
echo "Change-Id: $CHANGE_ID"
' --tag-name-filter cat -- --branches --tags
# 5. 验证
echo "验证 Change-Id 唯一性..."
DUPLICATES=$(git log --format='%H' | while read commit; do
git log -1 --format='%B' "$commit" | grep "^Change-Id:" | head -1 | cut -d' ' -f2
done | sort | uniq -d)
if [ -z "$DUPLICATES" ]; then
echo "✅ 所有 Change-Id 都是唯一的"
echo ""
echo "现在可以推送:"
echo " git push gerrit HEAD:refs/for/master"
else
echo "⚠️ 仍有重复的 Change-Id:"
echo "$DUPLICATES"
fi