In pratica, come fare il versioning per una dozzina di librerie ha funzionato tutte in parallelo


11

Stiamo facendo progetti, ma riutilizziamo molto codice tra i progetti e abbiamo molte librerie che contengono il nostro codice comune. Mentre implementiamo nuovi progetti, troviamo altri modi per scomporre il codice comune e inserirlo nelle librerie. Le librerie dipendono l'una dall'altra e i progetti dipendono dalle librerie. Ogni progetto e tutte le librerie utilizzate in quel progetto devono utilizzare la stessa versione di tutte le librerie a cui si riferiscono. Se rilasceremo un software dovremo correggere i bug e forse aggiungere nuove funzionalità per molti anni, a volte per decenni. Abbiamo circa una dozzina di biblioteche, le modifiche spesso attraversano più di due e diversi team lavorano su diversi progetti in parallelo, apportando modifiche simultanee a tutte queste librerie.

Di recente siamo passati a git e creato repository per ogni libreria e ogni progetto. Usiamo stash come repository comune, facciamo nuove cose sui rami delle funzionalità, quindi facciamo richieste pull e le uniamo solo dopo la revisione.

Molte delle questioni che dobbiamo affrontare nei progetti ci impongono di apportare modifiche a diverse librerie e al codice specifico del progetto. Questi includono spesso modifiche alle interfacce della libreria, alcune delle quali sono incompatibili. (Se pensi che questo suona male: ci interfacciamo con l'hardware e nascondiamo hardware specifico dietro interfacce generiche. Quasi ogni volta che integriamo l'hardware di altri fornitori ci imbattiamo in casi in cui le nostre interfacce attuali non prevedevano, e quindi dobbiamo raffinarle.) Per esempio, immaginate un progetto P1utilizzando le librerie L1, L2e L3. L1usa anche L2e L3, e L2usa L3anche. Il grafico delle dipendenze è simile al seguente:

   <-------L1<--+
P1 <----+  ^    |
   <-+  |  |    |
     |  +--L2   |
     |     ^    |
     |     |    |
     +-----L3---+

Ora immagina che una caratteristica di questo progetto richieda cambiamenti P1e L3che cambi l'interfaccia di L3. Ora aggiungi progetti P2e P3nel mix, che fanno riferimento anche a queste librerie. Non possiamo permetterci di passare tutti alla nuova interfaccia, eseguire tutti i test e distribuire il nuovo software. Quindi qual è l'alternativa?

  1. implementare la nuova interfaccia in L3
  2. fare una richiesta pull L3e attendere la revisione
  3. unisci il cambiamento
  4. creare una nuova versione di L3
  5. iniziare a lavorare sulla funzionalità P1facendola riferimento alla L3nuova versione, quindi implementare la funzionalità sul P1ramo delle funzionalità
  6. fare una richiesta pull, farla revisionare e fondere

(Ho appena notato che ho dimenticato di passare L1e L2alla nuova versione. E non so nemmeno dove attaccarlo, perché dovrebbe essere fatto in parallelo con P1...)

Si tratta di un processo noioso, soggetto a errori e molto lungo per implementare questa funzione, che richiede revisioni indipendenti (che rendono molto più difficile la revisione), non si ridimensiona affatto e probabilmente ci metterà fuori mercato perché noi impantanarsi così tanto nel processo che non facciamo mai nulla.

Ma come possiamo impiegare la ramificazione e la codifica per creare un processo che ci consenta di implementare nuove funzionalità in nuovi progetti senza troppe spese generali?


1
La modifica degli strumenti non dovrebbe influire troppo sui processi in atto. Quindi, come hai affrontato questo problema prima di passare a git?
Bart van Ingen Schenau,

È possibile semplicemente aggiungere un nuovo metodo all'interfaccia senza interrompere quello esistente quando troppe librerie dipendono da esso? Normalmente questa non è la migliore idea, ma almeno ti permetterebbe di "andare avanti" con l'implementazione della nuova funzionalità e puoi deprezzare correttamente il vecchio metodo ogni volta che c'è un momento libero. O queste interfacce sono troppo stateful per "interfacce parallele" come quella per funzionare?
Ixrec,

