Guida

git push: come inviare le modifiche al repository remoto

Aggiornato: · Skillaria.Top

git push invia i commit locali del branch corrente al repository remoto e vi sposta il puntatore del branch. La prima volta si pubblica il branch con git push -u origin nome-branch, poi basta git push. Se sul server ci sono commit altrui, bisogna prima recuperarli con git pull, non fare un force push.

Cosa fa git push

Il comando git push prende i commit del vostro branch locale che non sono ancora sul server, li trasferisce al repository remoto (di solito si chiama origin) e lì sposta il puntatore del branch sul vostro ultimo commit. I file della cartella di lavoro e le modifiche non committate non vanno da nessuna parte: si invia solo la cronologia, cioè i commit.

È importante capire che push non è una «sincronizzazione». Funziona in una sola direzione: dal repository locale a quello remoto. Per ricevere le modifiche altrui servono git fetch o git pull. Confondere queste direzioni è all'origine di quasi tutti i problemi con cui i principianti si rivolgono al mentor.

Un'altra regola di Git: per impostazione predefinita il push è consentito solo come «avanzamento rapido» (fast-forward). Il server accetta i vostri commit se proseguono la cronologia già presente sul server. Se qualcuno ha inviato i propri commit prima di voi, il vostro push verrà rifiutato: è una protezione, non un errore.

Sintassi del comando

git push [<remote>] [<branch-locale>[:<branch-remoto>]]

Se eseguite git push senza argomenti, Git prende il branch corrente e lo invia al branch impostato come upstream (tracciato). La forma esplicita è utile quando l'upstream non è ancora configurato o volete inviare il branch con un altro nome:

git push origin main                  # inviare il main locale in origin/main
git push origin feature/login         # inviare il branch feature/login
git push origin fix:hotfix-123        # inviare il fix locale come hotfix-123 sul server
git push origin HEAD                  # inviare il branch corrente con lo stesso nome

Come inviare le modifiche la prima volta

Il percorso tipico da un nuovo progetto al codice su GitHub o GitLab è questo. Prima create un repository vuoto sul server, poi collegate quello locale e inviate il branch.

git init
git add .
git commit -m "Primo commit"
git branch -M main
git remote add origin git@github.com:user/project.git
git push -u origin main

Il flag -u (forma estesa --set-upstream) collega il branch locale main a quello remoto origin/main. Da quel momento nel branch basta scrivere git push e git pull, e git status inizierà a mostrare di quanti commit siete avanti o indietro rispetto al server.

Per verificare a cosa punta il remote usate git remote -v. Se l'indirizzo è sbagliato, correggetelo con git remote set-url origin <nuovo-indirizzo>, invece di eliminare e ricreare il repository.

Come inviare un nuovo branch

Avete creato un branch per un'attività, fatto alcuni commit e scrivete git push. Git risponde:

fatal: The current branch feature/login has no upstream branch.
To push the current branch and set the remote as upstream, use

    git push --set-upstream origin feature/login

Non è un errore ma un suggerimento: il branch non ha ancora un corrispondente sul server. Eseguite il comando proposto o la forma breve git push -u origin feature/login. Perché Git lo faccia automaticamente per tutti i nuovi branch, attivate l'impostazione (disponibile da Git 2.37):

git config --global push.autoSetupRemote true

Perché git push viene rifiutato: rejected, non-fast-forward

La situazione più frequente nel lavoro di squadra:

! [rejected]        main -> main (fetch first)
error: failed to push some refs to 'github.com:user/project.git'
hint: Updates were rejected because the remote contains work that you do
hint: not have locally.

Un collega ha inviato i suoi commit prima di voi. La vostra cronologia e quella del server si sono separate, e se Git accettasse il vostro push i commit del collega andrebbero persi. La soluzione corretta è prima recuperare le modifiche e integrarvi le vostre:

git pull --rebase       # recuperare i commit altrui e spostare i propri sopra
# in caso di conflitti: correggere i file, git add, git rebase --continue
git push

