This was a problem I wanted to solve. When I searched up this question, I saw answers using git filter-branch
and git filter-repo
.
I can say with 100% confidence that git filter-branch
is too slow and I don't like that it can change the same commit in multiple branches. Not a useful method.
Next is git filter-repo
. It seems like a good solution but I'm using Ubuntu 20.04 LTS which doesn't seem to be supported. I would need to spend a little time to update to 22.04 LTS. So only useful on supported distros and versions.
Now here is my answer:
initialCommit=$(git log original-branch --format=format:"%h" | tail -1)
readarray -t commitsArray < <(git log original-branch --author="Original Author <originalauthor@email.com>" --format=format:"%h")
startCommitIndex=$(("${#commitsArray[@]}"-1))
git checkout -b new-branch "$initialCommit"
if [[ "$initialCommit" == "${commitsArray[$startCommitIndex]}" ]]; then
git commit --amend --author "New Author <newauthor@email.com>"
startCommitIndex=$(($startCommitIndex - 1))
fi
for (( commitIndex=$startCommitIndex; commitIndex >= 0; commitIndex-- )); do
git cherry-pick "${commitsArray[$commitIndex]}"
git commit --amend --author "New Author <newauthor@email.com>"
done
We learned that git commit --amend --author "New Author <newauthor@email.com>"
modifies the latest commit, also known as the HEAD commit (git log -1 --format=format:"%h"
).
Regarding --format=format:"%h"
, we can retrieve a commit's hash in two ways:
--format=format:"%H"
: A long hash. Retrieves the full hash of the commit (40 characters long).--format=format:"%h"
: A shortened version of the hash that uses the first 7 to 9 characters (length varies depending on history length). Git is smart enough to figure out what commit is meant with this hash.
The git cherry-pick
command allows you to pick a commit's hash from any branch and drop the contents of that commit into another branch. This creates a new commit hash in the branch with those contents, which becomes a HEAD commit.
git checkout -b new-branch "$initialCommit"
creates a new branch with $initialCommit
becoming the HEAD commit.
In this way, we are able to change the author and author email for multiple commits (that we found with readarray -t commitsArray < <(git log original-branch --author="Original Author <originalauthor@email.com>" --format=format:"%h")
).
Afterwards, we could choose to remove our original branch (git branch -D original-branch
) and rename our new branch (git branch -m new-branch original-branch
). Then if satisfied later decide to push our changes to GitHub, GitLab, or wherever your git project is stored in remotely. Done!