1
@Ixrec: mi è stato detto che "non è questo il modo geniale" di fare le cose. Tutti usano repository individuali per singoli progetti, quindi è stato deciso di farlo anche noi.
sabato

2
Direi che non sono progetti separati se spesso devono essere cambiati in tandem. I confini tra "progetti" dovrebbero sempre avere una sorta di garanzia di compatibilità a ritroso a lungo termine imo.
Ixrec,

1
@Ixrec: l'integrazione di più hardware è simile al codice di porting su più piattaforme: più lo hai fatto, meno devi cambiare per l'ennesimo hardware / piattaforma. Quindi, a lungo termine, il codice si stabilizzerà. Tuttavia, in questo momento dovremo trovare un processo che ci consenta di rimanere sul mercato abbastanza a lungo per arrivarci.
sabato

Risposte:


5

Tipo di mettere in evidenza l'ovvio qui, ma forse vale la pena menzionarlo.

Di solito, i repository git sono personalizzati per lib / project perché tendono ad essere indipendenti. Aggiorna il tuo progetto e non ti preoccupare del resto. Altri progetti a seconda di esso aggiorneranno semplicemente la loro lib ogni volta che lo riterranno opportuno.

Tuttavia, il tuo caso sembra fortemente dipendente dai componenti correlati, quindi una caratteristica di solito influisce su molti di essi. E il tutto deve essere impacchettato come un pacchetto. Poiché l'implementazione di una funzione / modifica / errore richiede spesso di adattare contemporaneamente diverse librerie / progetti, forse ha senso metterli tutti nello stesso repository.

Ci sono forti vantaggi / svantaggi in questo.

vantaggi:

  • Tracciabilità: il ramo mostra tutto ciò che è cambiato in ogni progetto / lib relativo a questa funzionalità / bug.
  • Bundling: basta scegliere un tag e otterrai tutte le fonti giuste.

svantaggi:

  • Fusione: ... a volte è già difficile con un singolo progetto. Con diversi team che lavorano su rami condivisi, preparati a prepararti all'impatto.
  • Fattore "oops" pericoloso: se un dipendente crea un errore nel repository commettendo un errore, potrebbe avere un impatto su tutti i progetti e i team.

Sta a te sapere se il prezzo vale il vantaggio.

MODIFICARE:

Funzionerebbe così:

  • La funzione X deve essere implementata
  • Crea ramo feature_x
  • Tutti gli sviluppatori coinvolti lavorano su questo ramo e lavorano in parallelo, probabilmente in directory dedicate relative al loro progetto / lib
  • Una volta terminato, esaminalo, testalo, impacchettalo, qualunque cosa
  • Uniscilo nuovamente nel master ... e questa potrebbe essere la parte più difficile da allora feature_ye feature_zpotrebbe anche essere stata aggiunta. Diventa una fusione "cross-team". Ecco perché è un grave svantaggio.

solo per la cronaca: penso che questo sia nella maggior parte dei casi una cattiva idea e dovrebbe essere fatto con cautela perché l'inconveniente di unione è di solito superiore a quello che si ottiene attraverso la gestione delle dipendenze / il monitoraggio delle funzionalità appropriate.


Grazie, stiamo attualmente esaminando questo. Ciò che non capisco alla luce di questo è come faremmo ramificando con git. In SVN, ramificazioni significa copiare una sottostruttura (nel repository) in un altro posto (nel repository). Con questo, è facile creare rami di qualsiasi sottostruttura nel tuo repository, quindi se hai molti progetti lì dentro, puoi ramificarli singolarmente. C'è qualcosa di simile in git, o saremmo sempre in grado di ramificare un intero repository?
sabato

@sbi: dirameresti l'intero repository. Non puoi lavorare con sottotitoli in diversi rami, il che potrebbe sconfiggere il punto nel tuo caso. Git non "copia" nulla, tuttavia, semplicemente traccerà le modifiche nel ramo su cui stai lavorando.
Dagnelies,

