‘git pull origin mybranch’ quitte le mybranch local N commet en avance sur l’origine. Pourquoi?

Je viens d’observer quelque chose d’étrange à propos de git pull , que je ne comprends pas.

Vendredi, j’ai travaillé sur une succursale locale. appelons ça mybranch . Avant de quitter le bureau, je l’ai poussé à l’origine (qui est mon repository github): git push origin mybranch .

Hier à la maison, j’ai pull ed mybranch sur mon ordinateur portable, fait un peu plus de codage, puis repoussé mes modifications vers github (origine).

Maintenant, je suis à nouveau au travail et j’ai essayé de transférer les modifications d’hier sur ma machine de travail (je n’ai rien changé dans le repo local de mon lieu de travail pendant le week-end):

 git pull origin mybranch 

cela a provoqué une fusion rapide, ce qui est bien. J’ai alors fait un git status , et il a dit:

 # On branch mybranch # Your branch is ahead of 'origin/mybranch' by 6 commits. # nothing to commit (working directory clean) 

Hein? Comment peut-il être 6 commettre à l’avance alors que je ne l’ai même pas touché au cours du week-end, ET simplement tiré de l’origine? Donc j’ai couru un git diff origin/mybranch et les diffs étaient exactement les 6 changements que je viens de retirer de la télécommande.

Je ne pouvais que “réparer” ceci en lançant git fetch origin :

 From git@github.com:me/project af8be00..88b0738 mybranch -> origin/mybranch 

Apparemment, mon repo local manquait d’objects de référence, mais comment cela peut-il être? Je veux dire, un pull fait déjà un fetch, et je n’ai pas travaillé sur autre chose que cette twig, donc un git fetch origin et un git fetch origin mybranch devraient avoir le même résultat?

Dois-je toujours utiliser git pull origin au lieu de git pull origin branchname ?

Je suis confus.

git pull appelle git fetch avec les parameters appropriés avant de fusionner les têtes récupérées explicitement (ou, si aucune twig distante n’est configurée pour la fusion) dans la twig en cours.

La syntaxe: git fetch est juste un nom de twig sans deux points est une extraction “one shot” qui ne fait pas une extraction standard de toutes les twigs suivies de la télécommande spécifiée mais récupère juste la twig nommée dans FETCH_HEAD .

Mise à jour: pour les versions de Git depuis 1.8.4, s’il existe une twig de suivi à distance qui suit la référence que vous avez demandé à récupérer, la twig de suivi sera désormais mise à jour par fetch . Cette modification a été apscope spécifiquement pour éviter la confusion provoquée par le comportement précédent.

Lorsque vous effectuez git pull , FETCH_HEAD est mis à jour comme ci-dessus, puis fusionné dans HEAD extrait, mais aucune des twigs de suivi standard du référentiel distant ne sera mise à jour (Git <1.8.4). Cela signifie que localement, il semblerait que vous soyez en avance sur la twig distante, alors que vous êtes en fait au courant.

Personnellement, je fais toujours git fetch suivi de git merge / car je peux voir des avertissements sur les mises à jour forcées avant de fusionner, et je peux prévisualiser mes fusions. Si j’ai utilisé git pull un peu plus comme je le faisais, je ferais la plupart du temps un git pull sans parameters, en me basant sur branch..remote et branch..merge pour “faire ce qui est bien”.

Que retourne git remote -v show lorsqu’il s’agit de l’origine?

Si l’origine pointe vers github, le statut doit être à jour et pas avant un repository à distance. Au moins, avec le Git1.6.5, j’utilise un test rapide.

Quoi qu’il en soit, pour éviter cela, définissez explicitement le repo distant de la twig maître:

 $ git config branch.master.remote yourGitHubRepo.git 

alors un git pull origin master , suivi d’un git status devrait renvoyer un statut propre (pas d’avance).
Pourquoi? car le maître d’origine get fetch (inclus dans le git pull origin master) ne mettrait pas simplement à jour FETCH_HEAD (comme l’explique Charles Bailey dans sa réponse ), mais mettrait également à jour la “twig principale distante” de votre repository Git local.
Dans ce cas, votre maître local ne semble plus être “en avance” sur le maître distant.


Je peux tester ceci, avec un git1.6.5:

Je crée d’abord un workrepo:

 PS D:\git\tests> cd pullahead PS D:\git\tests\pullahead> git init workrepo Initialized empty Git repository in D:/git/tests/pullahead/workrepo/.git/ PS D:\git\tests\pullahead> cd workrepo PS D:\git\tests\pullahead\workrepo> echo firstContent > afile.txt PS D:\git\tests\pullahead\workrepo> git add -A PS D:\git\tests\pullahead\workrepo> git commit -m "first commit" 

