Perché dovrei voler stage prima di impegnarmi in Git?


102

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.

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?

Modifica: penso di poter confondere la terminologia. Un file "messo in scena" è la stessa cosa di un file "tracciato"?


6
No. Un file tracciato è un file noto al repository (in genere da un commit precedente). Un file staged è quello che è stato aggiunto all'indice, che verrà successivamente utilizzato per eseguire il commit.
Mark Peters

Risposte:


82

Quando effettui il commit, eseguirà solo il commit delle modifiche nell'indice (i file "staged"). Ci sono molti usi per questo, ma il più ovvio è suddividere le modifiche di lavoro in parti più piccole e autonome. Forse hai corretto un bug mentre implementavi una funzionalità. Puoi git addsolo quel file (o git add -paggiungere solo una parte di un file!) E poi eseguire il commit della correzione del bug prima di eseguire il commit di tutto il resto. Se stai usando, git commit -astai solo forzando un adddi tutto prima del commit. Non utilizzare -ase desideri trarre vantaggio dai file di gestione temporanea.

È inoltre possibile trattare i file di staging come una copia di lavoro intermedia con i --cachedcomandi a molti. Ad esempio, git diff --cachedti mostrerà come la fase differisce da in HEADmodo da poter vedere cosa stai per impegnare senza mescolare le altre modifiche di lavoro.


25
L'altro uso veramente comune è quando alcune delle tue modifiche non dovrebbero mai essere applicate; per esempio, puoi mettere in scena le cose buone, commetterle, quindi spazzare via le cose cattive con git reset --hard.
Cascabel

3
@ BenJackson nel tuo esempio, qual è la differenza tra stage + commit e commit selettivo? Non vedo alcuna differenza.
Eugenio

9
Personalmente non sono stato in grado di ottenere una risposta soddisfacente al "Qual è lo scopo della messa in scena?" domanda. Francamente, è solo inutile. Sto già utilizzando una filiale locale, quindi non c'è rischio di rompere la build. Non pubblicherò né eseguirò una richiesta pull finché non sarò completamente soddisfatto delle mie modifiche. Posso già suddividere logicamente i miei commit. Non è necessario un passaggio intermedio. In quanto tale, non lo uso mai.
mrplainswalker

3
Non credo che tu abbia davvero risposto alla domanda. "ma la più ovvia è suddividere le modifiche di lavoro in parti più piccole e autonome." Perché qualcuno dovrebbe voler suddividere i propri cambiamenti in quelli più piccoli? Se hai intenzione di aggiungere e eseguire il commit di una singola correzione di bug prima di correggere il codice che originariamente intendevi modificare, perché non dovresti semplicemente eseguire il commit di quella correzione di bug invece di aggiungerla e quindi impegnarla?
kiwicomb123

1
@ kiwicomb123 Di solito perché hai trovato quel bug mentre lavoravi su qualcos'altro e vuoi avere quella correzione nel suo commit con il suo messaggio di log e la flessibilità di unire / cherry-pick / rebase che si risolve da qualche altra parte.
Ben Jackson

26
  • L'area di staging dà il controllo per ridurre il commit. Basta apportare una modifica logica nel codice, aggiungere i file modificati all'area di staging e, infine, se le modifiche sono errate, eseguire il checkout al commit precedente o altrimenti eseguire il commit delle modifiche.Da la flessibilità di dividere l'attività in attività più piccole e commit più piccole i cambiamenti. Con l'area di staging è più facile concentrarsi su piccoli compiti.
  • Ti offre anche la possibilità di fare una pausa e dimenticare quanto lavoro hai fatto prima di fare una pausa. Supponiamo di dover modificare tre file per apportare una modifica logica e di aver modificato il primo file e di aver bisogno di una lunga pausa fino a quando non inizi ad apportare le altre modifiche. In questo momento non puoi eseguire il commit e vuoi tenere traccia di quali file hai finito in modo che dopo essere tornato non devi cercare di ricordare quanto lavoro è stato fatto. Quindi aggiungi il file all'area di staging e salverà il tuo lavoro. Quando torni, git diff --stagedcontrolla quali file hai modificato e dove e inizia a fare altre modifiche.

13

Uno scopo pratico dello staging è la separazione logica dei file commit.

Poiché lo staging ti consente di continuare ad apportare modifiche ai file / directory di lavoro e di eseguire commit in parti quando ritieni che le cose siano pronte, puoi utilizzare fasi separate per modifiche logicamente non correlate.

