'git pull origin mybranch' laisse le mybranch N local commits avant l'origine. Pourquoi?

92

Je viens d'observer quelque chose d'étrange git pull, que je ne comprends pas.

Vendredi, j'ai travaillé dans une succursale locale. appelons-le mybranch. Avant de quitter le bureau , je poussais à l' origine ( ce qui est mon repo github): git push origin mybranch.

Hier à la maison, j'ai pullinstallé ma branche sur mon ordinateur portable , j'ai codé davantage, puis j'ai repoussé mes modifications sur github (origine).

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

git pull origin mybranch

cela a provoqué une fusion en avance rapide, ce qui est très bien. J'ai ensuite 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 y avoir 6 engagements à venir alors que je ne l'ai même pas touché pendant le week-end, ET juste retiré de l'origine? J'ai donc lancé un git diff origin/mybranchet les diffs étaient exactement les 6 changements que je viens de retirer de la télécommande.

Je ne pouvais "résoudre" cela qu'en exécutant git fetch origin:

From [email protected]:me/project
af8be00..88b0738  mybranch -> origin/mybranch

Apparemment, il manquait des objets de référence à mon dépôt local, mais comment est-ce possible? Je veux dire, un pull fait déjà une récupération, et je n'ai travaillé sur rien d'autre que cette branche, donc un git fetch originet git fetch origin mybranchdevrait avoir le même résultat?

Dois-je toujours utiliser à la git pull originplace de git pull origin branchname?

Je suis confus.

Matthias
la source
J'ai remarqué cela aussi; a git pushsemblera également le résoudre (rapporter «tout à jour»).
Ben James
4
git config --get-regexp br.*peut vous dire si votre configuration a une branche locale qui suit une autre branche
VonC
3
Pouvez-vous taper git config branch.master.remote yourGitHubRepo.gitvotre workRepo et vérifier (à la prochaine git pull origin) si le statut reste avec un avertissement «en avance»?
VonC
il n'est pas défini (sortie vide). mais git remote show originme montre que l'origine pointe vers mon référentiel GitHub, donc ça devrait aller, je suppose?
Matthias
1
git remote seul (afficher la bonne adresse pour le dépôt GitHub) ne suffit pas . Pour éviter d'avoir un Your branch is aheadmessage d'avertissement " " après a git pull, vous devez d'abord définir également le nom distant d'une branche . D'où ma suggestion: tapez git config branch.master.remote yourGitHubRepo.git, puis essayez a git pullet a git statuset voyez si le problème persiste.
VonC

Réponses:

115

git pullappelle git fetchavec les paramètres appropriés avant de fusionner les têtes explicitement extraites (ou si aucune branche distante configurée pour la fusion) dans la branche actuelle.

La syntaxe: git fetch <repository> <ref><ref>est juste un nom de branche sans deux-points est une extraction "one shot" qui ne fait pas une extraction standard de toutes les branches suivies de la télécommande spécifiée, mais à la place récupère uniquement la branche nommée dans FETCH_HEAD.

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

Lorsque vous effectuez git pull <repository> <ref>, FETCH_HEADest mis à jour comme ci-dessus, puis fusionné dans votre extrait, HEADmais aucune des branches de suivi standard pour le référentiel distant ne sera mise à jour (Git <1.8.4). Cela signifie que localement, il semble que vous soyez en avance sur la branche distante, alors qu'en fait, vous êtes à jour avec elle.

Personnellement, je fais toujours git fetchsuivi de git merge <remote>/<branch>parce que je peux voir les avertissements concernant les mises à jour forcées avant de fusionner, et je peux prévisualiser ce que je fusionne. Si j'utilisais git pullun peu plus que moi, je ferais un simple git pullsans paramètres le plus du temps, en comptant sur branch.<branch>.remoteet branch.<branch>.mergepour «faire la bonne chose».

