Perché git commit non contiene il nome del ramo su cui sono stati creati?


20

Quando lavoro con git in un team usando i rami delle caratteristiche, trovo spesso difficile capire la struttura dei rami nella storia.

Esempio:

Supponiamo che esistesse una funzione / make-coffee nel ramo delle caratteristiche e che la correzione dei bug continuasse sul master in parallelo al ramo delle caratteristiche.

La storia potrebbe apparire così:

*     merge feature/make-coffee
|\
| *   small bugfix
| |
* |    fix bug #1234
| |
| *    add milk and sugar
| |
* |    improve comments
| |
* |    fix bug #9434
| |
| *    make coffe (without milk or sugar)
| |
* |    improve comments
|/
*

Problema

A prima vista, trovo difficile stabilire da che parte sia il ramo delle caratteristiche. Di solito ho bisogno di sfogliare diversi commenti su entrambi i lati per farmi un'idea di quale sia. Ciò diventa più complicato se ci sono più rami di funzionalità in parallelo (in particolare se si tratta di funzioni strettamente correlate) o se vi è stata una fusione in entrambe le direzioni tra il ramo di funzionalità e il master.

Al contrario, in Subversion, questo è significativamente più semplice perché il nome del ramo fa parte della storia - quindi posso dire subito che un commit è stato originariamente fatto su "feature / make-coffee".

Git potrebbe renderlo più semplice includendo il nome del ramo corrente nei metadati di commit durante la creazione di un commit (insieme a autore, data ecc.). Tuttavia, git non lo fa.

C'è qualche motivo fondamentale per cui questo non viene fatto? O è solo che nessuno voleva la funzionalità? Se è quest'ultimo, ci sono altri modi per comprendere lo scopo dei rami storici senza vedere il nome?


1
Domanda correlata: trovare da quale ramo proviene un commit . Si tratta di come trovare queste informazioni nonostante il fatto che git non le registri esplicitamente.
sleske,

1
Nota a sé: È interessante notare che, in Mercurial il commit non contenere il nome del ramo. Quindi sembra che gli sviluppatori Mercurial abbiano preso una decisione di progettazione diversa rispetto agli sviluppatori Git. Vedi ad esempio felipec.wordpress.com/2012/05/26/… per i dettagli.
sleske,

Risposte:


14

Probabilmente perché i nomi dei rami sono significativi solo all'interno di un repository. Se faccio parte della make-coffeesquadra e invio tutte le mie modifiche tramite un gatekeeper, il mio masterramo potrebbe essere attirato dal make-coffee-guiramo del gatekeeper , che si fonderà con il suo make-coffee-backendramo, prima di ricadere in un make-coffeeramo che viene unito al masterramo centrale .

Anche all'interno di un repository, i nomi delle filiali possono e cambiano man mano che il flusso di lavoro si evolve. masterpotrebbe cambiare in seguito per essere chiamato development, ad esempio.

Come ha accennato CodeGnome, git ha una forte filosofia progettuale di non inserire qualcosa nei dati se non è necessario. Ci si aspetta che gli utenti utilizzino le opzioni git loge git diffproducano i dati a proprio piacimento dopo il fatto. Consiglio di provarne alcuni fino a quando non trovi un formato che funzioni meglio per te. git log --first-parent, ad esempio, non mostrerà i commit da un ramo che viene unito.


2
Va notato però che il primo genitore è di solito quello sbagliato. Perché di solito uno semplicemente attira il maestro in qualunque cosa stia lavorando e lo fa diventare il primo genitore e padroneggiare il secondo.
Jan Hudec,

1
@JanHudec: In realtà, questo dipende :-). Se la persona che fa il lavoro lo unisce, allora sì. Se l'unione si verifica in un secondo momento, forse dopo una revisione, è più probabile che il revisore abbia eseguito il checkout del master e unisca il ramo della funzionalità al master, lo testa e lo spinge.
sleske,

5

Traccia la cronologia delle filiali con --no-ff

In Git, un commit ha antenati, ma un "ramo" è in realtà solo l'attuale capo di qualche linea di sviluppo. In altre parole, un commit è un'istantanea dell'albero di lavoro in un determinato momento e può appartenere a qualsiasi numero di rami contemporaneamente. Questo fa parte di ciò che rende la ramificazione Git così leggera rispetto ad altri DVCS.

I commit di Git non portano informazioni sulle filiali perché non sono necessarie per la cronologia di Git. Tuttavia, le fusioni che non sono anticipate possono certamente portare ulteriori informazioni su quale succursale è stata unita. Puoi assicurarti che la tua cronologia contenga queste informazioni passando sempre la --no-ffbandiera alle tue fusioni. git-merge (1) dice:

   --no-ff
       Create a merge commit even when the merge resolves as a
       fast-forward. This is the default behaviour when merging an
       annotated (and possibly signed) tag.

In genere ciò creerà un messaggio di unione simile a Merge branch 'foo', quindi in genere è possibile trovare informazioni sui "rami degli antenati" con una riga simile alla seguente:

$ git log --regexp-ignore-case --grep 'merge branch'

Grazie, ma questo (principalmente) non risponde alla mia domanda. Non sto chiedendo quali altri modi ci siano per trovare il ramo originale, ma perché le informazioni non sono incluse come metadati regolari: è più una domanda di progettazione.
sleske,

