Git minimo valido per lo sviluppo basato su trunk

Nov 30 2022
Meno è di più quando si tratta di uno strumento potente e troppo complicato come Git Eli è co-fondatore e co-CEO di trunk.io.

Less is more quando si tratta di uno strumento potente e troppo complicato come Git

Eli è co-fondatore e co-CEO di trunk.io . Ha ricoperto posizioni di leadership ingegneristica presso Microsoft, Uber e Google (che ha acquisito la startup che ha co-fondato). Il suo obiettivo principale è aiutare i team a creare software migliore, più velocemente.

Git Inferno di Dante

Lo sviluppo in un ambiente collaborativo è un'operazione complicata che comporta il monitoraggio e il coordinamento del lavoro di più sviluppatori. Molti di noi usano Git per il controllo della versione per tenere sotto controllo tutte queste modifiche. Ma nelle stesse parole di Linus , Git è "il gestore delle informazioni dall'inferno". Offre così tante opzioni e stati strani, puoi facilmente trovarti in un brutto posto al punto in cui devi clonare nuovamente da zero, anche se non hai fatto nulla di sbagliato.

Molti di noi sanno che uno sviluppatore che ha studiato Git refspec e l'albero Merkle sottostante su cui si basa, ma essere un Git savant probabilmente non è nella descrizione del tuo lavoro. Posso guidare un'auto perfettamente senza capire come funziona effettivamente la trasmissione e, alla fine, Git è solo un mezzo per raggiungere un fine.

Per ottenere il massimo da Git, devi usarlo il meno possibile quando si tratta di sviluppo basato su trunk. Limita i comandi che usi, mantieni aggiornato correttamente il ramo delle funzionalità e standardizza l'utilizzo come team. Individualmente, puoi goderti la libertà di fare quello che vuoi. Ma come squadra, il prezzo della libertà si paga con l'attrito.

Limita le tue azioni Git

La pratica più semplice da implementare per la massima efficienza Git è attenersi a un sottoinsieme di comandi. Git può fare molte cose, ma la stragrande maggioranza del suo potere esiste solo per aiutarti a uscire da situazioni terribili. Il numero di comandi consentiti da Git rispetto al numero di comandi che dovresti effettivamente invocare è piuttosto diverso.

Una volta un CTO mi ha chiesto di tenere un corso di git avanzato al suo team di ingegneri. La mia risposta è stata semplice:

quando hai bisogno di un git avanzato - le ruote si sono già staccate dall'auto. concentriamoci invece sull'uso corretto di core git.

Ecco i 10 comandi git che uso per portare a termine il mio lavoro riducendo al minimo il numero di volte in cui atterro nell'inferno di Git:

Gestione filiale

git checkout -t -b {branch-name}
Crea un ramo, imposta l'upstream sul ramo corrente e passa ad esso. Puoi anche farlo tramite il comando `git branch`, ma richiede più comandi. Dal momento che voglio sempre passare a un ramo quando lo creo, questo comando è più succinto. Crea e controlla il ramo in un colpo solo. Di solito chiamo tutti i miei rami eli/{work-being-done}.

git checkout {branch-name}
Passa a una filiale.

git branch -vv
Visualizza le tue filiali attuali e i relativi upstream.

git branch -D {branch-name}
Elimina un ramo locale, di solito per ripulire i rami obsoleti/uniti nel principale.

Spingere e tirare

git fetch origin main:main
Porta il telecomando mainnel tuo locale main, anche quando non sei attualmente sul mainramo! Questo è il ; molto utile quando stai cercando di unire l'ultimo mainin un ramo PR.

git push origin
Spingi il mio codice sul telecomando; probabilmente far girare un sacco di macchine in CI per controllare il mio lavoro.

git pull
Aggiorna la mia filiale locale con il telecomando con lo stesso nome.

Commettere

git add -A .
Aggiungi tutto ciò su cui sto lavorando (file nuovi e modificati).

git commit -am "work"
Vedi qui per un tuffo profondo nei miei pensieri sui messaggi di commit locali

git merge main
Molto di più su questo sotto. Sono pragmatico e non un rebaser e felice di confondere la mia filiale locale con gli avvenimenti di main.

Mantieni aggiornato correttamente il ramo delle funzionalità

