Status quo
Załóżmy, że mamy repozytorium o nazwie repo-old
, która zawiera sub katalogu sub
, który chcielibyśmy przekształcić sub modułu z własnym repo repo-sub
.
Zamiarem jest ponadto repo-old
przekształcenie oryginalnego repozytorium w zmodyfikowane repozytorium, w repo-new
którym wszystkie zatwierdzenia dotykające wcześniej istniejącego podkatalogu sub
będą teraz wskazywać na odpowiednie zatwierdzenia naszego wyodrębnionego repozytorium podmodułów repo-sub
.
Zmieńmy się
Można to osiągnąć za pomocą git filter-branch
dwuetapowego procesu:
- Ekstrakcja podkatalogu z
repo-old
do repo-sub
(już wspomniana w zaakceptowanej odpowiedzi )
- Zamiana podkatalogów z
repo-old
na repo-new
(z odpowiednim mapowaniem zatwierdzeń)
Uwaga : wiem, że to pytanie jest stare i zostało już wspomniane, że git filter-branch
jest trochę przestarzałe i może być niebezpieczne. Ale z drugiej strony może pomóc innym dzięki osobistym repozytoriom, które można łatwo zweryfikować po konwersji. Więc uważaj ! I daj mi znać, jeśli istnieje inne narzędzie, które robi to samo, ale nie jest przestarzałe i jest bezpieczne w użyciu!
Poniżej wyjaśnię, jak zrealizowałem oba kroki na Linuksie z wersją git 2.26.2. Starsze wersje mogą działać w pewnym stopniu, ale należy to przetestować.
Dla uproszczenia ograniczę się do przypadku, gdy w oryginalnym repozytorium jest tylko master
gałąź i origin
pilot repo-old
. Ostrzegam również, że korzystam z tymczasowych tagów git z prefiksem, temp_
które zostaną usunięte w trakcie procesu. Jeśli więc istnieją już tagi o podobnych nazwach, możesz chcieć dostosować przedrostek poniżej. I na koniec, proszę, pamiętaj, że nie testowałem tego szczegółowo i mogą wystąpić narożne przypadki, w których przepis się nie powiedzie. Dlatego przed kontynuowaniem wykonaj kopię zapasową wszystkiego !
Następujące fragmenty basha można połączyć w jeden duży skrypt, który następnie powinien zostać wykonany w tym samym folderze, w którym znajduje się repozytorium repo-org
. Nie zaleca się kopiowania i wklejania wszystkiego bezpośrednio do okna poleceń (mimo że pomyślnie przetestowałem to)!
0. Przygotowanie
Zmienne
# Root directory where repo-org lives
# and a temporary location for git filter-branch
root="$PWD"
temp='/dev/shm/tmp'
# The old repository and the subdirectory we'd like to extract
repo_old="$root/repo-old"
repo_old_directory='sub'
# The new submodule repository, its url
# and a hash map folder which will be populated
# and later used in the filter script below
repo_sub="$root/repo-sub"
repo_sub_url='https://github.com/somewhere/repo-sub.git'
repo_sub_hashmap="$root/repo-sub.map"
# The new modified repository, its url
# and a filter script which is created as heredoc below
repo_new="$root/repo-new"
repo_new_url='https://github.com/somewhere/repo-new.git'
repo_new_filter="$root/repo-new.sh"
Skrypt filtru
# The index filter script which converts our subdirectory into a submodule
cat << EOF > "$repo_new_filter"
#!/bin/bash
# Submodule hash map function
sub ()
{
local old_commit=\$(git rev-list -1 \$1 -- '$repo_old_directory')
if [ ! -z "\$old_commit" ]
then
echo \$(cat "$repo_sub_hashmap/\$old_commit")
fi
}
# Submodule config
SUB_COMMIT=\$(sub \$GIT_COMMIT)
SUB_DIR='$repo_old_directory'
SUB_URL='$repo_sub_url'
# Submodule replacement
if [ ! -z "\$SUB_COMMIT" ]
then
touch '.gitmodules'
git config --file='.gitmodules' "submodule.\$SUB_DIR.path" "\$SUB_DIR"
git config --file='.gitmodules' "submodule.\$SUB_DIR.url" "\$SUB_URL"
git config --file='.gitmodules' "submodule.\$SUB_DIR.branch" 'master'
git add '.gitmodules'
git rm --cached -qrf "\$SUB_DIR"
git update-index --add --cacheinfo 160000 \$SUB_COMMIT "\$SUB_DIR"
fi
EOF
chmod +x "$repo_new_filter"
1. Ekstrakcja podkatalogów
cd "$root"
# Create a new clone for our new submodule repo
git clone "$repo_old" "$repo_sub"
# Enter the new submodule repo
cd "$repo_sub"
# Remove the old origin remote
git remote remove origin
# Loop over all commits and create temporary tags
for commit in $(git rev-list --all)
do
git tag "temp_$commit" $commit
done
# Extract the subdirectory and slice commits
mkdir -p "$temp"
git filter-branch --subdirectory-filter "$repo_old_directory" \
--tag-name-filter 'cat' \
--prune-empty --force -d "$temp" -- --all
# Populate hash map folder from our previously created tag names
mkdir -p "$repo_sub_hashmap"
for tag in $(git tag | grep "^temp_")
do
old_commit=${tag#'temp_'}
sub_commit=$(git rev-list -1 $tag)
echo $sub_commit > "$repo_sub_hashmap/$old_commit"
done
git tag | grep "^temp_" | xargs -d '\n' git tag -d 2>&1 > /dev/null
# Add the new url for this repository (and e.g. push)
git remote add origin "$repo_sub_url"
# git push -u origin master
2. Zastąpienie podkatalogu
cd "$root"
# Create a clone for our modified repo
git clone "$repo_old" "$repo_new"
# Enter the new modified repo
cd "$repo_new"
# Remove the old origin remote
git remote remove origin
# Replace the subdirectory and map all sliced submodule commits using
# the filter script from above
mkdir -p "$temp"
git filter-branch --index-filter "$repo_new_filter" \
--tag-name-filter 'cat' --force -d "$temp" -- --all
# Add the new url for this repository (and e.g. push)
git remote add origin "$repo_new_url"
# git push -u origin master
# Cleanup (commented for safety reasons)
# rm -rf "$repo_sub_hashmap"
# rm -f "$repo_new_filter"
Uwaga: Jeśli nowo utworzone repozytorium repo-new
zawiesza się podczas, git submodule update --init
spróbuj ponownie sklonować repozytorium rekurencyjnie raz zamiast tego:
cd "$root"
# Clone the new modified repo recursively
git clone --recursive "$repo_new" "$repo_new-tmp"
# Now use the newly cloned one
mv "$repo_new" "$repo_new-bak"
mv "$repo_new-tmp" "$repo_new"
# Cleanup (commented for safety reasons)
# rm -rf "$repo_new-bak"