Perché squash git si impegna per le richieste pull?


200

Perché ogni serio repository Github che faccio richiede di volere che io comprima i miei commit in un unico commit?

Pensavo che il registro di Git fosse lì in modo da poter ispezionare tutta la tua storia e vedere esattamente quali cambiamenti sono avvenuti dove, ma schiacciarlo lo estrae dalla storia e lo raccoglie in un unico commit. Qual è il punto?

Anche questo sembra andare contro il mantra "commetti presto e commetti spesso".


1
Per un grande repository, consente di risparmiare molto tempo e spazio quando le persone vogliono fare qualsiasi cosa con il repository.
raptortech97,

8
"Anche questo sembra andare contro il" commettere in anticipo e commettere spesso "mantra ". E il recensore non vuole guardarli attraverso, questo è sicuro.
JensG,

7
Non è necessario inserire una modifica nella domanda per riassumere quale sia la risposta percepita. Questo è il luogo delle risposte e dei voti accettati. Le persone possono trarre le proprie conclusioni dalla lettura delle risposte.

5
Non riesco ancora a capire perché vi sia il desiderio di impegnarsi a schiacciare. Penso che ci siano troppi contro nello schiacciare senza quasi nessun pro. È un problema di presentazione mascherato da problema di dati.
mkobit,

3
Qualsiasi utile analisi nel registro viene persa con le squash. Quindi sembra che uno sviluppatore abbia creato una funzione con un unico commit. È anche difficile capire perché esiste uno strano pezzo di codice. La storia di una funzionalità può essere importante e può fare molto per spiegare perché il codice è così com'è. La vista concisa del registro è utile, ma non può essere raggiunta in un altro modo?
Tjaart,

Risposte:


158

In modo da avere una cronologia git chiara e concisa che documenti in modo chiaro e semplice le modifiche apportate e i motivi per cui.

Ad esempio, un tipico log git 'non eliminato' potrebbe apparire come il seguente:

7hgf8978g9... Added new slideshow feature, JIRA # 848394839
85493g2458... Fixed slideshow display issue in ie
gh354354gh... wip, done for the week
789fdfffdf... minor alignment issue
787g8fgf78... hotfix for #5849564648
9080gf6567... implemented feature # 65896859
gh34839843... minor fix (typo) for 3rd test

Che casino!

Considerando che un registro git più attentamente gestito e unito con un po 'di attenzione in più sui messaggi per questo potrebbe apparire come:

7hgf8978g9... 8483948393 Added new slideshow feature
787g8fgf78... 5849564648 Hotfix for android display issue
9080gf6567... 6589685988 Implemented pop-up to select language

Penso che si possa vedere il punto di schiacciare i commit in generale e lo stesso principio si applica alle richieste pull - Leggibilità della storia. È inoltre possibile che si stia aggiungendo a un registro di commit che ha già centinaia o addirittura migliaia di commit e ciò contribuirà a mantenere la storia in crescita breve e concisa.

Vuoi impegnarti presto e spesso. È una buona pratica per molte ragioni. Trovo che questo mi porti ad avere spesso commit "wip" (work-in-progress) o "part A done" o "typo, minore correzione" in cui sto usando git per aiutarmi a lavorare e darmi punti di lavoro che Posso tornare indietro se il seguente codice non funziona mentre progredisco per far funzionare le cose. Tuttavia non ho bisogno o non voglio quella storia come parte della storia finale di git, quindi potrei schiacciare i miei commit - ma vedi le note sotto su cosa significa questo su un ramo di sviluppo vs. master.

Se ci sono importanti traguardi che rappresentano fasi di lavoro distinte, è comunque accettabile avere più di un commit per funzione / attività / bug. Tuttavia, ciò può spesso evidenziare il fatto che il biglietto in fase di sviluppo è "troppo grande" e deve essere suddiviso in pezzi più piccoli che possono essere autonomi, ad esempio:

8754390gf87... Implement feature switches

