Fusionner deux dépôts Git sans briser l'historique des fichiers

je dois fusionner deux dépôts Git dans un tout nouveau, troisième dépôt. J'ai trouvé de nombreuses descriptions de la façon de faire cela en utilisant une fusion de sous-classes (par exemple réponse de Jakub Narbski sur comment fusionner deux dépôts Git? ) et en suivant ces instructions fonctionne la plupart du temps, sauf que lorsque je commente la sous-arborescence fusionner tous les fichiers des anciens dépôts sont enregistrés comme nouveaux fichiers ajoutés. Je peux voir l'histoire de commit de la anciens dépôts quand je fais git log , mais si je fais git log <file> il n'affiche qu'une seule propagation pour ce fichier - la fusion de sous-arborescence. À en juger par les commentaires sur la réponse ci-dessus, je ne suis pas seul à voir ce problème, mais je n'ai trouvé aucune solution publiée pour elle.

est-il possible de fusionner des dépôts et de laisser l'historique des fichiers intact?

174
demandé sur Community 2012-10-24 03:41:43

5 réponses

il s'avère que la réponse est beaucoup plus simple si vous essayez simplement de coller deux dépôts ensemble et de faire en sorte qu'il semble que c'était de cette façon tout au long plutôt que de gérer une dépendance externe. Vous avez simplement besoin d'ajouter des télécommandes à vos anciennes repos, de les fusionner à votre nouveau maître, de déplacer les fichiers et dossiers à un sous-répertoire, de commit le mouvement, et de répéter pour tous les repos supplémentaires. Submodules, subtree merges, et fantaisie rebases sont destinés à résoudre un problème légèrement différent et ne sont pas convient pour ce que j'essayais de faire.

voici un exemple de script Powershell pour coller deux dépôts ensemble:

# 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
dir > deleteme.txt
git add .
git commit -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

# Clean up our dummy file because we don't need it any more
git rm .\deleteme.txt
git commit -m "Clean up initial file"

# 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 pourriez à la place fusionner old_b dans old_a (qui devient le nouveau repo combiné) si vous préférez faire cela – modifier le script pour convenir.

si vous voulez ajouter des branches 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 - ce n'est pas une fusion subtree, mais plutôt un argument à la fusion récursive normale qui dit à 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 .

218
répondu Eric Lee 2016-11-24 02:05:41

Voici une façon qui ne réécrit aucune histoire, donc tous les identifiants de propagation resteront valables. Le résultat final est que la deuxième pensions de fichiers dans un sous-répertoire.

  1. ajouter le deuxième repo comme une télécommande:

    cd firstgitrepo/
    git remote add secondrepo username@servername:andsoon
    
  2. assurez-vous que vous avez téléchargé toutes les commit de secondrepo:

    git fetch secondrepo
    
  3. créer une branche locale à partir du deuxième succursale de repo:

    git branch branchfromsecondrepo secondrepo/master
    
  4. déplacer 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/"
    
  5. fusionner la deuxième branche dans la branche principale de la première pension:

    git checkout master
    git merge --allow-unrelated-histories branchfromsecondrepo
    

votre dépôt aura plus d'une propagation racine, mais cela ne devrait pas poser de problème.

113
répondu Flimm 2016-09-02 02:51:32

, jetez un oeil à l'aide de

git rebase --root --preserve-merges --onto

pour relier deux histoires tôt dans leur vie.

si vous avez des chemins qui se chevauchent, fixez-les avec

git filter-branch --index-filter

lorsque vous utilisez log, 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 d'accès.

8
répondu Adam Dymitruk 2012-10-24 01:56:22

j'ai transformé la 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 ; \
  git fetch newRepo; \
  git branch \"\" newRepo/master; \
  git checkout \"\"; \
  mkdir -vp \"${GIT_PREFIX}\"; \
  git ls-tree -z --name-only HEAD | xargs -0 -I {} git mv {} \"${GIT_PREFIX}\"/; \
  git commit -m \"Moved files to '${GIT_PREFIX}'\"; \
  git checkout master; git merge --allow-unrelated-histories --no-edit -s recursive -X no-renames \"\"; \
  git branch -D \"\"; git remote remove newRepo; \
}; \
mergeRepo"
5
répondu Fredrik Erlandsson 2017-05-23 12:10:45

cette fonction va cloner remote repo en DIR repo local:

function git-add-repo
{
    repo=""
    dir="$(echo "" | 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!

3
répondu Andrey Izman 2017-04-11 08:24:06