Quali modelli di branching Git funzionano per te?


379

La nostra azienda sta attualmente utilizzando un semplice modello di diramazione trunk / release / hotfix e vorrebbe ricevere consigli su quali modelli di diramazione funzionano meglio per la propria azienda o processo di sviluppo.

  1. Flussi di lavoro / modelli di ramificazione

    Di seguito sono riportate le tre descrizioni principali di questo che ho visto, ma si contraddicono in parte le une con le altre o non vanno abbastanza lontano per risolvere i problemi successivi che abbiamo riscontrato (come descritto di seguito). Pertanto, per impostazione predefinita, il nostro team non ha soluzioni così eccezionali. Stai facendo qualcosa di meglio?

  2. Fusione vs rebasing (storia intricata vs sequenziale)

    Dovresti uno pull --rebaseo aspettare con l'unione di nuovo alla linea principale fino al completamento dell'attività? Personalmente mi propongo di fondermi poiché ciò conserva un'illustrazione visiva della base su cui è stata avviata e terminata un'attività, e preferisco anche merge --no-ffa questo scopo. Ha tuttavia altri inconvenienti. Inoltre, molti non hanno realizzato l'utile proprietà della fusione, che non è commutativa (la fusione di un ramo di argomento in master non significa unire il maestro nel ramo di argomento).

  3. Sto cercando un flusso di lavoro naturale

    A volte si verificano errori perché le nostre procedure non acquisiscono una situazione specifica con regole semplici. Ad esempio, una correzione necessaria per le versioni precedenti dovrebbe ovviamente basarsi sufficientemente a valle per essere possibile unire a monte in tutti i rami necessari (l'uso di questi termini è abbastanza chiaro?). Tuttavia accade che una correzione lo faccia diventare il master prima che lo sviluppatore si renda conto che avrebbe dovuto essere collocato più a valle, e se ciò è già stato spinto (anche peggio, unito o qualcosa basato su di esso), l'opzione rimanente è cherry-picking, con i pericoli associati. Quali semplici regole come queste usi?Anche in questo è inclusa l'imbarazzo di un ramo di argomento escludendo necessariamente altri rami di argomento (supponendo che siano ramificati da una linea di base comune). Gli sviluppatori non vogliono finire una funzione per avviarne un'altra sentendosi come se il codice che avevano appena scritto non fosse più lì

  4. Come evitare la creazione di conflitti di unione (a causa di cherry-pick)?

    Quello che sembra un modo sicuro per creare un conflitto di unione è quello di scegliere tra i rami, che non possono mai essere uniti di nuovo? Applicare lo stesso commit nel ripristino (come fare?) In entrambi i rami potrebbe risolvere questa situazione? Questo è uno dei motivi per cui non oso spingermi per un flusso di lavoro basato in gran parte sulla fusione.

  5. Come scomporre in rami topici?

    Ci rendiamo conto che sarebbe fantastico assemblare un'integrazione finita dai rami degli argomenti, ma spesso il lavoro dei nostri sviluppatori non è chiaramente definito (a volte semplice come "frugare") e se un po 'di codice è già entrato in un argomento "misc", non può essere tolto di nuovo da lì, secondo la domanda sopra? Come lavori per definire / approvare / laurearti / rilasciare i tuoi argomenti?

  6. Naturalmente, le procedure adeguate come la revisione del codice e la laurea sarebbero adorabili.

    Ma semplicemente non possiamo mantenere le cose abbastanza districate per gestire questo - qualche suggerimento? rami di integrazione, illustrazioni?

Di seguito è riportato un elenco di domande correlate:

Scopri anche cosa scrive Plastic SCM sullo sviluppo guidato dalle attività e, se Plastic non è la tua scelta, studia il modello di ramificazione di nvie e i suoi script di supporto .


2
Ah, grazie, in effetti ha ... In realtà ho letto la maggior parte di quella ... roba :-). È qualcosa per cui sono noto - non accontentarmi della soluzione mediocre ma continuare a cercare il perfetto. Spesso questo è un errore, ma in questo caso è in gioco molto e le soluzioni a portata di mano sono semplicemente troppo disordinate o povere che devo continuare a cercare. Quindi ho deciso di elencare tutti i problemi che ho con esso.
HiQ CJ,

Il blog di Plastic SCM ha gettato la sua opinione nella discussione, è almeno perspicace: codicesoftware.blogspot.com/2010/08/…
HiQ CJ

