118 votes

git pull *après* git rebase ?

J'ai une branche fonctionnelle et une branche principale.

La branche principale a évolué et je veux que ces mises à jour divergent le moins possible de la branche principale.

Alors je git pull dans les deux branches, git checkout feature/branch et enfin git rebase master .

Maintenant, ici, je m'attends à ce que tout fonctionne bien. ou des conflits apparaissent et je dois les résoudre avant de continuer à rebaser jusqu'à ce que tous les commits master soient réappliqués avec succès sur la branche feature.

Maintenant, ce qui s'est réellement passé dans mon cas est quelque chose que je ne comprends pas :

$>git rebase master
First, rewinding head to replay your work on top of it...
Applying: myFirstCommitDoneOnTheBranch
Applying: myOtherCommitDoneOnTheBranch
$>git status
On branch feature/branch
Your branch and 'origin/feature/feature' have diverged,
and have 27 and 2 different commits each, respectively.
  (use "git pull" to merge the remote branch into yours)
nothing to commit, working tree clean
$>git pull
*load of conflicts*

Maintenant, autant je peux comprendre la charge des conflits après le tirage, autant je ne comprends pas la nécessité d'un tirage. Logiquement, il devrait revenir à master quand il a été branché, sauvegarder les commits faits sur la branche, transférer le dernier commit sur master et ensuite appliquer les commits sauvegardés.

Je ne comprends pas à quoi servent les Applying Le message se réfère à : ce qui applique les commits sur quelle version ?

153voto

Enrico Campidoglio Points 17157

tl;dr Vous devez mettre à jour les deux master y feature con git pull y git pull --rebase avant rebasement feature sur le dessus de master . Il n'est pas nécessaire de faire un git pull après vous avez rebasé votre feature sur le dessus de master .

Avec votre flux de travail actuel, la raison pour laquelle git status vous dit ça :

Votre branche et votre "origine/caractéristique" ont divergé, et ont chacune 27 et 2 commits différents, respectivement.

c'est parce que votre rebasement feature La branche a maintenant 25 de nouveaux commits qui ne sont pas accessibles à partir de origin/feature (puisqu'ils proviennent de la rebase sur master ) plus 2 s'engage à ce que sont accessible à partir de origin/feature mais ont des identifiants de livraison différents. Ces commits contiennent les mêmes changements (c'est-à-dire qu'ils sont équivalent de patch ) mais ils ont des hachages SHA-1 différents parce qu'ils sont basés sur un commit différent dans le fichier origin/feature que celui sur lequel vous les avez rebasés dans votre référentiel local.

Voici un exemple. Supposons que votre historique soit le suivant avant en faisant git pull sur master :

A - B - C (master)
         \
          D - E (feature)

Après git pull , master a obtenu un engagement F :

A - B - C - F (master, origin/master)
         \
          D - E (feature)

A ce moment-là, vous rebasez feature sur le dessus de master qui s'applique D y E :

A - B - C - F (master, origin/master)
             \
              D - E (feature)

Pendant ce temps, la branche distante origin/feature est toujours basé sur le commit C :

A - B - C - F (master, origin/master)
         \   \
          \   D' - E' (feature)
           \
             D - E (origin/feature)

Si vous faites un git status sur feature Git vous dira que votre feature a divergé de origin/feature con 3 ( F , D' , E' ) et 2 ( D , E ), respectivement.

Notez que D' y E' contiennent les mêmes modifications que D y E mais ont des identifiants de livraison différents parce qu'ils ont été refondus au dessus de F .

La solution consiste à faire git pull sur les deux master y feature avant rebasement feature sur master . Cependant, comme vous pouvez avoir des commits sur feature que vous n'avez pas encore poussé vers origin que tu voudrais faire :

git checkout feature && git pull --rebase

pour éviter de créer un fusionner commettre entre origin/feature et votre feature .

Le point sur les conséquences du rebasage :

À la lumière de ce commentaire je me suis étendu sur les branches divergentes. La raison pour laquelle git status rapporte que feature y origin/feature diverger après le rebasement est dû au fait que le rebasement apporte de nouveaux commits à la base de données. feature De plus, il réécrites les commits qui ont été précédemment poussés vers origin/feature .

Considérez la situation après l'attraction mais avant le rebasement :

A - B - C - F (master)
         \
          D - E (feature, origin/feature)

A ce stade, feature y origin/feature pointent vers le même commit E -en d'autres termes, ils sont dans " sync ". Après avoir rebasé feature sur le dessus de master l'histoire ressemblera à ça :

A - B - C - F (master)
         \   \
          \   D' - E' (feature)
           \
             D - E (origin/feature)