Supponiamo di avere 4 file fileA.html, fileB.html, fileC.htmle fileD.html. Apporti modifiche a tutti e 4 i file e sei pronto per il commit, ma le modifiche fileA.htmle fileB.htmlsono logicamente correlate (ad esempio, la stessa implementazione della nuova funzionalità in entrambi i file) mentre le modifiche in fileC.htmle fileD.htmlsono separate e logicamente non correlate ai file precedenti. Si file prima fase si possono fileA.htmle fileB.htmle impegnarsi quelli.

git add fileA.html
git add fileB.html
git commit -m "Implemented new feature XYZ"

Quindi, nel passaggio successivo, si esegue lo stage e si applicano le modifiche ai due file rimanenti.

git add fileC.html
git add fileD.html
git commit -m "Implemented another feature EFG"

6
In questo esempio, non sono sicuro che la messa in scena sia davvero necessaria. Dopo aver modificato tutti e 4 i file, se voglio solo eseguire il commit di fileA.html e fileB.html, posso ancora eseguire il commit senza metterlo in scena. Il comando: git commit -m "Implemented new feature XYZ" fileA.html fileB.html funzionerebbe perfettamente senza bisogno dei comandi git add. Vengo dal mondo di sovversione in cui la messa in scena non è un concetto, quindi non sono convinto dell'utilità della messa in scena git
Pavan

5

È più facile capire l'uso dei comandi git adde commitse immagini un file di registro mantenuto nel tuo repository su Github. Il file di registro di un progetto tipico per me potrebbe essere simile a:

---------------- Day 1 --------------------
Message: Complete Task A
Index of files changed: File1, File2

Message: Complete Task B
Index of files changed: File2, File3
-------------------------------------------

---------------- Day 2 --------------------
Message: Correct typos
Index of files changed: File3, File1
-------------------------------------------
...
...
...and so on

Di solito inizio la mia giornata con una git pullrichiesta e la concludo con una git pushrichiesta. Quindi tutto ciò che si trova nel record di una giornata corrisponde a ciò che accade tra di loro. Durante ogni giorno, vengono completate una o più attività logiche che richiedono la modifica di alcuni file. I file modificati durante tale attività vengono elencati in un indice.

Ciascuna di queste attività secondarie (attività A e attività B qui) sono singole commit. Il git addcomando aggiunge file all'elenco "Indice dei file modificati". Questo processo è anche chiamato messa in scena. Il git commitcomando registra / finalizza le modifiche e l'elenco di indice corrispondente insieme a un messaggio personalizzato.

Ricorda che stai ancora cambiando solo la copia locale del tuo repository e non quella su Github. Dopodiché, solo quando esegui un "git push" tutte queste modifiche registrate, insieme ai tuoi file di indice per ogni commit, vengono registrati nel repository principale (su Github).

Ad esempio, per ottenere la seconda voce in quel file di log immaginario, avrei fatto:

git pull
# Make changes to these files
git add File3 File4
# Verify changes, run tests etc..
git commit -m 'Correct typos'
git push

In poche parole, git adde git commitconsente di suddividere una modifica al repository principale in modifiche secondarie logiche sistematiche. Come altre risposte e commenti hanno sottolineato, ci sono ovviamente molti altri usi. Tuttavia, questo è uno degli usi più comuni e un principio alla base del fatto che Git è un sistema di controllo delle revisioni a più stadi a differenza di altri popolari come Svn.


2

L'area di staging ci aiuta a creare i commit con maggiore flessibilità. Per crafting, intendo suddividere i commit in unità logiche. Questo è molto importante se vuoi un software manutenibile. Il modo più ovvio per ottenere questo risultato:

Puoi lavorare su più funzionalità / bug in una singola directory di lavoro e continuare a creare commit significativi. Anche avere una singola directory di lavoro che contiene tutto il nostro lavoro attivo è molto conveniente. (Questo può essere fatto senza un'area di staging, solo a condizione che le modifiche non si sovrappongano mai a un file. E hai anche l'ulteriore responsabilità di monitorare manualmente se si sovrappongono)

Puoi trovare altri esempi qui: Uses of Index

E la parte migliore è che i vantaggi non si fermano a questo elenco di flussi di lavoro. Se viene fuori un flusso di lavoro unico, puoi essere quasi sicuro che l'area di staging ti aiuterà.