Pertanto, ciò richiede che qualcuno crei un ramo di funzionalità per una libreria per unire anche tutte le altre durante l'unione o la riformulazione. Questo è un vero inconveniente. (A proposito, SVN fa anche solo copie pigre.)
sbi

@sbi: vedi edit
dagnelies,

1
Bene, attualmente molti di noi non sono a proprio agio. :-/Inoltre, anche quelli che sono (e che hanno spinto a passare a git), non sanno come adattare il nostro processo di sviluppo a git. Sospiro. Saranno alcuni mesi difficili, temo, finché le cose non diventeranno più fluide. Grazie comunque, la tua è la risposta più / finora utile.
sabato

4

La soluzione che stai cercando è uno strumento di gestione delle dipendenze in coordinamento con i sottomoduli git

Strumenti come:

  • Esperto di
  • Formica
  • Compositore

È possibile utilizzare tali strumenti per definire le dipendenze di un progetto.

È possibile richiedere che un sottomodulo sia almeno versione > 2.xx o che indichi una gamma di versioni compatibili = 2.2. * O inferiori a una versione specifica <2.2.3

Ogni volta che rilasci una nuova versione di uno dei pacchetti puoi etichettarlo con il numero di versione, in questo modo puoi inserire quella versione specifica del codice in tutti gli altri progetti


Ma la gestione delle dipendenze non è un nostro problema, questo è risolto. Attualmente apportiamo regolarmente modifiche in molte librerie e dobbiamo ridurre al minimo i costi di creazione di nuove versioni mantenendo al contempo versioni stabili del progetto. La tua risposta non sembra fornire una soluzione a questo.
sabato

@sbi Questo gestirà il sovraccarico di creare nuove versioni e mantenere versioni stabili del progetto. Poiché è possibile dettare che il progetto x si basa sul progetto y versione 2.1.1, è possibile creare nuove versioni del progetto y che non influirebbero sul progetto x.
Patrick,

Ancora una volta, dichiarare le dipendenze non è un nostro problema. Possiamo già farlo. Il problema è come gestire le modifiche tagliando diversi progetti / librerie in modo efficiente. La tua risposta non spiega questo.
sabato

@sbi: qual'è esattamente il tuo problema? Apportare le modifiche, aumentare la versione, aggiornare le dipendenze dove richiesto e voilà. Quello che hai descritto nel tuo post iniziale è tipico maven & co. cose. Ogni distribuzione si basa su librerie con versione chiaramente definite. Come potrebbe essere più chiaro?
Dagnelies,

@arnaud: i tempi di consegna di un tale processo per le modifiche (attualmente piuttosto comuni) che tagliano tre o più livelli ci ucciderebbero. Pensavo che la mia domanda lo descrivesse.
sabato

0

sottomoduli

Dovresti provare a usare i sottomoduli , come suggerito in un commento.

Quando il progetto P1fa riferimento ai tre sottomoduli L1, L2e L3in realtà memorizza un riferimento a particolari commit in tutti e tre i repository: quelle sono le versioni di lavoro di ciascuna libreria per quel progetto .

Quindi più progetti possono funzionare con più sottomoduli: P1potrebbero fare riferimento alla vecchia versione della libreria L1mentre il progetto P2utilizzava la nuova versione.

Cosa succede quando si consegna una nuova versione di L3?

  • implementare la nuova interfaccia in L3
  • commit, test , make pull request, review, merge, ... (non puoi evitarlo)
  • assicurarsi che L2funzioni con L3, commit, ...
  • assicurarsi che L1funzioni con nuovi L2, ...
  • assicurarsi che P1funzioni con le nuove versioni di tutte le librerie:
    • all'interno P1della copia di lavoro locale di L1, L2e L3, fetche i cambiamenti che ti interessano.
    • commit delle modifiche, git add L1 L2 L3per eseguire il commit del nuovo riferimento ai moduli
    • richiesta pull per P1, test, revisione, richiesta pull, unione ...

Metodologia

