SSD Nodes Learn 🎉 VPS da $5.50/mese
Guide Matt ConnorDi Matt Connor · Aggiornato 2026-08-13

Git e GitHub: differenze per chi gestisce un VPS

Git è il programma di controllo versione sul tuo computer o server; GitHub è un servizio online basato su Git. Ecco cosa cambia per il tuo VPS.

Che cos’è GitHub?

GitHub è un servizio in hosting che archivia repository Git e costruisce un sito web intorno a essi. Git è il programma per il controllo versione che viene eseguito sul proprio computer o sul proprio server. GitHub è il prodotto di una singola azienda basato su Git, di proprietà di Microsoft dal 2018. È possibile usare Git ogni giorno senza mai aprire GitHub. Non è possibile usare GitHub senza Git.

Questa distinzione diventa importante non appena si gestisce un VPS (virtual private server). Git registra la cronologia dei file di configurazione e degli script di deploy. GitHub è il luogo in cui viene conservata una copia di questa cronologia quando il server non la contiene, oltre a offrire un ambiente per eseguire build e revisioni. Questa guida segue un esempio, dalla cartella vuota fino al deploy su un server, e definisce ogni nuovo termine al primo utilizzo.

Cosa fa Git autonomamente

Git è un sistema di controllo versione: registra nel tempo lo stato di una directory, così puoi vedere che cosa è cambiato, quando e perché. È stato sviluppato nel 2005 per il lavoro sul kernel Linux. È distribuito, cioè ogni copia di un repository contiene l’intera cronologia. Nel suo modello non esiste un server centrale. Il portatile di un collega contiene una copia completa quanto quella di qualsiasi server.

Installalo e configura la tua identità. Git rifiuta di registrare un commit senza un nome e un indirizzo email, perché entrambi vengono scritti nel commit stesso.

sudo apt update && sudo apt install -y git
git --version
git config --global user.name "Your Name"
git config --global user.email "you@example.com"

Su Ubuntu 24.04, git --version stampa git version 2.43.0. Qualsiasi release degli ultimi anni si comporta nello stesso modo per tutto ciò che segue.

L'esempio: un repository per i file di deployment del tuo VPS

Un repository, spesso abbreviato in «repo», è una directory monitorata da Git. Diventa un repository quando esegui git init, che crea al suo interno una directory nascosta .git. Quella directory è il repository. Se elimini .git, rimane una directory normale senza cronologia.

mkdir vps-deploy && cd vps-deploy
git init -b main
printf '.env\n*.key\n' > .gitignore

-b main assegna al primo branch il nome main. Se ometti questa opzione, Git visualizza un lungo suggerimento sul nome del branch predefinito. .gitignore elenca i percorsi che Git non deve mai monitorare. Inserisci il file dei secret al suo interno fin dal primo giorno, perché un file sottoposto a commit anche una sola volta rimane nella cronologia dopo l'eliminazione. Per rimuoverlo correttamente è necessario riscrivere ogni commit successivo.

Commit: l'unità della cronologia

Ora aggiungi uno script e registralo.

printf '#!/bin/sh\nsudo systemctl restart caddy\n' > restart.sh
git add restart.sh .gitignore
git commit -m "Add restart script and gitignore"
git log --oneline

git add sposta una modifica nell'area di staging, cioè l'elenco degli elementi che verranno inclusi nel commit successivo. git commit scrive quell'elenco nella cronologia come una singola voce. Un commit contiene uno snapshot di ogni file tracciato, un messaggio, l'autore, un timestamp e un puntatore al commit precedente. git log --oneline stampa una riga per ogni commit; ciascuna riga inizia con un hash breve come a1b2c3d. Questo hash è il nome del commit e quasi tutti i comandi Git lo accettano.

Se ometti il passaggio git add, git commit risponde no changes added to commit (use "git add" and/or "git commit -a"). Non c'è alcun problema. Git indica che l'area di staging è vuota, quindi non c'è nulla da includere nello snapshot. git status è il comando da eseguire quando non sai come procedere: mostra il branch corrente, le modifiche nell'area di staging e i file visibili a Git che non sono tracciati.

Rami: una seconda linea nella cronologia

Un branch è un puntatore mobile a un commit. main è un branch e non ha nulla di speciale in Git. La sua creazione non ha costi, perché Git scrive un nuovo puntatore invece di copiare i file.

git switch -c add-backup
printf '#!/bin/sh\nrestic backup /srv\n' > backup.sh
git add backup.sh
git commit -m "Add nightly backup"
git switch main
ls

Dopo git switch main, backup.sh non compare più nell'elenco. Non è stato eliminato nulla. Il file esiste nel branch add-backup, mentre main non lo ha mai contenuto; per questo Git lo ha rimosso dalla directory di lavoro quando hai effettuato lo spostamento. Questo comportamento sorprende tutti la prima volta. git switch add-backup lo ripristina.