sembra "1 pezzo di lavoro". O esistono o no! Non sembra avere senso scoppiare. Tuttavia l'esperienza mi ha mostrato che (a seconda delle dimensioni e della complessità dell'organizzazione) un percorso più granulare potrebbe essere:

fgfd7897899... Add field to database, add indexes and a trigger for the dw group
9458947548g... Add 'backend' code in controller for when id is passed in url.
6256ac24426... Add 'backend' code to make field available for views.
402c476edf6... Add feature to UI

Piccoli pezzi significano revisioni più semplici del codice, test dell'unità più semplici, migliore opportunità di qa, migliore allineamento al principio di responsabilità singola, ecc.

Per la praticità di quando effettivamente fare tali squash, ci sono fondamentalmente due fasi distinte che hanno il loro flusso di lavoro

  • il tuo sviluppo, ad es. richieste pull
  • il tuo lavoro è stato aggiunto al ramo principale, ad esempio master

Durante lo sviluppo commetti "presto e spesso" e con messaggi "usa e getta" rapidi. Potresti voler schiacciare qui a volte, ad esempio schiacciando in wip e todo commit dei messaggi. Va bene, all'interno del ramo, conservare più commit che rappresentano passi distinti che hai fatto nello sviluppo. La maggior parte delle squash che scegli di fare dovrebbe essere all'interno di questi rami delle caratteristiche, mentre sono in fase di sviluppo e prima di fondersi con il master.
Quando si aggiunge al ramo della linea principale si desidera che gli commit siano concisi e formattati correttamente in base alla cronologia della linea principale esistente. Ciò potrebbe includere l'ID di sistema di Ticket Tracker, ad esempio JIRA, come mostrato negli esempi. Lo schiacciamento non si applica qui se non si desidera "eseguire il rollup" di diversi commit distinti sul master. Normalmente no.

L'utilizzo --no-ffdurante l'unione al master utilizzerà un commit per l'unione e preserverà anche la cronologia (nel ramo). Alcune organizzazioni ritengono che questa sia una buona pratica. Vedi di più su https://stackoverflow.com/q/9069061/631619 Vedrai anche l'effetto pratico in git logcui un --no-ffcommit sarà l'ultimo commit, nella parte superiore di HEAD (appena fatto), mentre senza --no-ffpotrebbe essere più in basso nella storia, a seconda delle date e di altri impegni.


30
Non sono completamente d'accordo con questa filosofia. Piccoli commit regolari suddividono un'attività in attività più piccole e spesso forniscono informazioni utili su ciò su cui si stava lavorando quando una linea veniva cambiata. Se hai piccoli commit "WIP", dovresti usare un rebase interattivo per ripulirlo prima di spingerli. Schiacciare le PR sembra sconfiggere completamente il punto dei piccoli commit regolari di IMHO. È quasi irrispettoso per l'autore che ha fatto lo sforzo di fornire messaggi di log con specifiche informazioni di commit e lo butti via.
Jez,

6
Interessante. Alla fine della giornata baso la mia "filosofia" su ciò che ho visto funziona. Per essere chiari, suddividere il lavoro in piccole parti e impegnarle ognuna è fantastico mentre si lavora sul cambiamento. La mia esperienza è stata che una volta che questo lavoro è stato unito al maestro, la cosa principale che tende ad essere esaminata è "cosa, nella sua totalità, cosa è questo impegno di unione che (di solito) ha causato problemi x ...
Michael Durrant,

6
Sono anche anti-squash. Non dovresti scrivere messaggi di commit stupidi in primo luogo. E nell'esempio (85493g2458 ... Risolto il problema di visualizzazione della presentazione in ie) Sarebbe molto più utile se dovessi eseguire il debug del codice associato.
Thomas Davis,

5
È un peccato che un tale dogma disattento e mal riposto sia emerso nella pratica SCM. Gli strumenti di filtro devono essere utilizzati per gestire la leggibilità della cronologia e non per commettere comportamenti.
ctpenrose,