Invece di --rebase si può fare un normale git pull: in quel caso comparirà un merge commit. Entrambe le varianti sono corrette; nella maggior parte dei team, per i propri commit locali si preferisce il rebase, così la cronologia resta lineare. L'importante è non «risolvere» il rifiuto con --force.

git push --force e --force-with-lease

L'invio forzato serve quando avete riscritto consapevolmente la cronologia del vostro branch: avete fatto git commit --amend, git rebase o unito dei commit prima della review. Un push normale non accetterà quel branch, perché i vecchi commit sul server non sono più antenati di quelli nuovi.

git push --force-with-lease

--force-with-lease sovrascrive il branch sul server solo se si trova ancora dove l'ha visto l'ultima volta il vostro Git locale (secondo il riferimento origin/<branch>). Se un collega ha fatto in tempo a pushare qualcosa, il comando rifiuterà e non cancellerete il suo lavoro. Il normale --force (-f) non fa questa verifica e sovrascrive tutto in silenzio.

C'è un dettaglio: se prima eseguite git fetch, il riferimento origin/<branch> si aggiorna e la protezione smette di funzionare, perché Git considera che abbiate «visto» i commit altrui. Quindi non fate fetch subito prima di un force push senza guardare cosa è arrivato.

ComandoCosa faQuando usarlo
git pushSolo fast-forwardSempre, come impostazione predefinita
git push --force-with-leaseSovrascrittura con verificaIl proprio branch dopo rebase o amend
git push --forceSovrascrittura senza verificheQuasi mai; mai sui branch condivisi

Sui branch condivisi (main, develop) non si fa push forzato: per chiunque abbia già scaricato la vecchia cronologia, questa divergerà da quella del server. Di solito questi branch sono anche protetti dalle impostazioni del repository, e il server risponderà protected branch hook declined.

Come inviare i tag

Un normale git push non invia i tag. Vanno trasmessi esplicitamente:

git tag -a v1.2.0 -m "Release 1.2.0"
git push origin v1.2.0          # un solo tag
git push --tags                 # tutti i tag locali
git push --follow-tags          # commit e tag annotati che puntano a essi

--follow-tags è più comodo di --tags: non invia tag leggeri locali creati per caso, ma solo quelli annotati e raggiungibili dai commit inviati.

Come eliminare un branch sul server

Anche l'eliminazione si fa con push:

git push origin --delete feature/login
# sintassi vecchia, fa la stessa cosa:
git push origin :feature/login

Il branch locale rimane; potete eliminarlo con git branch -d feature/login. Ai colleghi resterà il riferimento origin/feature/login finché non eseguono git fetch --prune.

Flag utili di git push

FlagScopo
-u, --set-upstreamMemorizzare il branch remoto come upstream
-n, --dry-runMostrare cosa verrebbe inviato senza inviare nulla
--allInviare tutti i branch locali
--tags / --follow-tagsInviare i tag
-d, --deleteEliminare un branch o un tag sul server
--force-with-leaseRiscrittura sicura della cronologia
-v, --verboseOutput dettagliato
--no-verifySaltare l'hook pre-push (da usare consapevolmente)

Errori frequenti con git push e come risolverli

Permission denied (publickey)

Il server non ha accettato la vostra chiave SSH. Verificate che la chiave esista (ls ~/.ssh), che la parte pubblica sia aggiunta all'account e che la connessione funzioni: ssh -T git@github.com. Se il remote è configurato via HTTPS, Git chiederà login e token, non la chiave: controllate git remote -v.

Authentication failed via HTTPS

GitHub e molti altri servizi non accettano più da tempo la password dell'account tramite HTTPS. Serve un token di accesso personale oppure il passaggio a SSH. Per salvare il token aiuta il credential helper: git config --global credential.helper store (lo conserva in chiaro) o il gestore di credenziali del vostro sistema operativo.

src refspec main does not match any

Non avete un branch con quel nome. Il più delle volte nel repository vuoto non è stato ancora fatto nessun commit, oppure il branch si chiama master. Controllate git branch e fate un commit o rinominate il branch: git branch -M main.