Comme vous pouvez le voir, feature y origin/feature ont divergent leur ancêtre commun étant l'engagement C . Cela s'explique par le fait que feature contient maintenant le nouveau commit F de master plus D' y E' (lu comme " D prime " et " E prime ") qui sont des engagements D y E appliqué sur le dessus de F . Bien qu'ils contiennent les mêmes modifications, Git les considère comme différents car ils ont des ID de commit différents. En attendant, origin/feature références fixes D y E .

A ce stade, vous avez histoire réécrite vous avez modifié des commits existants en les rebasant, créant ainsi de "nouveaux" commits.

Maintenant, si tu devais exécuter git pull sur feature c'est ce qui se passerait :

A - B - C - F (master)
         \   \
          \   D' - E'- M (feature)
           \         /
             D - E - (origin/feature)

Depuis git pull fait git fetch + git merge ce qui entraînerait la création du commit de fusion. M dont les parents sont E' y E .

Si, à la place, vous avez exécuté git pull --rebase (c'est-à-dire, git fetch + git rebase ) alors Git le ferait :

  1. Déplacements feature de s'engager C (l'ancêtre commun de feature y origin/feature )
  2. Appliquer D y E de origin/feature
  3. Appliquer F , D' y E'

Cependant, en remarquant que D' y E' contiennent les mêmes modifications que D y E Git les rejetterait tout simplement, ce qui donnerait un historique ressemblant à ceci :

A - B - C - F (master)
         \   
          D - E - F' (feature)
              ^
             (origin/feature)

Remarquez comment l'engagement F , précédemment accessible à partir de feature a été appliqué sur le dessus de origin/feature ce qui entraîne F' . A ce stade, git status vous dirait ceci :

Votre branche est en avance sur 'origin/feature' de 1 commit.

Cet engagement étant, bien sûr, F' .

41voto

ashmaroli Points 2514

Si les versions distantes de master y feature/branch sont à jour individuellement, il suffit alors de réinitialiser votre branche locale de fonctionnalité

git checkout feature/branch
git fetch origin feature/branch
git reset --hard origin/feature/branch

alors, si vous voulez apporter des changements dans les master branche,

git rebase origin/master

8voto

cmaster Points 7460

Lorsque vous avez rebasé votre branche de fonctionnalité sur master, vous avez créé un tas de nouveaux commits. Cependant, votre origin/feature La branche pointe toujours vers les anciennes. C'est la situation après le rebasement :

C' (feature)
B'
A'
* (master, origin/master)
*
*
| C (origin/feature)
| B
| A
|/
* some base commit

Alors que l'engagement A' contient un ensemble de changements similaires à ceux du commit A mais il ne s'agit en aucun cas du même engagement. Il contient un arbre différent, et a un parent différent.

Maintenant, quand vous essayez de tirer feature Encore une fois, vous essayez de créer cette histoire :

* (feature)
|\
C'|
B'|
A'|
* | (master, origin/master)
* |
* |
| C (origin/feature)
| B
| A
|/
* some base commit

Vous fusionnez deux branches qui ont introduit des changements très similaires, mais différents. Cela va créer une tonne de conflits, en plus d'être totalement inutile.

Ce que vous devez faire est d'informer votre repo amont de la refonte en utilisant git push -f . Cela supprimera l'ancienne histoire et la remplacera par celle qui a été réécrite. .

L'alternative est d'éviter d'utiliser git rebase sur des branches que vous avez déjà poussées vers un autre référentiel, ou éviter git rebase tout à fait. C'est l'approche la plus propre : Elle aboutit à l'histoire telle qu'elle s'est passée, au lieu de raconter des mensonges sur l'histoire telle qu'elle s'est passée. git rebase fait. C'est en tout cas ce que je préfère.

4voto

Greg Tarsa Points 1232

En have 27 and 2 different commits each vous dit que vous avez maintenant 27 nouveaux commits de master et 2 nouveaux commits dans votre branche qui ne sont pas présents dans origin/<yourbranch> .

Parce que origin/<yourbranch> a été massivement modifié par le rebasement, il n'a plus de base commune avec origin/<yourbranch> . Par conséquent, vous ne voulez pas tirer les changements de origin/<yourbranch> après le rebasement, parce que, comme vous le voyez, tout le H*** se déchaîne.

Si vous savez qu'il y a des changements dans origin/<yourbranch> dont vous avez besoin dans votre branche locale, puis tirez-les avant de rebaser.

Si vous êtes sûr que personne n'a changé origin/<yourbranch> depuis votre dernier push (un pari sûr s'il s'agit de votre propre branche de fonctionnalité), vous pouvez utiliser push --force pour les remettre en synchronisation. Puis origin/<yourbranch> aura à nouveau la même base que votre branche locale et cette base contiendra toutes les dernières versions de master changements.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X