4
tendenze inquietanti e dogma. yow. Preferirei un argomento più basato sui fatti presentato in un modo più gradevole. Sei invitato a pubblicare / votare un'opinione diversa. Questo è il punto del voto della comunità
Michael Durrant,

26

Perché spesso la persona che estrae una PR si preoccupa dell'effetto netto delle "funzionalità aggiunte X", non dei "modelli di base, funzione di correzione bug X, aggiunta della funzione Y, errori di battitura fissi nei commenti, parametri di ridimensionamento dei dati regolati, hashmap ha prestazioni migliori di list "... livello di dettaglio

Se ritieni che i tuoi 16 commit siano rappresentati al meglio da 2 commit anziché da 1 "Aggiunta funzione X, ricostituito Z per usare X", allora probabilmente va bene proporre un pr con 2 commit, ma potrebbe essere meglio proporre 2 pr separati in quel caso (se il repository insiste ancora su singoli commit)

Questo non va contro il mantra "commetti in anticipo e commetti spesso", come nel tuo repository, mentre stai sviluppando hai ancora i dettagli granulari, quindi hai minime possibilità di perdere il lavoro e altre persone possono rivedere / tirare / proporre pr è contro il tuo lavoro mentre il nuovo pr è in fase di sviluppo.


4
Ma quando commetti la zucca perdi anche quella storia nel tuo fork, giusto? Perché vorresti buttare via quella storia?
hamstar,

4
a) se vuoi conservare la cronologia nel tuo ramo, è abbastanza facile avere un ramo "per unire" e un ramo "dev" (che potrebbe essere quello che ti serve comunque, dato che potresti aggiungere nuovi commit al tuo sviluppatore principale ramo dopo aver proposto il PR) b) stai ancora preservando l'effetto netto di tali commit, sarebbe solo nei casi in cui desideri invertire, o scegliere un sottocomponente del pr che il singolo commit sarebbe necessario . In tal caso, probabilmente stai facendo più cose (ad esempio: correzione di bug X, aggiungi la funzione Y) in un singolo PR e potrebbero essere nuovamente necessarie più PR.
Andrew Hill,

@AndrewHill che grande idea! Voglio presentare questo flusso di lavoro al mio team, come ha funzionato per te? Ho scritto un post sul blog a riguardo , e mi sono imbattuto in questo commento durante la ricerca.
Droogans,

19

Il motivo principale da quello che posso vedere è il seguente:

  • L'interfaccia utente di GitHub per l'unione delle richieste pull attualmente (ottobre 2015) non consente di modificare la prima riga del messaggio di commit, forzandolo Merge pull request #123 from joebloggs/fix-snafoo
  • L'interfaccia utente di GitHub per la navigazione nella cronologia di commit attualmente non consente di visualizzare la cronologia della filiale dal --first-parentpunto di vista
  • L'interfaccia utente di GitHub per guardare la colpa su un file attualmente non ti consente di visualizzare la colpa del file con il --first-parentpunto di vista (nota che questo è stato risolto solo in Git 2.6.2, quindi potremmo perdonare GitHub per non averlo a disposizione)

Quindi, quando si combinano tutte e tre queste situazioni sopra, si ottiene una situazione in cui i commit non eliminati si uniscono brutti dall'interfaccia utente di GitHub.

La tua storia con commit schiacciati avrà un aspetto simile

1256556316... Merge pull request #423 from jrandom/add-slideshows
7hgf8978g9... Added new slideshow feature
56556316ad... Merge pull request #324 from ahacker/fix-android-display
787g8fgf78... Hotfix for android display issue
f56556316e... Merge pull request #28 from somwhere/select-lang-popup
9080gf6567... Implemented pop-up to select language

Considerando che senza impegni schiacciati la storia avrà un aspetto simile

1256556316... Merge pull request #423 from jrandom/add-slideshows
7hgf8978g9... Added new slideshow feature, JIRA # 848394839
85493g2458... Fixed slideshow display issue in ie
gh354354gh... wip, done for the week
789fdfffdf... minor alignment issue
56556316ad... Merge pull request #324 from ahacker/fix-android-display
787g8fgf78... hotfix for #5849564648
f56556316e... Merge pull request #28 from somwhere/select-lang-popup
9080gf6567... implemented feature # 65896859
gh34839843... minor fix (typo) for 3rd test

