Je dois fusionner deux référentiels Git dans un tout nouveau troisième référentiel. J'ai trouvé de nombreuses descriptions sur la manière de procéder en utilisant une fusion de sous-arbres (par exemple réponse de Jakub Narębski sur comment fusionnez-vous deux référentiels Git? ) et suivez-vous ces instructions principalement fonctionne, sauf que lorsque je valide la fusion de la sous-arborescence, tous les fichiers des anciens référentiels sont enregistrés en tant que nouveaux fichiers ajoutés. Je peux voir l'historique des validations à partir des anciens référentiels lorsque je fais git log
, mais si je git log <file>
, il affiche une seule validation pour ce fichier - la fusion de la sous-arborescence. À en juger par les commentaires sur la réponse ci-dessus, je ne suis pas le seul à avoir vu ce problème, mais je n'ai trouvé aucune solution publiée à ce problème.
Existe-t-il un moyen de fusionner des référentiels et de laisser l’historique des fichiers individuels intact?
Il s'avère que la réponse est beaucoup plus simple si vous essayez simplement de coller deux référentiels ensemble et de donner l’impression qu’il en a été ainsi au lieu de gérer une dépendance externe. Vous devez simplement ajouter des télécommandes à votre ancien dépôt, les fusionner avec votre nouveau maître, déplacer les fichiers et les dossiers dans un sous-répertoire, valider le déplacement et répéter toutes les autres mises en pension. Les sous-modules, les fusions de sous-arbres et les rebases sophistiqués sont destinés à résoudre un problème légèrement différent et ne conviennent pas à ce que j'essayais de faire.
Voici un exemple de script Powershell permettant de coller deux référentiels:
# Assume the current directory is where we want the new repository to be created
# Create the new repository
git init
# Before we do a merge, we have to have an initial commit, so we'll make a dummy commit
git commit --allow-empty -m "Initial dummy commit"
# Add a remote for and fetch the old repo
git remote add -f old_a <OldA repo URL>
# Merge the files from old_a/master into new/master
git merge old_a/master --allow-unrelated-histories
# Move the old_a repo files and folders into a subdirectory so they don't collide with the other repo coming later
mkdir old_a
dir -exclude old_a | %{git mv $_.Name old_a}
# Commit the move
git commit -m "Move old_a files into subdir"
# Do the same thing for old_b
git remote add -f old_b <OldB repo URL>
git merge old_b/master --allow-unrelated-histories
mkdir old_b
dir –exclude old_a,old_b | %{git mv $_.Name old_b}
git commit -m "Move old_b files into subdir"
Évidemment, vous pouvez plutôt fusionner old_b avec old_a (qui devient le nouveau référentiel combiné) si vous préférez le faire - modifiez le script à votre guise.
Si vous souhaitez également importer des branches de fonctionnalités en cours, utilisez ceci:
# Bring over a feature branch from one of the old repos
git checkout -b feature-in-progress
git merge -s recursive -Xsubtree=old_a old_a/feature-in-progress
C'est la seule partie non évidente du processus - il ne s'agit pas d'une fusion de sous-arborescence, mais d'un argument en faveur de la fusion récursive normale qui indique à Git que nous avons renommé la cible et qui aide Git à tout aligner correctement.
J'ai écrit une explication un peu plus détaillée ici .
Voici un moyen de ne pas réécrire d’historique. Tous les ID de validation resteront donc valides. Le résultat final est que les fichiers du deuxième référentiel se retrouveront dans un sous-répertoire.
Ajoutez le deuxième repo en tant que télécommande:
cd firstgitrepo/
git remote add secondrepo username@servername:andsoon
Assurez-vous d'avoir téléchargé tous les commits de secondrepo:
git fetch secondrepo
Créez une branche locale à partir de la branche du deuxième référentiel:
git branch branchfromsecondrepo secondrepo/master
Déplacez tous ses fichiers dans un sous-répertoire:
git checkout branchfromsecondrepo
mkdir subdir/
git ls-tree -z --name-only HEAD | xargs -0 -I {} git mv {} subdir/
git commit -m "Moved files to subdir/"
Fusionner la deuxième branche dans la branche principale du premier référentiel:
git checkout master
git merge --allow-unrelated-histories branchfromsecondrepo
Votre référentiel aura plus d'un commit root, mais cela ne devrait pas poser de problème.
s'il vous plaît jeter un oeil à l'aide
git rebase --root --preserve-merges --onto
pour relier deux histoires tôt dans leur vie.
Si vous avez des chemins qui se chevauchent, corrigez-les avec
git filter-branch --index-filter
lorsque vous utilisez le journal, assurez-vous de "trouver des copies plus difficiles" avec
git log -CC
de cette façon, vous trouverez tous les mouvements de fichiers dans le chemin.
J'ai transformé le solution de @Flimm ceci en un git alias
comme ceci (ajouté à mon ~/.gitconfig
):
[alias]
mergeRepo = "!mergeRepo() { \
[ $# -ne 3 ] && echo \"Three parameters required, <remote URI> <new branch> <new dir>\" && exit 1; \
git remote add newRepo $1; \
git fetch newRepo; \
git branch \"$2\" newRepo/master; \
git checkout \"$2\"; \
mkdir -vp \"${GIT_PREFIX}$3\"; \
git ls-tree -z --name-only HEAD | xargs -0 -I {} git mv {} \"${GIT_PREFIX}$3\"/; \
git commit -m \"Moved files to '${GIT_PREFIX}$3'\"; \
git checkout master; git merge --allow-unrelated-histories --no-edit -s recursive -X no-renames \"$2\"; \
git branch -D \"$2\"; git remote remove newRepo; \
}; \
mergeRepo"
Cette fonction clone le repo distant dans le repo local:
function git-add-repo
{
repo="$1"
dir="$(echo "$2" | sed 's/\/$//')"
path="$(pwd)"
tmp="$(mktemp -d)"
remote="$(echo "$tmp" | sed 's/\///g'| sed 's/\./_/g')"
git clone "$repo" "$tmp"
cd "$tmp"
git filter-branch --index-filter '
git ls-files -s |
sed "s,\t,&'"$dir"'/," |
GIT_INDEX_FILE="$GIT_INDEX_FILE.new" git update-index --index-info &&
mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"
' HEAD
cd "$path"
git remote add -f "$remote" "file://$tmp/.git"
git pull "$remote/master"
git merge --allow-unrelated-histories -m "Merge repo $repo into master" --edit "$remote/master"
git remote remove "$remote"
rm -rf "$tmp"
}
Comment utiliser:
cd current/package
git-add-repo https://github.com/example/example dir/to/save
Profit!
Quelques années ont passé et des solutions bien fondées ont été votées, mais je souhaite partager la mienne car elle était un peu différente car je souhaitais fusionner deux référentiels distants dans un nouveau sans supprimer. l'historique des référentiels précédents.
Créez un nouveau référentiel dans Github.
Téléchargez le référentiel nouvellement créé et ajoutez l'ancien référentiel distant.
git clone https://github.com/alexbr9007/Test.git
cd Test
git remote add OldRepo https://github.com/alexbr9007/Django-React.git
git remote -v
Récupérez tous les fichiers de l'ancien dépôt pour créer une nouvelle branche.
git fetch OldRepo
git branch -a
Dans la branche principale, effectuez une fusion pour combiner l'ancien référentiel avec le nouveau créé.
git merge remotes/OldRepo/master --allow-unrelated-histories
Créez un nouveau dossier pour stocker tout le nouveau contenu créé qui a été ajouté à partir de OldRepo et déplacez ses fichiers dans ce nouveau dossier.
Enfin, vous pouvez télécharger les fichiers à partir des pensions combinées et supprimer en toute sécurité le OldRepo de GitHub.
J'espère que cela peut être utile à quiconque est impliqué dans la fusion de référentiels distants.
Suivez les étapes pour intégrer un référentiel dans un autre référentiel, en ayant un seul historique git en fusionnant les deux historiques git.
git clone [email protected]: utilisateur/parent-repo.git
git clone [email protected]: user/child-repo.git
cd enfant-repo /
my/new/subdir
(3 occurrences) par la structure de répertoire dans laquelle vous souhaitez obtenir le référentiel enfant.git filter-branch --Prune-empty --tree-filter 'si [! -e mon/nouveau/sous-répertoire]; then mkdir -p my/new/subdir git ls-tree --nom uniquement $ GIT_COMMIT | xargs -I files mv files my/new/subdir fi '
cd ../parent-repo/
git remote ajoute enfant-distant ../child-repo/
git chercher une télécommande enfant
fusion git --allow-unrelated-histories enfant-distant/maître
Si vous consultez le journal git dans le référent parent maintenant, les validations de référant enfant devraient être fusionnées. Vous pouvez également voir la balise indiquant à partir de la source de validation.
L’article ci-dessous m’a aidé à Incorporer un dépôt dans un autre, ayant un seul historique git en fusionnant les deux historiques.
http://ericlathrop.com/2014/01/combining-git-repositories/
J'espère que cela t'aides. Bon codage!