I'm looking for a way to replicate what git commit -S does but on a specific commit, by giving its SHA for instance.
Is it possible?
I'm looking for a way to replicate what git commit -S does but on a specific commit, by giving its SHA for instance.
Is it possible?
Signing a commit will change the commit metadata, and thus change the underlying SHA1 commit ID. As you probably know, for Git, this has the same consequence of trying to change the contents of your history.
If you want to just re-sign your last commit you could run:
git commit -S --amend
If you want to re-sign a commit in the middle of your history you could do a couple of things, all of them being a bit nasty if you ask me:
reset --soft to the commit you want to sign. Run git commit -S --amend and then commit all the staged changes. This would merge all your history after that commit into a single commitreset --hard to the commit you want to sign. Sign it, and if you want to perserve commit history you could now git cherry-pick NEXTCOMMIT -S to re-build the whole signed history.If you want to sign all the existing commits on the branch without do any changes to them:
git rebase --exec 'git commit --amend --no-edit -n -S' -i origin/HEAD
If I had my branch created from master, I would use the following:
git rebase --exec 'git commit --amend --no-edit -n -S' -i master
This command will detect all my commits on my branch and reapply them as signed commits on top of master.
This worked for my case, where I wanted to rewrite all history signing every commit from the very first one. My repo was small, with 2 branches (master, develop) so there was some merging I had to deal with. The only info I "lost" was committer date, by setting to the same as author date on every commit (whatever).
Note that force-pushing rewritten history is not recommended, specially for repos that have forks and/or are public! This is advanced, so make sure you know exactly what you're doing! All commit hashes will change!
These are the steps I took, roughly (not showing all as commands, since I used GUI too):
# Make sure there are no unstaged changes before starting
# IMPORTANT! Make a backup by copying entire root folder; or make another clone
# Restore and clean everything, removing ignored and untracked files
git reset --hard ; git clean -xdf
# Merge all (both) branches together into a temporary "END" commit; note its hash
# Create a detached initial commit, now signed; point HEAD to it and note its hash
git rebase -f -r -S -i --committer-date-is-author-date --root $OLD_INITIAL_HASH
# Start rebasing everything on the new root, now signed
git rebase -f -r -S -i --committer-date-is-author-date $NEW_INITIAL_HASH $END_HASH
# Take a look at the list of commits in the interactive rebase
# And make sure you understand what it's going to do
Whenever a merge conflict happens, I need to solve it manually.
In my case, it seems I always wanted the "Incoming changes".
# Understand what commit it's recreating
# Ensure files are same as original commit
# Once all merges are resolved/staged, confirm with:
git rebase --continue
# repeat...
If rebasing went wrong, do git rebase --abort; then, delete folder and start over from the backup.
If the rebasing successfully finished, you can proceed:
# Confirm that all commits were signed:
git log --show-signature
# Move annotated tags to the corresponding new commits (and sign them)
# Move branches to the corresponding new commits
# Delete the "END" commit by resetting to the one before it
# Then copy all files (except .git folder) from the backup, overwriting
# This is to restore ignored and untracked files; this should cause no git changes
# Online, disable workflow actions temporarily, just in case
# Finally force-push (BE CAREFUL!) all changes, including tags, something like:
# git push --all --follow-tags --atomic --force-with-lease
we can also sign the commits from a commit hash so that every commit from that commit will be signed with this command:
git rebase --exec 'git commit --amend --no-edit -n -S' -i <commit hash>