1
Devi fare attenzione quando usi "unisci --no-ff", controlla questo per alcuni avvertimenti sandofsky.com/blog/git-workflow.html
Doppelganger

1
@Doppelganger Sarei interessato a come in particolare il --no-ff sta presumibilmente contribuendo al problema descritto nel link che pubblichi. Per me il problema descritto è il fallimento di bisect con commit di checkpoint, e il fallimento di git incolpare di aiutare in quel caso - ma non riesco a vedere come "--no-ff" cambi qualcosa, invece di non usarlo. L'autore si lamenta che un'unione con --no-ff non comporti la modifica di un file - ma senza di essa, il file non verrebbe modificato, vedresti solo il commit più vecchio nella tua cronologia, giusto?
codifica il

Altro modello di ramificazione: modello cactus barro.github.io/2016/02/… , modello mainline bitsnbites.eu/a-stable-mainline-branching-model-for-git . Questo modello di entrambe le diramazioni offre un approccio diverso rispetto a gitflow.
Mathieu Momal,

Risposte:


91

La caratteristica più preoccupante che i nuovi sviluppatori di DVCS devono realizzare riguarda il processo di pubblicazione :

  • puoi importare (recuperare / estrarre) qualsiasi repository remoto di cui hai bisogno
  • puoi pubblicare (push) su qualsiasi (nudo) repository che desideri

Da ciò, puoi rispettare alcune regole per semplificare le tue domande:

Adesso:

Flussi di lavoro / modelli di ramificazione :

ogni flusso di lavoro è lì per supportare un processo di gestione delle versioni , che è su misura per ogni progetto.
Quello che posso aggiungere al flusso di lavoro che menzioni è: ogni sviluppatore non dovrebbe creare un ramo di funzionalità, ma solo un ramo "sviluppatore attuale", perché la verità è: lo sviluppatore spesso non sa esattamente cosa produrrà il suo ramo: uno funzionalità, diverse (perché ha finito per essere una funzionalità troppo complessa), nessuna (perché non pronta in tempo per il rilascio), un'altra funzionalità (perché quella originale si era "trasformata"), ...

Solo un "integratore" dovrebbe stabilire diramazioni ufficiali su un repository "centrale", che può quindi essere recuperato dagli sviluppatori per rifare / unire la parte del loro lavoro che si adatta a quella funzione.

Fusione vs rebasing (storia intricata vs sequenziale) :

Mi piace la mia risposta di cui parli (" Descrizione del flusso di lavoro per l'utilizzo di git per lo sviluppo interno ")

Sto cercando un flusso di lavoro naturale :

per le correzioni, può aiutare ad associare ogni correzione con un ticket da un tracciamento dei bug, che aiuta lo sviluppatore a ricordare dove (cioè su quale ramo, ovvero un ramo dedicato "per correzioni") dovrebbe effettuare tali modifiche.
Quindi gli hook possono aiutare a proteggere un repository centrale da push da correzioni di bug non convalidate o da rami da cui non si dovrebbe spingere. (nessuna soluzione specifica qui, tutto ciò deve essere adattato al tuo ambiente)

Come evitare la creazione di conflitti di unione (a causa di cherry-pick)?

Come affermato da Jakub Narębski nella sua risposta , la raccolta delle ciliegie dovrebbe essere riservata alle rare situazioni in cui è richiesta.
Se la tua configurazione prevede molta raccolta delle ciliegie (ovvero "non è raro"), allora qualcosa è spento.

Applicando lo stesso commit nel ripristino (come si fa?)

git revert dovrebbe occuparsene, ma non è l'ideale.

Come scomporre in rami topici?

Finché un ramo non è stato ancora spinto ovunque, uno sviluppatore dovrebbe riorganizzare la sua storia di commit (una volta che vede finalmente che lo sviluppo prende una forma più definitiva e stabile) in:

  • più rami se necessario (uno per chiara caratteristica identificata)
  • un insieme coerente di commit all'interno di un ramo (vedi Ritagli Git Checkin )

Procedure adeguate come la revisione del codice e la laurea?

Il repository dei rami di integrazione (in un'integrazione dedicata) può aiutare lo sviluppatore a:

  • rebase il suo sviluppo in cima a quel ramo di integrazione remota (pull --rebase)
  • risolvere localmente
  • spingere lo sviluppo verso quel repository
  • verificare con l'integratore che non si traduca in un disordine;)

@UncleCJ: come puoi vedere, questa non è esattamente una risposta finale alla tua "domanda finale";)
VonC

