Est-il une différence entre git rebase et git merge --ff-seulement
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é, rebase fonctionne tout le temps. Mais la fusion -- ff-ne fonctionne que dans les scénarios où elle peut être transmise rapidement.
j'ai aussi remarqué que git merge crée une commit merge, mais si on utilise --ff-only, cela donne une histoire linéaire qui est essentiellement égale à git rebasing. Donc ... ça ne tue que le but de la fusion de git, pas vrai?
alors quelle est la différence réelle entre eux?
3 réponses
Notez que git rebase
a unemploigit merge
(avec ou sans --ff-only
). rebase
n'est à prendre s'engage et copier. Supposons, par exemple, que vous êtes sur branch1
et ont fait deux commits A
et B
:
...-o--o--A--B <-- HEAD=branch1
\
o--C <-- branch2
et que vous décidez que vous préférez avoir ces deux commits sur branch2
à la place. Vous pouvez:
- obtenir une liste des modifications que vous avez apportées dans
A
(diffA
contre son parent) - obtenir une liste des modifications que vous avez apportées dans
B
(diffB
contreA
) - passez à
branch2
- faites les mêmes changements que vous avez fait dans
A
et valider, la copie de votre message de validation deA
; appelons ce commitA'
- et ensuite faire les mêmes modifications que vous avez apportées dans
B
et valider, la copie de votre message de validation deB
; appelons celaB'
.
Il y a une commande git que cette diff-et-puis-copie-et-de s'engager pour vous: git cherry-pick
. Donc:
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
Maintenant, vous avez ceci:
...-o--o--A--B <-- branch1
\
o--C--A'-B' <-- HEAD=branch2
Maintenant, vous pouvez revenir à branch1
et supprimez votre original A
et B
, en utilisant git reset
(je vais utiliser --hard
ici, c'est plus pratique que comme il nettoie le travail-arbre):
git checkout branch1
git reset --hard HEAD~2
ceci supprime l'original A
et B
,1 alors maintenant, vous avez:
...-o--o <-- HEAD=branch1
\
o--C--A'-B' <-- branch2
Maintenant, vous avez juste besoin de re-check-out branch2
pour continuer à y travailler.
c'est Ce que git rebase
fait: il" déplace " les commits (mais pas en les déplaçant, car il ne peut pas: en git, une commit ne peut jamais être changée, donc même changer le parent-ID nécessite de le copier en une nouvelle commit légèrement différente).
En d'autres termes, tandis que git cherry-pick
est un système automatisé de diff-et-refaire de commit,git rebase
est un processus automatisé de refaire plusieurs engage, plus, à la fin, déplacer les étiquettes pour "oublier" ou cacher les originaux.
ci-dessus illustre le déplacement s'engage à partir d'une branche locale branch1
pour une autre branche locale branch2
, mais git utilise le exactement le même processus pour aller s'engage lorsque vous avez une distance de suivi de branche qui acquiert des nouveaux commits, quand tu fais un git fetch
(y compris les fetch
c'est la première étape de git pull
). Vous pourriez commencer par travailler sur la branchefeature
, qui a un amont de origin/feature
, et de faire une couple de validations de votre propre:
...-o <-- origin/feature
\
A--B <-- HEAD=feature
mais ensuite vous décidez que vous devriez voir ce qui s'est passé en amont, donc vous courez git fetch
,2 et, aha, quelqu'un en amont a écrit un commit C
:
...-o--C <-- origin/feature
\
A--B <-- HEAD=feature
a ce point vous pouvez simplement rebaser votre feature
A
et B
sur C
, donner:
...-o--C <-- origin/feature
\
A'-B' <-- HEAD=feature
ce sont des copies de votre original A
et B
, les originaux étant jetés (mais Voir note 1 de bas de page) après que les copies sont complètes.
parfois, il n'y a rien à reformater, c.-à-d. pas de travail que vous avez fait vous-même. Qui est, le graphe avant le fetch
ressembler à ceci:
...-o <-- origin/feature
`-- HEAD=feature
Si vous git fetch
et commit C
entre, cependant, il ne te reste que feature
branche pointant vers le vieux commit, alors que origin/feature
a progressé:
...-o--C <-- origin/feature
`---- <-- HEAD=feature
C'est là git merge --ff-only
: si vous demandez à fusionner votre branche courante feature
origin/feature
, git voit qu'il est possible de faire glisser la flèche vers l'avant, pour ainsi dire, de sorte que feature
points directement à commettre C
. Pas de fusion est nécessaire.
Si vous aviez votre propre commet A
et B
, cependant, et vous a demandé de faire fusionner ces C
, git ferait un réel, de fusion, d'en faire un nouveau commit de fusion M
:
...-o--C <-- origin/feature
\ `-_
A--B--M <-- feature
Ici --ff-only
arrêter et vous donnera une erreur. Rebase, d'un autre côté, peut copier A
et B
A'
et B'
et puis cacher le original A
et B
.
donc, en bref (ok, trop tard : -)), ils font simplement des choses différentes. Parfois, le résultat est le même, et parfois il ne l'est pas. Si c'est OK pour copier A
et B
, vous pouvez utiliser git rebase
; mais si il y a quelques bonnes raisons copier, vous pouvez utiliser git merge
peut-être --ff-only
, de fusionner ou de l'échec tant que de besoin.
1git conserve les originaux pendant un certain temps-normalement un mois dans ce cas-ci-mais les Cache. La façon la plus facile de les trouver est avec les "reflogs" de git, qui gardent une histoire d'où chaque branche pointait, et où HEAD
pointed, avant chaque changement qui a mis à jour la branche et / ou HEAD
.
éventuellement les entrées de l'historique reflog expirent, à quel moment ces propagations deviennent éligibles pour collecte des ordures.
2Ou, encore, vous pouvez utiliser git pull
, qui est un script de commodité qui commence pargit fetch
. Une fois que le fetch est fait, le script de commodité tourne soit git merge
ou git rebase
, dépend de la façon dont vous le configurez et l'exécutez.
Oui, il y a une différence. git merge --ff-only
échouera si elle ne peut pas avancer plus rapidement, et prend un commit (normalement une branche) pour fusionner. Il ne créera une commit de fusion que s'il ne peut pas aller de l'avant (c'est-à-dire qu'il ne le fera jamais avec --ff-only
).
git rebase
réécrit l'histoire de la branche courante, ou peut être utilisé pour rebaser une branche sur une branche. Dans ce cas, il ne créera pas de commit de fusion parce qu'il rebase, plutôt que de fusionner.
Oui, --ff-only
échouera toujours où une plaine git merge
échouerait, et pourrait échouer où une simple git merge
serait un succès. C'est le point - si vous essayez de garder une histoire linéaire, et la fusion ne peut pas être fait de cette façon, vous voulez échec.
une option qui ajoute des cas d'échec à une commande n'est pas inutile; c'est une façon de valider une condition préalable, donc si l'état actuel du système n'est pas ce que vous attendez, vous n'aggravez pas le problème.