Comment choisir une gamme de commits et fusionner dans une autre branche?
j'ai la disposition suivante du dépôt:
- branche master (production)
- intégration
- en cours
ce que je veux réaliser, c'est choisir une gamme de propagations à partir de la branche de travail et la fusionner dans la branche d'intégration. Je suis assez nouveau à git et je ne peux pas comprendre comment faire exactement (la sélection de cerise de gamme de commit dans une opération pas la fusion) sans déconner le référentiel. Tous les pointeurs ou des pensées à ce sujet? Merci!
7 réponses
quand il s'agit d'une gamme de commits, est était pas pratique.
Comme mentionnés ci-après par Keith Kim , Git 1.7.2+ a introduit la possibilité de piocher dans une série de commits (mais vous devez toujours être conscient de la conséquence de cherry-picking pour la fusion )
git cherry-pick "appris à choisir une gamme de commits
(par exemple "cherry-pick A..B
" et "cherry-pick --stdin
"), ainsi fait "git revert
"; ceux-ci ne supportent pas le plus nicer sequencing control "rebase [-i]
" a, cependant.
damian commente et nous avertit:
dans la "
cherry-pick A..B
"forme,A
doit être plus ancien queB
.
S'ils sont du mauvais ordre, la commande échouera silencieusement .
si vous voulez choisir la gamme B
à D
(inclus) qui serait B^..D
.
Voir " Git créer une branche à partir de la gamme de la précédente s'engage? " comme illustration.
Jubobs , mentionne le dans les commentaires :
cela suppose que
B
n'est pas un Root commit; vous obtiendrez une erreur "unknown revision
" autrement.
Note: à partir de Git 2.9.x / 2.10 (Q3 2016), vous pouvez sélectionner une gamme de commit directement sur une branche orpheline (empty head): voir " Comment faire de la branche existante un orphelin dans git ".
réponse Originale à cette question (janvier 2010)
A rebase --onto
serait mieux, où vous rejouez la gamme donnée de commit sur le dessus de votre branche d'intégration, comme Charles Bailey décrit ici .
(aussi, cherchez "Voici comment vous transplanter une branche thématique basée sur une branche à une autre" dans la git rebase man page , pour voir un exemple pratique de git rebase --onto
)
si votre branche actuelle est l'intégration:
# Checkout a new temporary branch at the current location
git checkout -b tmp
# Move the integration branch to the head of the new patchset
git branch -f integration last_SHA-1_of_working_branch_range
# Rebase the patchset onto tmp, the old location of integration
git rebase --onto tmp first_SHA-1_of_working_branch_range~1 integration
qui rejouera tout entre:
- après le parent de
first_SHA-1_of_working_branch_range
(d'où le~1
): le premier commit que vous voulez rejouer - jusqu'à
integration
"(qui pointe vers le dernier commit que vous voulez rejouer, de la brancheworking
)
à " tmp
" (qui indique où integration
pointait avant)
S'il y a un conflit quand l'un d'eux commet est rejoué:
- soit résoudre et exécuter "
git rebase --continue
". - ou sauter ce patch, et à la place exécuter "
git rebase --skip
" - ou annuler le tout avec un "
git rebase --abort
"et remettre la brancheintegration
sur la branchetmp
)
après que rebase --onto
, integration
sera de retour à la dernière commit de la branche d'intégration (c'est-à-dire tmp
"commits branche + tous les rejoués)
avec "cherry-picking" ou rebase --onto
, n'oubliez pas qu'il a des conséquences sur les fusions suivantes, comme décrit ici .
pur " cherry-pick
solution " est discuté ici , et impliquerait quelque chose comme:
si vous voulez utiliser une approche patch, alors" git format-patch|git am "et" git cherry " sont vos options.
Actuellement,git cherry-pick
accepte seulement un commit unique, mais si vous voulez choisir la gammeB
àD
qui seraitB^..D
en git lingo, donc
git rev-list --reverse --topo-order B^..D | while read rev
do
git cherry-pick $rev || break
done
mais de toute façon, quand vous avez besoin de " rejouer" une série de commits, le mot "replay" devrait vous pousser à utiliser les " rebase
" de Git.
à partir de git v1.7.2 cerise pick peut accepter une gamme de commits:
git cherry-pick
appris à choisir une gamme de commits (par exemplecherry-pick A..B
etcherry-pick --stdin
), ainsi faitgit revert
; ceux-ci ne supportent pas le contrôle de séquençage nicerrebase [-i]
a, cependant.
Êtes-vous sûr que vous ne voulez pas fusionner les branches? Si la branche a quelques récentes s'engage vous ne voulez pas, vous pouvez simplement créer une nouvelle branche avec une TÊTE à l'endroit que vous voulez.
maintenant, si vous voulez vraiment choisir une gamme de commits, pour quelque raison que ce soit, une façon élégante de le faire est de simplement tirer un patchset et de l'appliquer à votre nouvelle branche d'intégration:
git format-patch A..B
git checkout integration
git am *.patch
c'est essentiellement ce que git-rebase est faire de toute façon, mais sans la nécessité de jouer à des jeux. Vous pouvez ajouter --3way
à git-am
si vous avez besoin de fusionner. Assurez-vous il n'y a pas d'autres *.patch les fichiers déjà dans le répertoire où vous faites cela, si vous suivez les instructions mot à mot...
supposons que vous avez deux branches,
"branchA" : comprend les commits que vous souhaitez copier à partir de "commitA" à "commitB"
"branchB": la branche que vous voulez que les commits soient transférés de "branchA"
1)
git checkout <branchA>
2) Obtenir les IDs de "commitA" et" commib "
3)
git checkout <branchB>
4)
git cherry-pick <commitA>^..<commitB>
5) dans le cas vous avez un conflit, résolvez-le et tapez
git cherry-pick --continue
pour poursuivre le processus de cueillette.
j'ai enveloppé VonC code dans un court script bash, git-multi-cherry-pick
, pour faciliter le fonctionnement:
#!/bin/bash
if [ -z ]; then
echo "Equivalent to running git-cherry-pick on each of the commits in the range specified.";
echo "";
echo "Usage: "151900920" start^..end";
echo "";
exit 1;
fi
git rev-list --reverse --topo-order | while read rev
do
git cherry-pick $rev || break
done
je suis en train d'utiliser ceci alors que je reconstruis l'histoire d'un projet qui avait à la fois du code tiers et des personnalisations mélangées ensemble dans le même tronc svn. Je sépare maintenant le code de base de tiers, les modules de tiers et les personnalisations sur leurs propres branches git pour une meilleure compréhension des personnalisations à venir. git-cherry-pick
est utile dans cette situation puisque j'ai deux arbres dans le même dépôt, mais sans ancêtre commun.
toutes les options ci-dessus vous inviteront à résoudre les conflits de fusion. Si vous fusionnez les changements engagés pour une équipe, il est difficile de résoudre les conflits de fusion de développeurs et de poursuivre. Cependant, "git merge" fera la fusion en un coup, mais vous ne pouvez pas passer une plage de révisions comme argument. nous devons utiliser les commandes" git diff "et" git apply " pour effectuer la plage de fusion de revs. J'ai observé que "git apply" échouera si le fichier patch a des différences pour trop de fichiers, donc nous avons pour créer un patch par fichier et ensuite appliquer. Notez que le script ne pourra pas supprimer les fichiers supprimés dans la branche source. C'est un cas rare, vous pouvez supprimer manuellement de tels fichiers de la branche cible. Le statut de sortie de "Git apply" n'est pas zéro s'il n'est pas capable d'appliquer le patch, cependant si vous utilisez l'option-3way il retombera à la fusion à 3 voies et vous n'avez pas à vous soucier de cet échec.
ci-dessous est le script.
enter code here
#!/bin/bash
# This script will merge the diff between two git revisions to checked out branch
# Make sure to cd to git source area and checkout the target branch
# Make sure that checked out branch is clean run "git reset --hard HEAD"
START=
END=
echo Start version: $START
echo End version: $END
mkdir -p ~/temp
echo > /tmp/status
#get files
git --no-pager diff --name-only ${START}..${END} > ~/temp/files
echo > ~/temp/error.log
# merge every file
for file in `cat ~/temp/files`
do
git --no-pager diff --binary ${START}..${END} $file > ~/temp/git-diff
if [ $? -ne 0 ]
then
# Diff usually fail if the file got deleted
echo Skipping the merge: git diff command failed for $file >> ~/temp/error.log
echo Skipping the merge: git diff command failed for $file
echo "STATUS: FAILED $file" >> /tmp/status
echo "STATUS: FAILED $file"
# skip the merge for this file and continue the merge for others
rm -f ~/temp/git-diff
continue
fi
git apply --ignore-space-change --ignore-whitespace --3way --allow-binary-replacement ~/temp/git-diff
if [ $? -ne 0 ]
then
# apply failed, but it will fall back to 3-way merge, you can ignore this failure
echo "git apply command filed for $file"
fi
echo
STATUS=`git status -s $file`
if [ ! "$STATUS" ]
then
# status is null if the merged diffs are already present in the target file
echo "STATUS:NOT_MERGED $file"
echo "STATUS: NOT_MERGED $file$" >> /tmp/status
else
# 3 way merge is successful
echo STATUS: $STATUS
echo "STATUS: $STATUS" >> /tmp/status
fi
done
echo GIT merge failed for below listed files
cat ~/temp/error.log
echo "Git merge status per file is available in /tmp/status"
une autre option pourrait être de fusionner avec notre stratégie à la propagation avant la portée et ensuite une fusion "normale" avec la dernière propagation de cette portée (ou branche quand c'est la dernière). Supposons donc que seulement 2345 et 3456 commits of master soient fusionnés dans la branche feature:
master: 1234 2345 3456 4567
en fonction de la branche:
git merge -s ours 4567 git merge 2345