Remoti: dove compare finalmente GitHub

Finora tutto è stato eseguito su una sola macchina, senza alcuna rete. Un remote è un URL denominato che identifica un'altra copia dello stesso repository. GitHub ospita una di queste copie. Il nome convenzionale del remote principale è origin.

Create un repository vuoto tramite il sito web di GitHub, quindi collegatelo. In questo caso è preferibile SSH rispetto a HTTPS: una chiave SSH è un file sotto il vostro controllo e non scade come un personal access token.

ssh-keygen -t ed25519 -C "vps-deploy"
cat ~/.ssh/id_ed25519.pub
ssh -T git@github.com

Incollate la chiave pubblica visualizzata nella pagina delle chiavi SSH del vostro account GitHub, quindi eseguite di nuovo il test. Una chiave funzionante risponde Hi yourname! You've successfully authenticated, but GitHub does not provide shell access. GitHub non fornisce accesso a una shell, quindi questo rifiuto indica che il test è riuscito. git@github.com: Permission denied (publickey). significa che la vostra chiave non è mai stata presentata oppure non è stata accettata; verificate quindi di avere incollato il file .pub e non la chiave privata accanto a esso.

git remote add origin git@github.com:yourname/vps-deploy.git
git push -u origin main

git push invia i commit al remote. -u registra che main locale tiene traccia di main remoto, quindi in seguito è sufficiente un semplice git push. git clone <url> svolge l'operazione inversa su una nuova macchina: copia l'intero repository con la relativa cronologia e configura origin automaticamente. È possibile usare anche un remote HTTPS. Questo protocollo è lo stesso utilizzato da qualsiasi pagina web e può essere utile nelle reti che bloccano le connessioni in uscita sulla porta 22. Se questa frase richiede ulteriori dettagli, la composizione effettiva di una richiesta HTTP descrive il funzionamento.

Pull request, issue e fork: gli elementi che appartengono a GitHub, non a Git

Tutto ciò che precede appartiene a Git e funziona con qualsiasi server. I tre termini seguenti indicano funzionalità di GitHub. Altri host le replicano, ma Git non le conosce.

Una pull request (PR) è una richiesta di unire un branch a un altro, accompagnata da una pagina per la discussione. Esegui il push di add-backup, apri una PR destinata a main e il sito mostra le differenze commit per commit. Gli utenti possono commentare singole righe. I controlli automatici indicano se il branch supera o non supera le verifiche. Fai clic su Merge e GitHub esegue l'unione sulla propria copia, quindi aggiorna main. Il nome deriva dal flusso di lavoro originale, in cui chiedevi a un maintainer di fare pull del tuo branch nel proprio.

Una issue è una discussione numerata relativa a un bug o a un'attività. Risiede nel database di GitHub, non nel repository, un aspetto importante da considerare prima di scegliere un host: clonando il repository ottieni tutti i commit, ma nessuna issue. Per esportare le issue devi chiamare l'API.

Un fork è una copia lato server del repository di un altro utente. Hai accesso in scrittura alla copia, esegui il push di un branch su di essa e apri una pull request dalla tua copia verso quella originale. Questo è il modo per contribuire a un progetto i cui maintainer non ti conoscono ancora. Un fork è un clone che risiede su GitHub e conserva l'indicazione della propria origine.

Il software legge tutti e tre gli elementi tramite la stessa API usata dagli utenti. Un agente per la revisione delle pull request eseguito sul tuo server monitora le nuove PR, legge il diff e pubblica commenti sulle righe. Convenzioni come un file AGENTS.md nella radice di un repository esistono perché ora un repository viene letto sia dagli strumenti sia dagli utenti.

Che cosa fa davvero GitHub per chi gestisce un VPS

Inizia con l'archiviazione esterna al server. Gli script di deploy e i playbook devono trovarsi in una posizione diversa dal server che configurano. Ricrea il VPS da un'immagine pulita, esegui il clone e avvia il deploy. Mantieni il repository privato e assegna al server una deploy key: una chiave SSH associata a un solo repository, non all'intero account, configurata in sola lettura. Se una deploy key di sola lettura viene divulgata, espone un solo repository. Se viene divulgata una chiave dell'account, espone tutto ciò su cui puoi eseguire il push.

sudo git clone git@github.com:yourname/vps-deploy.git /srv/vps-deploy
cd /srv/vps-deploy
git pull --ff-only

--ff-only rifiuta di creare un commit di merge. Su un server che riceve soltanto le modifiche, un merge è sempre un errore, quindi questo flag trasforma una cronologia ambigua nel semplice errore fatal: Not possible to fast-forward, aborting.. Sul server è cambiato qualcosa che non dovrebbe essere cambiato. Individua la causa prima di eseguire di nuovo il pull.

