Le richieste pull di squash interrompono l'algoritmo di fusione di git?


16

Attualmente sto lavorando per un'azienda che utilizza VSTS per la gestione del codice git. Il modo "consigliato" di Microsoft di unire un ramo è quello di fare una "fusione squash", il che significa che tutti i commit per quel ramo vengono schiacciati in un nuovo commit che incorpora tutte le modifiche.

Il problema è che cosa succede se eseguo alcune modifiche in un ramo per un elemento di backlog, quindi voglio immediatamente iniziare a fare cambiamenti in un altro ramo per un altro elemento di backlog e tali cambiamenti dipendono dal set di modifiche del primo ramo?

Posso creare un ramo per quell'elemento arretrato e basarlo sul primo ramo. Fin qui tutto bene. Tuttavia, quando arriva il momento di creare una richiesta pull per me secondo ramo, il primo ramo è già stato unito in master e poiché è stato fatto come fusione di squash, git segnala un sacco di conflitti. Questo perché git non vede i commit originali su cui era basato il secondo ramo, vede solo l'unione di una grande squash e quindi, per unire il secondo ramo in master, cerca di riprodurre tutti i commit del primo ramo in la parte superiore della zucca si unisce, causando molti conflitti.

Quindi la mia domanda è: c'è un modo per aggirare questo (oltre a non basare mai un ramo di una funzione su un altro, il che limita il mio flusso di lavoro) o l'unione di squash rompe solo l'algoritmo di fusione di git?

Risposte:


15

Con Git, si impegna

  • sono immutabili,
  • e forma un grafico aciclico diretto.

Lo schiacciamento non combina i commit. Al contrario, registra un nuovo commit con le modifiche da più altri commit. Rebasing è simile, ma non combina commit. La registrazione di un nuovo commit con le stesse modifiche di un commit esistente si chiama riscrittura della cronologia . Ma poiché gli impegni esistenti sono immutabili, questo dovrebbe essere inteso come "scrivere una storia alternativa".

La fusione cerca di combinare i cambiamenti delle storie di due commit (rami) a partire da un commit antenato comune.

Quindi diamo un'occhiata alla tua storia:

                                 F  feature2
                                /
               1---2---3---4---5    feature1 (old)
              /
-o---o---o---A---o---o---S          master

A è l'antenato comune, 1–5 il ramo della funzione originale, F il ramo della nuova funzione e S il commit schiacciato che contiene le stesse modifiche di 1–5. Come puoi vedere, l'antenato comune di F e S è A. Per quanto riguarda git, non c'è relazione tra S e 1–5. Quindi la fusione tra master e S da un lato e feature2 con 1–5 dall'altro sarà in conflitto. Risolvere questi conflitti non è difficile, ma è un lavoro noioso e non necessario.

A causa di questi vincoli, ci sono due approcci per gestire l'unione / schiacciamento:

  • O usi la riscrittura della cronologia, nel qual caso otterrai più commit che rappresentano le stesse modifiche. Quindi ridisegnare il secondo ramo della feature sul commit schiacciato:

                                     F  feature2 (old)
                                    /
                   1---2---3---4---5    feature1 (old)
                  /
    -o---o---o---A---o---o---S          master
                              \
                               F'       feature2
    
  • Oppure non usi la riscrittura della cronologia, nel qual caso potresti ricevere ulteriori commit di unione:

                                     F  feature2
                                    /
                   1---2---3---4---5    feature1 (old)
                  /                 \
    -o---o---o---A---o---o-----------M  master
    

    Quando feature2 e master vengono uniti, l'antenato comune verrà eseguito il commit 5.

In entrambi i casi avrai qualche sforzo di fusione. Questo sforzo non dipende molto da quale delle due strategie sopra scelte. Ma assicurati

  • i rami sono di breve durata, per limitare fino a che punto possono spostarsi dal ramo principale e quello
  • unisci regolarmente il master nel ramo della tua funzione o ridisegna il ramo della caratteristica sul master per mantenere sincronizzati i rami.

Quando si lavora in gruppo, è utile coordinare chi sta lavorando su cosa. Questo aiuta a mantenere piccolo il numero di funzionalità in fase di sviluppo e può ridurre il numero di conflitti di unione.


2
La tua risposta non sembra affrontare ciò che accade se prima ti unisci feature1a squash in master, quindi vuoi unirti in feature2seguito. In tal caso, il primo approccio non comporterebbe conflitti poiché git tenta di riapplicare i feature1commit in cima al commit schiacciato, mentre il secondo consentirebbe a git di determinare che tali commit non devono essere uniti?
Jez,

@Jez Questo è esattamente ciò che accade quando schiacci un PR. Di recente ho dovuto riscrivere manualmente un PR su un progetto OSS ( git cloneingingendo il repository e copiando i miei file modificati!) Perché mi sono ramificato da un ramo e quindi il manutentore ha schiacciato il primo ramo. Nel mio lavoro fanno anche fusioni di zucca. Ciò significa che non posso lavorare su una funzione bche dipende dalla funzione afino a quando la funzione non aviene unita.
Getta via l'account

1
E quella non è una rottura davvero fastidiosa di qualcosa che altrimenti funzionerebbe, come git è progettato per? Vedi, vedo che varie organizzazioni come Microsoft e Github in realtà raccomandano queste fusioni di squash e mi sembrano stupide.
Jez,

1
@Jez Nello scenario originale, sì, si otterranno conflitti quando si fondono feature2 in master perché l'unione dei commit 1–5 entrerà in conflitto con le stesse modifiche in S. La soluzione è di ridefinire feature2 (soluzione 1) o di non utilizzare lo squash / rebasing in tutto (soluzione 2).
am

Se le fusioni di squash sono adatte a te dipende da cosa vuoi registrare nella cronologia dei controlli di versione. Se i rami delle caratteristiche hanno molti commit WIP, lo squash mette un unico grande commit con la funzionalità completa sul ramo master. Se si preferisce preservare tutti gli commit intermedi del ramo della feature, utilizzare il rebasing o l'unione.
am

11

L'unione di squash interrompe l'algoritmo di fusione per tutti i rami che contengono commit che sono stati rimossi dallo squash. Detto in altro modo, i rebase sono virali. Se si ribatte un ramo, è necessario rifare il passo di tutti gli altri rami che dipendono da quel ramo. Se si utilizza rerere , eventuali conflitti di unione risolti manualmente nel repository locale non dovranno essere nuovamente risolti manualmente, ma ciò non aiuta con i conflitti risolti da altre persone.

Ecco perché la nostra regola non scritta qui è ok schiacciare fino a quando nessun altro è mai dipeso dal tuo ramo di funzionalità, che è il caso forse del 90% delle volte. Altrimenti, una fusione diretta aiuta tutti a evitare problemi.


Un modo per avere sia un commit schiacciato nella cronologia principale sia un ramo di funzionalità intatto per creare un ramo separato solo per squash. Supponiamo di avere un feature-xyzramo. È possibile creare un feature-xyz-squashedramo iniziando dallo stesso commit del feature-xyzramo, git cherry-picki commit da feature-xyza feature-xyz-squashed, schiacciarli lì e unirli feature-xyz-squasheda master. Non dovresti unire feature-xyzallora. A volte quanto sopra ha senso (ad es. Non si desidera includere commit con una password inserita), ma è una soluzione alternativa, quasi una buona pratica.
9000
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.