Perché devo “git push –set-upstream origin “?

Ho creato una filiale locale per testare Solaris e Sun Studio. Ho quindi spinto il ramo a monte. Dopo aver eseguito una modifica e tentato di applicare 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?

C’è qualche ragionevole caso d’uso in cui qualcuno dovrebbe creare , spingere il in remoto e quindi richiedere un commit su non dovrebbe essere per ?


Ho seguito questa domanda e ho risposto su Stack Overflow: Spingi un nuovo ramo locale in un repository Git remoto e monitoralo anche tu . Sto indovinando il suo altro esempio di una risposta accettata incompleta o errata. Oppure, un’altra istanza di Git che svolge un compito semplice e lo rende difficile.


Ecco la vista su una macchina diversa. 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 

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


La risposta alla domanda che hai chiesto – che riformulerò un po ‘come “devo impostare un upstream” – è: no, non devi assolutamente impostare un upstream.

Tuttavia, se non si ha upstream per il ramo corrente, Git cambia il suo comportamento su git push e anche su altri comandi.

La storia push completa qui è lunga e noiosa e risale alla storia prima di Git versione 1.5. Per accorciarlo, git push stato implementato male. 1 A partire dalla versione 2.0 di Git, Git ora ha una manopola di configurazione push.default che ora è push.default come predefinita. Per diverse versioni di Git prima e dopo la 2.0, ogni volta che eseguivi git push , Git emetteva un sacco di rumore cercando di convincerti a impostare push.default solo per far push.default che git push a tacere.

Non menzioni la versione di Git che stai utilizzando, né se hai configurato push.default , quindi dobbiamo indovinare. La mia ipotesi è che tu stia usando Git versione 2-point-qualcosa, e che hai impostato push.default in modo simple per farlo chiudere. Esattamente quale versione di Git hai e se qualcosa a cui hai impostato push.default importante, a causa di questa lunga e noiosa cronologia, ma alla fine, il fatto che tu abbia ricevuto un altro reclamo da Git indica che il tuo Git è configurato per evitare uno degli errori del passato.

Cos’è un upstream?

Un upstream è semplicemente un altro nome di ramo, solitamente un ramo di localizzazione remota, associato a un ramo (regolare, locale).

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

L’upstream dovrebbe , ma non deve essere, un ramo valido (che si tratti di remote-tracking come origin/ B o locale come master ). Cioè, se il ramo corrente B ha upstream U , git rev-parse U dovrebbe funzionare. Se non funziona, se lamenta che U non esiste, allora la maggior parte di Git agisce come se l’upstream non fosse impostato affatto. Alcuni comandi, come git branch -vv , mostreranno l’impostazione upstream ma contrassegnati come “gone”.

A che serve un upstream?

Se push.default è impostato su simple o upstream , l’impostazione upstream renderà git push , utilizzato senza argomenti aggiuntivi, solo lavoro.

Questo è tutto, è tutto ciò che fa per git push . Ma questo è abbastanza significativo, dal momento che git push è uno dei luoghi in cui un semplice errore di battitura causa forti mal di testa.

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

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

L’upstream influenza git fetch

Se esegui git fetch senza argomenti aggiuntivi, Git calcola da quale remoto recuperare, consultando il ramo corrente a monte. Se l’upstream è un ramo di localizzazione remota, Git recupera da quel remoto. (Se l’upstream non è impostato o è un ramo locale, Git prova l’ origin recupero.)

L’upstream influisce anche su git merge e git rebase

Se esegui git merge o git rebase senza argomenti aggiuntivi, Git usa il ramo corrente upstream. Quindi riduce l’uso di questi due comandi.

L’upstream influenza git pull

Non dovresti mai usare git pull ogni caso, ma se lo fai, git pull usa l’impostazione upstream per capire da quale remoto recuperare, e poi quale branch si fonderà o rebase con. Vale a dire, git pull fa la stessa cosa di git fetch perché in realtà esegue git fetch – e poi fa la stessa cosa di git merge o git rebase , perché in realtà esegue 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 che cosa è andato storto e sai cosa fare al riguardo.)

L’upstream influenza git status

Questo potrebbe effettivamente essere il più importante. Una volta che hai un set upstream, lo git status può riportare la differenza tra il tuo attuale ramo e il suo upstream, in termini di commit.

Se, come nel caso normale, ci si trova sul ramo B con il suo set upstream su origin/ B , e si esegue lo git status , si vedrà immediatamente se ci sono commit che si possono spingere e / o commit si può unire o rebase su .