Se esegui il clone come root e poi esegui Git con un altro utente, ottieni fatal: detected dubious ownership in repository at '/srv/vps-deploy'. Git rifiuta di leggere un repository di proprietà di un altro utente, perché un .git/config ostile può fare in modo che Git esegua comandi. Correggi la proprietà con chown invece di aggiungere un'eccezione safe.directory, perché l'eccezione disattiva il controllo senza rimuoverne la causa.

Pipeline di build e deploy con GitHub Actions

Actions è il sistema CI/CD di GitHub (integrazione continua e distribuzione continua). Esegui il commit di un file YAML in .github/workflows/ e GitHub lo eseguirà quando si verifica l'evento specificato.

name: check
on:
  push:
    branches: [main]
jobs:
  shellcheck:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
      - run: sudo apt-get update && sudo apt-get install -y shellcheck
      - run: shellcheck *.sh

Il file è un workflow. Un job viene eseguito su una macchina. Uno step è un comando oppure un'action pubblicata. uses: include un'action da un altro repository e @v7 blocca la versione principale (v7 è quella corrente per actions/checkout ad agosto 2026). Blocca sempre una versione, perché un'action senza versione fissata esegue codice che non hai verificato e che può accedere ai tuoi secret.

runs-on: ubuntu-latest richiede a GitHub una nuova macchina virtuale, che viene eliminata al termine del job. I runner standard sono gratuiti nei repository pubblici e il piano gratuito include 2.000 minuti al mese per i repository privati ad agosto 2026. Consulta la pagina dei prezzi aggiornata prima di basare un preventivo su questo valore.

I secret vengono archiviati nelle impostazioni del repository e letti come ${{ secrets.DEPLOY_KEY }}. Un workflow attivato da una pull request proveniente da un fork riceve un token di sola lettura e non può accedere a questi secret. In caso contrario, un estraneo potrebbe aprire una PR il cui unico scopo è stamparli.

Eseguire il runner di Actions sul proprio VPS

runs-on: self-hosted invia invece il job a una macchina di tua proprietà. La pagina delle impostazioni del runner del repository fornisce una riga di download, l'indirizzo web del repository e un token di registrazione valido per un'ora. Inserisci gli ultimi due elementi in REPO_URL e RUNNER_TOKEN; a quel punto la configurazione richiede tre comandi.

./config.sh --url "$REPO_URL" --token "$RUNNER_TOKEN"
sudo ./svc.sh install
sudo ./svc.sh start
./svc.sh status

svc.sh status dovrebbe indicare il servizio come attivo e mostrare le righe recenti del log. Il runner apre una connessione HTTPS in uscita verso GitHub e richiede i job disponibili, quindi non devi aprire porte in ingresso. svc.sh install scrive l'unità systemd ed è il passaggio che spesso viene saltato: senza di essa, il runner termina insieme alla sessione SSH e ogni job successivo rimane in coda senza una spiegazione. la configurazione completa del runner self-hosted su un VPS descrive la messa in sicurezza e la pulizia necessarie per un runner utilizzato a lungo.

Il vantaggio è che un deploy non richiede più una chiave SSH in ingresso raggiungibile da Internet, perché il job è già in esecuzione sulla macchina. Anche la cache della build resta disponibile tra un'esecuzione e l'altra e non viene conteggiato alcun minuto.

Un avvertimento è fondamentale. La documentazione di GitHub raccomanda di usare i runner self-hosted solo per i repository privati, perché i fork di un repository pubblico possono eseguire codice pericoloso sul runner aprendo una pull request. Il runner esegue tutto ciò che specifica il file del workflow presente in quel branch. In un repository privato, dove controlli chi può effettuare il push, il rischio è ridotto. In un repository pubblico, considera ogni runner self-hosted come una macchina sulla quale estranei possono eseguire codice.

Ti serve davvero GitHub?

No. Git è lo standard, mentre GitHub è una soluzione pratica. Forgejo e Gitea sono forge self-hosted; una forge è un host Git con sistemi per issue e pull request integrati. Entrambi vengono distribuiti come un singolo binario Go ed entrambi funzionano su un VPS di piccole dimensioni. Forgejo è un fork di Gitea del 2022 e oggi alimenta Codeberg. Per spostare un repository basta un comando, perché il protocollo di rete è identico.

git remote -v
git remote set-url origin git@git.example.com:you/vps-deploy.git
git push origin main