Je simule un repo GitHub en créant un repo nu (qui peut recevoir des impulsions de n’importe où)

 PS D:\git\tests\pullahead\workrepo> cd .. PS D:\git\tests\pullahead> git clone --bare workrepo github 

J’ajoute un modif à mon repo de travail, que je pousse au repository github (ajouté en tant que télécommande)

 PS D:\git\tests\pullahead> cd workrepo PS D:\git\tests\pullahead\workrepo> echo aModif >> afile.txt PS D:\git\tests\pullahead\workrepo> git ci -a -m "a modif to send to github" PS D:\git\tests\pullahead\workrepo> git remote add github d:/git/tests/pullahead/github PS D:\git\tests\pullahead\workrepo> git push github 

Je crée un repo à la maison, cloné de GitHub, dans lequel je fais quelques modifications, poussé vers GitHub:

 PS D:\git\tests\pullahead\workrepo> cd .. PS D:\git\tests\pullahead> git clone github homerepo PS D:\git\tests\pullahead> cd homerepo PS D:\git\tests\pullahead\homerepo> type afile.txt firstContent aModif PS D:\git\tests\pullahead\homerepo> echo aHomeModif1 >> afile.txt PS D:\git\tests\pullahead\homerepo> git ci -a -m "a first home modif" PS D:\git\tests\pullahead\homerepo> echo aHomeModif2 >> afile.txt PS D:\git\tests\pullahead\homerepo> git ci -a -m "a second home modif" PS D:\git\tests\pullahead\homerepo> git push github 

Je clone ensuite workrepo pour une première expérience

 PS D:\git\tests\pullahead\workrepo4> cd .. PS D:\git\tests\pullahead> git clone workrepo workrepo2 Initialized empty Git repository in D:/git/tests/pullahead/workrepo2/.git/ PS D:\git\tests\pullahead> cd workrepo2 PS D:\git\tests\pullahead\workrepo2> git remote add github d:/git/tests/pullahead/github PS D:\git\tests\pullahead\workrepo2> git pull github master remote: Counting objects: 8, done. remote: Compressing objects: 100% (4/4), done. remote: Total 6 (delta 1), reused 0 (delta 0) Unpacking objects: 100% (6/6), done. From d:/git/tests/pullahead/github * branch master -> FETCH_HEAD Updating c2763f2..75ad279 Fast forward afile.txt | Bin 46 -> 98 bytes 1 files changed, 0 insertions(+), 0 deletions(-) 

Dans ce repo, git status mentionne master geing avant « origin »:

 PS D:\git\tests\pullahead\workrepo5> git status # On branch master # Your branch is ahead of 'origin/master' by 2 commits. # nothing to commit (working directory clean) 

Mais ce n’est que l’ origin n’est pas github:

 PS D:\git\tests\pullahead\workrepo2> git remote -v show github d:/git/tests/pullahead/github (fetch) github d:/git/tests/pullahead/github (push) origin D:/git/tests/pullahead/workrepo (fetch) origin D:/git/tests/pullahead/workrepo (push) 

Mais si je répète la séquence dans un repo qui a une origine à github (ou aucune origine, juste un github ‘distant défini), le statut est propre:

 PS D:\git\tests\pullahead\workrepo2> cd .. PS D:\git\tests\pullahead> git clone workrepo workrepo4 PS D:\git\tests\pullahead> cd workrepo4 PS D:\git\tests\pullahead\workrepo4> git remote rm origin PS D:\git\tests\pullahead\workrepo4> git remote add github d:/git/tests/pullahead/github PS D:\git\tests\pullahead\workrepo4> git pull github master remote: Counting objects: 8, done. remote: Compressing objects: 100% (4/4), done. remote: Total 6 (delta 1), reused 0 (delta 0) Unpacking objects: 100% (6/6), done. From d:/git/tests/pullahead/github * branch master -> FETCH_HEAD Updating c2763f2..75ad279 Fast forward afile.txt | Bin 46 -> 98 bytes 1 files changed, 0 insertions(+), 0 deletions(-) PS D:\git\tests\pullahead\workrepo4> git status # On branch master nothing to commit (working directory clean) 

Si je n’avais que l’ origin pointant sur github , le status serait propre pour git1.6.5.
Cela peut être avec un avertissement ‘anticipé’ pour git plus tôt, mais de toute façon, un git config branch.master.remote yourGitHubRepo.git défini explicitement devrait pouvoir s’en occuper, même avec les premières versions de Git.

Êtes-vous attentif à append toute votre télécommande (sauf l’ origin fournie avec votre clone d’origine) à l’aide de git remote add NAME URL ? J’ai vu ce bug quand ils viennent d’être ajoutés à la configuration de git.