Capisco, e ho anche un bel senso dell'ironia, va bene ;-)
HiQ CJ

3
@UncleCJ upstream è proprio da dove ti allontani regolarmente, dal mio post, ovunque finiscano tutti gli commit (la versione di rilascio o trunk in linguaggio SVN). A valle sono tutti sotto di loro. L'invio di roba a monte è il processo di fusione nel repository di rilascio (come Linux-2.6) e a valle i cambiamenti da lì in uscita, o dal tuo repository, come dire il gestore dello sviluppo di tale funzionalità per i tuoi servi ... I squadra cattiva.

2
@UncleCJ: "Trovo ancora difficile tagliare i check-in per ottenere una cronologia strettamente sequenziale": è più facile con Git1.7 e il suo rebase --interactive --autosquashche sposta automaticamente tutti i commit con lo stesso inizio di un altro messaggio di commit. Se tali commit utilizzano un numero di ticket (ad esempio), anche se le correzioni relative a quel ticket non sono state effettuate in sequenza in quel momento, l'autosquash consente un rapido riordino di tali commit.
VonC,

1
@UncleCJ: "cronologia strettamente sequenziale (è necessario o no ?!)": non sempre necessario, ma aiuta a tenere traccia delle dipendenze funzionali ( stackoverflow.com/questions/881092/… ) e dei conflitti semantici ( stackoverflow.com/questions / 2514502 /… )
VonC,

21

Penso, e potrei sbagliarmi, che una delle cose più fraintese su Git sia la sua natura distribuita. Questo rende molto diverso dire sovversione nei modi in cui puoi lavorare, anche se puoi imitare il comportamento SVN se vuoi. Il problema è praticamente che qualsiasi flusso di lavoro farà, il che è fantastico ma anche fuorviante.

Se avrò la mia comprensione dello sviluppo del kernel (mi concentrerò su quello) giusto, ognuno ha il proprio repository git per lo sviluppo del kernel. Esiste un repository, linux-2.6.git, curato da Torvalds, che funge da repository di rilascio. Le persone clonano da qui se desiderano iniziare a sviluppare una funzionalità contro il ramo "release".

Altri repository fanno un po 'di sviluppo. L'idea è quella di clonare da Linux-2.6, diramare tutte le volte che vuoi fino a quando hai una "nuova" funzione funzionante. Quindi, quando sarà pronto, potresti renderlo disponibile a qualcuno considerato affidabile, che estrarrà questo ramo dal tuo repository nel loro e lo fonderà nel mainstream. Nel kernel di Linux ciò accade su più livelli (luogotenenti fidati) fino a quando non raggiunge linux-2.6.git a quel punto diventa "il kernel".

Ora ecco dove diventa confuso. I nomi delle filiali non devono necessariamente essere coerenti tra i repository. Così posso git pull origin master:vanilla-codee ottenere un ramo dal originmaster in un ramo nel mio repository chiamato vanilla-code. A condizione che io sappia cosa sta succedendo, non importa davvero: è distribuito nel senso che tutti i repository sono pari tra loro e non solo condivisi su più computer come SVN.

Quindi, con tutto questo in mente:

  1. Penso che spetti ad ogni programmatore come fanno le loro ramificazioni. Tutto ciò di cui hai bisogno è un repository centrale per la gestione delle versioni, ecc. Trunk potrebbe essere head. Le versioni potrebbero essere tag o rami e gli hotfix sono probabilmente rami in sé. In effetti, probabilmente farei rilasci come rami in modo da poter continuare a patcharli.
  2. Mi unirei e non rifarei. Se ad esempio prendi un repository, clonalo, ramifica e fai qualche sviluppo, quindi estrai dal tuo origindovresti, nel tuo repository, probabilmente crea un altro ramo e unisci l'ultimo masterin yourbranchmodo che qualcun altro possa eseguire le modifiche con il minimo sforzo possibile. Nella mia esperienza, molto raramente è necessario riformulare veramente.
  3. Penso che sia un caso di capire come funziona Git e cosa può fare. Ci vuole un po 'di tempo e molta buona comunicazione - ho iniziato davvero a capire cosa stava succedendo quando ho iniziato a usare git con altri sviluppatori e anche ora, alcune cose di cui non sono sicuro.
  4. Unire i conflitti sono utili. Lo so, lo so, vuoi che tutto funzioni, ma il fatto è che il codice cambia e devi unire i risultati in qualcosa che funzioni. Unire i conflitti è in realtà solo una maggiore programmazione. Non ho mai trovato una spiegazione semplice per cosa fare al riguardo, quindi eccolo qui: annota i file che hanno conflitti di unione, vai e cambiali in quello che dovrebbero essere, git add .e poi git commit.
  5. Comunque va bene. Come ho già detto, ogni repository git degli utenti è il proprio con cui giocare e i nomi dei rami non devono necessariamente essere gli stessi . Ad esempio, se si disponeva di un repository di gestione temporanea, è possibile applicare uno schema di denominazione, ma non è necessario per ogni sviluppatore, solo nel repository di rilascio.
  6. Questa è la fase di unione. Ti unisci ai rami di rilascio ecc. Quando consideri il codice da rivedere / superare i test di qualità.

