189 lines
5.5 KiB
Markdown
189 lines
5.5 KiB
Markdown
|
|
# 修复重复的 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 都是唯一的
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
|