1
@sleske Penso che la mia risposta sia stata reattiva: Git non la traccia perché la cronologia di Git non ne ha bisogno; Non penso che sia più fondamentale di così. Dovresti cercare la fonte per i dettagli, ma la cronologia viene effettivamente calcolata all'indietro dalla punta del ramo corrente. Puoi sempre confrontarlo con qualche altro DVCS che tratta i rami come oggetti di prima classe e vedere se ti piace di più quella filosofia di design. YMMV.
CodeGnome,

1
Quest'ultimo dovrebbe essere git log --merges.
Dan,

3

La risposta più semplice è che i nomi dei rami sono effimeri. E se dovessi, per esempio, rinominare un ramo ( git branch -m <oldname> <newname>)? Cosa succederebbe a tutti gli impegni contro quel ramo? O cosa succede se due persone hanno filiali che hanno lo stesso nome su repository locali diversi?

L'unica cosa che ha significato in git è il checksum dello stesso commit. Questa è l'unità base di tracciamento.

Le informazioni sono lì, non le stai chiedendo e non sono esattamente lì.

Git memorizza il checksum del capo di ogni ramo .git/refs/heads. Per esempio:

... / .git / refs / heads $ ls test 
-rw-rw-r-- 1 test di michealt michealt 41 set 30 11:50
... / .git / refs / heads $ test gatto 
87111111111111111111111111111111111111d4

(sì, sto ostruendo i miei checksum git, non è speciale, ma sono archivi privati ​​che sto guardando in quel momento che non ha bisogno di avere trapelato qualcosa per caso).

Osservando questo e rintracciando ogni commit che ha questo come genitore, o genitore genitore o genitore genitore ... puoi scoprire in quali rami si trova un determinato commit. È solo un grande grafico da rendere.

Memorizzare dove nello specifico il nome di un ramo (che può cambiare come menzionato sopra) un commit è nel commit stesso è un sovraccarico extra sul commit che non lo aiuta. Conservare il genitore (i) del commit e il relativo bene.

Puoi vedere i rami eseguendo il comando git log con il flag appropriato.

git log --branches --source --pretty = oneline --graph
git log --all --source --pretty = oneline --graph

Esistono molti modi diversi per scegliere ciò che desideri - guarda la documentazione di git log - la -allsezione e le poche opzioni che seguono.

* 87111111111111111111111111111111111111d4 test Unisci il ramo 'test1' nel test
| \  
| * 42111111111111111111111111111111111111e8 aggiornamento test1: aggiungi elementi
* | dd11111111111111111111111111111111111159 test Unisci il ramo 'test3' nel test
| \ \  

Quel "test", "test1" e "test2" sono i rami in cui si sono impegnati.

Nota che la documentazione di git log è enorme ed è abbastanza possibile ricavarne quasi tutto ciò che desideri.


3

Perché git commit non contiene il nome del ramo su cui sono stati creati?

Solo una decisione di progettazione. Se sono necessarie tali informazioni, è possibile aggiungere un hook prepar-commit-msg o commit-msg per imporlo su un singolo repository.

Dopo il disastro di BitKeeper Linus Torvalds ha sviluppato git per abbinare il processo di sviluppo del kernel Linux. Lì, fino a quando una patch non viene accettata, viene rivista, modificata, lucidata più volte (per tutta Mail), firmata (= modifica di un commit), raccolta ciliegia, vagabondando verso i rami del tenente forse spesso ribassata, più modifiche, ciliegia -picks e così via fino a quando non si fondono definitivamente a monte di Linus.

In un tale flusso di lavoro potrebbe non esserci un'origine specifica di un commit e spesso un commit viene appena creato in un ramo di debug temporaneo in un repository casuale privato non importante con nomi di rami divertenti, poiché git viene distribuito.

Forse quella decisione di progettazione è avvenuta per coincidenza, ma corrisponde esattamente al processo di sviluppo del kernel Linux.


2

Perché in Git i commit come "make coffee" sono considerati parte di entrambi i rami quando il ramo delle caratteristiche viene unito. C'è anche un comando per verificare che:

% git branch --contains @
  feature/make-coffee
  master

Inoltre, le filiali sono economiche, locali ed eteree; possono essere facilmente aggiunti, rimossi, rinominati, inviati ed eliminati dal server.

Git segue il principio che tutto può essere fatto, il che spiega perché puoi rimuovere facilmente un ramo da un server remoto e puoi riscrivere facilmente la cronologia.

Diciamo che ero nel ramo "principale" e ho fatto due commit: "prepara il caffè" e "aggiungi latte e zucchero", quindi decido di utilizzare un nuovo ramo per quello, quindi ripristino "maestro" di nuovo a "origine" /maestro'. Non è un problema, tutta la storia è ancora pulita: "fare il caffè" e "aggiungere latte e zucchero" non fanno parte del maestro, ma solo funzionalità / caffè.

Mercurial è l'opposto; non vuole cambiare le cose. I rami sono permanenti, la cronologia della riscrittura è disapprovata, non puoi semplicemente cancellare un ramo dal server.

In breve: Git ti consente di fare tutto, Mercurial vuole semplificare le cose (e rende davvero difficile lasciarti fare quello che vuoi).

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.