Ogni commit viene trasferito, perché ogni clone contiene già l'intera cronologia. Non viene trasferito il livello costruito da GitHub: le issue e le discussioni nelle pull request. Anche la CI non viene trasferita. Forgejo dispone di una propria implementazione di Actions, che legge file YAML simili da .forgejo/workflows/, ma la documentazione chiarisce i limiti: GitHub Actions e Forgejo Actions non sono la stessa cosa e alcune funzionalità potrebbero non funzionare subito. Serve inoltre un runner dedicato. Pianifica questo passaggio come una migrazione, non come una copia.

Il motivo reale per cui la maggior parte dei progetti resta su GitHub sono i contributori. Il codice pubblico deve trovarsi dove le persone hanno già un account. I tuoi script privati di deploy non devono esserlo. Sono due decisioni separate e puoi rispondere in modo diverso a ciascuna.

Che cosa si interrompe per primo e cosa indica l’errore

Un push viene rifiutato. Viene visualizzato quanto segue:

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

Qualcuno ha eseguito un push dall’ultimo pull, spesso dopo una modifica effettuata nell’editor web. Esegui git pull --rebase per riapplicare i tuoi commit sopra quelli presenti nel repository remoto, quindi esegui di nuovo il push. Evita git push --force su un branch condiviso, perché rimuove gli altri commit da quel branch sul server.

fatal: refusing to merge unrelated histories. Hai eseguito git init in locale e hai anche consentito a GitHub di creare il repository con un README. Le due cronologie non condividono alcun commit, quindi Git non può determinare automaticamente come unirle. La soluzione più pulita consiste nel clonare la copia presente su GitHub in una nuova directory e spostare al suo interno i tuoi file.

error: src refspec main does not match any. Il branch indicato non esiste in questo repository. In genere il repository non contiene ancora alcun commit oppure il branch si chiama master. git branch --show-current risolve il problema.

Un secret è finito in un commit. Ruota subito la credenziale. Considerala pubblica dal momento del push, perché fork, mirror e copie memorizzate nella cache possono conservarne versioni che non hai modo di eliminare.

FAQ

GitHub è la stessa cosa di Git?

No. Git è un programma per il controllo di versione che si installa su una macchina e funziona senza rete e senza account. GitHub è un servizio commerciale in hosting che archivia repository Git e aggiunge un'interfaccia web, issue, pull request e CI. Git è stato rilasciato nel 2005 e GitHub è stato lanciato nel 2008 sulla base di Git. È possibile usare Git per sempre senza GitHub. Ogni funzionalità di GitHub dipende da Git.

Mi serve un account GitHub per usare Git sul mio VPS?

No. git init, git commit e git log funzionano su un server senza alcun remote configurato, e questo è già sufficiente per tenere traccia delle modifiche ai file /etc o agli script di deploy. Un account diventa utile quando si vuole una copia della cronologia che resti disponibile anche se il server non è più operativo, oppure una seconda macchina da cui eseguire il clone. Forge self-hosted come Forgejo e Gitea offrono la stessa funzionalità su hardware di proprietà, mentre un semplice remote SSH che punta a un bare repository su un altro host funziona anche senza alcun software forge.

Che cos'è una pull request?

Una pull request è una richiesta di unire un branch a un altro, con una pagina per la discussione associata. Si esegue il push di un branch, si apre la PR verso main e l'host mostra le modifiche commit per commit, così i revisori possono commentare le singole righe e i controlli automatici possono indicare se sono superati o falliti. È una funzionalità di GitHub, non di Git, quindi Git non dispone di alcun comando per gestirla. Altri host implementano la stessa idea, chiamandola talvolta merge request.

È opportuno eseguire un runner GitHub Actions sul proprio VPS?

Per un repository privato, spesso sì. Il job viene eseguito su hardware già disponibile, non vengono conteggiati minuti, la build cache resta disponibile e un deploy non richiede più una chiave SSH in ingresso esposta a Internet, perché il runner stabilisce una connessione in uscita verso GitHub e richiede il lavoro da eseguire. Per un repository pubblico, GitHub lo sconsiglia: chiunque può creare un fork del repository e aprire una pull request il cui workflow esegue codice sulla macchina.

Posso spostare in seguito i miei repository fuori da GitHub?

Il codice sì, senza difficoltà. Ogni clone contiene la cronologia completa, quindi git remote set-url origin <new url> seguito da un push trasferisce tutto ciò che contiene un commit. Rimane invece il livello gestito da GitHub: issue, discussioni delle pull request e cronologia di Actions risiedono nel suo database, non nella cartella .git. Gli strumenti di migrazione possono copiare le issue tramite API, mentre i file workflow di solito devono essere modificati per il CI del nuovo host. Tenerlo presente è un motivo per inserire la documentazione effettiva nel repository anziché nelle discussioni delle issue.

#github#git#version-control#ci-cd#developer-tools