2

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 addpassaggio separato , oltre al primo che introduce un file completamente nuovo. Con Mercurial, usi il hgcomando che seleziona alcuni commit, poi fai il tuo lavoro, poi corri hg commite il gioco è fatto. Con Git, usi git checkout, 1 poi fai il tuo lavoro, poi corri git adde poi git commit. Perché il git addpassaggio 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 switchinvece di git checkout. In questo caso particolare, questi due comandi fanno esattamente la stessa cosa. Il nuovo comando esiste perché è git checkoutstato riempito con troppe cose; sono stati suddivisi in due comandi separati git switche 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 .gitdirectory 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 addcomando 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 checkoutinizialmente 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 addloro, beh, ora l'indice e il tuo albero di lavoro corrispondono. Quindi corri git commite 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 --stagee git update-indexin particolare, puoi solo pensarlo come una copia.

3 Il git ls-files --stagecomando 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 statuscomando funziona effettivamente eseguendo due git diffcomandi separati per te (e anche facendo altre cose utili, come dirti su quale ramo ti trovi).

Il primo git diffconfronta 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.pyal suo interno, ma l'indice fa avere sub.pyal 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 diffconfronta 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 untrackedesiste 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 statusannuncerà anche i nomi di quei file, ma c'è un'eccezione speciale: se il nome di un file è elencato in un .gitignorefile, questo sopprime quest'ultimo elenco. Nota che elencare un file tracciato , uno che si trova nell'indice di Git, in a .gitignorenon 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 statusriassumere 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 -uallo 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 --forceper aggiungere un file che normalmente verrebbe saltato. Ci sono altri casi speciali normalmente minori, che si sommano a questo: il file .gitignorepotrebbe essere chiamato più correttamente .git-do-not-complain-about-these-untracked-files-and-do-not-auto-add-themo 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 -uaggiungerà 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 addfa 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 -ue poi git commit. Cioè, questo ti dà lo stesso comportamento che è conveniente in Mercurial.

In genere sconsiglio il git commit -apattern: trovo che sia meglio usarlo git statusspesso, 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 -ufunziona solo sulla directory e sottodirectory correnti, quindi devi prima salire al livello più alto del tuo albero di lavoro. L' git add -Aopzione ha un problema simile.

7 Dico più o meno equivalente perché in git commit -arealtà 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 addeliminato, perché Git elimina l'indice aggiuntivo temporaneo e torna a utilizzare l'indice principale .

Ci sono ulteriori complicazioni che arrivano se usi git commit --onlyqui. 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 addoperazioni separate .


1

Capisco il motivo per cui usare lo stage per rendere i commit più piccoli, come menzionato da @Ben Jackson e @Tapashee Tabassum Urmi ea volte lo uso per quello scopo, ma lo uso principalmente per rendere i miei commit più grandi! ecco il mio punto:

Diciamo che voglio aggiungere una piccola funzionalità che richiede diversi passaggi più piccoli. Non vedo alcun motivo per avere un commit separato per passaggi più piccoli e inondare la mia sequenza temporale. Tuttavia voglio salvare ogni passaggio e tornare indietro se necessario,

Metto semplicemente in scena i passaggi più piccoli uno sopra l'altro e quando sento che è degno di un impegno, mi impegno. In questo modo rimuovo i commit non necessari dalla timeline ma in grado di annullare (checkout) l'ultimo passaggio.

Vedo altri modi per farlo (semplificando la cronologia di git) che potresti usare a seconda delle tue preferenze:

  1. git modify (che cambia il tuo ultimo commit) che non è qualcosa che vuoi per questo scopo specifico (lo vedo principalmente come fare un cattivo commit e poi risolverlo)
  2. git rebase, che è un ripensamento e può causare seri problemi a te e ad altri che usano il tuo repository.
  3. creare un ramo temporaneo, unirlo e quindi eliminarlo in seguito (che è anche una buona opzione, richiede più passaggi ma dà più controllo)

0

È come una casella di controllo che offre la possibilità di scegliere quali file eseguire il commit.

ad esempio, se ho modificato fileA.txte fileB.txt.Ma voglio eseguire il commit delle modifiche fileA.txtsolo di. perché non ho ancora finito fileB.txt.

Posso semplicemente usare git add fileA.txte impegnarmi usando git commit -m "changed fileA.txt"e continuare a lavorare fileB.txte dopo aver terminato posso impegnarmi fileB.txtfacilmente

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.