Pourquoi dois-je "git push --set-upstream origin <branch>"?

146

J'ai créé une branche locale pour tester Solaris et Sun Studio. J'ai ensuite poussé la branche en amont. Après avoir validé une modification et tenté de pousser les modifications:

$ git commit blake2.cpp -m "Add workaround for missing _mm_set_epi64x"
[solaris 7ad22ff] Add workaround for missing _mm_set_epi64x
 1 file changed, 5 insertions(+)
$ git push
fatal: The current branch solaris has no upstream branch.
To push the current branch and set the remote as upstream, use

    git push --set-upstream origin solaris

Pourquoi dois-je faire quelque chose de spécial pour cela?

Y a-t-il un cas d'utilisation raisonnable où quelqu'un créerait <branch>, pousserait le <branch>vers distant, puis affirmerait qu'un commit sur <branch>n'est pas censé être pour <branch>?


J'ai suivi cette question et réponse sur Stack Overflow: Poussez une nouvelle branche locale vers un référentiel Git distant et suivez-la également . Je suppose que c'est une autre instance de réponse acceptée incomplète ou erronée. Ou, c'est une autre instance de Git prenant une tâche simple et la rendant difficile.


Voici la vue sur une machine différente. La branche existe clairement, elle a donc été créée et poussée:

$ git branch -a
  alignas
* master
  remotes/origin/HEAD -> origin/master
  remotes/origin/alignas
  remotes/origin/arm-neon
  remotes/origin/det-sig
  remotes/origin/master
  remotes/origin/solaris
jww
la source
Possible duplication de Pourquoi dois-je faire `--set-upstream` tout le temps?
Alexei Levenkov
2
Merci @Alexi. Malheureusement, le dup cité n'explique pas le cas d'utilisation ridicule qui est représenté par défaut. (Ce ne sont pas des questions rhétoriques. Je suis vraiment intéressé par la raison de la conception UX).
jww
1
Notez que cela est configurable. Si vous le faites git config --add push.default current, git push créera automatiquement la branche dans le référentiel distant si nécessaire.
Gogowitsch

Réponses:

271

TL; DR: git branch --set-upstream-to origin/solaris


La réponse à la question que vous avez posée - que je reformulerai un peu comme "dois-je définir un amont" - est: non, vous n'avez pas du tout à définir un amont.

Si vous n'avez pas d'amont pour la branche actuelle, cependant, Git change son comportement sur git push, et sur d'autres commandes également.

L'histoire complète des push ici est longue et ennuyeuse et remonte à l'histoire d'avant la version 1.5 de Git. Pour le raccourcir beaucoup, a git pushété mal implémenté. 1 À partir de la version 2.0 de Git, Git a maintenant un bouton de configuration orthographié push.defaultqui est désormais par défaut simple. Pour plusieurs versions de Git avant et après 2.0, chaque fois que vous exécutiez git push, Git crachait beaucoup de bruit en essayant de vous convaincre de régler push.defaultjuste pour pouvoir git pushvous taire.

Vous ne mentionnez pas la version de Git que vous utilisez, ni si vous l'avez configurée push.default, il faut donc deviner. Je suppose que vous utilisez Git version 2-point-quelque chose, et que vous avez configuré push.defaultpour simplele faire taire. Précisément quelle version de Git que vous avez, et si tout ce que vous avez push.defaultréglé sur, ne importe, en raison de cette histoire longue et ennuyeux, mais à la fin, le fait que vous obtenez une autre plainte de Git indique que votre Git est configuré pour éviter l'une des erreurs du passé.

Qu'est-ce qu'un amont?

Un amont est simplement un autre nom de branche, généralement une branche de suivi à distance, associé à une branche (régulière, locale).

Chaque branche a la possibilité d'avoir un (1) ensemble en amont. Autrement dit, chaque branche a un amont ou n'en a pas un. Aucune succursale ne peut en avoir plus d'une en amont.

L'amont devrait , mais ne doit pas être, une branche valide (qu'elle soit de type suivi à distance ou locale ). Autrement dit, si la branche actuelle B a U en amont , devrait fonctionner. Si cela ne fonctionne pas - s'il se plaint que U n'existe pas - alors la plupart de Git agit comme si l'amont n'était pas du tout défini. Quelques commandes, comme , afficheront le paramètre en amont mais le marqueront comme "parti".origin/Bmastergit rev-parse U git branch -vv

A quoi sert un amont?

Si votre push.defaultest défini sur simpleou upstream, le paramètre en amont fonctionnera git push, utilisé sans argument supplémentaire.

C'est tout - c'est tout ce qu'il fait git push. Mais c'est assez important, car git pushc'est l'un des endroits où une simple faute de frappe provoque des maux de tête majeurs.

Si votre push.defaultest réglé sur nothing, matchingou current, la configuration d'un amont ne fait rien du tout pour git push.

(Tout cela suppose que votre version de Git est au moins 2.0.)

L'amont affecte git fetch

