J'ai la disposition suivante du référentiel:
Ce que je veux réaliser est de choisir une série d’engagements dans la branche active et de les fusionner dans la branche intégration. Je suis assez nouveau pour git et je ne vois pas comment faire exactement cela (la sélection des plages de commit en une seule opération et non la fusion) sans déranger le référentiel. Des pointeurs ou des pensées à ce sujet? Merci!
En ce qui concerne une gamme de commits, choisir les cerises est était pas pratique.
Comme mentionné ci-dessous par Keith Kim , Git 1.7.2+ introduisait la possibilité de sélectionner une série de commits (mais vous devez toujours être conscient de la conséquence de la sélection des données pour une fusion future )
git cherry-pick "a appris à choisir une gamme de commits
(par exemple, "_cherry-pick A..B
_" et "_cherry-pick --stdin
_"), ainsi que "_git revert
_"; ceux-ci ne supportent pas le contrôle de séquençage plus agréable que "_rebase [-i]
_" a cependant.
damiancommentaires et nous avertit:
Dans le formulaire "_
cherry-pick A..B
_",A
DOIT ÊTRE PLUS ANCIEN QUEB
.
S'ils ne sont pas dans le bon ordre, la commande échouera silencieusement.
Si vous voulez choisir le plage B
à D
(inclus), ce serait B^..D
.
Voir " Git crée une branche à partir de la plage des commits précédents? " à titre d’illustration.
Comme Jubobs mentionne dans les commentaires :
Cela suppose que
B
n'est pas un commit root; sinon, vous obtiendrez une erreur "_unknown revision
_".
Note: à partir de Git 2.9.x/2.10 (T3 2016), vous pouvez sélectionner une plage de commit directement sur une branche orpheline (tête vide): voir " Comment faire d'une branche existante un orphelin dans git ".
Réponse originale (janvier 2010)
Un _rebase --onto
_ serait mieux si vous rejouez la plage donnée de commit en plus de votre branche d'intégration, comme Charles Bailey décrit ici .
(aussi, cherchez "Voici comment transplanter une branche de sujet basée sur une branche à une autre" dans la page de manuel git rebase , 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
_
Cela rejouera tout entre:
first_SHA-1_of_working_branch_range
_ (d'où le _~1
_): le premier commit que vous souhaitez rejouerintegration
" (qui pointe vers le dernier commit que vous voulez rejouer, à partir de la branche working
)to "tmp
" (qui pointe là où integration
pointait avant)
S'il y a un conflit lorsqu'un de ces commits est rejoué:
git rebase --continue
_".git rebase --skip
_"git rebase --abort
_" (et remettez la branche integration
sur la branche tmp
)Après cela _rebase --onto
_, integration
sera de retour au dernier commit de la branche d'intégration (c'est-à-dire "tmp
" branch + tous les commits rejoués)
Avec la sélection de cerises ou _rebase --onto
_, n'oubliez pas que cela a des conséquences sur les fusions ultérieures, comme décrit ici .
Une solution pure "_cherry-pick
_" est discutée ici , et impliquerait quelque chose comme:
Si vous souhaitez utiliser une approche patch, alors "git format-patch | git am" et "git cherry" sont vos options.
Actuellement, _git cherry-pick
_ n’accepte qu’un seul commit, mais si vous souhaitez choisir la plageB
àD
, celle-ci serait _B^..D
_ dans git jargon, alors
_git rev-list --reverse --topo-order B^..D | while read rev
do
git cherry-pick $rev || break
done
_
Quoi qu'il en soit, lorsque vous devez "rejouer" une série de commits, le mot "rejouer" devrait vous inciter à utiliser la fonctionnalité "rebase
" de Git.
A partir de git v1.7.2, cherry pick peut accepter une gamme de commits:
git cherry-pick
a appris à choisir une plage de validations (par exemple,cherry-pick A..B
etcherry-pick --stdin
), ainsigit revert
; ceux-ci ne supportent pas le contrôle de séquençage plus agréable querebase [-i]
a, cependant.
Supposons que vous ayez 2 branches,
"branchA": inclut les commits que vous voulez copier (de "commitA" à "commitB"
"branchB": la branche vers laquelle vous voulez transférer les commits 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) En cas de conflit, résolvez-le et tapez
git cherry-pick --continue
poursuivre le processus de sélection des cerises.
Êtes-vous sûr de ne pas vouloir fusionner les branches? Si la branche active a des commits récents que vous ne voulez pas, vous pouvez simplement créer une nouvelle branche avec un HEAD au point souhaité.
Maintenant, si vous voulez vraiment choisir une gamme d’engagements, quelle que soit la raison, une manière élégante de procéder consiste simplement à créer un patch et à 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 fait de toute façon, mais sans avoir besoin de jouer à des jeux. Vous pouvez ajouter --3way
à git-am
si vous devez 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 à la lettre ...
J'ai enveloppé code de VonC dans un court script bash, git-multi-cherry-pick
, pour une exécution 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
Je l'utilise actuellement pour reconstruire l'historique d'un projet comportant à la fois du code tiers et des personnalisations mélangées dans le même tronc svn. Je suis en train de scinder le code tiers principal, les modules tiers et les personnalisations en leurs propres branches git pour une meilleure compréhension des personnalisations à venir. git-cherry-pick
est utile dans cette situation car j'ai deux arbres dans le même référentiel, mais sans ancêtre partagé.
Toutes les options ci-dessus vous inviteront à résoudre les conflits de fusion. Si vous fusionnez des modifications validées pour une équipe, il est difficile de résoudre les conflits de fusion des développeurs et de continuer. Cependant, "git merge" fera la fusion en un seul coup, mais vous ne pouvez pas passer une plage de révisions en argument. nous devons utiliser les commandes "git diff" et "git apply" pour faire la plage de fusion des révolutions. J'ai observé que "git apply" échouera si le fichier de correctif a diff pour trop de fichiers, nous devons donc créer un correctif par fichier, puis appliquer. Notez que le script ne pourra pas supprimer les fichiers supprimés dans la branche source. Dans de rares cas, vous pouvez supprimer manuellement ces fichiers de la branche cible. Le statut de sortie de "git apply" n'est pas égal à zéro s'il n'est pas en mesure d'appliquer le correctif. Toutefois, si vous utilisez l'option -3way, il sera réduit à la fusion à 3 voies et vous n'aurez pas à vous inquiéter 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 stratégie our au commit avant la plage, puis une fusion "normale" avec le dernier commit de cette plage (ou branche quand il s'agit de la dernière). Supposons donc que seuls 2345 et 3456 commits du maître soient fusionnés dans une branche de fonctionnalité:
maître: 1234 2345 3456 4567
dans la branche de fonctionnalité:
git merge -s our our 4567 git fusion 2345