Per espandere la risposta di Ben Jackson , che va bene, esaminiamo attentamente la domanda originale. (Vedi la sua risposta per capire perché preoccuparsi del tipo di domande; questo è più su cosa sta succedendo .)
Sono nuovo nel controllo della versione e capisco che "eseguire il commit" significa essenzialmente creare un backup durante l'aggiornamento della nuova versione "corrente" di ciò su cui stai lavorando.
Questo non è del tutto corretto. I backup e il controllo della versione sono certamente correlati, esattamente quanto fortemente dipende da alcune cose che sono in una certa misura questioni di opinione, ma ci sono certamente alcune differenze, anche se solo nell'intento: i backup sono tipicamente progettati per il ripristino di emergenza (la macchina si guasta, il fuoco distrugge intero edificio compresi tutti i supporti di memorizzazione, ecc.). Il controllo della versione è in genere progettato per interazioni più dettagliate e offre funzionalità che i backup non fanno. I backup vengono generalmente archiviati per un po 'di tempo, quindi eliminati come "troppo vecchi": un backup più fresco è tutto ciò che conta. Il controllo della versione normalmente salva per sempre ogni versione impegnata.
Quello che non capisco è a cosa serve la messa in scena da una prospettiva pratica. La messa in scena è qualcosa che esiste solo di nome o serve a uno scopo? Quando ti impegni, commetterà comunque tutto, giusto?
Sì e no. Il design di Git qui è alquanto peculiare. Esistono sistemi di controllo della versione che non richiedono un passaggio di staging separato. Ad esempio, Mercurial, che altrimenti è molto simile a Git in termini di utilizzo, non richiede un hg add
passaggio separato , oltre al primo che introduce un file completamente nuovo. Con Mercurial, usi il hg
comando che seleziona alcuni commit, poi fai il tuo lavoro, poi corri hg commit
e il gioco è fatto. Con Git, usi git checkout
, 1 poi fai il tuo lavoro, poi corri git add
e poi git commit
. Perché il git add
passaggio in più?
Il segreto qui è ciò che Git chiama, variamente, l' indice o l' area di staging , o talvolta, raramente oggigiorno, la cache . Questi sono tutti nomi per la stessa cosa.
Modifica: penso di poter confondere la terminologia. Un file "messo in scena" è la stessa cosa di un file "tracciato"?
No, ma sono correlati. Un file tracciato è quello che esiste nell'indice di Git. Per comprendere correttamente l'indice, è bene iniziare con la comprensione dei commit.
Dalla versione 2.23 di Git, puoi usare git switch
invece di git checkout
. In questo caso particolare, questi due comandi fanno esattamente la stessa cosa. Il nuovo comando esiste perché è git checkout
stato riempito con troppe cose; sono stati suddivisi in due comandi separati git switch
e git restore
, per rendere più facile e sicuro l'uso di Git.
Si impegna
In Git, un commit salva un'istantanea completa di ogni file di cui Git è a conoscenza. (Di quali file è a conoscenza Git? Lo vedremo nella sezione successiva.) Queste istantanee sono archiviate in una forma speciale, di sola lettura, di sola Git, compressa e deduplicata, che in generale solo Git stesso può leggere . (Ci sono più cose in ogni commit oltre a questa istantanea, ma questo è tutto ciò che tratteremo qui.)
La deduplicazione aiuta con lo spazio: normalmente cambiamo solo pochi file, quindi facciamo un nuovo commit. Quindi la maggior parte dei file in un commit sono per lo più gli stessi dei file nel commit precedente. Semplicemente riutilizzando direttamente quei file, Git risparmia molto spazio: se abbiamo toccato solo un file, il nuovo commit occupa solo spazio per una nuova copia. Anche in questo caso è compresso, a volte molto compresso, anche se in realtà accade in seguito, in modo che una .git
directory possa effettivamente essere più piccola dei file che contiene, una volta espansa ai normali file di tutti i giorni. La deduplicazione è sicura perché i file salvati vengono congelati per sempre. Nessuno può cambiarne uno, quindi è sicuro che i commit dipendono dalle copie degli altri.
Poiché i file archiviati sono in questo formato speciale, congelato per sempre, solo Git, Git deve espandere ogni file in una normale copia di tutti i giorni. Questa copia ordinaria non è la copia di Git : è la tua copia, da fare come vuoi. Git scriverà solo a questi quando gli dici di farlo, in modo che tu abbia le tue copie con cui lavorare. Queste copie utilizzabili sono nell'albero di lavoro o nell'albero di lavoro .
Ciò significa che quando si estrae un commit particolare, ci sono automaticamente due copie di ogni file:
Git ha una copia Git-ified congelata nel commit corrente . Non puoi cambiare questa copia (anche se puoi ovviamente selezionare un commit diverso o crearne uno nuovo).
Hai, nel tuo albero di lavoro, una copia in formato normale. Puoi fare tutto ciò che vuoi, usando uno qualsiasi dei comandi sul tuo computer.
Altri sistemi di controllo della versione (incluso Mercurial come menzionato sopra) si fermano qui, con queste due copie. Devi solo modificare la tua copia dell'albero di lavoro, quindi eseguire il commit. Git ... non lo fa.
L'indice
Tra queste due copie, Git memorizza una terza copia 2 di ogni file. Questa terza copia è nel formato congelato , ma a differenza della copia congelata nel commit, puoi cambiarla. Per cambiarlo, usi git add
.
Il git add
comando significa fare in modo che la copia dell'indice del file corrisponda alla copia dell'albero di lavoro . Cioè, stai dicendo a Git: Sostituisci la copia in formato congelato, deduplicata che si trova nell'indice ora, comprimendo la mia copia dell'albero di lavoro aggiornata, deduplicandola e preparandola per essere congelata in un nuovo commit. Se non lo usi git add
, l'indice conserva ancora la copia in formato congelato dal commit corrente.
Quando si esegue git commit
, Git pacchi fino ciò che è l'indice a destra e poi utilizzare come nuova istantanea. Poiché è già nel formato congelato e pre-deduplicato, Git non deve fare molto lavoro extra.
Questo spiega anche di cosa trattano i file non tracciati . Un file non tracciato è un file che si trova nel tuo albero di lavoro ma non è nell'indice di Git in questo momento . Non importa come il file sia finito in questo stato. Forse l'hai copiato da qualche altro posto sul tuo computer, nel tuo albero di lavoro. Forse l'hai creato fresco qui. Forse c'è stata una copia nell'indice di Git, ma è stato rimosso quella copia con git rm --cached
. In un modo o nell'altro, c'è una copia qui nel tuo albero di lavoro, ma non c'è una copia nell'indice di Git. Se fai un nuovo commit adesso, quel file non sarà nel nuovo commit.
Nota che git checkout
inizialmente compila l'indice di Git dal commit che controlli. Quindi l'indice inizia a corrispondere al commit. Git riempie anche il tuo albero di lavoro da questa stessa fonte. Quindi, inizialmente, tutti e tre corrispondono. Quando modifichi i file nel tuo albero di lavoro e git add
loro, beh, ora l'indice e il tuo albero di lavoro corrispondono. Quindi corri git commit
e Git fa un nuovo commit dall'indice, e ora tutti e tre corrispondono di nuovo.
Poiché Git fa nuovi commit dall'indice, possiamo mettere le cose in questo modo: l'indice di Git contiene il prossimo commit che prevedi di fare. Questo ignora il ruolo espanso che l'indice di Git assume durante un'unione in conflitto, ma vorremmo comunque ignorarlo per ora. :-)
È tutto quello che c'è da fare, ma è ancora piuttosto complicato! È particolarmente complicato perché non esiste un modo semplice per vedere esattamente cosa c'è nell'indice di Git. 3 Ma v'è un comando Git che ti dice cosa sta succedendo, in un modo che è molto utile, e questo comando è git status
.
2 Tecnicamente, questa non è affatto una copia . Invece, è un riferimento al file Git-ified, pre-deduplicato e tutto il resto. Ci sono anche più cose qui, come la modalità, il nome del file, un numero di staging e alcuni dati della cache per rendere Git veloce. Ma a meno che non ti metti a lavorare con alcuni dei comandi di basso livello di Git, git ls-files --stage
e git update-index
in particolare, puoi solo pensarlo come una copia.
3 Il git ls-files --stage
comando ti mostrerà i nomi e i numeri di staging di ogni file nell'indice di Git, ma di solito questo non è molto utile comunque.
git status
Il git status
comando funziona effettivamente eseguendo due git diff
comandi separati per te (e anche facendo altre cose utili, come dirti su quale ramo ti trovi).
Il primo git diff
confronta il commit corrente, che, ricorda, è congelato per sempre, con qualsiasi cosa sia nell'indice di Git. Per i file che sono gli stessi , Git non dirà nulla. Per i file che sono diversi , Git ti dirà che questo file è organizzato per il commit . Questo include tutti i nuovi file, se l'impegno non ha sub.py
al suo interno, ma l'indice fa avere sub.py
al suo interno, poi viene aggiunto-e questo file qualsiasi file rimossi, che erano (e sono) nel commettere, ma non sono in l'indice ancora ( git rm
, forse).
Il secondo git diff
confronta tutti i file nell'indice di Git con i file nel tuo albero di lavoro. Per i file che sono gli stessi , Git non dice nulla. Per i file che sono diversi , Git ti dirà che questo file non è predisposto per il commit . A differenza della prima differenza, questo particolare elenco non include i file completamente nuovi: se il file untracked
esiste nel tuo albero di lavoro, ma non nell'indice di Git, Git lo aggiunge semplicemente all'elenco dei file non tracciati . 4
Alla fine, dopo aver accumulato questi file non tracciati in un elenco, git status
annuncerà anche i nomi di quei file, ma c'è un'eccezione speciale: se il nome di un file è elencato in un .gitignore
file, questo sopprime quest'ultimo elenco. Nota che elencare un file tracciato , uno che si trova nell'indice di Git, in a .gitignore
non ha alcun effetto qui : il file è nell'indice, quindi viene confrontato e viene eseguito il commit, anche se è elencato in .gitignore
. Il file ignora sopprime solo i reclami "file non tracciato". 5
4 Quando si utilizza la versione breve di git status
- git status -s
- i file non tracciati non sono così separati, ma il principio è lo stesso. L'accumulo di file in questo modo consente anche di git status
riassumere un gruppo di nomi di file non tracciati stampando semplicemente il nome di una directory, a volte. Per ottenere l'elenco completo, utilizzare git status -uall
o git status -u
.
5 Elencare un file fa anche aggiungere in massa molte operazioni sui file come git add .
o git add *
saltare il file non tracciato. Questa parte diventa un po 'più complicata, dal momento che puoi usare git add --force
per aggiungere un file che normalmente verrebbe saltato. Ci sono altri casi speciali normalmente minori, che si sommano a questo: il file .gitignore
potrebbe essere chiamato più correttamente .git-do-not-complain-about-these-untracked-files-and-do-not-auto-add-them
o qualcosa di altrettanto ingombrante. Ma è troppo ridicolo, così .gitignore
è.
git add -u
, git commit -a
, Ecc
Ci sono diverse scorciatoie utili da conoscere qui:
git add .
aggiungerà tutti i file aggiornati nella directory corrente e in qualsiasi sottodirectory. Questo rispetta .gitignore
, quindi se un file attualmente non tracciato non viene lamentato da git status
, non verrà aggiunto automaticamente.
git add -u
aggiungerà automaticamente tutti i file aggiornati ovunque nel tuo albero di lavoro . 6 Questo riguarda solo i file tracciati . Nota che se hai rimosso la copia dell'albero di lavoro, questo rimuoverà anche la copia dell'indice ( git add
fa questo come parte del suo fare in modo che l'indice corrisponda alla cosa dell'albero di lavoro ).
git add -A
è come correre git add .
dal livello più alto del tuo albero di lavoro (ma vedi la nota 6).
Oltre a questi, puoi correre git commit -a
, che equivale più o meno a 7 a correre git add -u
e poi git commit
. Cioè, questo ti dà lo stesso comportamento che è conveniente in Mercurial.
In genere sconsiglio il git commit -a
pattern: trovo che sia meglio usarlo git status
spesso, guarda attentamente l'output e se lo stato non è quello che ti aspettavi, scopri perché è così. Utilizzando git commit -a
, è troppo facile da modificare accidentalmente un file e commettere un cambiamento non avete intenzione di commettere. Ma questa è principalmente una questione di gusti / opinioni.
6 Se la tua versione di Git è precedente a Git 2.0, fai attenzione qui: git add -u
funziona solo sulla directory e sottodirectory correnti, quindi devi prima salire al livello più alto del tuo albero di lavoro. L' git add -A
opzione ha un problema simile.
7 Dico più o meno equivalente perché in git commit -a
realtà funziona creando un indice extra e usando quell'altro indice per eseguire il commit. Se il commit funziona , ottieni lo stesso effetto del fare git add -u && git commit
. Se il commit non funziona, se fai in modo che Git salti il commit in uno dei tanti modi in cui puoi farlo, in seguito nessun file viene git add
eliminato, perché Git elimina l'indice aggiuntivo temporaneo e torna a utilizzare l'indice principale .
Ci sono ulteriori complicazioni che arrivano se usi git commit --only
qui. In questo caso, Git crea un terzo indice e le cose diventano molto complicate, specialmente se usi hook pre-commit. Questo è un altro motivo per utilizzare git add
operazioni separate .