Nel mio ultimo pezzo , ho discusso su come gestire il codice di destinazione su main. Ma spesso durante lo sviluppo, e sicuramente prima della fusione, è necessario essere aggiornati con le ultime modifiche mainperché:

  • Hai bisogno di alcune funzionalità che sono arrivate mainper far funzionare le tue funzionalità.
  • Qualcosa mainè cambiato e se non lo raccogli, lo spezzerai quando proverai a far atterrare il tuo codice.
  • Vuoi solo rimanere fresco perché sei un ingegnere e ami le cose nuove e brillanti.

Porta 1: git merge

Questa è la mia strategia di fusione. È semplice, affidabile e senza fronzoli. Una base git mergeprende tutte le modifiche avvenute maine le unisce insieme come commit insieme alle tue modifiche. Puoi leggere fino alla nausea sugli aspetti negativi di questa soluzione senza fronzoli, ma onestamente mi limito a scrollare le spalle e dire "Non mi interessa". Se unisci e unisci i rami del tuo lavoro, mainallora tutte quelle sciocchezze sull'inquinamento da git-log diventano proprio questo.

Onestamente non importa davvero come/perché/cosa è entrato nel mio ramo di lavoro. Ciò che conta è: si costruisce, funziona e il codice fa quello che voglio che faccia? Il resto è accademico.

In pratica, e io sono un ingegnere molto pratico, git mergeti consente di gestire i conflitti di unione in modo pulito e tornare a costruire. Risolvi tutti i conflitti di unione una volta e sei a posto. Questa è l'opzione migliore per i principianti, gli utenti avanzati impegnati e quelli di voi che non sognano ad occhi aperti di diventare guru di Git.

Porta 2: git rebase

Questa è di gran lunga l'opzione peggiore. Ok, lo so; c'è praticamente un'intera generazione che visualizza rebase vs merge qualcosa come schede vs spazi. Ma nei progetti "reali", in cui uno o più team si uniscono quotidianamente a un repository, il rebase richiede la risoluzione di n conflitti di unione invece di uno con un rebase di unione o squash (ne parleremo più avanti).

Inoltre, qualsiasi ribasamento riscrive la cronologia di Git e ciò significa che se il tuo ramo ha una controparte remota, devi git push --forcecalpestare la sua cronologia con la nuova cronologia ribasata del tuo ramo. Questo va bene in teoria, ma ha il potenziale per eliminare accidentalmente il tuo lavoro precedente in un modo estremamente difficile da recuperare. È semplicemente molto più pericoloso della fusione e onestamente mi fa venire il mal di testa solo a pensarci.

Ai fini di questo articolo, ho provato a ribasare solo per ricordare a me stesso quanto non mi piace questo flusso di lavoro. Guarda cosa un semplice comando rebase vomita nel mio terminale:

buoni flussi di lavoro non dovrebbero richiedere suggerimenti per funzionare

C'è così tanto che non va in quel terminale. Ricevo note sugli hash di commit: non voglio vederle. Ricevo quattro righe di suggerimenti gialli per ricordarmi come funziona l'intera faccenda. E ora sono in un flusso di lavoro solo per ottenere il codice dal mainmio ramo. L'albero decisionale è stupidamente complicato; git rebase --skip- Cos'è quello?! Perché dovrebbe andare bene saltare un commit? Sai cosa? Non dirmelo perché ho del lavoro da fare.

Porta 3: git squash rebase

Sicuramente non vuoi cosa c'è dietro la porta n. 2, ma alcune persone sono irriducibili rebaser. Il mio co-fondatore, il co-CEO David Apirian, mi ha mostrato il suo ingresso segreto nel backstage per rebase ed è fantastico. Lo chiameremo squash rebase .

Ho ribadito l'importanza della semplicità di Git, ma con questo approccio puoi avere il tuo rebase e mangiarlo anche tu. Ottieni la magica stratificazione di un rebase senza la follia di un flusso di rebase standard. Con un rebase squash, puoi:

  • Guarda una differenza dei tuoi commit locali prima di eseguire il push su GitHub.
  • Annulla facilmente il tuo ultimo commit.
  • Evita di inquinare la vista di GitHub della tua richiesta pull con un sacco di piccoli commit locali.

Passaggio 1: schiaccia tutti i tuoi commit locali:

git reset --soft $(git merge-base HEAD main) &&
git commit -am "" --allow-empty-message

git rebase main

Puoi mettere tutto questo insieme in un unico comando uber per estrarre l'ultimo main, schiacciare tutti i tuoi commit e rebase sull'ultimo main:

git reset --soft $(git merge-base HEAD main) &&
git commit -am "" --allow-empty-message &&
git fetch origin main:main &&
git rebase main

