# 修复重复的 Change-Id 问题 ## 问题 推送时出现错误: ``` same Change-Id in multiple changes. Squash the commits with the same Change-Id or ensure Change-Ids are unique for each commit ``` 这是因为多个提交使用了相同的 Change-Id。Gerrit 要求每个提交都有唯一的 Change-Id。 ## 解决方案 ### 方法一:使用 commit-msg hook 重新生成(推荐) commit-msg hook 会基于提交内容自动生成唯一的 Change-Id: ```bash cd /d/zhini/zhini_im # 1. 确保 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 # 2. 使用 filter-branch 重新为所有提交生成 Change-Id git filter-branch -f --msg-filter ' # 读取原始提交信息 ORIGINAL_MSG=$(cat) # 移除旧的 Change-Id(如果存在) CLEAN_MSG=$(echo "$ORIGINAL_MSG" | sed "/^Change-Id:/d" | sed "/^$/N;/^\n$/d") # 输出清理后的提交信息 echo "$CLEAN_MSG" # 使用 commit-msg hook 生成新的 Change-Id # hook 会基于提交内容(树对象、父提交、作者等)生成唯一的 Change-Id echo "" # 临时保存提交信息 echo "$CLEAN_MSG" > /tmp/commit_msg.txt # 模拟提交过程,让 hook 生成 Change-Id # 这里我们需要手动生成,基于提交的 SHA1 和内容 COMMIT_SHA=$(git rev-parse HEAD) TREE_SHA=$(git rev-parse HEAD^{tree}) PARENT_SHA=$(git rev-parse HEAD^ 2>/dev/null || echo "") # 生成基于提交内容的唯一 Change-Id CHANGE_ID_BASE=$(echo -n "$COMMIT_SHA$TREE_SHA$PARENT_SHA" | git hash-object --stdin) CHANGE_ID="I${CHANGE_ID_BASE:0:40}" echo "Change-Id: $CHANGE_ID" ' --tag-name-filter cat -- --branches --tags ``` ### 方法二:使用 git rebase(更可靠) 这个方法会真正运行 commit-msg hook: ```bash cd /d/zhini/zhini_im # 1. 确保 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 # 2. 创建备份 git branch backup-before-rebase # 3. 从第一个提交开始 rebase FIRST_COMMIT=$(git rev-list --max-parents=0 HEAD) git rebase -i --root # 在编辑器中: # - 将所有 'pick' 改为 'reword' # - 保存并关闭 # # 对每个提交: # - 提交信息编辑器会自动打开 # - 不要修改内容,直接保存退出 # - commit-msg hook 会自动为每个提交生成唯一的 Change-Id # - 继续下一个提交 ``` ### 方法三:使用脚本自动处理(最简单) 创建一个脚本来自动为每个提交生成唯一的 Change-Id: ```bash cd /d/zhini/zhini_im # 确保 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 # 使用 filter-branch,为每个提交生成基于其内容的唯一 Change-Id git filter-branch -f --msg-filter ' # 读取原始提交信息 ORIGINAL_MSG=$(cat) # 移除所有旧的 Change-Id 行 CLEAN_MSG=$(echo "$ORIGINAL_MSG" | sed "/^Change-Id:/d") # 输出清理后的提交信息(去除末尾多余空行) echo "$CLEAN_MSG" | sed -e :a -e "/^\$/{\$!N;ba}" -e "s/\n\$//" # 为当前提交生成唯一的 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=$(git log -1 --format="%an <%ae> %at" HEAD) # 组合所有信息生成唯一哈希 UNIQUE_STRING="${COMMIT_SHA}${TREE_SHA}${PARENT_SHA}${AUTHOR}" 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 ``` ## 推荐操作步骤 执行以下命令: ```bash cd /d/zhini/zhini_im # 1. 确保 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 # 2. 创建备份 git branch backup-before-fix-changeid # 3. 为所有提交重新生成唯一的 Change-Id git filter-branch -f --msg-filter ' ORIGINAL_MSG=$(cat) # 移除旧的 Change-Id CLEAN_MSG=$(echo "$ORIGINAL_MSG" | sed "/^Change-Id:/d" | sed -e :a -e "/^\$/{\$!N;ba}" -e "s/\n\$//") echo "$CLEAN_MSG" # 生成基于提交内容的唯一 Change-Id 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_TIME=$(git log -1 --format="%an|%ae|%at|%cn|%ce|%ct" HEAD) UNIQUE_STRING="${COMMIT_SHA}${TREE_SHA}${PARENT_SHA}${AUTHOR_TIME}" CHANGE_ID_BASE=$(echo -n "$UNIQUE_STRING" | shasum -a 1 | cut -c1-40) CHANGE_ID="I${CHANGE_ID_BASE}" echo "" echo "Change-Id: $CHANGE_ID" ' --tag-name-filter cat -- --branches --tags # 4. 验证所有 Change-Id 都是唯一的 git log --format='%H %s' | while read commit subject; do changeid=$(git log -1 --format='%B' "$commit" | grep "^Change-Id:" | head -1) echo "$commit $changeid" done | sort -k2 | uniq -d -f1 # 如果没有输出,说明所有 Change-Id 都是唯一的 # 5. 重新推送 git push gerrit HEAD:refs/for/master ``` ## 验证 Change-Id 唯一性 ```bash # 检查是否有重复的 Change-Id git log --format='%H' | while read commit; do changeid=$(git log -1 --format='%B' "$commit" | grep "^Change-Id:" | head -1 | cut -d' ' -f2) echo "$changeid $commit" done | sort | uniq -d # 如果没有输出,说明所有 Change-Id 都是唯一的 ```