Si tratta di un processo noioso, soggetto a errori e molto lungo per implementare questa funzione, che richiede revisioni indipendenti (che rendono molto più difficile la revisione), non si ridimensiona affatto e probabilmente ci metterà fuori mercato perché noi impantanarsi così tanto nel processo che non facciamo mai nulla.

Sì, richiede revisioni indipendenti, poiché si modifica:

  • la Biblioteca
  • librerie che dipendono da esso
  • progetti che dipendono da più librerie

Saresti messo fuori mercato perché ti fai schifo? (Forse no, in realtà). In caso affermativo, è necessario eseguire test e rivedere le modifiche.

Con gli strumenti git appropriati (anche gitk), puoi facilmente vedere quali versioni delle librerie utilizzano ogni progetto e puoi aggiornarle in modo indipendente in base alle tue esigenze. I sottomoduli sono perfetti per la tua situazione e non rallenteranno il processo.

Forse puoi trovare un modo per automatizzare parte di questo processo, ma la maggior parte dei passaggi precedenti richiede cervelli umani. Il modo più efficace per ridurre i tempi sarebbe garantire che le tue librerie e i tuoi progetti siano facili da evolvere. Se la tua base di codice è in grado di gestire i nuovi requisiti con garbo, le revisioni del codice saranno più semplici e richiederanno poco del tuo tempo.

(Modifica) un'altra cosa che potrebbe esserti utile è raggruppare le revisioni del codice correlate. Impegni tutte le modifiche e attendi fino a quando non le hai propagate a tutte le librerie e ai progetti che le utilizzano prima di soddisfare le richieste pull (o prima di occupartene). Si finisce per fare una revisione più ampia per l'intera catena di dipendenze. Forse questo può aiutarti a risparmiare tempo se ogni modifica locale è piccola.


Descrivi come risolvere il problema di dipendenza (che, come ho detto prima, abbiamo risolto) e negare il problema che stiamo riscontrando. Perché ti preoccupi ancora? (FWIW, scriviamo software che guida le centrali elettriche. Il codice pulito, sicuro e accuratamente rivisto è una caratteristica fondamentale.)
sbi

@sbi Cosa sono i sottomoduli se non un caso speciale di diramazione e tagging? Pensi che i sottomoduli riguardino la gestione delle dipendenze, perché tengono anche traccia delle dipendenze. Ma certo, per favore, reinventa i sottomoduli con i tag, se vuoi, non mi dispiace. Non capisco il tuo problema: se il codice rivisto è una caratteristica fondamentale, è necessario un po 'di tempo per le recensioni. Non sei impantanato, vai il più velocemente possibile con i vincoli che ti pongono.
coredump,

Le recensioni sono molto importanti per noi. Questo è uno dei motivi per cui siamo preoccupati che un problema (e le sue recensioni) venga suddiviso in più recensioni per diverse modifiche in diversi repository. Inoltre non vogliamo impantanarci nell'amministrarci a morte per un problema è che preferiremmo dedicare il tempo a scrivere e rivedere il codice. Re sottomoduli: finora, l'unica cosa che ho sentito su di loro è "non preoccuparti, non è questo il modo giusto". Bene, dato che i nostri requisiti sembrano essere così unici, forse dovremmo rivedere questa decisione ...
sbi

0

Quindi quello che ho capito sei tu per P1 che vuoi cambiare l'interfaccia L3 ma vuoi che gli altri P2 e P3 che dipendono dall'interfaccia L3 cambino immediatamente. Questo è un tipico caso di compatibilità con le versioni precedenti. C'è un bell'articolo su questa conservazione della compatibilità con le versioni precedenti

Esistono diversi modi per risolvere questo problema:

  • Devi creare nuove interfacce ogni volta che possono estendere le vecchie interfacce.

O

  • Se vuoi ritirare la vecchia interfaccia dopo qualche tempo puoi avere diverse versioni di interfacce e una volta che tutti i progetti dipendenti vengono spostati rimuovi le vecchie interfacce.

1
No, la compatibilità con le versioni precedenti è garantita attraverso i rami di rilascio e non è un nostro problema. Il problema è che ci si siede su una base di codice attualmente in rapido cambiamento, che wet vuole ora separarsi in librerie, nonostante il fatto che le interfacce siano ancora nella fase in cui cambiano spesso. So come gestire queste bestie in SVN, ma non so come farlo in git senza essere annegato nell'amministrazione.
sabato

