git rebase, en gardant une trace de 'local' et 'remote'

174

Lorsque je fais un rebase git, j'ai souvent du mal à comprendre ce qui se passe avec le «local» et le «distant» lors de la résolution de conflits. J'ai parfois l'impression qu'ils changent de camp d'un engagement à l'autre.

C'est probablement (définitivement) parce que je n'ai toujours pas bien compris.

Lors du rebasage, qui est «local» et qui est «distant»?

(J'utilise P4Merge pour résoudre les conflits)

Benjol
la source
Est-il possible que cette lecture vous aide? Le reste du tutoriel est également très utile ....
ivans
Une autre excellente ressource git .
Inconnu
Est-ce que stackoverflow.com/questions/2959443/... vous aiderait? (pas pour la git svnpartie ` `, juste pour la git rebasepartie ' ')
VonC
@VonC, oui, c'est exactement ça. Si vous voulez copier le morceau pertinent de votre réponse ici, je le coche (je le ferai vraiment cette fois, je le promets!)
Benjol
d'accord ... je vais mordre;) Extraits pertinents postés.
VonC le

Réponses:

244

TL, DR;

Pour résumer (comme le commente Benubird ), quand:

git checkout A
git rebase   B    # rebase A on top of B
  • localest B(rebase sur ),
  • remote est A

Et:

git checkout A
git merge    B    # merge B into A
  • localest A(fusionner dans ),
  • remote est B

Un rebase bascule ours(branche actuelle avant le début du rebase) et theirs(la branche au-dessus de laquelle vous voulez rebase).


kutschkem souligne que, dans un contexte de mergetool d'interface graphique :

  • local fait référence aux commits partiellement rebasés : " ours" (la branche amont)
  • remote fait référence aux modifications entrantes : " theirs" - la branche actuelle avant le rebase.

Voir les illustrations dans la dernière partie de cette réponse.


Inversion lors du rebase

La confusion pourrait être liée à l' inversion de ourset theirspendant un rebase .
(extraits pertinents)

git rebasepage de manuel :

Notez qu'une fusion de rebase fonctionne en rejouant chaque commit à partir de la branche de travail au-dessus de la <upstream>branche.

Pour cette raison, lorsqu'un conflit de fusion se produit:

  • le côté signalé comme ` ours` est la série rebasée jusqu'à présent, commençant par <upstream>,
  • et ' theirs' est la branche active. En d'autres termes, les côtés sont échangés.

Inversion illustrée

Sur une fusion

x--x--x--x--x(*) <- current branch B ('*'=HEAD)
    \
     \
      \--y--y--y <- other branch to merge

, nous ne changeons pas la branche actuelle 'B', donc ce que nous avons est toujours ce sur quoi nous travaillions (et nous fusionnons depuis une autre branche)

x--x--x--x--x---------o(*)  MERGE, still on branch B
    \       ^        /
     \     ours     /
      \            /
       --y--y--y--/  
               ^
              their

Sur un rebase:

Mais sur un rebase , on change de côté car la première chose qu'un rebase fait est de vérifier la branche amont! (pour rejouer les commits actuels dessus)

x--x--x--x--x(*) <- current branch B
    \
     \
      \--y--y--y <- upstream branch

A git rebase upstreampassera d'abord HEADde B à la branche amont HEAD(d'où le changement de «le nôtre» et «le leur» par rapport à la branche de travail «actuelle» précédente).

x--x--x--x--x <- former "current" branch, new "theirs"
    \
     \
      \--y--y--y(*) <- upstream branch with B reset on it,  
                       new "ours", to replay x's on it

, puis le rebase rejouera 'leurs' commits sur la nouvelle branche B 'notre':

x--x..x..x..x <- old "theirs" commits, now "ghosts", available through reflogs
    \
     \
      \--y--y--y--x'--x'--x'(*) <-  branch B with HEAD updated ("ours")
               ^
               |
        upstream branch

Remarque: la notion «amont» est l'ensemble référentiel de données (un tout repo ou, comme ici, une branche, qui peut être une branche locale ) à partir duquel les données sont lues ou auxquelles de nouvelles données sont ajoutées / créées.


' local' et ' remote' contre ' mine' et ' theirs'

Pandawood ajoute dans les commentaires :

Pour moi, la question demeure, qui est "local" et qui est "distant" (puisque les termes "nôtre" et "leur" ne sont pas utilisés lors du rebasage dans git, y faire référence semble juste rendre la réponse plus confuse) .

GUI git mergetool

kutschkem ajoute, et à juste titre:

Lors de la résolution des conflits, git dira quelque chose comme:

local: modified file and remote: modified file. 

Je suis tout à fait sûr que la question vise à définir à ce stade le terme local et distant. À ce stade, il me semble d'après mon expérience que:

  • local fait référence aux commits partiellement rebasés : " ours" (la branche amont)
  • remote fait référence aux modifications entrantes : " theirs" - la branche actuelle avant le rebase.

git mergetoolmentionne en effet «local» et «distant» :

Merging:
f.txt

Normal merge conflict for 'f.txt':
  {local}: modified file
  {remote}: modified file
Hit return to start merge resolution tool (kdiff3):

Par exemple, KDiff3 serait afficher la résolution de fusion comme ceci :

