412 votes

Ce que signifie exactement git ' s « rebase--preserve-fusions » (et pourquoi ?)

Documentation de Git pour la commande de rebasage est très brève :

Donc ce qui arrive réellement lorsque vous utilisez--preserve-fusions ? En quoi est-elle différente de celui par défaut (sans cet indicateur) ? Que signifie « recréer » une fusion, etc...

510voto

Chris Points 2318

Une réponse provisoire. Affichage de fournir au moins une idée de ce qui se passe.

Comme avec un git rebase, git --preserve-fusionne identifie d'abord une liste de commits réalisés dans un le cadre de la validation graphique, puis les replays ceux qui s'engage sur le dessus de l'autre partie. Les différences avec --preserve-fusionne préoccupation qui s'engage sont sélectionnés pour la relecture et de la façon que la relecture des œuvres de fusion s'engage.

Pour être plus explicite sur les différences principales entre le normal et de fusion de la préservation de rebase:

  • Fusion de la préservation de rebase est prêt à rejouer (certains) de fusion s'engage, alors que la normale rebase ignore complètement fusion s'engage.
  • Parce qu'il est disposé à répondre de fusion s'engage, de fusion et de la préservation de rebase a définir ce que cela signifie pour rejouer une fusion s'engager, et de traiter avec quelques rides supplémentaires
    • La partie la plus intéressante, sur le plan conceptuel, est peut-être dans le fait de ramasser ce que le nouveau commit de fusion et les parents devraient être.
    • La relecture de fusion s'engage également exiger explicitement de vérifier notamment s'engage ("git checkout <désiré premier parent>"), alors que la normale rebase n'avez pas à vous inquiéter à ce sujet.
  • Fusion de la préservation de rebase considère profond ensemble de commits pour la relecture:
    • En particulier, il ne prend en compte la relecture des commits effectués depuis la dernière fusion de la base de(s) -- c'est à dire la plus récente de l' époque, les deux branches ont divergé --, alors que la normale rebase pourrait relecture s'engage à revenir à la première fois que les deux branches ont divergé.
    • Pour être provisoire et incertaine, je crois que c'est finalement un moyen de dépister une relecture de "la vieille s'engage" qui ont déjà été "intégrés" une fusion de commettre.

Je vais d'abord essayer de décrire "suffisamment exactement" ce que rebase --preserve-fusionne, et puis il y aura quelques exemples. On peut bien sûr commencer avec les exemples, si cela vous semble plus utile.

L'Algorithme de la "Brève"

Si vous voulez vraiment obtenir dans les mauvaises herbes, de télécharger de la source de git et d'explorer le fichier "git-rebase--interactive.sh". (Rebase ne fait pas partie de Git C de base, mais plutôt est écrit en bash. Et, en coulisses, il partage de code avec "rebase interactif".)

Mais ici, je vais esquisse de ce que je pense est l'essence. Afin de réduire le nombre de choses à penser, j'ai pris quelques libertés. (par exemple, je n'ai pas essayer de le capturer avec une précision de 100% l'ordre précis dans lequel les calculs lieu, et d'ignorer certains moins centrale-semblant sujets, par exemple, que faire à propos des commits qui ont déjà été cerise cueillies entre les branches).

Tout d'abord, notez qu'un non-fusion de la préservation de rebase est plutôt simple. C'est plus ou moins:

Find all commits on B but not on A ("git log A..B")
Reset B to A ("git reset --hard A") 
Replay all those commits onto B one at a time in order.

Rebase --preserve-fusionne est relativement compliqué. Voici aussi simple que je l'ai été en mesure de le faire sans perdre les choses qui semblent assez important:

Find the commits to replay:
  First find the merge-base(s) of A and B (ie the most recent common ancestor(s))
    This (these) merge base(s) will serve as a root/boundary for the rebase.
    In particular, we'll take its (their) descendants and replay them on top of new parents
  Now we can define C, the set of commits to reply. In particular, it's those commits:
    1) reachable from B but not A (as in a normal rebase), and ALSO
    2) descendants of the merge base(s)
  If we ignore cherry-picks and other cleverness preserve-merges does, it's more or less:
    git log A..B --not $(git merge-base --all A B)
Replay the commits:
  Create a branch B_new, on which to replay our commits.
  Switch to B_new (ie "git checkout B_new")
  Proceeding parents-before-children (--topo-order), replay each commit c in C on top of B_new:
    If it's a non-merge commit, cherry-pick as usual (ie "git cherry-pick c")
    Otherwise it's a merge commit, and we'll construct an "equivalent" merge commit c':
      To create a merge commit, its parents must exist and we must know what they are.
      So first, figure out which parents to use for c', by reference to the parents of c:
        For each parent p_i in parents_of(c):
          If p_i is one of the merge bases mentioned above:
            # p_i is one of the "boundary commits" that we no longer want to use as parents
            For the new commit's ith parent (p_i'), use the HEAD of B_new.
          Else if p_i is one of the commits being rewritten (ie if p_i is in R):
            # Note: Because we're moving parents-before-children, a rewritten version
            # of p_i must already exist. So reuse it:
            For the new commit's ith parent (p_i'), use the rewritten version of p_i.
          Otherwise:
            # p_i is one of the commits that's *not* slated for rewrite. So don't rewrite it
            For the new commit's ith parent (p_i'), use p_i, i.e. the old commit's ith parent.
      Second, actually create the new commit c':
        Go to p_1'. (i.e. "git checkout p_1'", p_1' being the "first parent" we want for our new commit)
        Merge in the other parent(s):
          For a typical two-parent merge, it's just "git merge p_2'".
          For an octopus merge, it's "git merge p_2' p_3' p_4' ...".
        Switch (ie "git reset") B_new to the current commit (ie HEAD), if it's not already there
  Change the label B to apply to this new branch, rather than the old one. (ie "git reset --hard B")

