Skip to content

Instantly share code, notes, and snippets.

@MissKittin
Created June 24, 2024 14:33
Show Gist options
  • Save MissKittin/bbd8948a15cd2b414915cd9029616853 to your computer and use it in GitHub Desktop.
Save MissKittin/bbd8948a15cd2b414915cd9029616853 to your computer and use it in GitHub Desktop.
How to rebase a submodule and fix gitlinks in a superproject

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

Submodule

Prepare

git clone "git@github.com:MyName/my-submodule.git"
cd ./my-submodule

Commit message fix

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

Invalid commit author and email

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

Fix tags!

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.

Testing the submodule repository

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

Superproject

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.

Expired commit (gitlink) hashes in superproject

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"

Commit message fix

This step is the same as for the submodule.

Invalid commit author and email

This step is the same as for the submodule.

Fix tags!

This step is the same as for the submodule.

Testing the superproject repository

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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment