Git in modo succinto rami

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.


Sviluppo ramificato di base

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.


Manipolando i rami

Git separa la funzionalità del ramo in alcuni comandi diversi. Il ramo di git comando è usato per elencare, creare o eliminare rami.

Elenco dei 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.

Creazione di rami

È 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.


Creare un nuovo ramo

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.


Impegnarsi sul 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.

Eliminazione di rami

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.


Controllando i rami

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.


Controllando diversi rami

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.


Sviluppo di più funzionalità in parallelo

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 ...

HEAD distaccati

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.


Verifica un vecchio 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.


Unione di rami

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:

  • L'unione "avanti veloce"
  • L'unione "a 3 vie"

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.

Fusione veloce

Il primo scenario si presenta così:


Prima della fusione veloce in avanti

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 in avanti veloce

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.

3-Way Merges

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:


Prima dell'unione a 3 vie

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.


Dopo l'unione a 3 direzioni

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.

Unisci conflitti

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 in  Unione 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.


Ramificazione dei flussi di lavoro

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.

Tipi di rami

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à).

Rami permanenti

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.


Usando il maestro filiale esclusivamente per comunicati pubblici

Rami di argomento

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".


Sviluppo di una funzione in un ramo isolato

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.


patching 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à.


ribasamento

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:


ribasamento 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:


Integrazione 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.

Rebasing interattivo

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:


Ribattendo in modo interattivo il 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.

Riscrivere la storia

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.