Rebase avec une "--sur C" argument devrait être très similaire. Tout juste plutôt que de commencer à s'engager de lecture à la TÊTE de B, vous commencez à commettre la lecture à la TÊTE de C à la place. (Et l'utilisation de C_new au lieu de B_new.)

Exemple 1

Prenez, par exemple, s'engager graphique

  B---C <-- master
 /                     
A-------D------E----m----H <-- topic
         \         /
          F-------G

m est une fusion s'engager avec les parents E et G.

Supposons que nous relocalisée sujet (H) sur le dessus de maître (C) en utilisant une normale, non-fusion de la préservation de rebase. (Par exemple, la caisse de sujet, rebase maître.) Dans ce cas, git sélectionnez la suite s'engage pour la relecture:

  • choisir D
  • choisir E
  • pick F
  • pick G
  • pick H

et puis la mise à jour de la validation graphique comme suit:

  B---C <-- master
 /     \                
A       D'---E'---F'---G'---H' <-- topic

D' est le relus équivalent de D, etc..)

Notez que l'opération de fusion s'engager m n'est pas sélectionné pour la relecture.

Si nous avons plutôt fait un --preserve-fusionne rebaes de H sur le dessus de C. (Par exemple, checkout sujet; rebase --preserve-fusionne maître.) Dans ce nouveau cas, git sélectionnez la suite s'engage pour la relecture:

  • choisir D
  • choisir E
  • pick F
  • pick G
  • choisissez Fusionner la branche "sous-rubrique" dans la rubrique
  • pick H

Maintenant m a été choisi pour la relecture. Notez également que la fusion des parents E et G ont été choisi pour l'inclusion avant de fusionner commettre m.

Voici le résultat de commettre graphique:

 B---C <-- master
/     \                
A      D'-----E'----m'----H' <-- topic
        \          / 
         F'-------G'

Encore une fois, D' est une cerise cueillies (c'est à dire recréé) version de D. de Même pour E', etc.. Tous les s'engager à ne pas sur le master a été relus. Les deux E et F (de la fusion des parents de m) ont été recréé E' et F' servir en tant que parents de m'.

Exemple 2

Contrairement à la normale rebase, de fusion et de la préservation de rebase pouvez créer plusieurs les enfants de l'amont de la tête.

Par exemple, pensez à:

  B---C <-- master
 /                     
A-------D------E---m----H <-- topic
 \                 |
  ------- F-----G--/ 

Si nous rebase H (sujet) sur le dessus de C (master), puis il s'engage choisi pour rebase sont: - choisir D - pick E - pick F - pick G - pick m - pick H

Et le résultat est comme suit:

  B---C  <-- master
 /    | \                
A     |  D'----E'---m'----H' <-- topic
       \            |
         F'----G'---/

Exemple 3

Dans les exemples ci-dessus, les deux la fusion s'engager et de ses deux parents sont relus s'engage, plutôt que les parents d'origine que la fusion d'origine commettre avoir. Cependant, en d'autres rebases un rejoué commit de fusion peuvent se retrouver avec des parents qui étaient déjà dans le s'engager graphique avant la fusion.

Par exemple, pensez à:

  B--C---D <-- master
 /    \                
A---E--m------F <-- topic

Si nous rebase sujet sur master (en conservant les fusions), puis l'engage à relire sera

  • choisissez fusionner commettre m
  • pick F

Le nouveau commettre graphique ressemble à:

                     B--C--D <-- master
                    /       \             
                   A-----E---m'--F'; <-- topic

Ici relus fusion commettre m' obtient les parents qui pré-existait dans le commit graphique, à savoir D (la TÊTE de master) et E (l'un des parents de la fusion d'origine commettre m).

Exemple 4

Fusion de la préservation de rebase peuvent se confondre dans certains "vide commettre" des cas. Au moins c'est vrai que certaines anciennes versions de git (par exemple 1.7.8.)

Prendre ce commit graphique:

                   A--------B-----C-----m2---D <-- master
                    \        \         /
                      E--- F--\--G----/
                            \  \
                             ---m1--H <--topic

Notez que les deux commettre de m1 et de m2 devrait inclure toutes les modifications à partir de B et F.

Si nous essayons de faire "git --preserve-fusionne" rebase de H (sujet) sur D (maître), puis la suite s'engage sont choisis pour la relecture:

  • choisir m1
  • pick H

Notez que les modifications (B, F) - en m1 devrait déjà être intégré dans D. (Ces changements devraient déjà être intégrées m2, parce m2 fusionne les deux enfants de a, B et F.) Donc, sur le plan conceptuel, la relecture de m1 sur le dessus de D doit probablement être un no-op ou créer un vide s'engager (c'est à dire celui où la diff entre les révisions successives est vide).

Au lieu de cela, cependant, git peut rejeter la tentative de relecture de m1 sur le dessus de D. Vous pouvez obtenir une erreur comme suit:

error: Commit 90caf85 is a merge but no -m option was given.
fatal: cherry-pick failed

Elle ressemble on a oublié de passer un drapeau pour git, mais le problème sous-jacent est que git dégoûts de la création de vide s'engage.

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