Por que tenho que “git push --set-upstream origin <branch>”?

146

Criei uma filial local para testar o Solaris e o Sun Studio. Eu então empurrei o ramo a montante. Após confirmar uma alteração e tentar enviá-la:

$ 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

Por que tenho que fazer algo especial para isso?

Existe algum caso de uso razoável em que alguém criaria <branch>, enviará o comando <branch>para remoto e, em seguida, alegará que <branch>não é para se comprometer <branch>?


Eu segui esta pergunta e respondi no Stack Overflow: Envie uma nova ramificação local para um repositório Git remoto e rastreie-a também . Suponho que seja outra instância de uma resposta aceita incompleta ou errada. Ou, é outra instância do Git pegando uma tarefa simples e dificultando.


Aqui está a visão em uma máquina diferente. A ramificação existe claramente, portanto foi criada e enviada por push:

$ 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
fonte
Possível duplicata de Por que eu preciso fazer o `--set-upstream` o tempo todo?
Alexei Levenkov
2
Obrigado @Alexi. Infelizmente, o dup citado não explica o caso de uso ridículo que está sendo representado por padrão. (Essas não são perguntas retóricas. Estou realmente interessado no motivo do design do UX).
JWW
1
Observe que isso é configurável. Se você o fizer git config --add push.default current, o git push criará automaticamente a ramificação no repositório remoto, se necessário.
Gogowitsch 8/06/19

Respostas:

271

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


A resposta para a pergunta que você fez - que vou reformular um pouco como "preciso configurar um upstream" - é: não, você não precisa configurar um upstream.

Se você não possui upstream para a ramificação atual, o Git altera seu comportamento git pushe também em outros comandos.

A história completa do push aqui é longa e chata e remonta à história anterior à versão 1.5 do Git. Para encurtá-lo muito, git pushfoi mal implementado. 1 A partir da versão 2.0 do Git, o Git agora possui um botão de configuração escrito, push.defaultque agora é o padrão simple. Para várias versões do Git antes e depois da 2.0, toda vez que você executava git push, o Git emitia muito ruído tentando convencê-lo a definir push.defaultapenas para se git pushcalar.

Você não menciona qual versão do Git está executando, nem se configurou push.default, portanto devemos adivinhar. Meu palpite é que você está usando a versão Git 2 pontos-alguma coisa, e que você definiu push.defaultpara simpleobtê-lo calar a boca. Precisamente qual versão do Git você tem, e que se qualquer coisa que você tenha push.defaultdefinido para, não importa, devido a que a história longa e chata, mas no final, o fato de que você está recebendo ainda uma outra queixa de Git indica que o Git é configurado para evitar um dos erros do passado.

O que é um upstream?

Um upstream é simplesmente outro nome de filial, geralmente uma filial de rastreamento remoto, associada a uma filial (regular, local).

Cada filial tem a opção de ter um (1) conjunto upstream. Ou seja, todas as ramificações possuem um montante ou um montante não. Nenhuma ramificação pode ter mais de um upstream.

O upstream deve , mas não precisa ser, uma ramificação válida (seja como rastreamento remoto ou local ). Ou seja, se o ramo atual B possuir U a montante , deve funcionar. Se não funcionar - se reclamar que U não existe -, a maioria do Git age como se o upstream não estivesse definido. Alguns comandos, como , mostrarão a configuração upstream, mas marcarão como "ido".origin/Bmastergit rev-parse U git branch -vv

De que serve um montante?

Se você push.defaultestiver definido como simpleou upstream, a configuração upstream fará git push, usada sem argumentos adicionais, apenas funcionar.

É isso - é tudo o que faz git push. Mas isso é bastante significativo, já que git pushé um dos lugares onde um simples erro de digitação causa grandes dores de cabeça.

Se você push.defaultestiver definido como nothing, matchingou current, a configuração de um upstream não fará nada git push.

(Tudo isso assume que sua versão do Git é pelo menos 2.0.)

A montante afeta git fetch

Se você executar git fetchsem argumentos adicionais, o Git descobrirá de qual remoto buscar, consultando a filial atual da corrente. Se o upstream for um ramo de rastreamento remoto, o Git buscará nesse remoto. (Se o upstream não estiver definido ou for uma ramificação local, o Git tentará buscar origin.)

