Question

J'ai un dépôt Git dans un dossier appelé XXX , et j'ai deuxième dépôt Git appelé YYY .

Je veux importer le XXX dépôt dans le YYY dépôt comme un sous-répertoire nommé ZZZ et ajouter tous les XXX de «l'historique des modifications à YYY .

Structure de dossier avant:

XXX
 |- .git
 |- (project files)
YYY
 |- .git
 |- (project files)

Structure de dossier après:

YYY
 |- .git  <-- This now contains the change history from XXX
 |-  ZZZ  <-- This was originally XXX
      |- (project files)
 |-  (project files)

Peut-on faire, ou dois-je avoir recours à l'utilisation de sous-modules?

Était-ce utile?

La solution

La façon la plus simple serait de tirer le XXX des choses dans une branche YYY , puis fusionner en maître:

YYY :

git remote add other /path/to/XXX
git fetch other
git checkout -b ZZZ other/master
mkdir ZZZ
git mv stuff ZZZ/stuff                      # repeat as necessary for each file/dir
git commit -m "Moved stuff to ZZZ"
git checkout master                
git merge ZZZ --allow-unrelated-histories   # should add ZZZ/ to master
git commit
git remote rm other
git branch -d ZZZ                           # to get rid of the extra branch before pushing
git push                                    # if you have a remote, that is

En fait, je viens d'essayer cela avec un couple de mes mises en pension et il fonctionne. Contrairement à réponse de Jörg il ne laissera pas vous continuez à utiliser l'autre repo, mais je ne pense pas que vous avez indiqué que de toute façon.

Note: Étant donné que ce a été écrit en 2009, git a ajouté la fusion de sous-arbre mentionné dans la réponse ci-dessous. Je probablement utiliser aujourd'hui cette méthode, même si bien sûr cette méthode ne fonctionne toujours.

Autres conseils

Si vous souhaitez conserver la validation exacte histoire du second dépôt et donc conserver également la possibilité de fusionner facilement des changements en amont dans le futur est la méthode alors ici que vous voulez. Il en résulte dans l'histoire non modifiée du sous-arbre importé dans votre repo plus une fusion commettras pour déplacer le référentiel fusionné au sous-répertoire.

git remote add XXX_remote <path-or-url-to-XXX-repo>
git fetch XXX_remote
git merge -s ours --no-commit --allow-unrelated-histories XXX_remote/master
git read-tree --prefix=ZZZ/ -u XXX_remote/master
git commit -m "Imported XXX as a subtree."

Vous pouvez suivre les changements en amont comme ceci:

git pull -s subtree XXX_remote master

Git figure sur son propre où les racines sont avant de faire de la fusion, de sorte que vous n'avez pas besoin de spécifier le préfixe sur les fusions ultérieures.

versions Git avant 2.9 :. Vous n'avez pas besoin de passer l'option --allow-unrelated-histories à git merge

La méthode dans l'autre réponse qui utilise read-tree et saute l'étape de merge -s ours est effectivement pas différent que de copier les fichiers avec cp et d'enregistrer le résultat.

La source originale était de github de l'article d'aide "Fusion Subtree" .

