Reputation: 5480
I have a project where I'd started working on my old laptop and then continued on my new one. On my old laptop I had properly configured Git with user.name
and user.email
, but when I switched to my new one I just cloned the repo and continued working without configuring user.name
and user.email
. As a result, the commits I made from my new laptop were attributed to my local machine's user for user.name
and [computer_name]@local
for user.email
. I wanted to amend all the commits made from my new laptop and found the way to do it here.
git rebase -r <last commit from my old laptop> \
--exec 'git commit --amend --no-edit --reset-author'
Note, I'm the only one working on this repo, so rewriting history was not a big problem. However, one thing that did go wrong was that all the tags and releases I had created in my Git repo where left orphaned. GitHub shows me a message next to each tag that reads something like this:
~"this tag points to a commit outside any of the repository branches, or points to a different repo"~.
This is because amending the commit's author changes its identifying hash. So the tags point to the hashes of the old commits before the rebase
command.
So, need to re-apply each tag to the new commit that corresponds to the old one it's pointing to.
How do I do that?
Upvotes: 1
Views: 577
Reputation: 1
In my case i have to remove files from repo with bfg and all the tags was messedup i use this code to rewrite the tags
git show-ref --tags | \
while read commit; do \
date=$(git show -s --format=%ct $commit)
ncom=$(git log --oneline --since=$date --until=$date --format="%H")
com=${commit:0:40}
tag=${commit:51:8}
if [ $ncom = $com ]; then
se=""
else
echo ----
echo $commit
echo $ncom $tag
git tag -f $tag $ncom
echo ++++++++++++
sleep 0.1
fi;
done
git push --tags -f
I take all the old tags commit hash and date, searh in the repo for a commit made at the same time, then assign the tag to the commit foud at the same date
Upvotes: 0
Reputation: 3589
to restore tags by author date
note: this does not handle annotated tags
#!/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)'
)
Upvotes: 0
Reputation: 5480
I managed to do this with a combination of rev-list
, awk
, log
and 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
Here's a breakdown of what it does:
git rev-list --all --tags
: Lists all the commits that are reachable from any reference (branches, tags) in the repository.while read commit; do ... done
: Reads each commit ID from the output of git rev-list
and executes the following commands for each commit.new_commit=$(git log --format="%H" -n 1 $commit)
: Retrieves the new commit ID corresponding to the old commit ID by using the %H
format option, which represents the full commit hash.git tag -f $(git tag --contains $commit) $new_commit
: Reapplies the tags that contain the old commit ID ($commit
) to the corresponding new commit ID ($new_commit
) using the git tag
command with the -f
(force) option. This overwrites any existing tags on the new commits with the same name.With this, each old tag will be repositioned to point to the corresponding new commit ID.
P.S.: Full disclosure, I used an AI assistant to figure this out.
Upvotes: 0