A montante afeta git mergee git rebasetambém

Se você executar git mergeou git rebasesem argumentos adicionais, o Git usará o upstream da ramificação atual. Portanto, reduz o uso desses dois comandos.

A montante afeta git pull

Você nunca deve usar 2 degit pull qualquer maneira, mas, se o fizer, git pullusa a configuração de upstream para descobrir de qual controle remoto buscar e, em seguida, qual ramificação deve ser mesclada ou restaurada. Ou seja, git pullfaz o mesmo que git fetch- porque na verdade é executado git fetch - e depois faz o mesmo que git mergeou git rebase, porque na verdade executa git merge ou git rebase.

(Em geral, você deve executar essas duas etapas manualmente, pelo menos até conhecer o Git bem o suficiente para que, quando uma delas falhar, o que acabará por acontecer, você reconheça o que deu errado e saiba o que fazer a respeito.)

A montante afeta git status

Isso pode realmente ser o mais importante. Depois de definir um conjunto upstream, é git statuspossível relatar a diferença entre sua filial atual e a upstream, em termos de confirmações.

Se, como é o caso normal, você estiver na ramificação Bcom o upstream definido como e executar , verá imediatamente se você possui confirmações que pode enviar por push e / ou confirmações nas quais pode mesclar ou refazer.origin/Bgit status

Isso ocorre porque é git statusexecutado:

  • git rev-list --count @{u}..HEAD: quantos commits você tem Bque não estão ?origin/B
  • git rev-list --count HEAD..@{u}: quantos commits você tem que não estão ?origin/BB

Definir um upstream fornece todas essas coisas.

Como masterjá tem um conjunto upstream?

Quando você clona pela primeira vez de algum controle remoto, usando:

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

ou similar, o último passo que o Git executa é, essencialmente git checkout master,. Isso verifica sua filial local master- apenas você não tem uma filial local master.

Por outro lado, você não tem um ramo de rastreamento remoto chamado origin/master, porque você só clonado lo.

Git adivinha que você deve ter querido dizer: "Faça-me um novo local, masterque aponta para o mesmo cometer tão remota-tracking origin/master, e, enquanto você está nisso, definir o montante para mastera origin/master."

Isso acontece para todos os ramos git checkoutque você ainda não possui. O Git cria o ramo e faz com que ele "rastreie" (tenha como montante) o ramo de rastreamento remoto correspondente.

Mas isso não funciona para novas ramificações, ou seja, ramificações sem ramificação de rastreamento remoto ainda .

Se você criar uma nova ramificação:

$ git checkout -b solaris

ainda não existe origin/solaris. Seu local solaris não pode rastrear ramificações de rastreamento remoto origin/solarisporque elas não existem.

Quando você pressiona o novo ramo pela primeira vez:

$ git push origin solaris

que cria solaris na origin, e, portanto, também cria origin/solarisem seu próprio repositório Git. Mas é tarde demais: você já tem um local solarisque não possui upstream . 3

O Git não deveria apenas definir isso agora como o upstream automaticamente?

Provavelmente. Consulte "Implementado mal" e a nota de rodapé 1. É difícil mudar agora : existem milhões de scripts 4 que usam o Git e alguns podem depender do seu comportamento atual. Alterar o comportamento requer uma nova versão principal, o nag-ware para forçar você a definir algum campo de configuração e assim por diante. Em suma, o Git é vítima de seu próprio sucesso: quaisquer erros que ele possua, atualmente, só podem ser corrigidos se a mudança for na maior parte invisível, claramente muito melhor, ou realizada lentamente ao longo do tempo.

O fato é que não acontece hoje, a menos que você use --set-upstreamou -udurante o git push. É isso que a mensagem está dizendo.

Você não precisa fazer assim. Bem, como observamos acima, você não precisa fazer nada, mas digamos que você queira um upstream. Você já criou ramo solarisde origin, através de um impulso mais cedo, e como seus git branchshows de saída, você já tem origin/solaris em seu repositório local.

Você simplesmente não o define como o upstream para solaris.

