Perché devo "git push --set-upstream origin <branch>"?


146

Ho creato una filiale locale per testare Solaris e Sun Studio. Ho quindi spinto il ramo a monte. Dopo aver eseguito una modifica e aver tentato di inviare le modifiche:

$ 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

Perché devo fare qualcosa di speciale per questo?

Esiste un caso d'uso ragionevole in cui qualcuno dovrebbe creare <branch>, spingere il <branch>telecomando e quindi dichiarare che un commit su <branch>non dovrebbe essere previsto <branch>?


Ho seguito questa domanda e ho risposto a StackTranslate.it: invia un nuovo ramo locale a un repository Git remoto e traccialo anche . Immagino sia un altro esempio di risposta accettata incompleta o errata. Oppure, è un'altra istanza di Git che prende un compito semplice e lo rende difficile.


Ecco la vista su un'altra macchina. Il ramo esiste chiaramente, quindi è stato creato e spinto:

$ 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


2
Grazie @Alexi. Sfortunatamente, il duplicato citato non spiega il ridicolo caso d'uso rappresentato per impostazione predefinita. (Quelle non sono domande retoriche. Sono sinceramente interessato al motivo del design UX).
JWW

1
Si noti che questo è configurabile. In tal caso git config --add push.default current, git push creerà automaticamente il ramo nel repository remoto, se necessario.
Gogowitsch,

Risposte:


271

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


La risposta alla domanda che hai posto, che mi riformulare un po 'come "devo impostare un monte", è: no, non si deve impostare un monte a tutti.

Se non si dispone di upstream per il ramo corrente, tuttavia, Git modifica il suo comportamento su git pushe anche su altri comandi.

La storia push completa qui è lunga e noiosa e risale alla storia precedente alla versione 1.5 di Git. Per accorciarlo molto, è git pushstato implementato male. 1 A partire dalla versione 2.0 di Git, Git ora dispone di una manopola di configurazione push.defaultche ora è predefinita simple. Per diverse versioni di Git prima e dopo la 2.0, ogni volta che correvi git push, Git emetteva un sacco di rumore nel tentativo di convincerti a impostare push.defaultsolo per stare git pushzitto.

Non menzioni quale versione di Git stai eseguendo, né se hai configurato push.default, quindi dobbiamo indovinare. La mia ipotesi è che si sta utilizzando la versione Git 2 punti-qualcosa, e che è stato impostato push.defaultper simpleper farlo stare zitto. Proprio quale versione di Git che hai, e che cosa se tutto quello che avete push.defaultimpostato, non importa, a causa di quella storia lunga e noiosa, ma alla fine, il fatto che stai ricevendo l'ennesima denuncia da Git indica che il Git è configurato per evitare uno degli errori del passato.

Che cos'è un monte?

Un upstream è semplicemente un altro nome di ramo, di solito un ramo di tracciamento remoto, associato a un ramo (normale, locale).

Ogni ramo ha la possibilità di avere un (1) set upstream. Cioè, ogni ramo o ha un upstream o non ha un upstream. Nessun ramo può avere più di un upstream.

L'upstream dovrebbe , ma non deve essere, un ramo valido (sia come localizzazione remota che come locale ). Cioè, se l'attuale ramo B ha U a monte , dovrebbe funzionare. Se non funziona, se si lamenta che U non esiste, la maggior parte di Git si comporta come se l'upstream non fosse impostato. Alcuni comandi, come , mostreranno l'impostazione a monte ma la contrassegneranno come "sparita".origin/Bmastergit rev-parse U git branch -vv

A che serve un monte?

Se push.defaultè impostato su simpleo upstream, l'impostazione upstream farà git push, utilizzata senza argomenti aggiuntivi, funzionerà.

Questo è tutto, è tutto ciò che fa git push. Ma questo è abbastanza significativo, poiché git pushè uno dei luoghi in cui un semplice errore di battitura provoca grossi mal di testa.

Se push.defaultè impostato su nothing, matchingo current, l'impostazione di un upstream non fa nulla per git push.

(Tutto questo presuppone che la tua versione Git sia almeno 2.0.)

L'upstream colpisce git fetch