git-subtree est un script conçu pour exactement ce cas d'utilisation de la fusion de plusieurs référentiels en un tout en préservant l'histoire (et / ou l'histoire de division de sous-arbres, bien que ce soit semble être rien à voir avec cette question). Il est distribué dans le cadre de l'arbre git depuis la version 1.7.11 .

Pour fusionner un référentiel <repo> à <rev> de révision comme sous-répertoire <prefix>, utilisez git subtree add comme suit:

git subtree add -P <prefix> <repo> <rev>

git-sous-arborescence implémente la stratégie de fusion sous-arbre dans un plus convivial manière.

Pour votre cas, dans le repository YYY, vous devez exécuter:

git subtree add -P ZZZ /path/to/XXX.git master

Il est une instance bien connue de ce dans le dépôt Git lui-même, qui est collectivement connu dans la communauté Git comme « le plus cool jamais merge" (après la ligne de sujet Linus Torvalds utilisé dans l'e-mail à la liste de diffusion Git qui décrit cette fusion). Dans ce cas, l'gitk Git GUI qui fait maintenant partie de Git appropriée, en fait utilisé comme un projet distinct. Linus a réussi à fusionner ce référentiel dans le dépôt Git de manière que

  • il apparaît dans le dépôt Git comme si elle avait toujours été mis au point dans le cadre de Git,
  • toute l'histoire est conservé intact et
  • il peut encore être développé de manière indépendante dans son ancien référentiel, les changements sont tout simplement git pulled.

Le courrier électronique contient les étapes nécessaires à la reproduction, mais ce n'est pas pour les faibles de cœur: d'abord, Linus a écrit Git, donc il sait sans doute un peu plus à ce sujet que vous ou moi et deuxièmement, il y a près de 5 ans et Git a amélioré considérablement depuis, alors peut-être il est maintenant beaucoup plus facile.

Je suppose que de nos jours, on utiliserait en particulier, un sous-module gitk, dans ce cas précis.

La façon simple de le faire est d'utiliser le format-patch git.

Supposons que nous avons 2 dépôts git foo et bar .

foo contient:

  • foo.txt
  • .git

bar contient:

  • bar.txt
  • .git

et nous voulons bout avec foo contenant bar et l'histoire de ces fichiers:

  • foo.txt
  • .git
  • foobar / bar.txt

Donc, pour le faire:

 1. create a temporary directory eg PATH_YOU_WANT/patch-bar
 2. go in bar directory
 3. git format-patch --root HEAD --no-stat -o PATH_YOU_WANT/patch-bar --src-prefix=a/foobar/ --dst-prefix=b/foobar/
 4. go in foo directory
 5. git am PATH_YOU_WANT/patch-bar/*

Et si nous voulons réécrire tout un message engage de bar que nous pouvons faire, par exemple, sous Linux:

git filter-branch --msg-filter 'sed "1s/^/\[bar\] /"' COMMIT_SHA1_OF_THE_PARENT_OF_THE_FIRST_BAR_COMMIT..HEAD

Cela va ajouter "[bar]" au début de chaque message de commit.

sur cet article , est sous-arbre en utilisant ce qui a fonctionné pour moi et que l'histoire applicable a été transféré. Affichage ici au cas où quelqu'un a besoin des étapes (assurez-vous de remplacer les espaces réservés aux valeurs applicables à vous):

dans votre source sous-dossier split dépôt dans une nouvelle branche

git subtree split --prefix=<source-path-to-merge> -b subtree-split-result

dans l'opération de fusion repo de destination dans la branche de résultat divisé

git remote add merge-source-repo <path-to-your-source-repository>
git fetch merge-source-repo
git merge -s ours --no-commit merge-source-repo/subtree-split-result
git read-tree --prefix=<destination-path-to-merge-into> -u merge-source-repo/subtree-split-result

vérifier vos modifications et engager

git status
git commit

Ne pas oublier de

Nettoyer en supprimant la branche subtree-split-result

git branch -D subtree-split-result

Retirez la télécommande que vous avez ajouté pour récupérer les données de repo source

git remote rm merge-source-repo

Cette fonction clone repo à distance en dir repo local, après la fusion de tous les commits seront sauvés, git log sera montrer les commits et les chemins appropriés d'origine:

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

Si faire un peu de changements que vous pouvez même déplacer des fichiers / répertoires de repo fusionnée dans des chemins différents, par exemple:

repo="https://github.com/example/example"
path="$(pwd)"

tmp="$(mktemp -d)"
remote="$(echo "$tmp" | sed 's/\///g' | sed 's/\./_/g')"

git clone "$repo" "$tmp"
cd "$tmp"

GIT_ADD_STORED=""

function git-mv-store
{
    from="$(echo "$1" | sed 's/\./\\./')"
    to="$(echo "$2" | sed 's/\./\\./')"

    GIT_ADD_STORED+='s,\t'"$from"',\t'"$to"',;'
}

# NOTICE! This paths used for example! Use yours instead!
git-mv-store 'public/index.php' 'public/admin.php'
git-mv-store 'public/data' 'public/x/_data'
git-mv-store 'public/.htaccess' '.htaccess'
git-mv-store 'core/config' 'config/config'
git-mv-store 'core/defines.php' 'defines/defines.php'
git-mv-store 'README.md' 'doc/README.md'
git-mv-store '.gitignore' 'unneeded/.gitignore'

git filter-branch --index-filter '
    git ls-files -s |
    sed "'"$GIT_ADD_STORED"'" |
    GIT_INDEX_FILE="$GIT_INDEX_FILE.new" git update-index --index-info &&
    mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"
' HEAD

GIT_ADD_STORED=""

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"

Avis Les chemins par sed remplace, alors assurez-vous qu'il se déplaçait dans les chemins appropriés après la fusion.
Le paramètre --allow-unrelated-histories existe seulement depuis git> = 2.9.

Ajout d'une autre réponse que je pense que cela est un peu plus simple. Une traction de repo_dest se fait dans repo_to_import puis une poussée en amont --set-url:. Maître repo_dest est fait

Cette méthode a fonctionné pour moi d'importer plusieurs prises en pension plus petits dans une plus grande.

Comment importer: repo1_to_import à repo_dest

# checkout your repo1_to_import if you don't have it already 
git clone url:repo1_to_import repo1_to_import
cd repo1_to_import

# now. pull all of repo_dest
git pull url:repo_dest
ls 
git status # shows Your branch is ahead of 'origin/master' by xx commits.
# now push to repo_dest
git push --set-upstream url:repo_dest master

# repeat for other repositories you want to import

Renommer ou déplacer des fichiers et répertoires dans la position désirée en repo d'origine avant de faire l'importation. par exemple.

cd repo1_to_import
mkdir topDir
git add topDir
git mv this that and the other topDir/
git commit -m"move things into topDir in preparation for exporting into new repo"
# now do the pull and push to import

La méthode décrite au lien suivant inspiré cette réponse. Je l'ai aimé comme il semblait plus simple. Mais méfiez-vous! Il y aura des dragons! https://help.github.com/articles/importing-an-external -git-dépôt git push --mirror url:repo_dest pousse votre historique repo local et état à distance (url: repo_dest). MAIS il supprime l'histoire ancienne et l'état de la télécommande. Ensues Fun! : -E

Je voulais importer uniquement certains fichiers de l'autre référentiel (XXX) dans mon cas. Le sous-arbre était trop compliqué pour moi et les autres solutions ne fonctionne pas. Voilà ce que je l'ai fait:

ALL_COMMITS=$(git log --reverse --pretty=format:%H -- ZZZ | tr '\n' ' ')

Cela vous donne une liste séparée par des espaces de tous les commits qui affectent les fichiers que je voulais importer (ZZZ) dans l'ordre inverse (vous pourriez avoir à ajouter à la capture --follow renomme aussi bien). Je suis ensuite allé dans le référentiel cible (YYY), a ajouté l'autre référentiel (XXX) comme à distance, a fait un des extractions et enfin:

git cherry-pick $ALL_COMMITS

qui ajoute tous les commits à votre succursale, vous aurez donc tous les fichiers avec leur histoire et peut faire tout ce que vous voulez avec eux comme ils ont toujours été dans ce dépôt.

J'étais dans une situation où je cherchais -s theirs mais bien sûr, cette stratégie n'existe pas. Mon histoire était que j'avais fourchue un projet sur GitHub, et maintenant pour une raison quelconque, mon master locale ne pouvait pas être fusionné avec upstream/master bien que je l'avais pas apporté de changements locaux à cette branche. (Ne sais vraiment pas ce qui est arrivé? - Je suppose que avais fait en amont des poussées sales dans les coulisses, peut-être)

Ce que je fini par faire était

# as per https://help.github.com/articles/syncing-a-fork/
git fetch upstream
git checkout master
git merge upstream/master
....
# Lots of conflicts, ended up just abandonging this approach
git reset --hard   # Ditch failed merge
git checkout upstream/master
# Now in detached state
git branch -d master # !
git checkout -b master   # create new master from upstream/master

Alors maintenant, mon master est à nouveau en phase avec upstream/master (et vous pourriez répéter ce qui précède pour toute autre branche que vous souhaitez également synchroniser de la même).

Voir Exemple de base cet article et examine ces dépôts sur la cartographie:

  • A <-> YYY,
  • B <-> XXX

Après toutes les activités décrites dans ce chapitre (après la fusion), enlever la branche B-master:

$ git branch -d B-master

Ensuite, appuyez sur les changements.

Il fonctionne pour moi.

Je peux suggérer une autre solution (alternative à la git-sous-modules ) pour votre problème - outil gil (liens git)

Il permet de décrire et gérer les dépendances complexes de dépôts git.

En outre, il fournit une solution au git problème des dépendances récursives sous-modules.

Considérez-vous les dépendances de projet suivantes: graphe de dépendance de dépôt git

Ensuite, vous pouvez définir le fichier .gitlinks avec des référentiels description relation:

# Projects
CppBenchmark CppBenchmark https://github.com/chronoxor/CppBenchmark.git master
CppCommon CppCommon https://github.com/chronoxor/CppCommon.git master
CppLogging CppLogging https://github.com/chronoxor/CppLogging.git master

# Modules
Catch2 modules/Catch2 https://github.com/catchorg/Catch2.git master
cpp-optparse modules/cpp-optparse https://github.com/weisslj/cpp-optparse.git master
fmt modules/fmt https://github.com/fmtlib/fmt.git master
HdrHistogram modules/HdrHistogram https://github.com/HdrHistogram/HdrHistogram_c.git master
zlib modules/zlib https://github.com/madler/zlib.git master

# Scripts
build scripts/build https://github.com/chronoxor/CppBuildScripts.git master
cmake scripts/cmake https://github.com/chronoxor/CppCMakeScripts.git master

Chaque ligne décrit lien git dans le format suivant:

  1. Nom unique du référentiel
  2. Chemin relatif du référentiel (démarré à partir du chemin du fichier .gitlinks)
  3. dépôt Git qui sera utilisé dans la commande clone git branche dépôt à la caisse
  4. ligne vide ou ligne commençant par # ne sont pas analysables (traités comme commentaire).

Enfin, vous devez mettre à jour votre référentiel exemple racine:

# Clone and link all git links dependencies from .gitlinks file
gil clone
gil link

# The same result with a single command
gil update

le résultat que vous aurez cloner tous les projets nécessaires et de les relier les uns aux autres d'une manière appropriée.

Si vous voulez valider toutes les modifications que vous pouvez le faire avec une seule commande dans certains référentiel avec tous les changements dans les dépôts liés aux enfants:

gil commit -a -m "Some big update"

tirer, pousser les commandes fonctionne d'une manière similaire:

gil pull
gil push

outil Gil (liens git) prend en charge les commandes suivantes:

usage: gil command arguments
Supported commands:
    help - show this help
    context - command will show the current git link context of the current directory
    clone - clone all repositories that are missed in the current context
    link - link all repositories that are missed in the current context
    update - clone and link in a single operation
    pull - pull all repositories in the current directory
    push - push all repositories in the current directory
    commit - commit all repositories in the current directory

En savoir plus sur git récursive problème de dépendance des sous-modules .

Je ne sais pas d'un moyen facile de le faire. Vous pouvez faire ceci:

  1. Utiliser un filtre-branche git pour ajouter un répertoire super-ZZZ sur le dépôt XXX
  2. Appuyez sur la nouvelle branche du référentiel YYY
  3. Fusionner la branche poussé avec le coffre de YYY.

Je peux modifier avec des détails si cela semble intéressant.

Je pense que vous pouvez le faire en utilisant 'git mv' et 'git pull'.

Je suis un noob juste git - alors soyez prudent avec votre dépôt principal - mais je viens d'essayer cela dans un répertoire temp et il semble fonctionner

.

Tout d'abord - renommer la structure du XXX selon la façon dont vous voulez regarder quand il est dans YYY:

cd XXX
mkdir tmp
git mv ZZZ tmp/ZZZ
git mv tmp ZZZ

XXX ressemble à ceci:

XXX
 |- ZZZ
     |- ZZZ

Utilisez 'git pull' pour aller chercher les changements dans:

cd ../YYY
git pull ../XXX

YYY ressemble à ceci:

YYY
 |- ZZZ
     |- ZZZ
 |- (other folders that already were in YYY)
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top