kdiff3

Et meld l' afficherait aussi :

Meld diff

Idem pour VimDiff , qui affiche :

Appelez Vimdiff en tant qu'outil de fusion avec git mergetool -t gvimdiff. Les versions récentes de Git invoquent Vimdiff avec la disposition de fenêtre suivante:

+--------------------------------+
| LOCAL  |     BASE     | REMOTE |
+--------------------------------+
|             MERGED             |
+--------------------------------+
  • LOCAL:
    Un fichier temporaire contenant le contenu du fichier sur la branche courante.
  • BASE:
    Un fichier temporaire contenant la base commune pour la fusion.
  • REMOTE:
    Un fichier temporaire contenant le contenu du fichier à fusionner.
  • MERGED:
    Le fichier contenant les marqueurs de conflit.

Git a effectué autant de résolution automatique des conflits que possible et l'état de ce fichier est une combinaison des deux LOCALet REMOTEavec des marqueurs de conflit entourant tout ce que Git n'a pas pu résoudre lui-même.
Le mergetooldoit écrire le résultat de la résolution dans ce fichier.

VonC
la source
13
Pour moi, la question demeure, qui est "local" et qui est "distant" (puisque les termes "nôtre" et "leur" ne sont pas utilisés lors du rebasage dans git, y faire référence semble juste rendre la réponse plus confuse) . La question est "qui est local et qui est distant" - donc une réponse nécessite sûrement de mentionner les mots "local" et "distant"
PandaWood
@PandaWood: "local" est "branche courante" (qui devient "la leur"), "remote" est "branche amont" (qui devient "la nôtre").
VonC le
3
Donc, pour résumer: quand votre git checkout A; git rebase Blocal est B, distant est A. Tout ce que j'avais besoin de savoir ...
Benubird
1
git est un tel clusterfk d'utilisabilité. cela n'a aucun sens: lorsque vous git checkout A; git rebase Best locale B, est à distance A . Si je checkout Apuis je suis actuellement à la recherche les fichiers tels qu'ils existent sur A, comment est - ce de quelque façon que la distance ? (Je ne dis pas que Benubird a tort; je dis que git a un UX stupide)
Rafa
1
@VonC sûr; mon point (éloquent) est qu'il ne devrait pas prendre la lecture de documentation, regarder des diagrammes et avoir à parcourir StackOverflow. Si seulement la commande donnait une rétroaction claire et sans ambiguïté. Par exemple, au lieu de local / distant / leur / le nôtre / le mien / le vôtre, affichez simplement {branch A}et / {branch B}ou similaire.
Rafa
45

La ligne du bas

git rebase

  • LOCAL = la base que vous êtes rebasage sur
  • REMOTE = les commits que vous montez en haut

git merge

  • LOCAL = la branche d'origine dans laquelle vous fusionnez
  • REMOTE = l'autre branche dont vous fusionnez les commits

En d'autres termes, LOCAL est toujours l'original, et REMOTE est toujours le gars dont les commits n'étaient pas là avant, car ils sont fusionnés ou rebasés par-dessus

Prouve le!

Certainement. Ne me croyez pas sur parole! Voici une expérience facile que vous pouvez faire pour voir par vous-même.

Tout d'abord, assurez-vous que git mergetool est correctement configuré. (Sinon, vous ne liriez probablement pas cette question de toute façon.) Trouvez ensuite un répertoire dans lequel travailler.

Configurez votre référentiel:

md LocalRemoteTest
cd LocalRemoteTest

Créez un commit initial (avec un fichier vide):

git init
notepad file.txt  (use the text editor of your choice)
  (save the file as an empty file)
git add -A
git commit -m "Initial commit."

Créez un commit sur une branche qui n'est pas master:

git checkout -b notmaster
notepad file.txt
  (add the text: notmaster)
  (save and exit)
git commit -a -m "Add notmaster text."

Créez un commit sur la branche master:

git checkout master
notepad file.txt
  (add the text: master)
  (save and exit)
git commit -a -m "Add master text."

gitk --all

À ce stade, votre référentiel devrait ressembler à ceci:

Référentiel avec un commit de base et deux branches à un commit

Maintenant, pour le test de rebase:

git checkout notmaster
git rebase master
  (you'll get a conflict message)
git mergetool
  LOCAL: master
  REMOTE: notmaster

Maintenant, le test de fusion. Fermez votre outil de fusion sans enregistrer les modifications, puis annulez le rebase:

git rebase --abort

Ensuite:

git checkout master
git merge notmaster
git mergetool
  LOCAL: master
  REMOTE: notmaster
git reset --hard  (cancels the merge)

Vos résultats doivent être les mêmes que ceux affichés en haut.

Ryan Lundy
la source
1
+1. Cela clarifie les aspects local/ remoteavec lesquels j'ai lutté dans ma propre réponse ci-dessus (qui parle plus de l'inversion de oursvs de theirstoute façon)
VonC
3

Je n'ai pas compris votre problème exactement, mais je pense que le diagramme suivant résout votre problème. (Rebase: Référentiel distant ---> Espace de travail)

http://assets.osteele.com/images/2008/git-transport.png

Source: Mon flux de travail Git

Chathuranga Chandrasekara
la source