Si vous exécutez git fetchsans argument supplémentaire, Git détermine la télécommande à partir de laquelle récupérer en consultant l'amont de la branche actuelle. Si l'amont est une branche de suivi à distance, Git récupère à partir de cette télécommande. (Si l'amont n'est pas défini ou est une branche locale, Git essaie de récupérer origin.)

L'amont affecte git mergeet git rebaseaussi

Si vous exécutez git mergeou git rebasesans arguments supplémentaires, Git utilise l'amont de la branche actuelle. Cela raccourcit donc l'utilisation de ces deux commandes.

L'amont affecte git pull

Vous ne devriez jamais 2 utiliser de git pulltoute façon, mais si vous le faites, git pullutilise le paramètre en amont pour savoir qui à distance pour aller chercher de, puis quelle branche fusion ou rebasage avec. Autrement dit, git pullfait la même chose que git fetch—parce qu'il s'exécute réellement git fetch— et ensuite fait la même chose que git mergeou git rebase, parce qu'il exécute en faitgit merge ou git rebase.

(Vous devriez généralement simplement faire ces deux étapes manuellement, au moins jusqu'à ce que vous connaissiez suffisamment bien Git pour que lorsque l'une ou l'autre étape échoue, ce qu'elle finira par faire, vous reconnaîtrez ce qui n'a pas fonctionné et sachez quoi faire.)

L'amont affecte git status

C'est peut-être le plus important. Une fois que vous avez un ensemble en amont, vous git statuspouvez signaler la différence entre votre branche actuelle et son amont, en termes de commits.

Si, comme c'est le cas normal, vous êtes sur une branche Bavec son amont réglé sur , et que vous exécutez , vous verrez immédiatement si vous avez des commits que vous pouvez pousser et / ou des commits sur lesquels vous pouvez fusionner ou rebaser.origin/Bgit status

C'est parce que git statuss'exécute:

  • git rev-list --count @{u}..HEAD: combien de commits avez-vous sur Bqui ne sont pas activés ?origin/B
  • git rev-list --count HEAD..@{u}: combien de commits avez-vous sur qui ne sont pas activés ?origin/BB

Mettre en place un amont vous donne toutes ces choses.

Comment se fait-il que masterdéjà un ensemble en amont?

Lorsque vous clonez pour la première fois à partir d'une télécommande, en utilisant:

$ git clone git://some.host/path/to/repo.git

ou similaire, la dernière étape Git est, essentiellement, git checkout master. Cela vérifie votre succursale locale master-seulement vous n'ont une succursale locale master.

D'autre part, vous n'avez une branche de suivi à distance nommé , parce que vous venez cloné il.origin/master

Git suppose que vous devez avoir voulu dire: "faites-moi un nouveau local masterqui pointe vers le même commit que le suivi à distance origin/master, et, pendant que vous y êtes, définissez l'amont sur masterto origin/master."

Cela se produit pour chaque branche git checkoutque vous n'avez pas déjà. Git crée la branche et la fait «suivre» (avoir en amont) la branche de suivi à distance correspondante.

Mais cela ne fonctionne pas pour les nouvelles branches, c'est-à-dire les branches sans branche de suivi à distance encore .

Si vous créez une nouvelle branche:

$ git checkout -b solaris

il n'y a pas encore origin/solaris. Votre agence locale solaris ne peut pas suivre la branche de suivi à distance origin/solariscar elle n'existe pas.

Lorsque vous poussez pour la première fois la nouvelle branche:

$ git push origin solaris

qui crée solaris sur origin, et donc crée également origin/solarisdans votre propre référentiel Git. Mais il est trop tard: vous avez déjà un local solarisqui n'a pas d'amont . 3

Git ne devrait-il pas simplement définir cela, maintenant, comme amont automatiquement?

Probablement. Voir «mal implémenté» et note de bas de page 1. Il est difficile de changer maintenant : il y a des millions 4 de scripts qui utilisent Git et certains peuvent bien dépendre de son comportement actuel. Changer le comportement nécessite une nouvelle version majeure, nag-ware pour vous forcer à définir un champ de configuration, et ainsi de suite. En bref, Git est victime de son propre succès: quelles que soient les erreurs qu'il y a, aujourd'hui, ne peuvent être corrigées que si le changement est soit essentiellement invisible, nettement mieux, soit effectué lentement au fil du temps.

Le fait est que ce n'est pas le cas aujourd'hui, sauf si vous utilisez --set-upstreamou -upendant le git push. C'est ce que le message vous dit.

Vous n'êtes pas obligé de le faire comme ça. Eh bien, comme nous l'avons noté ci-dessus, vous n'avez pas à le faire du tout, mais disons que vous voulez un en amont. Vous avez déjà créé la branche solarissur origin, par une poussée plus tôt, et que vos git branchspectacles de sortie, vous avez déjà avoir origin/solaris dans votre dépôt local.

Vous ne l'avez tout simplement pas défini comme amont pour solaris.

Pour le régler maintenant, plutôt que lors de la première poussée, utilisez git branch --set-upstream-to. La --set-upstream-tosous-commande prend le nom de n'importe quelle branche existante, telle que origin/solaris, et définit la branche actuelle en amont sur cette autre branche.

C'est tout - c'est tout ce qu'il fait - mais cela a toutes les implications mentionnées ci-dessus. Cela signifie que vous pouvez simplement exécuter git fetch, puis regarder autour de vous, puis exécuter git mergeou git rebaseselon le cas, puis faire de nouveaux commits et exécuter git push, sans trop de tracas.


1 Pour être juste, il n'était pas clair à l'époque que la mise en œuvre initiale était sujette aux erreurs. Cela n'est devenu clair que lorsque chaque nouvel utilisateur a commis les mêmes erreurs à chaque fois. Il est désormais "moins pauvre", ce qui ne veut pas dire "génial".

2 "Jamais" est un peu fort, mais je trouve que les débutants de Git comprennent beaucoup mieux les choses quand je sépare les étapes, surtout quand je peux leur montrer ce qui a git fetchréellement fait, et ils peuvent alors voir ce qui git mergeou git rebaseva faire ensuite.

3 Si vous exécutez votre premier en git push tant que git push -u origin solaris— c'est-à-dire si vous ajoutez l' -uindicateur — Git sera défini origin/solariscomme amont pour votre branche actuelle si (et seulement si) la transmission réussit. Vous devez donc fournir dès -ula première poussée. En fait, vous pouvez le fournir sur n'importe quelle poussée ultérieure, et il définira ou modifiera l'amont à ce stade. Mais je pense que git branch --set-upstream-toc'est plus facile, si vous avez oublié.

4 Mesuré par la méthode Austin Powers / Dr Evil qui consiste simplement à dire "un MILLLL-YUN", de toute façon.

Torek
la source
2
Si le cas courant est de {créer une branche / pousser une branche / utiliser une branche}, alors le résultat de Pousser une nouvelle branche locale vers un dépôt Git distant et de la suivre ne devrait- il pas être quelque chose qui fonctionne réellement? Et si quelqu'un veut {créer une branche / pousser une branche / ne pas utiliser de branche}, alors ne devrait-il pas avoir à faire quelque chose de spécial, comme --set-upstream /dev/null? Pourquoi le fardeau pèse-t-il sur le cas commun? Je ne comprends vraiment pas certaines de ces décisions d'ingénierie et d'utilisabilité.
jww
1
@VonC: d'accord, c'est le but de git push -u, mais il semble vraiment que ce git push -udevrait être la valeur par défaut, ou du moins la valeur par défaut s'il n'y a pas encore d' amont , et il devrait y avoir un git push --no-set-upstreamquand il n'y a pas actuellement d'amont et que vous voulez garder il de cette façon (pour une raison incompréhensible :-)).
torek le
2
"Vous continuez à poser des questions comme celle-ci parce que, je pense, vous avez qualifié Git de" vraiment désagréable "." Veuillez garder ce genre de spéculation pour vous. Je suis tombé sur cette question parce que je n'arrête pas de me poser ce genre de questions. Je ne suis pas le meilleur concepteur UX au monde, mais je reconnais même que le comportement par défaut dans ce scénario particulier pourrait être meilleur.
Steven Byks
4
@torek - Merci. Votre réponse était par ailleurs fantastique; bien pensé, bien structuré et extrêmement instructif. :-)
Steven Byks
6
Notez que cela est configurable. Si vous le faites git config --add push.default current, git push créera automatiquement la branche dans le référentiel distant si nécessaire.
Gogowitsch
31