Questo perché git status gira:

  • git rev-list --count @{u}..HEAD : quanti commit hai su B che non sono in origin/ B ?
  • git rev-list --count [email protected]{u} : quanti commit hai origin/ B che non sono su B ?

Impostare un upstream ti dà tutte queste cose.

Come mai il master ha già un set upstream?

Quando si clona per la prima volta da qualche telecomando, usando:

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

o simili, l’ultimo passo che Git fa è, in sostanza, git checkout master . Questo controlla il tuo master ramo locale: non hai un master ramo locale.

D’altra parte, si ha un ramo di localizzazione remota chiamato origin/master , perché è stato appena clonato.

Git suppone che tu abbia voluto dire: “Fammi un nuovo master locale che punta allo stesso commit origin/master localizzazione remota, e, mentre ci sei, imposta l’upstream per il master su origin/master .”

Questo succede per ogni ramo che fai git checkout che non hai già. Git crea il ramo e lo rende “traccia” (avere come upstream) il corrispondente ramo di localizzazione remota.

Ma questo non funziona per i nuovi rami, vale a dire, rami senza alcun ramo di localizzazione remota ancora .

Se crei un nuovo ramo:

 $ git checkout -b solaris 

non c’è, ancora, origin/solaris . Il tuo solaris locale non può tracciare l’ origin/solaris ramo di localizzazione remota origin/solaris perché non esiste.

Quando si preme per la prima volta il nuovo ramo:

 $ git push origin solaris 

che crea solaris origin e quindi crea anche origin/solaris nel tuo repository Git. Ma è troppo tardi: hai già un solaris locale che non ha upstream . 3

Non dovrebbe Git solo impostare quello, ora, come l’upstream automaticamente?

Probabilmente. Vedi “implementato male” e la nota 1. È difficile cambiare ora : ci sono milioni di 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 alcuni campi di configurazione e così via. In breve, Git è vittima del proprio successo: qualsiasi errore che ha in esso, oggi, può essere risolto solo se il cambiamento è per lo più invisibile, chiaramente molto migliore o fatto lentamente nel tempo.

Il fatto è che non lo fa oggi, a meno che non si usi --set-upstream o -u durante il git push . Questo è quello che ti sta dicendo il messaggio.

Non devi farlo in quel modo. Bene, come abbiamo notato sopra, non devi farlo affatto, ma diciamo che vuoi un upstream. Hai già creato branch solaris origin , tramite una push precedente, e come mostra il tuo output git branch , hai già origin/solaris nel tuo repository locale.

Semplicemente non hai impostato come upstream per solaris .

Per impostarlo ora, anziché durante il primo push, usa git branch --set-upstream-to . Il sottosistema --set-upstream-to prende il nome di qualsiasi ramo esistente, come origin/solaris , e imposta il ramo corrente a monte dell’altro ramo.

Questo è tutto, è tutto ciò che fa, ma ha tutte quelle implicazioni di cui sopra. Significa che puoi solo eseguire git fetch , quindi dare un’occhiata, quindi eseguire git merge o git rebase come appropriato, quindi eseguire nuovi commit ed eseguire git push , senza un po ‘di complicazioni aggiuntive.


1 Per essere onesti, non era chiaro all’epoca che l’implementazione iniziale fosse soggetta a errori. Questo è diventato chiaro solo quando ogni nuovo utente ha commesso gli stessi errori ogni volta. Ora è “meno povero”, che non vuol dire “grande”.

2 “Never” è un po ‘forte, ma trovo che i neofiti di Git capiscano molto meglio le cose quando separo i passaggi, specialmente quando posso mostrare loro che cosa git fetch effettivamente fatto, e possono vedere cosa git merge o git rebase farà il prossimo.

3 Se esegui il tuo primo git push come git push -u origin solaris source git push -u origin solaris -ie, se aggiungi il flag -u -Git imposterà origin/solaris come upstream per il tuo ramo corrente se (e solo se) la push avrà successo. Quindi dovresti fornire -u alla prima spinta. In effetti, è ansible fornirlo in qualsiasi spinta successiva, e imposterà o modificherà l’upstream in quel punto. Ma penso che git branch --set-upstream-to sia più facile, se lo dimenticassi.

4 Misurato dal metodo Austin Powers / Dr Evil di dire semplicemente “one MILLLL-YUN”, comunque.

Un comando sostanzialmente completo è come git push : . Se si esegue solo git push , git non sa cosa fare esattamente a meno che non si sia fatto un po ‘di configurazione che aiuti git a prendere una decisione. In un repository git, possiamo configurare più telecomandi. Inoltre possiamo spingere 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.