I rami moltiplicano le funzionalità di base offerte dai commit consentendo agli utenti di riporre la propria cronologia. La creazione di un nuovo ramo è simile alla richiesta di un nuovo ambiente di sviluppo, completo di una directory di lavoro isolata, area di gestione temporanea e cronologia del progetto.
Questo ti dà la stessa tranquillità di una copia "sicura" del tuo progetto, ma ora hai la capacità aggiuntiva di lavorare su più versioni allo stesso tempo. I rami abilitano a flusso di lavoro non lineare-la capacità di sviluppare funzionalità non correlate in parallelo. Come scopriremo più avanti, un flusso di lavoro non lineare è un importante precursore della natura distribuita del modello di collaborazione di Git.
A differenza di SVN o CVS, l'implementazione di ramo di Git è incredibilmente efficiente. SVN abilita i rami copiando l'intero progetto in una nuova cartella, proprio come faresti senza alcun software di controllo di revisione. Ciò rende le fusioni goffe, soggette a errori e lente. Al contrario, i rami Git sono semplicemente un puntatore a un commit. Poiché lavorano sul livello di commit anziché direttamente a livello di file, le filiali di Git rendono molto più facile unire storie divergenti. Ciò ha un impatto drammatico sui flussi di lavoro di ramificazione.
Git separa la funzionalità del ramo in alcuni comandi diversi. Il ramo di git
comando è usato per elencare, creare o eliminare rami.
Innanzitutto, devi essere in grado di visualizzare i tuoi rami esistenti:
ramo di git
In questo modo verranno visualizzati tutti i rami correnti, insieme a un asterisco accanto a quello attualmente "ritirato" (ne riparleremo più avanti):
* master alcune-funzionalità quick-bug-fix
Il maestro
branch è il ramo predefinito di Git, che viene creato con il primo commit in qualsiasi repository. Molti sviluppatori usano questo ramo come la "principale" storia del progetto, una branca permanente che contiene tutti i principali cambiamenti che attraversa.
È possibile creare un nuovo ramo passando il nome del ramo allo stesso ramo di git
comando:
ramo di git
Questo crea un puntatore alla corrente CAPO
, ma lo fa non passa al nuovo ramo (ne avrai bisogno git checkout
per quello). Subito dopo aver richiesto un nuovo ramo, il tuo repository avrà un aspetto simile al seguente.
Il tuo attuale ramo (maestro
) e il nuovo ramo (un po 'di funzionalità
) entrambi fanno riferimento allo stesso commit, ma qualsiasi nuovo commit registrato sarà esclusivo per il ramo corrente. Di nuovo, questo ti consente di lavorare su funzionalità non correlate in parallelo, mantenendo allo stesso tempo storie sensate. Ad esempio, se il tuo attuale ramo era un po 'di funzionalità
, la tua cronologia sarà simile alla seguente dopo aver eseguito un'istantanea.
un po 'di funzionalità
ramo Il nuovo CAPO
(indicato dal commit evidenziato) esiste solo nel file un po 'di funzionalità
ramo. Non verrà visualizzato nell'output del registro di maestro
, né le sue modifiche appariranno nella directory di lavoro dopo il check out maestro
.
È possibile visualizzare il nuovo ramo nel database interno aprendo il file .git / refs / teste /
. Il file contiene l'ID del commit di riferimento ed è l'unica definizione di un ramo Git. Questo è il motivo per cui i rami sono così leggeri e facili da gestire.
Infine, puoi eliminare i rami tramite il -d
bandiera:
git branch -d
Ma la dedizione di Git a non perdere mai il tuo lavoro gli impedisce di rimuovere filiali con commit non interrotti. Per forzare la cancellazione, utilizzare il -D
bandiera invece:
git branch -D
Le modifiche non interrotte andranno perse, quindi essere molto attenti con questo comando.
Naturalmente, la creazione di rami è inutile senza la possibilità di passare da uno all'altro. Git chiama questo "check out" di un ramo:
git checkout
Dopo aver controllato il ramo specificato, la directory di lavoro viene aggiornata per corrispondere al commit del ramo specificato. Inoltre, il CAPO
viene aggiornato per puntare al nuovo ramo e tutti i nuovi commit verranno memorizzati sul nuovo ramo. Puoi pensare di controllare un ramo come passare a una nuova cartella di progetto, tranne che sarà molto più facile riportare le modifiche nel progetto.
Tenendo presente questo, di solito è una buona idea avere un pulito directory di lavoro prima di controllare una filiale. Una directory pulita esiste quando non ci sono modifiche non salvate. Se questo non è il caso, git checkout
ha il potenziale per sovrascrivere le tue modifiche.
Come con una revisione "sicura", sei libero di sperimentare su una nuova filiale senza timore di distruggere le funzionalità esistenti. Ma ora hai una storia dedicata con cui lavorare, così puoi registrare i progressi di un esperimento usando la stessa identica aggiungi git
e Git commit
comandi precedenti nel libro.
Questa funzionalità diventerà ancora più potente una volta che impareremo come unire le storie divergenti nel ramo "principale" (ad es.., maestro
). Ci arriveremo tra un momento, ma prima, c'è un caso d'uso importante di git checkout
che deve essere considerato ...
Git ti consente anche di usare git checkout
con tag e commit ID, ma così facendo ti mette in a stato di TESTA distaccato. Ciò significa che non sei più su un ramo: stai visualizzando direttamente un commit.
Puoi guardarti intorno e aggiungere nuovi commit come al solito, ma dal momento che non c'è alcun ramo che punta alle aggiunte, perderai tutto il tuo lavoro non appena torni a un ramo reale. Fortunatamente, creando un nuovo ramo in un distaccato CAPO
stato è abbastanza facile:
git checkout -b
Questa è una scorciatoia per ramo di git
seguito da git checkout
. Dopo di che, avrai un nuovo brillante riferimento al ramo precedentemente distaccato CAPO
. Questa è una procedura molto utile per estirpare gli esperimenti da vecchie revisioni.
L'unione è il processo di estrazione dei commit da un ramo all'altro. Esistono molti modi per combinare i rami, ma l'obiettivo è sempre quello di condividere le informazioni tra i rami. Questo rende la fusione di una delle caratteristiche più importanti di Git. Le due metodologie di unione più comuni sono:
Entrambi usano lo stesso comando, git si fondono
, ma il metodo viene determinato automaticamente in base alla struttura della cronologia. In ogni caso, il ramo in cui vuoi unire deve essere estratto, e il ramo bersaglio rimarrà invariato. Le prossime due sezioni presentano due possibili scenari di unione per i seguenti comandi:
git checkout master git unire alcune funzionalità
Ancora una volta, questo unisce il un po 'di funzionalità
ramo nel maestro
ramo, lasciando intatto il primo. In genere, esegui questi comandi una volta completata una funzione e desideri integrarla nel progetto stabile.
Il primo scenario si presenta così:
Abbiamo creato una filiale per sviluppare alcune nuove funzionalità, aggiunto due commit e ora è pronta per essere integrata nella base di codice principale. Invece di riscrivere i due commit mancanti maestro
, Git può "avanzare rapidamente" maestro
puntatore del ramo per corrispondere alla posizione di un po 'di funzionalità
.
Dopo l'unione, il maestro
il ramo contiene tutta la cronologia desiderata e il ramo della funzione può essere eliminato (a meno che non si voglia continuare a svilupparlo). Questo è il tipo più semplice di unione.
Naturalmente, avremmo potuto fare i due commit direttamente sul maestro
ramo; tuttavia, l'utilizzo di un ramo di funzionalità dedicato ci ha fornito un ambiente sicuro per sperimentare con il nuovo codice. Se non fosse andato bene, avremmo potuto semplicemente cancellare il ramo (contrario al reset / ripristino). Oppure, se aggiungessimo un gruppo di commit intermedi contenenti codice non funzionante, potremmo ripulirlo prima di unirlo maestro
(vedi Riscossione sotto). Man mano che i progetti diventano più complicati e acquisiscono più collaboratori, questo tipo di sviluppo ramificato rende Git un fantastico strumento organizzativo.
Ma non tutte le situazioni sono abbastanza semplici per un commit rapido. Ricorda, il vantaggio principale dei rami è la possibilità di esplorare contemporaneamente molte linee di sviluppo indipendenti. Di conseguenza, incontrerai spesso uno scenario simile al seguente:
Questo è iniziato come una fusione veloce, ma abbiamo aggiunto un commit al maestro
ramo mentre stavamo ancora sviluppando un po 'di funzionalità
. Ad esempio, avremmo potuto smettere di lavorare sulla funzionalità per correggere un bug sensibile al tempo. Ovviamente, la correzione di bug dovrebbe essere aggiunta al repository principale il prima possibile, quindi finiremo nello scenario mostrato sopra.
Unione del ramo di funzionalità in maestro
in questo contesto risulta un'unione "a 3 vie". Questo si ottiene usando gli stessi identici comandi della fusione veloce nella sezione precedente.
Git non può eseguire l'avanzamento rapido del maestro
puntatore a un po 'di funzionalità
senza retrocedere. Invece, genera un nuovo Unisci commit che rappresenta l'istantanea combinata di entrambi i rami. Si noti che questo nuovo commit ha Due il genitore si impegna, dandogli accesso a entrambe le storie (anzi, correndo log git
dopo la fusione a 3 vie mostra i commit da entrambe le filiali).
Il nome di questo algoritmo di fusione deriva dal metodo interno utilizzato per creare il commit di unione. Git guarda tre si impegna a generare lo stato finale dell'unione.
Se provi a combinare due rami che apportano modifiche diverse alla stessa porzione di codice, Git non saprà quale versione utilizzare. Questo è chiamato a unire conflitto. Ovviamente, questo non può mai accadere durante una fusione veloce. Quando Git incontra un conflitto di unione, verrà visualizzato il seguente messaggio:
Auto-merging index.html CONFLICT (contenuto): Unisci conflitto inUnione automatica fallita; correggere i conflitti e quindi confermare il risultato.
Invece di aggiungere automaticamente il merge commit, Git si ferma e ti chiede cosa fare. In esecuzione stato git
in questa situazione restituirà qualcosa di simile al seguente:
# Sul master del ramo # Percorsi non raggruppati: # # entrambi modificati:
Ogni file con un conflitto è memorizzato nella sezione "Percorsi non raggruppati". Git annota questi file per mostrarti il contenuto di entrambe le versioni:
<<<<<<< HEAD This content is from the current branch. ======= This is a conflicting change from another branch. >>>>>>> alcune funzionalità
La parte prima del =======
è dal maestro
ramo, e il resto è dal ramo che stai cercando di integrare.
Per risolvere il conflitto, rimuovere il <<<<<<
, =======
, e >>>>>>>
notazione e cambia il codice in qualunque cosa tu voglia mantenere. Quindi, dì a Git che hai finito di risolvere il conflitto con il aggiungi git
comando:
aggiungi git
Giusto; tutto ciò che devi fare è mettere in scena il file in conflitto per contrassegnarlo come risolto. Infine, completa l'unione a 3 vie generando il commit unione:
Git commit
Il messaggio di registro è seminato con una notifica di unione, insieme a un elenco di "conflitti", che può essere particolarmente utile quando si cerca di capire dove qualcosa è andato storto in un progetto.
E questo è tutto ciò che c'è da unire in Git. Ora che abbiamo una comprensione dei meccanismi alla base delle filiali di Git, possiamo dare uno sguardo approfondito su come i veterani utenti Git fanno leva sulle filiali nel loro flusso di lavoro quotidiano.
I flussi di lavoro presentati in questa sezione sono il segno distintivo del controllo di revisione basato su Git. La natura leggera e di facile unione dell'implementazione delle filiali di Git li rende uno degli strumenti più produttivi nel vostro arsenale di sviluppo software.
Tutti i flussi di lavoro ramificati ruotano attorno al ramo di git
, git checkout
, e git si fondono
comandi presentati all'inizio di questo capitolo.
Spesso è utile assegnare un significato speciale a diversi rami per l'organizzazione di un progetto. Questa sezione introduce i tipi più comuni di ramificazioni, ma tieni presente che queste distinzioni sono puramente superficiali - a Git, un ramo è un ramo.
Tutti i rami possono essere classificati come entrambi permanente rami o rami di argomento. Il primo contiene la storia principale di un progetto (ad es., maestro
), mentre i secondi sono rami temporanei utilizzati per implementare uno specifico argomento, quindi scartato (ad es., un po 'di funzionalità
).
I rami permanenti sono la linfa vitale di qualsiasi repository. Contengono tutti i principali waypoint di un progetto software. La maggior parte degli sviluppatori usa maestro
esclusivamente per codice stabile. In questi flussi di lavoro, tu mai impegnarsi direttamente maestro
-è solo un ramo di integrazione per le funzionalità completate che sono state costruite in rami argomento dedicati.
Inoltre, molti utenti aggiungono un secondo livello di astrazione in un altro ramo di integrazione (chiamato convenzionalmente sviluppare
, anche se qualsiasi nome sarà sufficiente). Questo libera il maestro
filiale per veramente codice stabile (ad es. commit pubblici) e usi sviluppare
come un ramo di integrazione interna per preparare un rilascio pubblico. Ad esempio, lo schema seguente mostra alcune funzionalità integrate sviluppare
, quindi un singolo, finale si fondono in maestro
, che simboleggia una versione pubblica.
maestro
filiale esclusivamente per comunicati pubblici I rami degli argomenti si dividono generalmente in due categorie: feature branch e rami di aggiornamento rapido. I branch feature sono rami temporanei che incapsulano una nuova funzione o refactoring, proteggendo il progetto principale da codice non testato. Generalmente derivano da un altro ramo di funzionalità o da un ramo di integrazione, ma non dal ramo "super stabile".
I rami di aggiornamento rapido sono simili in natura, ma derivano dal ramo di rilascio pubblico (ad es., maestro
). Invece di sviluppare nuove funzionalità, servono per aggiornare rapidamente la linea principale di sviluppo. In genere, questo significa correzioni di bug e altri aggiornamenti importanti che non possono attendere fino alla prossima versione principale.
maestro
con un ramo di aggiornamento rapido Di nuovo, i significati assegnati a ciascuno di questi rami sono puramente convenzionali: Git non vede alcuna differenza maestro
, sviluppare
, funzionalità e hotfix. Con questo in mente, non aver paura di adattarli ai tuoi fini. La bellezza di Git è la sua flessibilità. Quando capisci i meccanismi alla base delle filiali Git, è facile progettare nuovi flussi di lavoro che si adattino al tuo progetto e alla tua personalità.
La ridefinizione è il processo di spostamento di un ramo in un nuovo base. Le funzionalità di rebasing di Git rendono le filiali ancora più flessibili consentendo agli utenti di organizzare manualmente le loro filiali. Mi piace fondere, git rebase
richiede il controllo del ramo e prende come argomento la nuova base:
git checkout some-feature git rebase master
Questo sposta l'intero un po 'di funzionalità
ramo sulla punta di maestro
:
un po 'di funzionalità
sul maestro
ramo Dopo il rebase, il ramo della funzione è a estensione lineare di maestro
, che è un modo molto più pulito per integrare i cambiamenti da un ramo all'altro. Confronta questa storia lineare con una fusione di maestro
in un po 'di funzionalità
, che risulta nella stessa identica base di codice nell'istantanea finale:
maestro
in un po 'di funzionalità
con un unione a 3 direzioni Dal momento che la cronologia è divergente, Git deve utilizzare un commit di unione aggiuntivo per combinare i rami. Fare questo molte volte durante lo sviluppo di una funzionalità di lunga durata può portare a una cronologia molto complicata.
Questi commit extra merge sono superflui: esistono solo per tirare i cambiamenti da maestro
in un po 'di funzionalità
. In genere, ti consigliamo di eseguire il commit della fusione significare qualcosa, come il completamento di una nuova funzionalità. Questo è il motivo per cui molti sviluppatori scelgono di apportare modifiche con git rebase
, dal momento che risulta in una storia completamente lineare nel ramo della funzione.
Il rebasing interattivo fa un ulteriore passo avanti e ti permette di farlo modificare commette mentre li stai spostando nella nuova base. È possibile specificare un rebase interattivo con -io
bandiera:
git rebase -i master
Questo popola un editor di testo con un riepilogo di ogni commit nel ramo della funzione, insieme a un comando che determina Come dovrebbe essere trasferito alla nuova base. Ad esempio, se si hanno due commit su un ramo di funzionalità, è possibile specificare un rebase interattivo come il seguente:
scegli 58dec2a Primo commit per la nuova funzionalità 6ac8a9f Seconda commit per la nuova funzione
Il predefinito raccogliere
il comando sposta il primo commit sulla nuova base esattamente come il normale git rebase
, ma poi il schiacciare
il comando dice a Git di combinare il secondo commit con quello precedente, quindi si finisce con un commit contenente tutte le modifiche:
un po 'di funzionalità
ramo Git fornisce diversi comandi rebasing interattivi, ognuno dei quali è riassunto nella sezione commenti della lista di configurazione. Il punto è il rebasing interattivo che ti consente completamente riscrivi la storia di un ramo secondo le tue specifiche esatte. Ciò significa che puoi aggiungere tutti i commit intermedi a un ramo di funzionalità di cui hai bisogno, quindi tornare indietro e correggerli in una progressione significativa dopo il fatto.
Altri sviluppatori penseranno che tu sia un programmatore brillante e hai saputo esattamente come implementare l'intera funzionalità in un colpo solo. Questo tipo di organizzazione è molto importante per garantire che i grandi progetti abbiano una storia navigabile.
Il rebasing è uno strumento potente, ma devi essere giudizioso nella tua riscrittura della storia. Entrambi i tipi di rebasing in realtà non lo sono mossa Impegni esistenti-loro creare nuovi di zecca (contrassegnati da un asterisco nello schema sopra). Se controlli i commit che sono stati sottoposti a un rebase, noterai che hanno ID diversi, anche se rappresentano lo stesso contenuto. Questo significa ribasare distrugge Impegni esistenti nel processo di "spostarli".
Come puoi immaginare, questo ha conseguenze drammatiche per i flussi di lavoro collaborativi. Distruggere un commit pubblico (ad esempio, qualsiasi cosa su maestro
ramo) è come strappare la base del lavoro di tutti gli altri. Git non avrà alcuna idea di come combinare i cambiamenti di tutti e avrai un sacco di scuse da fare. Daremo un'occhiata più approfondita a questo scenario dopo aver appreso come comunicare con i repository remoti.
Per ora, rispetta la regola d'oro della ribellione: mai rebase di un ramo che è stato inserito in un repository pubblico.
Questa lezione rappresenta un capitolo da Git in modo succinto, un eBook gratuito dal team di Syncfusion.