[alias]
 smartcommit = !git add -A . && git commit -am "" --allow-empty-message
 squash = !git reset --soft $(git merge-base HEAD main) && git commit -am "" --allow-empty-message
 squashrebase = !git squash && git fetch origin main:main && git rebase main
 smart = !git smartcommit && git squashrebase

Uno degli aspetti più interessanti di schiacciare sempre i tuoi commit locali è che in cima al tuo git logc'è tutto il lavoro che hai fatto nel ramo. Il tuo lavoro locale ora ricorda più da vicino l'aspetto che avrà il PR unito alla zucca quando arriverà in main.

Puoi in qualsiasi momento estrarre l'ultimo commit da HEAD con il tuo ramo locale e vedere tutto il lavoro che hai fatto su main. Questo è simile alla visualizzazione dettagliata che GitHub ti offre di ciò che è cambiato, ma ti consente di rimanere nel tuo terminale ed evitare di alternare in modo confuso tra due diverse interfacce utente.

Devo ammettere che la prima volta che David mi ha mostrato questo flusso di lavoro sono rimasto impressionato. Aveva per lo più ucciso il rebasedrago e la possibilità di visualizzare localmente tutti i file che hai modificato nel tuo ramo è super cool.

Sebbene il rebasing di squash ti aiuti a mantenere uno stato di flusso migliore, la stragrande maggioranza degli sviluppatori fa meglio a fondersi. L'uso errato di rebase o squash rebase provoca attriti che uccidono la produttività.

Le insidie ​​della guida del ramo GitHub

queste caselle di controllo in GitHub sembrano utili... ma non lo sono

GitHub offre questa impostazione ☝️ per suggerire l'unione mainautomatica nel tuo ramo se non è aggiornato. Questa impostazione è spesso controproducente. Voglio che la mia filiale locale sia l'unica fonte di verità per il mio PR e nessun altro (incluso GitHub) dovrebbe spingerlo. Lavorare con GitHub sul mio ramo personale dovrebbe essere una strada a senso unico. Codifico localmente e spingo.

Se configuri GitHub per iniziare a inviare aggiornamenti al tuo ramo di lavoro, stai chiedendo che il traffico inizi a dirigersi nella direzione sbagliata. E questa è una ricetta per il dolore.

Questo problema sorge anche quando i revisori del codice ti lasciano piccoli suggerimenti di commit sulla tua richiesta pull e tu li applichi allegramente. Se poi fai qualche lavoro sulla tua filiale locale prima di apportare quei cambiamenti ... hai aperto un piccolo sacco di guai.

Spesso scopro di averlo fatto accidentalmente a me stesso quando provo a inviare i miei aggiornamenti al telecomando e git mi dice che non sono sincronizzato. A quel punto ho due possibilità:

  1. git push --forcee soffia via qualsiasi cosa sul telecomando non sulla tua filiale locale. Generalmente qualsiasi cosa con la parola forza è distruttiva e scoraggiata.
  2. git merge origin/{my-work-branch}per unire le modifiche remote nella vista locale del ramo.

Questa impostazione di GitHub ️☝️ richiede che i PR siano aggiornati mainprima di unirsi a essa. Questo generalmente "risolve" il potenziale per avere un guasto main; tuttavia, funziona solo con repository a velocità molto bassa. Una volta che una manciata di sviluppatori tenta di unire più PR al giorno, finiscono in una corsa per unirsi a main- o altrimenti si ritrovano a eseguire costantemente un'unione/rebase mainnel loro ramo per essere di nuovo "aggiornati" e fondamentalmente gareggiano con i loro colleghi essere il primo a fondersi. Doloroso. In genere, se desideri questa funzionalità ma non sei uno sviluppatore solista, probabilmente desideri invece una coda di unione, che è il modo scalabile per ottenere l'assenza di interruzioni. Questo è qualcosa che abbiamo ritenuto abbastanza importante da produrre con Trunk Merge .

Riduci a icona Git per ottenere il massimo valore

Mantenendo il tuo utilizzo di Git il più semplice possibile, dai agli sviluppatori meno corda con cui impiccarsi e più tempo per fare il lavoro di sviluppo che conta davvero. Fai in modo che Git svolga un ruolo di supporto e non diventi il ​​protagonista. Per mantenere un ambiente di sviluppo sano e collaborativo, ricorda di:

  • Scegli la superficie più piccola di Git da usare e mantienila.
  • Gestisci sempre le tue modifiche a monte allo stesso modo.
  • Standardizza le pratiche all'interno del tuo team in modo che anche i tuoi colleghi non scappino dai binari.