Everything up-to-date, anche se ci sono modifiche

Avete dimenticato il commit. git push invia commit, non file. Eseguite git status, poi git add e git commit. Seconda possibilità: vi trovate in un branch diverso da quello che vi aspettate di vedere sul server.

File troppo grande

I servizi limitano la dimensione dei file (su GitHub 100 MB). Se un file del genere è già finito nella cronologia, non basta eliminarlo con un nuovo commit: bisogna riscrivere la cronologia o toglierlo dai commit non ancora inviati (git reset --soft, poi un nuovo commit). Per i file binari di grandi dimensioni si usa Git LFS.

Il ciclo di lavoro con git push

  1. git switch -c feature/x: un branch per l'attività.
  2. Commit in piccoli passi logici.
  3. git fetch e git rebase origin/main: allinearsi al branch principale aggiornato.
  4. git push -u origin feature/x: pubblicare il branch.
  5. Aprire una pull request; dopo la review, commit aggiuntivi e un normale git push.
  6. Se avete dovuto riscrivere la cronologia: git push --force-with-lease.

Prima dell'invio è utile vedere cosa andrà esattamente sul server: git log origin/main..HEAD --oneline mostra i commit che sul server non ci sono ancora.

Cosa configurare una volta per tutte

Alcune impostazioni globali evitano la maggior parte delle sorprese durante l'invio. push.default stabilisce cosa invia git push senza argomenti. Il valore predefinito nelle versioni moderne è simple: si invia solo il branch corrente e solo all'upstream con lo stesso nome. Di solito non serve cambiarlo, ma conviene verificare che in una vecchia configurazione non sia rimasto matching, che invia insieme tutti i branch con nome corrispondente.

git config --global push.default simple
git config --global push.autoSetupRemote true
git config --global push.followTags true
git config --global pull.rebase true

push.followTags rende --follow-tags il comportamento predefinito, mentre pull.rebase evita merge commit superflui quando il push viene rifiutato e bisogna prima recuperare le modifiche altrui.

L'hook pre-push

Il file .git/hooks/pre-push viene eseguito prima dell'invio. Se termina con un codice diverso da zero, il push non avviene. I team spesso vi lanciano test o linter, perché il codice rotto non arrivi sul server. Aggirare l'hook con --no-verify ha senso solo quando capite esattamente perché fallisce.

git push pubblica i vostri commit nel repository remoto e funziona in una sola direzione. Fate il primo invio di un branch con -u, in caso di rifiuto recuperate prima le modifiche altrui con pull, e inviate la cronologia riscritta del vostro branch solo con --force-with-lease. Il lavoro con i repository remoti, push, pull e chiavi SSH è affrontato in pratica nel corso su Git.

Domande frequenti

git push invia le modifiche non committate?

No. Il push trasferisce solo i commit; le modifiche nella cartella di lavoro e nell'index restano in locale finché non ne fate il commit.

Come annullare un git push?

Nel proprio branch si può annullare il commit in locale (git reset) e inviare con git push --force-with-lease. In un branch condiviso è più sicuro creare un commit inverso con git revert e fare un push normale.

Si può inviare un solo commit?

Sì, indicandone l'hash: git push origin abc1234:main invia tutta la cronologia fino a quel commit incluso, ma non i commit successivi. Non si può inviare un commit senza i suoi antenati.

Che differenza c'è tra git push e git push origin main?

Senza argomenti Git usa l'upstream configurato del branch corrente, mentre la forma esplicita indica direttamente remote e branch e funziona anche senza upstream configurato.

Come fare push su due repository contemporaneamente?

Aggiungete un secondo remote e inviate a entrambi per nome, oppure aggiungete a origin due indirizzi di push con git remote set-url --add --push origin indirizzo (da eseguire per entrambi gli indirizzi, compreso quello originale, altrimenti il push andrà solo al secondo).

Impara l’argomento in video

Questo argomento è trattato in modo approfondito, con pratica, nel corso «Git da zero a sviluppatore sicuro».

Vai al corso