Quando hai un sacco di commit in una traccia PR in cui è avvenuta una modifica può diventare un po 'un incubo se ti limiti a utilizzare l'interfaccia utente di GitHub .

Ad esempio, trovi un puntatore nullo che viene de-referenziato da qualche parte in un file ... quindi dici "chi è stato e quando? Quali versioni di versione sono interessate?". Quindi ti avvicini alla vista della colpa nell'interfaccia utente di GitHub e vedi che la linea è stata cambiata789fdfffdf... "oh, ma aspetta un secondo, quella riga stava appena cambiando il suo rientro per adattarsi al resto del codice", quindi ora devi navigare nello stato dell'albero per quel file nel commit genitore e visitare nuovamente la pagina della colpa ... alla fine trovi il commit ... è un commit di 6 mesi fa ... "oh **** questo potrebbe influenzare gli utenti per 6 mesi" dici ... ah ma aspetta, quel commit era in realtà in una richiesta pull ed è stato unito solo ieri e nessuno ha ancora rilasciato un comunicato ... "Accidenti a voi per aver unito i commit senza schiacciare la storia" è il grido che di solito può essere ascoltato dopo circa 2 o 3 spedizioni di codice archeologico tramite il Interfaccia utente di GitHub

Ora consideriamo come funziona se usi la riga di comando Git (e la super 2.6.2 che ha la correzione per git blame --first-parent)

  • Se si utilizzava la riga di comando Git, si sarebbe in grado di controllare completamente il messaggio di commit unione e quindi il commit unione potrebbe avere una bella riga di riepilogo.

Quindi la nostra cronologia di commit sarebbe simile

$ git log
1256556316... #423 Added new slideshow feature
7hgf8978g9... Added new slideshow feature, JIRA # 848394839
85493g2458... Fixed slideshow display issue in ie
gh354354gh... wip, done for the week
789fdfffdf... minor alignment issue
56556316ad... #324 Hotfix for android display issue
787g8fgf78... hotfix for #5849564648
f56556316e... #28 Implemented pop-up to select language
9080gf6567... implemented feature # 65896859
gh34839843... minor fix (typo) for 3rd test

Ma possiamo anche fare

$ git log --first-parent
1256556316... #423 Added new slideshow feature
56556316ad... #324 Hotfix for android display issue
f56556316e... #28 Implemented pop-up to select language

(In altre parole: la Gi CLI ti consente di avere la tua torta e mangiarla anche tu)

Ora, quando colpiamo il problema del puntatore nullo ... beh, lo usiamo git blame --first-parent -w dodgy-file.ce ci viene dato immediatamente il commit esatto in cui il de-riferimento del puntatore null è stato introdotto nel ramo master ignorando le semplici modifiche degli spazi bianchi.

Naturalmente se stai eseguendo le fusioni utilizzando l'interfaccia utente di GitHub, allora git log --first-parentè davvero scadente grazie a GitHub che impone la prima riga del messaggio di commit unione:

1256556316... Merge pull request #423 from jrandom/add-slideshows
56556316ad... Merge pull request #324 from ahacker/fix-android-display
f56556316e... Merge pull request #28 from somwhere/select-lang-popup

Quindi, per farla breve:

L'interfaccia utente di GitHub (ottobre 2015) presenta una serie di carenze nel modo in cui unisce le richieste pull, in che modo presenta la cronologia del commit e in che modo attribuisce le informazioni sulla colpa. L'attuale modo migliore per aggirare questi difetti nell'interfaccia utente di GitHub è richiedere alle persone di eliminare i propri impegni prima di unirsi.

L'interfaccia della riga di comando di Git non presenta questi problemi e puoi facilmente scegliere quale vista vuoi vedere, così puoi scoprire sia il motivo per cui una particolare modifica è stata fatta in quel modo (guardando la cronologia dei commit non annullati) sia vedere gli commit effettivamente schiacciati.