0

Se sto riscontrando il tuo problema giusto:

  • hai 4 moduli correlati, da P1 e da L1 a L3
  • è necessario apportare una modifica a P1 che alla fine influirà da L1 a L3
  • conta come un fallimento del processo se devi cambiare tutti e 4 insieme
  • conta come un fallimento del processo se devi cambiarli tutti 1 per 1.
  • viene considerato un errore di processo se è necessario identificare in anticipo i blocchi in cui devono essere apportate le modifiche.

Quindi l'obiettivo è che puoi fare P1 e L1 in una volta sola, e poi un mese dopo fare L2 e L3 in un'altra.

Nel mondo Java, questo è banale, e forse il modo predefinito di lavorare:

  • tutto va in un repository senza un uso rilevante della ramificazione
  • i moduli sono compilati + collegati tra loro da Maven in base ai numeri di versione, non al fatto che siano tutti nella stessa struttura di directory.

Quindi puoi avere il codice sul tuo disco locale per L3 che non verrebbe compilato se si stesse compilando con la copia di P1 nell'altra directory sul tuo disco; per fortuna non lo sta facendo. Java può farlo direttamente perché la compilazione / collegamento dei racconti si colloca su file jar compilati, non su codice sorgente.

Non sono a conoscenza di una soluzione ampiamente utilizzata preesistente a questo problema per il mondo C / C ++ e immagino che difficilmente vogliate cambiare lingua. Ma qualcosa potrebbe essere facilmente hackerato insieme a creare file che hanno fatto la stessa cosa:

  • librerie installate + intestazioni in directory note con numeri di versione incorporati
  • modificato i percorsi del compilatore per modulo nella directory per i numeri di versione appropriati

Potresti anche usare il supporto C / C ++ in Maven , anche se la maggior parte degli sviluppatori C ti guarderebbe stranamente se lo facessi ...


"conta come un fallimento del processo se devi cambiare tutti e 4 insieme" . In realtà non lo sarebbe. In effetti, questo è esattamente ciò che abbiamo fatto usando SVN.
sabato

Nel qual caso, quindi, suppongo che non ci siano problemi nel mettere semplicemente tutti i progetti nel repository.
soru,

Stiamo valutando di inserire le librerie in solo due repository. Questo è ancora più di uno, ma molto meno di "uno per ogni progetto", e le librerie possono benissimo essere divise in due gruppi. Grazie per il tuo contributo!
sabato

PS: "Immagino che difficilmente vuoi cambiare lingua." Questa è roba incorporata. :)
sabato

-1

C'è una soluzione semplice: tagliare i rami delle versioni nell'intero repository, unire tutte le correzioni a tutte le versioni attivamente spedite (è facile in caso chiaro dovrebbe essere possibile in git).

Tutte le alternative creeranno un disastro orribile nel tempo e con la crescita del progetto.


Potresti per favore elaborare? Non sono sicuro di ciò che stai suggerendo.
sabato

Nel caso chiaro si definisce un punto di diramazione come ramo di base e timestamp. Hai la seguente gerarchia: ramo di base -> ramo-sviluppo-rilascio -> ramo di sviluppo privato. Tutto lo sviluppo viene eseguito su filiali private e quindi unito alla gerarchia. Le filiali di rilascio del cliente sono interrotte dal ramo di sviluppo-rilascio. Non ho familiarità con Git ma sembra la cosa più vicina a chiarire il caso tra i sistemi di controllo del codice sorgente gratuiti.
zzz777,

Un'attenta lettura della mia domanda avrebbe dovuto mostrarti che abbiamo problemi con le spese generali legate alla propagazione delle modifiche tra i repository. Questo non ha nulla a che fare con la tua risposta.
sabato

@sbi Mi dispiace di aver frainteso la tua domanda. E temo che prima o poi dovrai affrontare un disastro orribile.
zzz777,
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.