Spero che aiuti. Mi rendo conto che VonC ha appena pubblicato una spiegazione molto simile ... Non riesco a digitare abbastanza velocemente!

Modifica alcune ulteriori considerazioni su come utilizzare git in un ambiente commerciale, poiché ciò sembra rilevante ai fini del PO dai commenti:

  • Il repository di release, lo chiameremo product.git, è accessibile da un numero di programmatori senior / tecnici responsabili di occuparsi effettivamente del prodotto stesso. Sono analoghi al ruolo dei manutentori in OSS.
  • Questi programmatori probabilmente guidano anche in parte lo sviluppo di nuove versioni, quindi potrebbero anche codificarsi e mantenere vari repository. Potrebbero gestire repository di gestione temporanea per funzionalità davvero nuove e potrebbero anche avere i propri repository.
  • Sotto di loro ci sono programmatori responsabili dello sviluppo di singoli bit. Ad esempio, qualcuno potrebbe essere responsabile del lavoro dell'interfaccia utente. Gestiscono quindi il repository UI.git.
  • Sotto di loro ci sono i veri programmatori che sviluppano le funzionalità come il loro lavoro quotidiano.

Quindi cosa succede? Bene, tutti attingono all'inizio di ogni giorno dalla fonte "upstream", ovvero il repository di rilascio (che conterrà probabilmente anche il materiale più recente dello sviluppo dei giorni precedenti). Tutti lo fanno direttamente. Questo andrà su un ramo nel loro repository, probabilmente chiamato "master" o forse se sei tu chiamato "ultimo". Il programmatore farà quindi del lavoro. Questo lavoro potrebbe essere qualcosa di cui non sono sicuri, quindi fanno un ramo, fanno il lavoro. Se non funziona, possono eliminare il ramo e tornare indietro. In tal caso, dovranno unirsi al ramo principale su cui stanno attualmente lavorando. Diremo che questo è un programmatore dell'interfaccia utente su latest-uicui sta lavorando , git checkout latest-uiseguito dagit merge abc-ui-mywhizzynewfeature. Quindi dice al suo capo tecnico (il capo dell'interfaccia utente) ehi, ho completato un compito del genere, tiralo fuori da me. Quindi lo fa l'interfaccia utente git pull user-repo lastest-ui:lastest-ui-suchafeature-abc. Il lead dell'interfaccia utente quindi lo guarda su quel ramo e dice, in realtà, è molto buono, lo unirò ui-latest. Potrebbe quindi dire a tutti quelli sotto di lui di estrarre da lui sui loro ui-latestrami o qualunque sia il nome che gli hanno dato, e quindi la funzione viene esplorata dagli sviluppatori. Se il team è felice, il responsabile dell'interfaccia utente potrebbe chiedere al responsabile del test di ritirarsi da lui e unire le modifiche. Questo si propaga a tutti (a valle della modifica) che lo verifica e invia segnalazioni di bug, ecc. Infine, se la funzione supera il test, ecc., Uno dei principali lead tecnici potrebbe fonderlo nella copia di lavoro corrente del programma, a quel punto tutte le modifiche vengono quindi propagate indietro. E così via.

Non è un modo "tradizionale" di lavorare ed è progettato per essere "peer driven" piuttosto che "gerarchico" come SVN / CVS. In sostanza, tutti hanno accesso al commit, ma solo localmente. È l'accesso al repository e quale repository designare come repository di rilascio che consente di utilizzare la gerarchia.


Grazie mille per la tua ampia risposta (e voti), la leggerò ancora un paio di volte per ricavarne informazioni utili. Tuttavia, siamo un'azienda, non un comitato di sviluppo OSS ;-), e devo aiutare i miei sviluppatori con linee guida più chiare di "armeggiare come vuoi nel tuo repository". Vediamo dove porta questo post, sento un buon momento, continuate a venire!
HiQ CJ,

@VonC Grazie. @UncleCJ è vero, ma tu, ne sono sicuro, hai i gestori delle versioni ecc. Chiunque abbia accesso al repository può fare queste cose. Per quanto riguarda lo sviluppo, perché non dare agli sviluppatori la libertà, entro limiti ragionevoli, di espandersi? Se si dispone di un protocollo per concordare le fusioni e il / i proprio / i repository centrale / i viene nominato come desiderato, non c'è alcun problema. Detto questo, uno schema di denominazione comune non è una cattiva idea. Tendo a usare le iniziali-versione-funzionalità-subbranches per i rami personali e la versione per i rami.

@UncleCJ Ho aggiunto un esempio di come potrebbe funzionare in un'azienda. Sono essenzialmente i ruoli OSS sostituiti con i gestori, ma ti viene l'idea. Ha l'ulteriore vantaggio su SVN che anche i tuoi sviluppatori possono lavorare offline (hanno solo bisogno della rete per tirare / spingere) e penso che semplifichi il test delle funzionalità, se lo implementi bene.

Wow, in realtà è un ottimo esempio, potremmo iniziare a usare qualcosa del genere per la laurea. Non intendevo così tanto che, poiché non stiamo facendo OSS, tutti devono essere regolati, in realtà siamo un team piuttosto piccolo e piatto, ma dobbiamo cercare di collaborare in modo efficiente su un programma serrato e anche imparare come una squadra . Ecco perché sono qui a fare queste stupide domande in modo da poter aiutare il resto della squadra in seguito :-). Ho anche capito da #git che la linea di base mal definita unita alla pressione per abbreviare i tempi di consegna ci sta facendo inciampare in piedi ... torneremo più tardi.
HiQ CJ,