La différence entre
git push origin <branch>
et
git push --set-upstream origin <branch>
est qu'ils poussent tous les deux très bien vers le référentiel distant, mais c'est lorsque vous tirez que vous remarquez la différence.

Si vous faites:
git push origin <branch>
lorsque vous tirez, vous devez faire:
git pull origin <branch>

Mais si vous faites:
git push --set-upstream origin <branch>
alors, lorsque vous tirez, vous n'avez qu'à faire:
git pull

Ainsi, l'ajout de --set-upstreampermet de ne pas avoir à spécifier de quelle branche vous voulez extraire à chaque fois que vous le faites git pull.

Adam
la source
la différence entre deux versions de "git push" dont je ne sais pas pourquoi je voudrais / devrais les utiliser. Inutile!
Frank Puck
17

Une commande fondamentalement complète est comme git push <remote> <local_ref>:<remote_ref>. Si vous exécutez juste git push, git ne sait pas quoi faire exactement à moins que vous n'ayez fait une configuration qui aide git à prendre une décision. Dans un dépôt git, nous pouvons configurer plusieurs télécommandes. Nous pouvons également pousser une référence locale vers n'importe quelle référence distante. La commande complète est le moyen le plus simple de pousser. Si vous voulez taper moins de mots, vous devez d'abord configurer, comme --set-upstream.

ElpieKay
la source