Se si esegue git fetchsenza argomenti aggiuntivi, Git scopre da quale telecomando prelevare consultando l'upstream della filiale corrente. Se l'upstream è un ramo di tracciamento remoto, Git recupera da quel telecomando. (Se l'upstream non è impostato o è un ramo locale, Git prova a recuperare origin.)

Il monte colpisce git mergee git rebasetroppo

Se si esegue git mergeo git rebasesenza argomenti aggiuntivi, Git utilizza l'upstream del ramo corrente. Quindi accorcia l'uso di questi due comandi.

L'upstream colpisce git pull

Si dovrebbe mai 2 uso git pullin ogni caso, ma se lo fai, git pullutilizza l'impostazione a monte per capire quale remota per recuperare da, e poi quale ramo di unione o rebase con. Cioè, git pullfa la stessa cosa come git fetch-perché effettivamente eseguito git fetch -e poi fa la stessa cosa come git mergeo git rebase, perché in realtà viene eseguito git merge o git rebase.

(Di solito dovresti semplicemente fare questi due passaggi manualmente, almeno finché non conosci Git abbastanza bene che quando uno dei due passaggi fallisce, cosa che alla fine riconoscerai cosa è andato storto e sai cosa fare al riguardo.)

L'upstream colpisce git status

Questo potrebbe effettivamente essere il più importante. Una volta che hai un set upstream, git statuspuoi segnalare la differenza tra la tua filiale corrente e la sua upstream, in termini di commit.

Se, come nel caso normale, sei sul ramo Bcon il suo upstream impostato su , e corri , vedrai immediatamente se hai commit che puoi spingere e / o commit su cui puoi unire o rifare.origin/Bgit status

Questo perché git statusesegue:

  • git rev-list --count @{u}..HEAD: quanti commit hai e Bche non sono attivi ?origin/B
  • git rev-list --count HEAD..@{u}: quanti commit hai e che non sono attivi ?origin/BB

L'impostazione di un upstream ti dà tutte queste cose.

Come mai masterha già un set upstream?

Quando cloni per la prima volta da qualche telecomando, usando:

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

o simili, l'ultimo passo Git fa è, essenzialmente, git checkout master. Questo verifica la tua filiale locale, mastersolo che non hai una filiale locale master.

D'altra parte, si fa avere un ramo remoto il monitoraggio di nome origin/master, perché hai appena clonato esso.

Git indovina che si deve aver significato: "mi fanno un nuovo locale masterche punta allo stesso commettono come remote-tracking origin/master, e, mentre ci sei, impostare il monte per mastera origin/master."

Questo succede per ogni ramo git checkoutche non hai già. Git crea il ramo e lo rende "traccia" (ha come upstream) il ramo di tracciamento remoto corrispondente.

Ma questo non funziona per i nuovi rami, ovvero i rami senza ramo di monitoraggio remoto ancora .

Se si crea una nuova filiale:

$ git checkout -b solaris

c'è, ancora, no origin/solaris. Il tuo locale solaris non può tracciare il ramo di tracciamento remoto origin/solarisperché non esiste.

Quando si preme per la prima volta il nuovo ramo:

$ git push origin solaris

che crea solaris su origin, e quindi crea anche origin/solarisnel proprio repository Git. Ma è troppo tardi: hai già un locale solarische non ha monte . 3

Git non dovrebbe semplicemente impostarlo automaticamente come upstream?

Probabilmente. Vedi "implementato male" e la nota 1. È difficile cambiare ora : ci sono milioni di 4 script che usano Git e alcuni potrebbero dipendere dal suo comportamento attuale. La modifica del comportamento richiede una nuova versione principale, nag-ware per forzare l'impostazione di un campo di configurazione e così via. In breve, Git è vittima del suo stesso successo: qualsiasi errore abbia oggi, può essere risolto solo se il cambiamento è per lo più invisibile, chiaramente molto meglio o fatto lentamente nel tempo.

Il fatto è che non lo è oggi, a meno che non si usi --set-upstreamo -udurante il git push. Ecco cosa ti dice il messaggio.

Non devi farlo in quel modo. Bene, come abbiamo notato sopra, non devi assolutamente farlo, ma diciamo che vuoi un upstream. È già stato creato ramo solarissu origin, attraverso una spinta in precedenza, e come i vostri git branchspettacoli di uscita, è già avere origin/solaris nella vostra repository locale.

Semplicemente non lo hai impostato come upstream per solaris.

Per impostarlo ora, anziché durante la prima pressione, utilizzare git branch --set-upstream-to. Il --set-upstream-tocomando secondario prende il nome di qualsiasi ramo esistente, ad esempio origin/solaris, e imposta il ramo corrente a monte di quell'altro ramo.

Questo è tutto - è tutto ciò che fa - ma ha tutte quelle implicazioni annotate sopra. Significa che puoi semplicemente correre git fetch, quindi guardarti intorno, quindi correre git mergeo git rebasecome appropriato, quindi fare nuovi commit ed eseguire git push, senza un mucchio di problemi aggiuntivi.


1 Ad essere onesti, all'epoca non era chiaro che l'implementazione iniziale fosse soggetta a errori. Ciò è diventato chiaro solo quando ogni nuovo utente ha commesso gli stessi errori ogni volta. Ora è "meno povero", il che non vuol dire "grande".

2 "Mai" è un po 'forte, ma trovo che i neofiti di Git capiscano le cose molto meglio quando separo i passaggi, specialmente quando posso mostrare loro ciò che git fetcheffettivamente hanno fatto, e possono quindi vedere cosa git mergeo git rebasefaranno dopo.

3 Se esegui il tuo primo git push come git push -u origin solaris—ie, se aggiungi il -uflag — Git imposterà origin/solariscome upstream per il tuo ramo attuale se (e solo se) il push ha successo. Quindi dovresti fornire -ualla prima spinta. In effetti, puoi fornirlo su qualsiasi spinta successiva, e imposterà o cambierà l'upstream in quel punto. Ma penso che git branch --set-upstream-tosia più facile, se hai dimenticato.

4 Misurato dal metodo Austin Powers / Dr Evil di dire semplicemente "un MILLLL-YUN", comunque.


2
Se il caso comune è {creare branch / push branch / utilizzare branch}, il risultato di Push di un nuovo branch locale in un repository Git remoto e rintracciarlo non dovrebbe essere qualcosa che funziona davvero? E se qualcuno vuole {creare ramo / spingere ramo / non usare ramo}, allora non dovrebbero fare qualcosa di speciale, come --set-upstream /dev/null? Perché l'onere è spinto sul caso comune? Non capisco davvero alcune di queste decisioni di ingegneria e usabilità.
JWW

1
@VonC: giusto, questo è il punto git push -u, ma sembra davvero che git push -udovrebbe essere il valore predefinito, o almeno il valore predefinito se non c'è ancora upstream , e ci dovrebbe essere un git push --no-set-upstreamquando non c'è attualmente un upstream e si desidera mantenere in quel modo (per qualunque ragione incomprensibile :-)).
Torek,

