我有一个项目,我开始在我的旧笔记本电脑上工作,然后继续在我的新笔记本电脑上工作。在我的旧笔记本电脑上,我已经使用
user.name
和 user.email
正确配置了 Git,但是当我切换到新笔记本电脑时,我只是克隆了存储库并继续工作,而无需配置 user.name
和
user.email
。因此,我从新笔记本电脑所做的提交归因于我的本地计算机用户的
user.name
和
[computer_name]@local
的
user.email
。我想修改我的新笔记本电脑所做的所有提交,并在here找到了执行此操作的方法。
git rebase -r <last commit from my old laptop> \
--exec 'git commit --amend --no-edit --reset-author'
注意,我是唯一一个在这个仓库上工作的人,所以重写历史并不是一个大问题。
然而,确实出错的一件事是,我在 Git 存储库中创建的所有标签和版本都处于孤立状态。 GitHub 在每个标签旁边显示一条消息,内容如下:
~“此标签指向任何存储库分支之外的提交,或指向不同的存储库”~。这是因为修改提交的作者会更改其标识哈希。因此,标签指向
rebase
命令之前旧提交的哈希值。
我该怎么做?
rev-list
、
awk
、log
和 tag
来做到这一点。git rev-list --all --tags | \
while read commit; do \
new_commit=$(git log --format="%H" -n 1 $commit); \
git tag -f $(git tag --contains $commit) $new_commit; \
done
以下是其功能的详细说明:
git rev-list --all --tags
while read commit; do ... done
git rev-list
的输出中读取每个提交 ID,并为每个提交执行以下命令。new_commit=$(git log --format="%H" -n 1 $commit)
%H
格式选项检索与旧提交ID对应的新提交ID,它代表完整的提交哈希。git tag -f $(git tag --contains $commit) $new_commit
$commit
命令和 $new_commit
(强制)选项,将包含旧提交 ID (git tag
) 的标签重新应用到相应的新提交 ID (-f
)。这会覆盖具有相同名称的新提交上的任何现有标签。
P.S.:完全公开,我使用人工智能助手来解决这个问题。
#!/usr/bin/env bash
# fix git tags after rewriting the git history
# with "git-filter-repo" or "git rebase"
# what branches are allowed for tags?
# usually, tags are not allowed on backup branches
#branches="master branch2 branch3"
branches="master"
branches_regex="$(printf '|%s' $branches)"
branches_regex="(${branches_regex:1})"
echo "branches regex: $branches_regex"
while read tag_commit tag_ref; do
tag_name=${tag_ref#*/*/}
echo
echo "checking tag $tag_name (commit $tag_commit) (date $tag_author_date)"
tag_author_date=$(git log -1 --format='format:%ad' $tag_ref)
tag_commit_branches="$(git branch --contains $tag_commit --format='%(refname)')"
if ! echo "$tag_commit_branches" | grep -q -x -E "refs/heads/$branches_regex"; then
echo "tag $tag_name is not part of branches $branches_regex"
found=false
for branch in $branches; do
commit_candidates="$(git log $branch --format=format:"%ad %H" | grep "^$tag_author_date ")"
if [[ -z "$commit_candidates" ]]; then
#echo "error: found no commit candidates for tag $tag_name"
:
elif [[ $(echo "$commit_candidates" | wc -l) == 1 ]]; then
commit_hash=${commit_candidates##* }
echo "ok: found one commit candidate ($commit_hash) in branch $branch with author date $tag_author_date"
echo git tag -f $tag_name $commit_hash
git tag -f $tag_name $commit_hash
found=true
break
else
echo "error: found multiple commit candidates for tag $tag_name"
echo "please fix this tag manually with one of these commands:"
while read commit_candidate; do
commit_hash=${commit_candidate##* }
echo " git tag -f $tag_name $commit_hash"
done <<<"$commit_candidates"
fi
done
if ! $found; then
echo "error: found no commit candidates for tag $tag_name in branches $branches_regex"
fi
else
tag_branches="$(echo "$tag_commit_branches" | grep -x -E "refs/heads/$branches_regex" | sed 's|^refs/heads/||')"
echo "ok: tag $tag_name is part of branches" $tag_branches
fi
done < <(
git tag -l --format='%(objectname) %(refname)'
)