D'après ce que j'ai lu, les deux nous aident à obtenir une histoire linéaire.
D'après ce que j'ai expérimenté, le rebase fonctionne tout le temps. Mais la fusion --ff-only ne fonctionne que dans les scénarios où elle peut être transmise rapidement.
J'ai également remarqué que git merge crée un commit de fusion, mais si nous utilisons --ff-only, il donne un historique linéaire qui est essentiellement égal à git rebasing. Donc --ff-only tue le but de la fusion git, non?
Quelle est donc la véritable différence entre eux?
Notez que git rebase
a un travail différent que git merge
(avec ou sans --ff-only
). Ce que rebase
fait est de prendre les validations existantes et de les copier . Supposons, par exemple, que vous êtes sur branch1
et ont effectué deux commits A
et B
:
...-o--o--A--B <-- HEAD=branch1
\
o--C <-- branch2
et vous décidez que vous préférez que ces deux validations soient sur branch2
au lieu. Vous pouvez:
A
(diff A
par rapport à son parent)B
(diff B
contre A
)branch2
A
et validez-les, en copiant votre message de validation depuis A
; appelons ce commit A'
B
et validez-les, en copiant votre message de validation depuis B
; appelons cela B'
.Il y a une commande git qui fait ce diff-puis-copie-et-commit pour vous: git cherry-pick
. Alors:
git checkout branch2 # switch HEAD to branch2 (commit C)
git cherry-pick branch1^ # this copies A to A'
git cherry-pick branch1 # and this copies B
Vous avez maintenant ceci:
...-o--o--A--B <-- branch1
\
o--C--A'-B' <-- HEAD=branch2
Vous pouvez maintenant revenir à branch1
et supprimez vos A
et B
originaux, en utilisant git reset
(Je vais utiliser --hard
ici, c'est plus pratique de cette façon car il nettoie aussi l'arbre de travail):
git checkout branch1
git reset --hard HEAD~2
Cela supprime les A
et B
d'origine,1 maintenant vous avez:
...-o--o <-- HEAD=branch1
\
o--C--A'-B' <-- branch2
Il ne vous reste plus qu'à re-check-out branch2
pour continuer à y travailler.
C'est quoi git rebase
fait: il "déplace" les validations (mais pas en les déplaçant réellement, car il ne le peut pas: dans git, une validation ne peut jamais être modifiée, donc même le simple changement de l'ID parent nécessite de le copier dans une nouvelle validation légèrement différente ).
En d'autres termes, tandis que git cherry-pick
est un diff-and-redo automatisé de un commit, git rebase
est un processus automatisé de refaire plusieurs validations, plus, à la fin, de déplacer les étiquettes pour "oublier" ou cacher les originaux.
L'illustration ci-dessus illustre le déplacement des validations à partir d'une branche locale branch1
vers une autre succursale locale branch2
, mais git utilise exactement le même processus pour déplacer les validations lorsque vous avez une branche de suivi à distance qui acquiert de nouvelles validations lorsque vous effectuez une git fetch
(y compris le fetch
qui est la première étape de git pull
). Vous pourriez commencer par travailler sur la branche feature
, qui a en amont de Origin/feature
, et faites vous-même quelques commits:
...-o <-- Origin/feature
\
A--B <-- HEAD=feature
Mais vous décidez ensuite de voir ce qui s'est passé en amont, vous exécutez donc git fetch
,2 et, aha, quelqu'un en amont a écrit un commit C
:
...-o--C <-- Origin/feature
\
A--B <-- HEAD=feature
À ce stade, vous pouvez simplement rebaser vos feature
A
et B
sur C
, donnant:
...-o--C <-- Origin/feature
\
A'-B' <-- HEAD=feature
Ce sont des copies de vos A
et B
originaux, les originaux étant jetés (mais voir référence 1) une fois les copies terminées.
Parfois, il n'y a rien à rebaser, c'est-à-dire aucun travail que vous avez fait vous-même. Autrement dit, le graphique avant le fetch
ressemble à ceci:
...-o <-- Origin/feature
`-- HEAD=feature
Si vous alors git fetch
et commit C
entre, cependant, vous vous retrouvez avec votre feature
branche pointant vers l'ancien valider, tandis que Origin/feature
a progressé:
...-o--C <-- Origin/feature
`---- <-- HEAD=feature
C'est ici que git merge --ff-only
entre en jeu: si vous demandez de fusionner votre branche actuelle feature
avec Origin/feature
, git voit qu'il est possible de faire glisser la flèche vers l'avant, pour ainsi dire, de sorte que feature
pointe directement pour valider C
. Aucune fusion réelle n'est requise.
Si vous aviez vos propres validations A
et B
, et que vous demandiez de fusionner celles-ci avec C
, git ferait une véritable fusion, créant une nouvelle validation de fusion M
:
...-o--C <-- Origin/feature
\ `-_
A--B--M <-- feature
Ici, --ff-only
s'arrêtera et vous donnera une erreur. Rebase, d'autre part, peut copier A
et B
vers A'
et B'
, puis masquez les A
et B
originaux.
Donc, en bref (ok, trop tard :-)), ils font simplement des choses différentes. Parfois, le résultat est le même et parfois non. Si vous pouvez copier A
et B
, vous pouvez utiliser git rebase
; mais s'il y a une bonne raison de ne pas les copier, vous pouvez utiliser git merge
, peut-être avec --ff-only
, pour fusionner ou échouer selon le cas.
1Git conserve les originaux pendant un certain temps - normalement un mois dans ce cas - mais il les cache. Le moyen le plus simple de les trouver est avec les "reflogs" de git, qui gardent un historique de l'endroit où chaque branche pointait, et où HEAD
pointait, avant chaque changement qui mettait à jour la branche et/ou HEAD
.
Finalement, les entrées d'historique de reflog expirent, auquel cas ces validations deviennent éligibles pour garbage collection .
2Ou, encore une fois, vous pouvez utiliser git pull
, qui est un script pratique qui commence par exécuter git fetch
. Une fois l'extraction terminée, le script de commodité exécute soit git merge
ou git rebase
, selon la façon dont vous le configurez et l'exécutez.
Oui, il y a une différence. git merge --ff-only
s'interrompra s'il ne peut pas avancer rapidement et prend un commit (normalement une branche) pour fusionner. Il ne créera un commit de fusion que s'il ne peut pas avancer rapidement (c'est-à-dire qu'il ne le fera jamais avec --ff-only
).
git rebase
réécrit l'historique sur la branche actuelle, ou peut être utilisé pour rebaser une branche existante sur une branche existante. Dans ce cas, il ne créera pas de validation de fusion car il s'agit de rebaser plutôt que de fusionner.
Oui, --ff-only
échouera toujours là où un simple git merge
échouerait et pourrait échouer là où un simple git merge
réussirait. C'est le point - si vous essayez de conserver un historique linéaire, et que la fusion ne peut pas être effectuée de cette façon, vous voulez qu'il échoue.
Une option qui ajoute des cas d'échec à une commande n'est pas inutile; c'est un moyen de valider une condition préalable, donc si l'état actuel du système n'est pas celui que vous attendez, vous n'aggravez pas le problème.