Para configurá-lo agora, e não durante o primeiro push, use git branch --set-upstream-to. O --set-upstream-tosubcomando pega o nome de qualquer ramificação existente, como origin/solaris, e define a ramificação atual upstream para essa outra ramificação.

É isso, é tudo o que faz, mas tem todas as implicações mencionadas acima. Isso significa que você pode simplesmente executar git fetch, olhar ao redor, executar git mergeou git rebaseconforme apropriado, fazer novos commits e executar git push, sem muita confusão.


1 Para ser justo, não estava claro na época que a implementação inicial era propensa a erros. Isso ficou claro quando todos os novos usuários cometiam os mesmos erros todas as vezes. Agora é "menos pobre", o que não quer dizer "ótimo".

2 "Never" é um pouco forte, mas acho que os novatos do Git entendem muito melhor as coisas quando separo as etapas, especialmente quando posso mostrar o que git fetchrealmente fez e elas podem ver o que git mergeou o que git rebasefará a seguir.

3 Se você executar o seu primeiro git push como - git push -u origin solarisou seja, se você adicionar o -usinalizador - o Git definirá origin/solariscomo o upstream da sua ramificação atual se (e somente se) o envio for bem-sucedido. Então você deve fornecer -uno primeiro impulso. Na verdade, você pode fornecê-lo em qualquer push posterior, e ele definirá ou alterará o upstream nesse ponto. Mas acho que git branch --set-upstream-toé mais fácil, se você esqueceu.

4 Medido pelo método Austin Powers / Dr Evil de simplesmente dizer "um MILLLL-YUN", de qualquer maneira.

torek
fonte
2
Se o caso comum for {criar branch / push branch / use branch}, o resultado de Enviar uma nova ramificação local para um repositório Git remoto e não deve ser algo que realmente funcione? E se alguém quiser {criar branch / push branch / não usar branch}, então eles não deveriam fazer algo especial, como --set-upstream /dev/null? Por que o fardo é empurrado para o caso comum? Realmente não entendo algumas dessas decisões de engenharia e usabilidade.
JWW
1
@VonC: direito, que é o ponto de git push -u, mas realmente parece que git push -udeve ser o padrão, ou pelo menos o padrão se não houver a montante ainda , e deve haver um git push --no-set-upstreamquando não há atualmente um montante e que deseja manter dessa maneira (por qualquer motivo incompreensível :-)).
Torek
2
"Você continua fazendo perguntas como essa porque, acho, você classificou o Git como" realmente desagradável "." Por favor, mantenha esse tipo de especulação para si mesmo. Me deparei com essa pergunta porque também continuo me perguntando esse tipo de pergunta. Não sou o melhor designer de UX do mundo, mas até reconheço que o comportamento padrão nesse cenário específico poderia ser melhor.
Steven Byks
4
@torek - Obrigado. Sua resposta foi fantástica; bem pensado, bem estruturado e extremamente informativo. :-)
Steven Byks
6
Observe que isso é configurável. Se você o fizer git config --add push.default current, o git push criará automaticamente a ramificação no repositório remoto, se necessário.
Gogowitsch 8/06/19
31

A diferença entre
git push origin <branch>
e
git push --set-upstream origin <branch>
é que ambos enviam muito bem ao repositório remoto, mas é quando você puxa que percebe a diferença.

Se você faz:
git push origin <branch>
ao puxar, você deve fazer:
git pull origin <branch>

Mas se você faz:
git push --set-upstream origin <branch>
então, ao puxar, você só precisa fazer:
git pull

Portanto, adicionar no --set-upstreampermite não precisar especificar qual ramificação você deseja extrair a cada vez que faz git pull.

Adão
fonte
a diferença entre duas versões do "git push", que não sei por que iria querer / precisar usá-las. Sem sentido!
Frank Puck
17

Um comando basicamente completo é como git push <remote> <local_ref>:<remote_ref>. Se você executar apenas git push, o git não sabe exatamente o que fazer, a menos que você tenha feito alguma configuração que ajude o git a tomar uma decisão. Em um repositório git, podemos configurar vários controles remotos. Também podemos enviar uma referência local para qualquer referência remota. O comando completo é a maneira mais direta de fazer um push. Se você quiser digitar menos palavras, terá que configurar primeiro, como --set-upstream.

ElpieKay
fonte