Comment fusionner deux dépôts Git?
envisager le scénario suivant:
j'ai développé un petit projet expérimental A dans sa propre git repo. Il a maintenant mûri, et J'aimerais que A fasse partie du plus grand projet B, qui a son propre grand dépôt. J'aimerais maintenant ajouter un sous-répertoire de B.
comment fusionner A en B, sans perdre l'histoire d'un côté ou de l'autre?
21 réponses
une seule branche d'un autre dépôt peut être facilement placée sous un sous-répertoire conservant son historique. Par exemple:
git subtree add --prefix=rails git://github.com/rails/rails.git master
ceci apparaîtra comme une commit unique où tous les fichiers de la branche maître de Rails sont ajoutés dans le répertoire" rails". Toutefois, le titre de la Commission contient une référence à l'arbre de l'histoire ancienne:
ajouter "rails /" de commit
<rev>
où <rev>
est un SHA-1 commit hash. Vous pouvez encore voir l'histoire, blâmer certains changements.
git log <rev>
git blame <rev> -- README.md
notez que vous ne pouvez pas voir le préfixe de répertoire d'ici car il s'agit d'une ancienne branche réelle laissée intacte. Vous devez traiter cela comme un déplacement de fichier commettre: vous aurez besoin d'un supplément de sauter lors de l'atteindre.
# finishes with all files added at once commit
git log rails/README.md
# then continue from original tree
git log <rev> -- README.md
il y a des solutions plus complexes comme faire cela manuellement ou réécrire l'histoire comme décrit dans d'autres réponses.
la commande git-subtree fait partie du GIT-contrib officiel, certains gestionnaires de paquets l'installent par défaut (OS X Homebrew). Mais vous pourriez avoir à installer par vous-même en plus de git.
si vous voulez fusionner project-a
en project-b
:
cd path/to/project-b
git remote add project-a path/to/project-a
git fetch project-a
git merge --allow-unrelated-histories project-a/master # or whichever branch you want to merge
git remote remove project-a
tiré de: git fusionner différents dépôts?
cette méthode a plutôt bien fonctionné pour moi, elle est plus courte et à mon avis beaucoup plus propre.
Note: le paramètre --allow-unrelated-histories
n'existe que depuis git >= 2,9. Voir documentation de fusion Git - git / -- allow-unlikely-histories
voici deux solutions possibles:
Submodules
soit copier le dépôt A dans un répertoire séparé du projet B, soit (peut-être mieux) cloner le dépôt A dans un sous-répertoire du projet B. puis utiliser sous-module git pour faire de ce dépôt un sous-module 1519120920" d'un dépôt B.
il s'agit d'une bonne solution pour dépôts, où le développement dans le dépôt A continue, et la majeure partie du développement est un développement autonome séparé dans A. Voir aussi SubmoduleSupport et GitSubmoduleTutorial pages sur Git Wiki.
sous-arbre de fusion
vous pouvez fusionner le dépôt A dans un sous-répertoire d'un projet B en utilisant la stratégie subtree merge . C'est décrit dans sous-Arborescence de la Fusion et de Vous par Markus Prinz.
git remote add -f Bproject /path/to/B
git merge -s ours --allow-unrelated-histories --no-commit Bproject/master
git read-tree --prefix=dir-B/ -u Bproject/master
git commit -m "Merge B project as our subdirectory"
git pull -s subtree Bproject master
(Option --allow-unrelated-histories
est nécessaire pour Git >= 2.9.0.)
ou vous pouvez utiliser git subtree outil ( dépôt sur GitHub ) par apenwarr (Avery Pennarun), a annoncé par exemple dans son billet de blog une nouvelle alternative aux sous-modules Git: Git subtree .
je pense que dans votre cas (A doit faire partie du projet plus grand B) la solution correcte serait d'utiliser subtree merge .
l'approche submodule est bonne si vous voulez maintenir le projet séparément. Cependant, si vous voulez vraiment fusionner les deux projets dans le même dépôt, alors vous avez un peu plus de travail à faire.
La première chose serait d'utiliser git filter-branch
réécrire les noms de tout dans le deuxième référentiel d'être dans le sous-répertoire où vous voulez à la fin. Donc au lieu de foo.c
, bar.html
, vous auriez projb/foo.c
et projb/bar.html
.
alors, vous devriez être en mesure de faire quelque chose comme ce qui suit:
git remote add projb [wherever]
git pull projb
le git pull
fera un git fetch
suivi d'un git merge
. Il ne devrait pas y avoir de conflits, si le dépôt vers lequel vous tirez n'a pas encore de répertoire projb/
.
une recherche plus poussée indique que quelque chose de similaire a été fait pour fusionner gitk
en git
. Junio Camano en parle ici: http://www.mail-archive.com/git@vger.kernel.org/msg03395.html
git-subtree
est agréable, mais ce n'est probablement pas celui que vous voulez.
par exemple, si projectA
est le répertoire créé en B, après git subtree
,
git log projectA
listes seule s'engager: la fusion. Les propagations du projet fusionné sont pour des chemins différents, donc elles n'apparaissent pas.
la réponse de Greg Hewgill se rapproche le plus, bien qu'elle ne effectivement dire comment réécrire les chemins.
la solution est étonnamment simple.
(1),
PREFIX=projectA #adjust this
git filter-branch --index-filter '
git ls-files -s |
sed "s,\t,&'"$PREFIX"'/," |
GIT_INDEX_FILE=$GIT_INDEX_FILE.new git update-index --index-info &&
mv $GIT_INDEX_FILE.new $GIT_INDEX_FILE
' HEAD
Note: Ceci réécrit l'histoire, donc si vous avez l'intention de continuer à utiliser cette réponse, vous pouvez vouloir cloner (copier) une copie de sortie de celui-ci d'abord.
(2) puis en B, exécuter
git pull path/to/A
voilà! Vous avez un projectA
répertoire en B. Si vous lancez git log projectA
, vous verrez toutes les propagations de A.
dans mon cas, je voulais deux sous-répertoires, projectA
et projectB
. Dans ce cas, j'ai fait le pas (1) à B aussi.
si les deux dépôts ont le même type de fichiers (comme deux dépôts sur Rails pour des projets différents), vous pouvez récupérer les données du dépôt secondaire dans votre dépôt actuel:
git fetch git://repository.url/repo.git master:branch_name
et la fusionner avec le dépôt actuel:
git merge --allow-unrelated-histories branch_name
si votre version Git est plus petite que 2.9, supprimer --allow-unrelated-histories
.
après cela, des conflits peuvent survenir. Vous pouvez les résoudre par exemple avec git mergetool
. kdiff3
peut être utilisé uniquement avec le clavier, de sorte que 5 fichiers de conflit prend lors de la lecture du code quelques minutes seulement.
N'oubliez pas de terminer la fusion:
git commit
j'ai continué à perdre l'historique en utilisant la fusion, donc j'ai fini par utiliser rebase car dans mon cas les deux dépôts sont assez différents pour ne pas finir par fusionner à chaque propagation:
git clone git@gitorious/projA.git projA
git clone git@gitorious/projB.git projB
cd projB
git remote add projA ../projA/
git fetch projA
git rebase projA/master HEAD
= > résoudre les conflits, puis continuer, autant de fois que nécessaire...
git rebase --continue
cela conduit à un projet ayant toutes les commissions de projA suivies de commits de projB
dans mon cas, j'avais un dépôt my-plugin
et un dépôt main-project
, et je voulais prétendre que my-plugin
avait toujours été développé dans le sous-répertoire plugins
de main-project
.
fondamentalement, j'ai réécrit l'histoire du dépôt my-plugin
de sorte qu'il semble que tout le développement a eu lieu dans le sous-répertoire plugins/my-plugin
. Puis, j'ai ajouté l'histoire du développement de my-plugin
dans l'histoire de main-project
, et ai fusionné les deux les arbres ensemble. Comme il n'y avait pas de répertoire plugins/my-plugin
déjà présent dans le dépôt main-project
, il s'agissait d'une fusion sans conflit. Le dépôt résultant contenait toute l'histoire des deux projets originaux, et avait deux racines.
TL; DR
$ cp -R my-plugin my-plugin-dirty
$ cd my-plugin-dirty
$ git filter-branch -f --tree-filter "zsh -c 'setopt extended_glob && setopt glob_dots && mkdir -p plugins/my-plugin && (mv ^(.git|my-plugin) plugins/my-plugin || true)'" -- --all
$ cd ../main-project
$ git checkout master
$ git remote add --fetch my-plugin ../my-plugin-dirty
$ git merge my-plugin/master --allow-unrelated-histories
$ cd ..
$ rm -rf my-plugin-dirty
version longue
tout d'abord, créez une copie du dépôt my-plugin
, parce que nous allons réécrire l'histoire de ce dépôt.
maintenant, naviguez jusqu'à la racine du dépôt my-plugin
, vérifiez votre branche principale (probablement master
), et lancez la commande suivante. Bien sûr, vous devez remplacer my-plugin
et plugins
quels que soient vos noms.
$ git filter-branch -f --tree-filter "zsh -c 'setopt extended_glob && setopt glob_dots && mkdir -p plugins/my-plugin && (mv ^(.git|my-plugin) plugins/my-plugin || true)'" -- --all
maintenant une explication. git filter-branch --tree-filter (...) HEAD
exécute la commande (...)
sur chaque commit qui est accessible à partir de HEAD
. Notez que cela fonctionne directement sur les données stockées pour chaque commit, donc nous n'avons pas à nous soucier des notions de "répertoire de travail", "index", "mise en scène", et ainsi de suite.
si vous exécutez une commande filter-branch
qui échoue, elle laissera quelques fichiers dans le répertoire .git
et la prochaine fois que vous essayez filter-branch
elle se plaindra à ce sujet, à moins que vous fournissiez l'option -f
à filter-branch
.
quant à la commande actuelle, je n'ai pas eu beaucoup de chance d'obtenir bash
pour faire ce que je voulu, donc à la place j'utilise zsh -c
pour faire zsh
exécuter une commande. J'ai d'abord défini l'option extended_glob
, qui permet la syntaxe ^(...)
dans la commande mv
, ainsi que l'option glob_dots
, qui me permet de sélectionner des dotfiles (comme .gitignore
) avec un glob ( ^(...)
).
ensuite, j'utilise la commande mkdir -p
pour créer à la fois plugins
et plugins/my-plugin
en même temps.
Enfin, j'utilise la fonction zsh
"négatif glob " ^(.git|my-plugin)
pour apparier tous les fichiers du répertoire racine du dépôt à l'exception de .git
et du dossier nouvellement créé my-plugin
. (Exclure .git
pourrait ne pas être nécessaire ici, mais essayer de déplacer un répertoire dans lui-même est une erreur.)
dans mon dépôt, la propagation initiale n'incluait aucun fichier, de sorte que la commande mv
a renvoyé une erreur sur la propagation initiale (puisque rien n'était disponibles pour se déplacer). Par conséquent, j'ai ajouté un || true
de sorte que git filter-branch
ne serait pas avorter.
l'option --all
indique filter-branch
pour réécrire l'historique de toutes branches dans le dépôt, et l'extra --
est nécessaire pour dire git
pour l'interpréter comme une partie de la liste d'options pour les branches à réécrire, au lieu d'une option à filter-branch
elle-même.
maintenant, naviguez vers votre main-project
référentiel et vérifiez dans quelle branche vous voulez fusionner. Ajouter votre copie locale du dépôt my-plugin
(avec son historique modifié) comme une télécommande de main-project
avec:
$ git remote add --fetch my-plugin $PATH_TO_MY_PLUGIN_REPOSITORY
vous avez maintenant deux arbres sans rapport dans votre histoire de propagation, que vous pouvez visualiser en utilisant:
$ git log --color --graph --decorate --all
pour les fusionner, utilisez:
$ git merge my-plugin/master --allow-unrelated-histories
noter qu'en pré-2.9.0 Git, le L'option --allow-unrelated-histories
n'existe pas. Si vous utilisez une de ces versions, omettez simplement l'option: le message d'erreur --allow-unrelated-histories
empêche était aussi ajouté en 2.9.0.
vous ne devriez pas avoir de conflits de fusion. Si vous le faites, cela signifie probablement que la commande filter-branch
n'a pas fonctionné correctement ou qu'il y avait déjà un répertoire plugins/my-plugin
dans main-project
.
assurez-vous d'entrer un message d'engagement explicatif pour les futurs contributeurs se demandant ce que hackery allait faire pour un dépôt avec deux racines.
vous pouvez visualiser le nouveau graphe de commit, qui devrait avoir deux commits root, en utilisant la commande git log
ci-dessus. Notez que seulement la master
branche sera fusionnée . Cela signifie que si vous avez un travail important sur d'autres my-plugin
branches que vous voulez fusionner dans l'arbre main-project
, vous devez évitez de supprimer la télécommande my-plugin
jusqu'à ce que vous ayez fait ces fusions. Si vous ne le faites pas, alors les propagations de ces branches seront toujours dans le dépôt main-project
, mais certaines seront inaccessibles et susceptibles d'être récupérées. (De plus, vous devrez vous y référer par SHA, parce que supprimer une télécommande supprime ses branches de suivi à distance.)
en option, après avoir fusionné tout ce que vous voulez garder de my-plugin
, vous pouvez supprimer le my-plugin
à distance à l'aide:
$ git remote remove my-plugin
vous pouvez maintenant supprimer en toute sécurité la copie du dépôt my-plugin
dont vous avez modifié l'historique. Dans mon cas, j'ai aussi ajouté un avis de dépréciation au véritable dépôt my-plugin
après que la fusion ait été terminée et poussée.
testé sur Mac OS X El Capitan avec git --version 2.9.0
et zsh --version 5.2
. Votre kilométrage peut varier.
, les Références:
- https://git-scm.com/docs/git-filter-branch
- https://unix.stackexchange.com/questions/6393/how-do-you-move-all-files-including-hidden-from-one-directory-to-another
- http://www.refining-linux.org/archives/37/ZSH-Gem-2-Extended-globbing-and-expansion/
- Purge des fichiers de repo Git échoué, incapable de créer une nouvelle sauvegarde
- git filter-branch sur toutes les branches
j'ai essayé de faire la même chose pendant des jours, j'utilise git 2.7.2. Subtree ne préserve pas l'histoire.
Vous pouvez utiliser cette méthode si vous n'utilisez pas l'ancien projet de nouveau.
je vous suggère d'abord la branche B et de travailler dans la branche.
Voici les étapes sans ramification:
cd B
# You are going to merge A into B, so first move all of B's files into a sub dir
mkdir B
# Move all files to B, till there is nothing in the dir but .git and B
git mv <files> B
git add .
git commit -m "Moving content of project B in preparation for merge from A"
# Now merge A into B
git remote add -f A <A repo url>
git merge A/<branch>
mkdir A
# move all the files into subdir A, excluding .git
git mv <files> A
git commit -m "Moved A into subdir"
# Move B's files back to root
git mv B/* ./
rm -rf B
git commit -m "Reset B to original state"
git push
si vous enregistrez maintenant l'un des fichiers dans la subdivision A, vous obtiendrez l'historique complet
git log --follow A/<file>
C'était le post qui m'aident à le faire:
je sais que c'est longtemps après les faits, mais je n'étais pas heureux avec les autres réponses que j'ai trouvé ici, donc j'ai écrit ceci:
me=$(basename "151900920")
TMP=$(mktemp -d /tmp/$me.XXXXXXXX)
echo
echo "building new repo in $TMP"
echo
sleep 1
set -e
cd $TMP
mkdir new-repo
cd new-repo
git init
cd ..
x=0
while [ -n "" ]; do
repo=""; shift
git clone "$repo"
dirname=$(basename $repo | sed -e 's/\s/-/g')
if [[ $dirname =~ ^git:.*\.git$ ]]; then
dirname=$(echo $dirname | sed s/.git$//)
fi
cd $dirname
git remote rm origin
git filter-branch --tree-filter \
"(mkdir -p $dirname; find . -maxdepth 1 ! -name . ! -name .git ! -name $dirname -exec mv {} $dirname/ \;)"
cd ..
cd new-repo
git pull --no-commit ../$dirname
[ $x -gt 0 ] && git commit -m "merge made by $me"
cd ..
x=$(( x + 1 ))
done
j'ai rassemblé beaucoup d'informations ici sur le débordement de la pile, etc., et avoir réussi à mettre en place un script qui résout le problème pour moi.
la mise en garde est qu'elle ne prend en compte que la branche "développement" de chaque dépôt et la fusionne dans un répertoire séparé dans un dépôt complètement nouveau.
Les étiquetteset les autres branches sont ignorées - ce n'est peut-être pas ce que vous voulez.
le script gère même la fonctionnalité branches et tags-les renommer dans le nouveau projet afin que vous sachiez d'où ils viennent.
#!/bin/bash
#
################################################################################
## Script to merge multiple git repositories into a new repository
## - The new repository will contain a folder for every merged repository
## - The script adds remotes for every project and then merges in every branch
## and tag. These are renamed to have the origin project name as a prefix
##
## Usage: mergeGitRepositories.sh <new_project> <my_repo_urls.lst>
## - where <new_project> is the name of the new project to create
## - and <my_repo_urls.lst> is a file containing the URLs to the repositories
## which are to be merged on separate lines.
##
## Author: Robert von Burg
## eitch@eitchnet.ch
##
## Version: 0.2.0
## Created: 2015-06-17
##
################################################################################
#
# Disallow using undefined variables
shopt -s -o nounset
# Script variables
declare SCRIPT_NAME="${0##*/}"
declare SCRIPT_DIR="$(cd ${0%/*} ; pwd)"
declare ROOT_DIR="$PWD"
# Detect proper usage
if [ "$#" -ne "2" ] ; then
echo -e "ERROR: Usage: "151900920" <new_project> <my_repo_urls.lst>"
exit 1
fi
# Script functions
function failed() {
echo -e "ERROR: Merging of projects failed:"
echo -e ""
exit 1
}
function commit_merge() {
current_branch="$(git symbolic-ref HEAD 2>/dev/null)"
CHANGES=$(git status | grep "working directory clean")
MERGING=$(git status | grep "merging")
if [[ "$CHANGES" != "" ]] && [[ "$MERGING" == "" ]] ; then
echo -e "INFO: No commit required."
else
echo -e "INFO: Committing ${sub_project}..."
if ! git commit --quiet -m "[Project] Merged branch '' of ${sub_project}" ; then
failed "Failed to commit merge of branch '' of ${sub_project} into ${current_branch}"
fi
fi
}
## Script variables
PROJECT_NAME=""
PROJECT_PATH="${ROOT_DIR}/${PROJECT_NAME}"
REPO_FILE=""
REPO_URL_FILE="${ROOT_DIR}/${REPO_FILE}"
# Make sure the REPO_URL_FILE exists
if [ ! -e "${REPO_URL_FILE}" ] ; then
echo -e "ERROR: Repo file ${REPO_URL_FILE} does not exist!"
exit 1
fi
# Make sure the required directories don't exist
if [ -e "${PROJECT_PATH}" ] ; then
echo -e "ERROR: Project ${PROJECT_NAME} already exists!"
exit 1
fi
# Create the new project
echo -e "INFO: Creating new git repository ${PROJECT_NAME}..."
echo -e "===================================================="
cd ${ROOT_DIR}
mkdir ${PROJECT_NAME}
cd ${PROJECT_NAME}
git init
echo "Initial Commit" > initial_commit
# Since this is a new repository we need to have at least one commit
# thus were we create temporary file, but we delete it again.
# Deleting it guarantees we don't have conflicts later when merging
git add initial_commit
git commit --quiet -m "[Project] Initial Master Repo Commit"
git rm --quiet initial_commit
git commit --quiet -m "[Project] Initial Master Repo Commit"
echo
# Merge all projects into th branches of this project
echo -e "INFO: Merging projects into new repository..."
echo -e "===================================================="
for url in $(cat ${REPO_URL_FILE}) ; do
# Extract the name of this project
export sub_project=${url##*/}
sub_project=${sub_project%*.git}
echo -e "INFO: Project ${sub_project}"
echo -e "----------------------------------------------------"
# Fetch the project
echo -e "INFO: Fetching ${sub_project}..."
git remote add "${sub_project}" "${url}"
if ! git fetch --no-tags --quiet ${sub_project} 2>/dev/null ; then
failed "Failed to fetch project ${sub_project}"
fi
# Add remote branches
echo -e "INFO: Creating local branches for ${sub_project}..."
while read branch ; do
branch_ref=$(echo $branch | tr " " "\t" | cut -f 1)
branch_name=$(echo $branch | tr " " "\t" | cut -f 2 | cut -d / -f 3-)
echo -e "INFO: Creating branch ${branch_name}..."
# Create and checkout new merge branch off of master
git checkout --quiet -b "${sub_project}/${branch_name}" master
git reset --hard --quiet
git clean -d --force --quiet
# Merge the project
echo -e "INFO: Merging ${sub_project}..."
if ! git merge --quiet --no-commit "remotes/${sub_project}/${branch_name}" 2>/dev/null ; then
failed "Failed to merge branch 'remotes/${sub_project}/${branch_name}' from ${sub_project}"
fi
# And now see if we need to commit (maybe there was a merge)
commit_merge "${sub_project}/${branch_name}"
# Relocate projects files into own directory
if [ "$(ls)" == "${sub_project}" ] ; then
echo -e "WARN: Not moving files in branch ${branch_name} of ${sub_project} as already only one root level."
else
echo -e "INFO: Moving files in branch ${branch_name} of ${sub_project} so we have a single directory..."
mkdir ${sub_project}
for f in $(ls -a) ; do
if [[ "$f" == "${sub_project}" ]] ||
[[ "$f" == "." ]] ||
[[ "$f" == ".." ]] ; then
continue
fi
git mv -k "$f" "${sub_project}/"
done
# Commit the moving
if ! git commit --quiet -m "[Project] Move ${sub_project} files into sub directory" ; then
failed "Failed to commit moving of ${sub_project} files into sub directory"
fi
fi
echo
done < <(git ls-remote --heads ${sub_project})
# Checkout master of sub probject
if ! git checkout "${sub_project}/master" 2>/dev/null ; then
failed "sub_project ${sub_project} is missing master branch!"
fi
# Copy remote tags
echo -e "INFO: Copying tags for ${sub_project}..."
while read tag ; do
tag_ref=$(echo $tag | tr " " "\t" | cut -f 1)
tag_name=$(echo $tag | tr " " "\t" | cut -f 2 | cut -d / -f 3)
# hack for broken tag names where they are like 1.2.0^{} instead of just 1.2.0
tag_name="${tag_name%%^*}"
tag_new_name="${sub_project}/${tag_name}"
echo -e "INFO: Copying tag ${tag_name} to ${tag_new_name} for ref ${tag_ref}..."
if ! git tag "${tag_new_name}" "${tag_ref}" 2>/dev/null ; then
echo -e "WARN: Could not copy tag ${tag_name} to ${tag_new_name} for ref ${tag_ref}"
fi
done < <(git ls-remote --tags ${sub_project})
# Remove the remote to the old project
echo -e "INFO: Removing remote ${sub_project}..."
git remote rm ${sub_project}
echo
done
# Now merge all project master branches into new master
git checkout --quiet master
echo -e "INFO: Merging projects master branches into new repository..."
echo -e "===================================================="
for url in $(cat ${REPO_URL_FILE}) ; do
# extract the name of this project
export sub_project=${url##*/}
sub_project=${sub_project%*.git}
echo -e "INFO: Merging ${sub_project}..."
if ! git merge --quiet --no-commit "${sub_project}/master" 2>/dev/null ; then
failed "Failed to merge branch ${sub_project}/master into master"
fi
# And now see if we need to commit (maybe there was a merge)
commit_merge "${sub_project}/master"
echo
done
# Done
cd ${ROOT_DIR}
echo -e "INFO: Done."
echo
exit 0
vous pouvez aussi l'obtenir de http://paste.ubuntu.com/11732805
créez D'abord un fichier avec L'URL de chaque dépôt, par exemple:
git@github.com:eitchnet/ch.eitchnet.parent.git
git@github.com:eitchnet/ch.eitchnet.utils.git
git@github.com:eitchnet/ch.eitchnet.privilege.git
ensuite, appelez le script en donnant le nom du projet et le chemin vers le script:
./mergeGitRepositories.sh eitchnet_test eitchnet.lst
le script lui-même a beaucoup de commentaires qui devraient expliquer ce qu'elle fait.
si vous essayez simplement de coller deux dépôts ensemble, les sous-modules et les fusions de sous-arborescence sont le mauvais outil à utiliser parce qu'ils ne préservent pas toute l'histoire du fichier (comme les gens ont noté sur d'autres réponses). Voir cette réponse ici pour la façon simple et correcte de le faire.
si vous voulez mettre les fichiers d'une branche dans un repo B dans un subtree de repo A et aussi préserver l'histoire, continuez à lire. (Dans l'exemple ci-dessous, je suppose que nous voulons que la branche principale de la banque soit fusionnée à la branche principale de la banque.)
Dans Un repo, d'abord faire ce qui suit pour faire pensions de B:
git remote add B ../B # Add repo B as a new remote.
git fetch B
maintenant nous créer une toute nouvelle branche (avec un seul commit) dans la réplique que nous appelons new_b_root
. La propagation résultante aura les fichiers qui ont été engagés dans la première propagation de la branche principale du rapport B mais mis dans un sous-répertoire appelé path/to/b-files/
.
git checkout --orphan new_b_root master
git rm -rf . # Remove all files.
git cherry-pick -n `git rev-list --max-parents=0 B/master`
mkdir -p path/to/b-files
git mv README path/to/b-files/
git commit --date="$(git log --format='%ai' $(git rev-list --max-parents=0 B/master))"
explication: l'option --orphan
de la commande checkout vérifie les fichiers de la branche maître de A mais ne crée pas de commit. Nous aurions pu sélectionner n'importe quel commit car ensuite nous éliminons tous les fichiers de toute façon. Ensuite, sans commettre encore ( -n
), nous sélectionnons le premier commit de la branche principale de B. (Le choix préserve le message de commit original, ce qu'un simple checkout ne semble pas faire.) Puis nous créons le sous-arborescence où nous voulons mettre tous les fichiers de repo B. Nous devons ensuite déplacer tous les fichiers qui ont été introduits dans le "cherry-pick" vers le sous-arborescence. Dans l'exemple ci-dessus, il n'y a qu'un fichier README
à déplacer. Puis nous commettons notre B-repo root commit, et, en même temps, nous conservons également l'horodatage du commit original.
maintenant, nous allons créer une nouvelle branche B/master
en plus de la nouvelle branche new_b_root
. Nous appelons la nouvelle branche b
:
git checkout -b b B/master
git rebase -s recursive -Xsubtree=path/to/b-files/ new_b_root
maintenant, nous fusionnons notre b
branche dans A/master
:
git checkout master
git merge --allow-unrelated-histories --no-commit b
git commit -m 'Merge repo B into repo A.'
enfin, vous pouvez supprimer le B
branches distantes et temporaires:
git remote remove B
git branch -D new_b_root b
le graphique final aura une structure comme celle-ci:
j'ai eu un défi similaire, mais dans mon cas, nous avions développé une version du codebase en repo A, puis nous l'avons cloné dans un nouveau repo, repo B, pour la nouvelle version du produit. Après avoir corrigé quelques bugs dans repo A, Nous avons dû corriger les changements dans repo B. Nous avons fini par faire ce qui suit:
- ajout d'une télécommande à la position repo B pointant vers la position repo a (git remote add...)
- tirant la branche courante (nous n'utilisions pas master pour corriger les bogues) (git pull remoteForRepoA bugFixBranch)
- Pousser fusionne à github
ouvré:)
similaire à @Smar mais utilise des chemins de système de fichiers, définis en primaire et secondaire:
PRIMARY=~/Code/project1
SECONDARY=~/Code/project2
cd $PRIMARY
git remote add test $SECONDARY && git fetch test
git merge test/master
puis vous fusionnez manuellement.
(adapté de post by Anar Manafov )
lorsque vous voulez fusionner trois ou plusieurs projets dans une single commit, faites les étapes décrites dans les autres réponses ( remote add -f
, merge
). Puis, (soft) réinitialiser l'index à la vieille tête (où aucune fusion ne s'est produite). Ajoutez tous les fichiers ( git add -A
) et engagez-les (message " fusionner les projets A, B, C et D en un seul projet). C'est maintenant le commit-id de maître.
maintenant, créer .git/info/grafts
avec le contenu suivant:
<commit-id of master> <list of commit ids of all parents>
Exécuter git filter-branch -- head^..head head^2..head head^3..head
. Si vous avez plus de trois branches, ajoutez autant de head^n..head
que vous avez de branches. Pour mettre à jour les tags, ajoutez --tag-name-filter cat
. N'ajoutez pas toujours cela, car cela pourrait provoquer une réécriture de certaines propagations. Pour plus de détails voir man page de filter-branch , rechercher "grafts".
maintenant, votre dernier engagement a les bons parents associés.
pour fusionner a à l'intérieur de B:
1) dans le projet a
git fast-export --all --date-order > /tmp/ProjectAExport
2) dans le projet B
git checkout -b projectA
git fast-import --force < /tmp/ProjectAExport
dans cette branche faites toutes les opérations que vous devez faire et engagez-les.
C) puis retour au maître et fusion classique entre les deux branches:
git checkout master
git merge projectA
de la Fusion de 2 repos
git clone ssh://<project-repo> project1
cd project1
git remote add -f project2 project2
git merge --allow-unrelated-histories project2/master
git remote rm project2
delete the ref to avoid errors
git update-ref -d refs/remotes/project2/master
est la meilleure solution possible.
git subtree add --prefix=MY_PROJECT git://github.com/project/my_project.git master
cette fonction clonera Remote repo dans repo dir local, après la fusion toutes les propagations seront sauvegardées, git log
affichera les propagations originales et les chemins appropriés:
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
si vous faites quelques changements, vous pouvez même déplacer des fichiers / DRS de repo fusionnés 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 "" | sed 's/\./\./')"
to="$(echo "" | 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
Chemin remplace via sed
, donc assurez-vous qu'il s'est déplacé dans les chemins appropriés après la fusion.
Le paramètre --allow-unrelated-histories
n'existe que depuis git >= 2.9.
je fusionne les projets légèrement manuellement, ce qui me permet d'éviter d'avoir à gérer les conflits de fusion.
d'abord, copiez dans les fichiers de l'autre projet comme vous le souhaitez.
cp -R myotherproject newdirectory
git add newdirectory
pull suivante dans l'histoire
git fetch path_or_url_to_other_repo
indiquer à git pour fusionner dans l'histoire de la dernière extraction chose
echo 'FETCH_HEAD' > .git/MERGE_HEAD
maintenant commettre toutefois vous le feriez normalement s'engager
git commit