È abbastanza giusto: ci sono stato di recente, ed è esattamente come ho preso quell'esempio, provandolo e fallendo molto ... e anche adattandomi ai modi di lavorare di alcuni progetti OSS. Immagino che il vero problema sia che non importa come ti dirigi e dove sono i tuoi repository ... puoi definirli come vuoi, il che è stato un vero shock per me! Ma ti permette di fare alcune cose interessanti. Ad ogni modo, buona fortuna e buon divertimento!

9

Un modello che ho usato con buoni risultati è il seguente:

Un repository "benedetto" che tutti spingono e tira da / verso, sostanzialmente una topologia client-server.

Non esiste un ramo master, quindi nessuno sviluppatore può inserire alcun codice in "mainline".

Tutti gli sviluppi avvengono su settori tematici. Abbiamo nomi con nomi per individuare facilmente chi ne è responsabile: jn / newFeature o jn / issue-1234

C'è anche una mappatura quasi 1 a 1 tra i rami e le carte kanban / mischia sulla lavagna.

Per rilasciare un ramo viene spinto nel repository benedetto e la kanban-card viene spostata in pronta per la revisione.

Quindi, se il ramo viene accettato dalla recensione, è un candidato per un rilascio.

Una versione si verifica quando un insieme di filiali accettate viene unito e contrassegnato con un numero di versione.

Spingendo il nuovo tag nel repository benedetto c'è una nuova base possibile per nuove funzionalità.

Per evitare conflitti di unione, gli sviluppatori sono pregati di aggiornare (unire) i loro rami non rilasciati all'ultimo tag di rilascio.


2

Personalmente, provo a mantenere solo il codice pronto per il rilascio nel ramo principale.

Quando lavoro su una nuova funzionalità o correzione di bug, lo faccio in un ramo. Ho anche unit-test nel ramo. Se tutto funziona correttamente, solo allora posso unire / rebase nuovamente in master.

Cerco anche di utilizzare convenzioni di denominazione delle filiali comuni, come:

  • bugfix / recursive_loop
  • bugfix / sql_timeout
  • caratteristica / new_layout
  • caratteristica / enhanced_search
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.