2
"Continui a fare domande come questa perché, penso, hai scritto Git come" davvero odioso "." Tieni questo tipo di speculazione per te. Mi sono imbattuto in questa domanda perché continuo anche a farmi questo tipo di domande. Non sono il miglior designer di UX al mondo, ma riconosco anche che il comportamento predefinito in questo particolare scenario potrebbe essere migliore.
Steven Byks,

4
@torek - Grazie. La tua risposta è stata altrimenti fantastica; ben ponderato, ben strutturato ed estremamente informativo. :-)
Steven Byks

6
Si noti che questo è configurabile. In tal caso git config --add push.default current, git push creerà automaticamente il ramo nel repository remoto, se necessario.
Gogowitsch,

31

La differenza tra
git push origin <branch>
e
git push --set-upstream origin <branch>
è che entrambi spingono bene nel repository remoto, ma è quando tiri che noti la differenza.

Se lo fai:
git push origin <branch>
quando tiri, devi fare:
git pull origin <branch>

Ma se lo fai:
git push --set-upstream origin <branch>
quindi, quando tiri, devi solo fare:
git pull

Quindi aggiungendo il --set-upstreamcomando non è necessario specificare da quale ramo si desidera estrarre ogni volta che lo si fa git pull.


la differenza tra due versioni di "git push" che non so perché le vorrei / dovrei usare. Inutile!
Frank Puck,

17

Un comando sostanzialmente completo è come git push <remote> <local_ref>:<remote_ref>. Se corri solo git push, git non sa cosa fare esattamente se non hai fatto una configurazione che aiuta Git a prendere una decisione. In un repository git, possiamo impostare più telecomandi. Inoltre, possiamo inviare un riferimento locale a qualsiasi riferimento remoto. Il comando completo è il modo più semplice per fare una spinta. Se vuoi digitare meno parole, devi prima configurare, come --set-upstream.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.