web-dev-qa-db-fra.com

git pull * après * git rebase?

J'ai une branche principale et une branche principale.

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

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

Maintenant, ici, je m'attends soit à ce que tout fonctionne correctement ou que des conflits apparaissent et que je dois les résoudre avant de continuer à rebaser jusqu'à ce que toutes les validations principales soient réappliquées avec succès sur la branche de fonctionnalité.

Maintenant, ce qui m'est vraiment arrivé 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 que je puisse comprendre, il charge de conflits après le pull; Je ne comprends pas la nécessité d'un pull. Logiquement, il devrait revenir en arrière au maître quand il a été créé, sauvegarder les validations effectuées sur la branche, transférer au dernier commit sur maître, puis appliquer les validations enregistrées.

Je ne comprends pas à quoi le message Applying fait référence: quelle est l'application des commits sur quelle version?

22
tisek

tl; dr Vous devez mettre à jour master et feature avec git pull et git pull --rebasebefore rebasant feature par-dessus master. Il n'est pas nécessaire de faire ungit pullaprès que vous ayez rebasé votre branche feature au-dessus de master.

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

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

c'est parce que votre branche feature rebasée a maintenant 25 nouveaux commits qui ne sont pas accessibles depuis Origin/feature (puisqu'ils sont issus de la base de base master) plus 2 commet que sont accessible depuis Origin/feature mais ont différents ID de validation. Ces commits contiennent les mêmes modifications (c'est-à-dire équivalent du correctif) mais ils ont des hachages SHA-1 différents, car ils sont basés sur un commit différent de Origin/feature que celui sur lequel vous les avez rebasés dans votre référentiel local. .

Voici un exemple. Supposons qu'il s'agit de votre historique avant faisant git pull sur master:

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

Après git pull, master a reçu commit F:

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

À ce stade, vous redéfinissez feature au-dessus de master, qui s'appliqueD et E:

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

Entre-temps, la branche distante Origin/feature est toujours basée sur 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 branche feature a divergé de Origin/feature avec 3 (F, D', E') et 2 (D, E), respectivement.

Notez que D' et E' contiennent les mêmes modifications que D et E mais ont des ID de validation différents car ils ont été redéfinis au-dessus de F.

La solution consiste à faire git pull à la fois sur master et featureavant rebasant feature sur master. Cependant, étant donné que vous pouvez avoir des validations sur feature que vous n'avez pas encore poussées vers Origin, vous voudrez faire:

git checkout feature && git pull --rebase

pour éviter de créer un fusion fusionner entre Origin/feature et votre feature locale.

Mise à jour sur les conséquences du rebasement:

À la lumière de ce commentaire , j’ai développé les branches divergentes. La raison pour laquelle git status rapporte que feature et Origin/featuredivergence après la modification de la base est due au fait que la redéfinition de la base amène de nouveaux commits à feature, auxquels s'ajoute réécrit les validations précédemment transférées à Origin/feature .

Considérons la situation après le pull mais avant le rebase:

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

À ce stade, feature et Origin/feature pointent vers le même commit E - en d'autres termes, ils sont dans "sync}". Après rebasement de feature par-dessus master, l’historique ressemblera à ceci:

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

Comme vous pouvez le constater, feature et Origin/feature ont divergé, leur ancêtre commun étant commit C. Ceci est dû au fait que feature contient maintenant les nouveaux commit F de master plus D' et E' (comme "D prime" et "E prime") qui sont des validations D et E appliquées en haut de F. Même s'ils contiennent les mêmes modifications, Git considère qu'elles sont différentes car elles ont des identifiants de validation différents. Pendant ce temps, Origin/feature fait toujours référence à D et E.

À ce stade, vous avez historique réécrit: vous avez modifié les commits existants en les rebasonnant, ce qui en crée effectivement de "nouveaux".

Maintenant, si vous exécutiez git pull sur feature, voici ce qui se produirait:

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

Puisque git pull fait git fetch + git merge, cela entraînerait la création du commit de fusion M, dont les parents sont E' et E.

Si vous exécutiez plutôt git pull --rebase (c'est-à-dire git fetch + git rebase), alors Git:

  1. Déplacer feature pour commettre C (ancêtre commun de feature et Origin/feature)
  2. Appliquer D et E à partir de Origin/feature
  3. Appliquer F, D' et E'

Cependant, remarquant que D' et E' contiennent les mêmes modifications que D et E, Git les écarterait simplement, ce qui donnerait un historique ressemblant à ceci:

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

Remarquez comment commit F, précédemment accessible à partir de feature, s’applique au-dessus de Origin/feature, ce qui donne F'. À ce stade, git status vous dira ceci:

Votre branche est en avance sur 'Origine/fonctionnalité' de 1 commit.

Ce commit étant bien sûr, F'.

54
Enrico Campidoglio

Si les versions distantes de master et feature/branch sont à jour individuellement, réinitialisez simplement votre branche de fonctions locale

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

alors si vous voulez apporter des modifications dans la branche master,

git rebase Origin/master
6
ashmaroli

Le have 27 and 2 different commits each vous indique 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>.

Étant donné que Origin/<yourbranch> a été modifié massivement par la base, celle-ci n'a plus de base commune avec Origin/<yourbranch>. Par conséquent, vous ne voulez pas extraire les modifications de Origin/<yourbranch> après la base, car, comme vous le voyez, tout H *** se détache.

Si vous savez que Origin/<yourbranch> contient les modifications dont vous avez besoin dans votre branche locale, retirez-les avant de modifier votre base.

Si vous êtes sûr que personne n'a changé de Origin/<yourbranch> depuis votre dernier Push (une valeur sûre s'il s'agit de votre propre branche), vous pouvez utiliser Push --force pour les synchroniser à nouveau. Ensuite, Origin/<yourbranch> aura à nouveau la même base que votre branche locale et cette base contiendra toutes les dernières modifications master.

0
Greg Tarsa

Lorsque vous avez modifié la base de votre branche de fonctionnalité par rapport au maître, vous avez créé un ensemble de nouveaux commits. Cependant, votre branche Origin/feature pointe toujours sur les anciennes. Voici la situation après le rebase:

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

Alors que commit A' contient un jeu de modifications similaire à commit A, il ne s’agit en aucun cas du même commit. Il contient un arbre différent et a un parent différent.

Maintenant, lorsque vous essayez de tirer à nouveau feature, vous essayez de créer cet historique:

* (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 et très différents. Ceci est voué à créer une tonne de conflits, en plus d'être totalement inutile.

Ce que vous devez faire, c'est informer votre référent en amont de la base en utilisant git Push -f. Cela va perdre l'ancienne histoire, et le remplacer par celui réécrit .

L'alternative consiste à éviter d'utiliser git rebase sur les branches que vous avez déjà insérées dans un autre référentiel, ou à éviter git rebase complètement. C’est l’approche plus propre : Il en résulte que l’histoire est telle qu’elle s’est produite, au lieu de dire des mensonges sur l’histoire comme le fait git rebase. C'est du moins ce que je préfère.

0
cmaster