Post Script

L'ultimo motivo spesso citato per i commit di compressione è quello di rendere più semplice il backport ... se hai solo un commit per il back port (cioè il commit schiacciato) allora è facile scegliere ...

Bene, se stai guardando la storia di git con git log --first-parentallora puoi semplicemente scegliere la commit di fusione. La maggior parte delle persone si confondono con la raccolta delle ciliegie. I commit di merge perché devi specificare l' -m Nopzione ma se hai ricevuto il commit git log --first-parentallora sai che è il primo genitore che vuoi seguire, quindi saràgit cherry-pick -m 1 ...


1
Bella risposta! Ma scegliere una gamma di ciliegie è abbastanza semplice, non sono sicuro di acquistarlo come motivo.
Stiggler

1
@stiggler Personalmente non lo compro come motivo, ma l'ho visto citato da altri come motivo. IMHO lo strumento git è quello che vuoi e lo schiacciare commette è male ... ma direi che aver guidato la riparazione di --first-parentme stesso per vincere una discussione contro lo schiacciamento si commette ;-)
Stephen Connolly

1
Questa dovrebbe essere la risposta accettata. Molto meno dogma e dimostra che il filtro storico può essere usato per "avere la tua torta e mangiarla anche tu". Non è necessario annientare la storia per rendere più chiare le esplorazioni della storia.
ctpenrose,

Non sarà così facile scegliere se schiacciando i tuoi commit finirai per raggruppare molte modifiche a file diversi in un unico commit. Immagino che questo varia molto in base alla base di codice, quali modifiche sono state apportate, quante persone stanno collaborando e così via. Non penso che possiamo elaborare un'unica regola generale per essere valida in tutti i casi.
Amy Pellegrini,

2

Concordo con i sentimenti espressi in altre risposte in questo thread riguardo a una cronologia chiara e concisa delle funzionalità aggiunte e dei bug corretti. Tuttavia, volevo affrontare un altro aspetto al quale la tua domanda è sfuggita ma che non è stata esplicitamente dichiarata. Parte del blocco che potresti avere su alcuni dei metodi di lavoro di git è che git ti consente di riscrivere la storia che sembra strano quando viene introdotto a git dopo aver usato altre forme di controllo del codice sorgente in cui tali azioni sono impossibili. Inoltre, ciò va anche contro il principio generalmente accettato del controllo del codice sorgente secondo cui una volta che qualcosa è stato impegnato / archiviato nel controllo del codice sorgente, si dovrebbe essere in grado di ritornare a quello stato indipendentemente dalle modifiche apportate a seguito di tale commit. Come insinui nella tua domanda, questa è una di quelle situazioni. Penso che git sia un ottimo sistema di controllo delle versioni; tuttavia, per capirlo è necessario comprendere alcuni dei dettagli di implementazione e le decisioni di progettazione alla base, e di conseguenza ha una curva di apprendimento più ripida. Tieni presente che git era inteso come un sistema di controllo della versione distribuito e ciò contribuirà a spiegare perché i progettisti consentono di riscrivere la cronologia di git con i commit di squash che ne sono un esempio.


A rigor di termini, Git non ti consente di cambiare la cronologia. Gli oggetti di commit sono immutabili. Sono mutabili solo i puntatori di succursale , e solo allora con un "aggiornamento forzato", generalmente considerato qualcosa che si fa solo per scopi di sviluppo, non sul ramo principale. Puoi sempre tornare a uno stato precedente usando il reflog, a meno che non git gcsia stato eseguito per scartare oggetti senza riferimento.
Jon Purdy,

Hai ragione, e forse avrei dovuto menzionarlo sopra. Tuttavia, direi qualcosa come una spinta forzata o un commit di rebasing che sono già stati spinti in un repository remoto, si qualificherebbe come riscrittura della storia, poiché l'ordine e la disposizione dei commit sono importanti quanto gli commit stessi.
Fred Thomsen,

