Some time ago I did a rebase of my PHP toolkit repository.
The project is divided into 2 repositories: an application and a submodule with libraries.
Make a backup of the repositories before starting work
git clone "git@github.com:MyName/my-submodule.git"
cd ./my-submodule
git rebase -i --committer-date-is-author-date --root
Now replace all pick
with reword
If it's vim, type: :%s/pick/reword/g
(ENTER) :wq
Now edit all commit messages: i
(edit) (ESC) :wq
git rebase -i --committer-date-is-author-date --root
Now replace all pick
with edit
If it's vim, type: :%s/pick/edit/g
(ENTER) :wq
Run for each commit:
GIT_COMMITTER_DATE=$(git log -n 1 --format=%aD) git commit --amend --author="MyName <12345678+MyName@users.noreply.github.com>" --date="$(git log -n 1 --format=%aD)" --no-edit
git rebase --continue
With each rebase/amend, the commit hashes change.
git tag -f -a TAGNAME -m "TAG MESSAGE" newCommitHash
git push --force origin master
git push --force origin TAGNAME
You don't have to worry about the Releases section.
Before pushing to the target repository, you can push the changes to the test repository:
create a new repository on github and add a new remote address:
git remote add testrepo "git@github.com:MyName/my-submodule-test.git"
git push testrepo master
git push testrepo TAGNAME
Now when you see the superproject repository, when entering the submodule you will get the message:
"This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository."
and if you prune the remote repository you will get an error that the remote repository does not contain the requested commit.
Now the hardest part: updating gitlinks.
Prepare a list superprojectCommitName -> rebasedSubmoduleCommitHash
- it will be needed for git checkout
.
You can't do a rebase because it will throw a merge conflict. But cherry picking will work.
For this purpose, you need to create a new repository:
mkdir ./my-rebased-superproject
cd ./my-rebased-superproject
git init
If you use relative paths, you must also clone the submodule repository:
cd ..
git clone --bare "git@github.com:MyName/my-submodule.git"
cd ./my-rebased-superproject
If you use user.email
and user.name
locally, configure them now:
git config user.email "my@email"
git config user.name "MyName"
Do the dirty work:
git remote add oldrepo "git@github.com:MyName/my-superproject.git"
git fetch oldrepo
git cherry-pick firstSuperprojectCommitHash
git rm my-submodule
GIT_COMMITTER_DATE=$(git log -n 1 --format=%aD) git commit --amend --author="MyName <12345678+MyName@users.noreply.github.com>" --date="$(git log -n 1 --format=%aD)" --no-edit
We have our first commit. Now for each subsequent one do:
git cherry-pick superprojectCommitHash
git rm my-submodule
git cherry-pick --continue
In this way, we have cut out the submodule from the history.
Now we need to add the submodule back - we do rebase:
git rebase -i --committer-date-is-author-date --root
Now replace all pick
with edit
If it's vim, type: :%s/pick/edit/g
(ENTER) :wq
Now we add the submodule to the first commit:
git -c protocol.file.allow=always submodule add -b master ../my-submodule.git my-submodule
and repeat for each commit:
# if the submodule was not updated at that time, skip this checkout
cd my-submodule
git checkout rebasedSubmoduleCommitHash
cd ..
git add my-submodule
GIT_COMMITTER_DATE=$(git log -n 1 --format=%aD) git commit --amend --author="MyName <12345678+MyName@users.noreply.github.com>" --date="$(git log -n 1 --format=%aD)" --no-edit
git rebase --continue
Done. You can push (see Testing the superproject repository
below) or continue editing:
git remote add origin "git@github.com:MyName/my-superproject.git"
git push --force origin master
Now when you clone the superproject repository, the submodule's remote address will point to the github server instead of the local directory:
cd ..
rm -r -f ./my-submodule.git
rm -r -f ./my-rebased-superproject
git clone "git@github.com:MyName/my-superproject.git"
This step is the same as for the submodule.
This step is the same as for the submodule.
This step is the same as for the submodule.
Before pushing to the target repository, you can push the changes to the test repository:
create a new repository on github and add a new remote address:
git remote add testrepo "git@github.com:MyName/my-superproject-test.git"
git push testrepo master
git push testrepo TAGNAME