Comment choisir une cerise gamme de commits et se fondre dans une autre branche?
-
22-09-2019 - |
Question
J'ai la disposition du référentiel suivant:
- branche principale (production)
- intégration
- travail
Ce que je veux atteindre est de cerise choisir une gamme de commits de la branche de travail et la fusion dans la branche d'intégration. Je assez nouveau pour connard et je ne peux pas comprendre comment faire exactement cela (le picorage commit des gammes en une seule opération pas la fusion) sans déconner vers le haut du dépôt. Tous les pointeurs ou réflexions sur ce sujet? Merci!
La solution
En ce qui concerne une gamme de commits, picorage est n'a pas pratique.
mentionné ci-dessous par conséquence de picorage pour la future fusion )
écrémer git » appris à choisir une gamme de commits
(Par exemple "cherry-pick A..B
" et "cherry-pick --stdin
"), tout comme "git revert
"; ceux-ci ne prennent pas en charge la plus belle contrôle de séquençage «rebase [-i]
» a, cependant.
commentaires et nous met en garde contre:
Dans la forme de "
cherry-pick A..B
",A
devrait être plus queB
.
Si elles sont le mauvais ordre la commande échouer en mode silencieux .
Si vous voulez choisir la gamme B
par D
(inclus) qui serait B^..D
.
Voir " Git création d'une branche de la gamme des précédentes commits? " à titre d'illustration.
Jubobs mentionne dans les commentaires :
Cela suppose que
B
n'est pas un engagement racine; vous obtiendrez une erreur «unknown revision
» autrement.
Note: Depuis Git 2.9.x / 2.10 (Q3 2016), vous pouvez écrémer une gamme de commettre directement sur une branche orpheline (tête vide): voir " Comment faire une branche existante orphelin git ".
Réponse originale (Janvier 2010)
rebase --onto
serait mieux, où vous rejouez la plage donnée de s'engager au-dessus de votre branche d'intégration, comme Charles Bailey décrit ici .
(Aussi, recherchez « Voici comment vous transplanter une branche thématique basée sur une branche à l'autre » dans la git rebase page de manuel, pour voir un exemple concret de git rebase --onto
)
Si votre branche courante 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
Cela rejouer tout entre:
- après le parent de
first_SHA-1_of_working_branch_range
(d'où le~1
): la première vous commettras voulez rejouer - jusqu'à «
integration
» (qui pointe vers le dernier que vous commettez voulez rejouer, de la brancheworking
)
à "tmp
" (qui pointe à l'endroit où integration
pointait avant)
En cas de conflit lorsque l'un de ces commits est rejoué:
- soit le résoudre et exécuter «
git rebase --continue
». - ou sauter ce patch, et au lieu de courir "
git rebase --skip
" - ou annuler la toute chose avec un «
git rebase --abort
» (et remettre la brancheintegration
sur la branchetmp
)
Après que rebase --onto
, integration
sera de retour à la dernière validation de la branche d'intégration (qui est la branche « tmp
» + tous les commits rejoués)
Avec picorage ou rebase --onto
, ne pas oublier qu'elle a des conséquences sur les fusions successives, comme décrit ici .
Une solution pure "de cherry-pick
" est discuté ici et impliquerait quelque chose comme:
Si vous souhaitez utiliser une approche patch puis "le format-patch git | git am" et "cerise git" sont vos options
. À l'heure actuelle,git cherry-pick
accepte un seul commit, mais si vous voulez choisir la gammeB
parD
qui seraitB^..D
dans 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 fonctionnalité « rejouer » une gamme de commits, le mot « rejouer » devrait vous pousser à utiliser le « rebase
» de Git.
Autres conseils
Au choix git cerise v1.7.2 peut accepter une gamme de commits:
git cherry-pick
appris à choisir une gamme de commits (par exemplecherry-pick A..B
etcherry-pick --stdin
), donc a faitgit revert
; ceux-ci ne prennent pas en charge a, bien que lerebase [-i]
plus agréable de contrôle de séquençage.
Supposons que vous avez 2 branches,
"branchA": comprend commits que vous souhaitez copier (de "commitA" à "commitB"
"branchB": la branche que vous voulez que les commits à transférer de "branchA"
1)
git checkout <branchA>
2) obtenir les identifiants de "commitA" et "commitB"
3)
git checkout <branchB>
4)
git cherry-pick <commitA>^..<commitB>
5) Si vous avez un conflit, le résoudre et le type
git cherry-pick --continue
pour continuer le processus écrémer.
Êtes-vous sûr que vous ne voulez pas fusionner réellement les branches? Si la branche de travail a des commits récents vous ne voulez pas, vous pouvez simplement créer une nouvelle branche avec une tête au point que vous voulez.
Maintenant, si vous voulez vraiment cerise choisir une gamme de commits, pour une raison quelconque, d'une manière élégante de faire est de simplement tirer d'un patchset et l'appliquer à votre nouvelle branche d'intégration:
git format-patch A..B
git checkout integration
git am *.patch
Ceci est essentiellement ce que git-rebase fait de toute façon, mais sans avoir besoin de jouer à des jeux. Vous pouvez ajouter --3way
à git-am
si vous avez besoin de fusionner. Assurez-vous qu'il n'y a pas d'autres fichiers * .patch déjà dans le répertoire où vous faites cela, si vous suivez les instructions verbatim ...
J'enroulai Code de VonC dans un court script bash, git-multi-cherry-pick
, pour un fonctionnement facile:
#!/bin/bash
if [ -z $1 ]; then
echo "Equivalent to running git-cherry-pick on each of the commits in the range specified.";
echo "";
echo "Usage: $0 start^..end";
echo "";
exit 1;
fi
git rev-list --reverse --topo-order $1 | while read rev
do
git cherry-pick $rev || break
done
J'utilise actuellement ce que je reconstruis l'histoire d'un projet qui avait à la fois le code 3e parti et personnalisations mélangées dans le même tronc svn. Je vais partager maintenant à part le code 3ème partie noyau, modules 3ème partie, et personnalisations sur leurs propres branches git pour une meilleure compréhension des personnalisations à l'avenir. git-cherry-pick
est utile dans cette situation depuis que j'ai deux arbres dans le même référentiel, mais sans ancêtre commun.
Toutes les options ci-dessus vous invite à résoudre les conflits de fusion. Si vous fusionnez des changements engagés pour une équipe, il est difficile de se résoudre les conflits de fusion des développeurs et continuer. Cependant, « git merge » fera la fusion en un seul coup, mais vous ne pouvez pas passer une série de révisions en argument. nous devons utiliser « git diff » et « git appliquer » les commandes pour faire la gamme de fusion de régime moteur. Je l'ai observé que « git appliquer » échouera si le fichier patch a diff pour le fichier trop grand nombre, donc nous devons créer un patch par fichier, puis appliquer. Notez que le script ne sera pas en mesure de supprimer les fichiers qui sont supprimés dans la branche source. Ceci est un cas rare, vous pouvez supprimer manuellement ces fichiers de branche cible. L'état de la sortie de « git appliquer » n'est pas nul si elle ne peut pas appliquer le patch, si vous utilisez l'option -3way il retombera à 3 fusion de façon et vous n'avez pas à vous soucier de cet échec.
Ci-dessous 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=$1
END=$2
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 la nôtre de stratégie à la validation avant la plage, puis une fusion « normale » avec la dernière livraison de cette plage (ou branche quand il est le dernier). Ainsi, supposons que 2345 et 3456 commits de maître à fusionner dans la branche de fonction:
master: 1234 2345 3456 4567
dans la branche de fonction:
git merge -s ours 4567 git merge 2345