Files
gerrit/彻底修复Change-Id.md
2025-12-22 17:12:39 +08:00

5.7 KiB
Raw Blame History

彻底修复 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

在编辑器中

  1. 将所有 pick 改为 reword(或简写 r
  2. 保存并关闭(:wq

对每个提交

  1. 提交信息编辑器会自动打开
  2. 不要修改任何内容,直接保存退出(:wq
  3. commit-msg hook 会自动为这个提交生成唯一的 Change-Id
  4. 继续下一个提交

这个过程会自动完成,每个提交都会获得基于其内容的唯一 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