189 lines
5.7 KiB
Markdown
189 lines
5.7 KiB
Markdown
# 彻底修复 Change-Id 重复问题
|
||
|
||
## 问题
|
||
|
||
即使使用了 SHA1,仍然有重复的 Change-Id。这可能是因为 filter-branch 的处理方式有问题。
|
||
|
||
## 最可靠的解决方案:使用 git rebase + commit-msg hook
|
||
|
||
这是 Gerrit 推荐的标准方法,commit-msg hook 会基于提交内容自动生成唯一的 Change-Id。
|
||
|
||
### 步骤 1:清理 filter-branch 的备份引用
|
||
|
||
```bash
|
||
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 已安装
|
||
|
||
```bash
|
||
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
|
||
|
||
```bash
|
||
# 创建备份
|
||
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:验证并推送
|
||
|
||
```bash
|
||
# 验证所有 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 太麻烦)
|
||
|
||
如果提交太多,可以使用这个脚本自动化处理:
|
||
|
||
```bash
|
||
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
|
||
```
|
||
|
||
## 最简单的解决方案(推荐)
|
||
|
||
如果上面的方法还是有问题,使用这个最简单直接的方法:
|
||
|
||
```bash
|
||
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
|
||
|
||
|