CB Bailey
la source
4
+1 C'est vraiment une bonne explication! Je savais que l'explication se cachait quelque part à l'intérieur de 'git help fetch' mais je n'ai pas pu la sortir ...
Stefan Näwe
1
+1. Bon message, avec une approche similaire à gitster.livejournal.com/28309.html
VonC
1
Alors, est-ce qu'un git fetchaprès un git pull <repository> <ref>résoudre le problème puisque la récupération mettrait à jour les branches de suivi standard? Aussi, merci pour cette réponse, qui commence à avoir du sens :)
Bart Jedrocha
1
J'ai rencontré ce problème aussi et vous auriez besoin de faire git fetchsuivi de git merge origin/master master.
user1027169
3

Qu'est-ce que les git remote -v showretours en termes d'origine?

Si l'origine pointe vers github, le statut doit être à jour et ne doit pas être antérieur à un dépôt distant. Au moins, avec le Git1.6.5, j'utilise pour un test rapide.

Quoi qu'il en soit, pour éviter cela, définissez explicitement le dépôt distant de la branche principale:

$ git config branch.master.remote yourGitHubRepo.git

puis a git pull origin master, suivi d'un git statusdevrait retourner un statut propre (pas d'avance).
Pourquoi? parce que le maître d'origine get fetch (inclus dans le maître d'origine git pull) ne ferait pas que mettre à jour FETCH_HEAD(comme l' explique Charles Bailey dans sa réponse ), mais il mettrait également à jour la "branche principale distante" dans votre dépôt Git local.
Dans ce cas, votre maître local ne semble plus être "en avance" sur le maître distant.


Je peux tester cela, 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 dépôt GitHub en créant un dépôt nu (qui peut recevoir des push 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 vers le repo 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 home, cloné de GitHub, dans lequel j'apporte 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 dépôt, git status mentionne le 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 originpas 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 dépôt qui a une origine vers github (ou aucune origine du tout, 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 originpointer sur github, statusserait propre pour git1.6.5.
Cela peut être avec un avertissement 'avant' pour git antérieur, mais de toute façon, un git config branch.master.remote yourGitHubRepo.gitdéfini explicitement devrait être capable de s'en occuper, même avec les premières versions de Git.

VonC
la source
Merci d'avoir pris le temps d'examiner cela. La télécommande d'origine pointe déjà vers mon dépôt GitHub. J'ai cloné ce projet à partir d'une URL GitHub et ma branche principale locale suit l'origine / maître. En ce qui concerne mybranch, je suis presque sûr de l'avoir créé à partir de la branche origin / mybranch, qui devrait le suivre automatiquement. Mais encore, c'est peut-être le problème? Que le mybranch local ne suit pas réellement l'origine / mybranch? PS: J'utilise git 1.6.1 (via MacPorts).
Matthias
Existe-t-il une commande git qui me permet de voir si une branche locale suit une autre branche? Je ne le trouve pas dans les pages de manuel.
Matthias
Vous pouvez voir avec quelles branches distantes sont suivies git remote show origin.
Ted Percival
2

Faites-vous attention d'ajouter toute votre télécommande (sauf celle originqui est fournie avec votre clone d'origine) en utilisant git remote add NAME URL? J'ai vu ce bogue quand ils viennent d'être ajoutés à la configuration git.

Pat Notz
la source
Je l'ai fait lors du clonage du repo. Cependant, je n'ai pas fait cela avec chaque branche. Pour par exemple mybranch, je chercherais d'abord à l'origine, alors git checkout -b mybranch origin/mybranch. Selon la page de manuel de git-branch, l'origine / mybranch est le point de départ, et de plus, il indique pour --track: "... Utilisez ceci si vous tirez toujours de la même branche amont vers la nouvelle branche, et si vous ne voulez pas utiliser explicitement "git pull <repository> <refspec>". Ce comportement est le comportement par défaut lorsque le point de départ est une branche distante. "
Matthias