Questo è vero per un determinato commit. In una prospettiva più ampia, penso al rebasing interattivo e al filtro-branch come a riscrivere efficacemente la storia cambiando la sua composizione.
Michael Durrant,

1
Per essere onesti, SVN mantiene l'approccio "ogni commit è sacro", ma quando la fusione crea un singolo commit con quella fusione e nasconde le revisioni che hanno contribuito a ciò, quindi sembra che tutte le fusioni SVN siano "ridisegnate". Non lo sono, devi solo dire al comando history di mostrarti i componenti uniti. Mi piace questo approccio al meglio.
gbjbaanb,

1

A causa della prospettiva ... La migliore pratica è avere un singolo commit per singolo <<issue-management-system>>problema, se possibile.

Potresti avere tutti gli commit che vuoi nel tuo ramo / repository di funzionalità, ma è la tua storia rilevante per quello che stai facendo ora per la TUA prospettiva ... non è lui STORIA per l'intero TEAM / PROGETTO o applicazione dal loro prospettiva da mantenere tra qualche mese ...

Quindi, ogni volta che desideri commettere una correzione di bug o una funzionalità in un repository comune (questo esempio è con il ramo di sviluppo), cosa potresti fare nel modo seguente:

come reimpostare rapidamente il ramo delle funzioni in modo che si sviluppi

    # set your current branch , make a backup of it , caveat minute precision
    curr_branch=$(git rev-parse --abbrev-ref HEAD); git branch "$curr_branch"--$(date "+%Y%m%d_%H%M"); git branch -a | grep $curr_branch | sort -nr

    # squash all your changes at once 
    git reset $(git merge-base develop $curr_branch)

    # check the modified files to add 
    git status

    # add the modified files dir by dir, or file by file or all as shown
    git add --all 

    # check once again 
    git log --format='%h %ai %an %m%m %s'  | less

    # add the single message of your commit for the stuff you did 
    git commit -m "<<MY-ISSUE-ID>>: add my very important feature"

    # check once again 
    git log --format='%h %ai %an %m%m %s'  | less

    # make a backup once again , use seconds precision if you were too fast ... 
    curr_branch=$(git rev-parse --abbrev-ref HEAD); git branch "$curr_branch"--$(date "+%Y%m%d_%H%M"); git branch -a | grep $curr_branch | sort -nr

    # compare the old backup with the new backup , should not have any differences 
    git diff <<old-backup>>..<<new-backup-branch>>


    # you would have to git push force to your feature branch 
    git push --force 

questo non tenta nemmeno di rispondere alla domanda posta, perché squash git si impegna per le richieste pull? Vedi Come rispondere
moscerino

dopo il tentativo di spiegazione dovrebbe farlo ...
Yordan Georgiev

In qualche modo non riesco a vedere come la spiegazione aggiunta offra qualcosa di sostanziale rispetto ai punti formulati e spiegati nelle risposte più votate che sono state pubblicate diversi anni fa (tuttavia si apprezzano gli sforzi per migliorare la prima revisione)
moscerino

la parola chiave è la "prospettiva", il concetto di prospettiva rende molto più semplice la spiegazione di questo tipo di problemi nell'IT, ad esempio - la tua prospettiva per la risposta a questa domanda è quella già fornita, la mia prospettiva sta usando la parola chiave "prospettiva" e fornendo un esempio pratico di come implementare le stesse migliori pratiche spiegate attraverso diverse prospettive ...
Yordan Georgiev

1

Vorrei vedere se il codice diventa pubblico o no.

Quando i progetti rimangono privati:

Consiglierei di non schiacciare e vedere la preparazione della salsiccia. Se usi commit piccoli e buoni, strumenti come git bisect sono molto utili e le persone possono individuare rapidamente i commit di regressione e capire perché l'hai fatto (a causa del messaggio di commit).

Quando i progetti diventano pubblici:

Schiaccia tutto perché alcuni commit possono contenere falle di sicurezza. Ad esempio una password che è stata salvata e rimossa di nuovo.

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.