Statu quo
Supposons que nous ayons un référentiel appelé repo-old
qui contient un sous répertoire sub
que nous aimerions convertir en un sous module avec son propre repo repo-sub
.
Il est également prévu que le repo original repo-old
devrait être converti en un repo modifié repo-new
où toutes les modifications touchent le sous-répertoire existant précédemment sub
vont maintenant pointer vers les commits correspondants de notre repo de sous-module extrait repo-sub
.
Changeons
Il est possible d'y parvenir avec l'aide de git filter-branch
en deux étapes :
- Extraction de sous-répertoires à partir de
repo-old
a repo-sub
(déjà mentionnée dans le document accepté responder )
- Remplacement du sous-répertoire de
repo-old
a repo-new
(avec un mappage correct des livraisons)
Remarque : Je sais que cette question est ancienne et qu'il a déjà été mentionné que git filter-branch
est en quelque sorte dépréciée et peut être dangereuse. Mais d'un autre côté, cela pourrait aider d'autres personnes avec des dépôts personnels qui sont faciles à valider après la conversion. Donc, soyez averti ! Et merci de me faire savoir s'il existe un autre outil qui fait la même chose sans être déprécié et qui peut être utilisé en toute sécurité !
Je vais expliquer comment j'ai réalisé ces deux étapes sous linux avec git version 2.26.2 ci-dessous. Les versions plus anciennes peuvent fonctionner dans une certaine mesure, mais cela doit être testé.
Par souci de simplicité, je me limiterai au cas où il n'y a qu'un master
et une branche origin
dans le repo original repo-old
. Sachez également que je m'appuie sur des balises git temporaires avec le préfixe temp_
qui seront supprimées au cours du processus. Donc, s'il y a déjà des balises nommées de façon similaire, vous pourriez vouloir ajuster le préfixe ci-dessous. Enfin, sachez que je n'ai pas testé cette méthode de manière approfondie et qu'il peut y avoir des cas où la recette échoue. Alors, s'il vous plaît sauvegarder tout avant de procéder !
Les snippets bash suivants peuvent être concaténés en un seul gros script qui doit ensuite être exécuté dans le même dossier que celui où se trouve le repo. repo-org
vies. Il n'est pas recommandé de tout copier et coller directement dans une fenêtre de commande (même si j'ai testé cela avec succès) !
0. Préparation
Variables
# 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"
Filtre script
# 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. Extraction de sous-répertoire
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. Remplacement des sous-répertoires
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"
Remarque : Si le repo nouvellement créé repo-new
se bloque pendant git submodule update --init
alors essayez de re-cloner le dépôt récursivement une fois à la place :
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"
0 votes
stackoverflow.com/questions/1365541/ peut en aider certains :)
2 votes
Cela ne fait pas partie de la question initiale, mais ce qui serait encore plus cool serait un moyen de conserver l'historique des fichiers qui ont commencé en dehors du dossier et qui ont été déplacés dans celui-ci. Pour l'instant, toutes les réponses perdent l'historique antérieur au déplacement.
2 votes
Le lien de @ggll est en panne. Voici une copie archivée.