Cosa significa schiacciare i commit in git?


117

Cosa significa Squashing commit in git. Come faccio a schiacciare i commit in Github?

Sono nuovo su Git e ho chiesto di essere assegnato a un bug nuovo arrivato in coala-analyzer. Ho corretto il bug e ora mi è stato chiesto di eliminare i miei commit. Come lo faccio?


Hey @Lakshman, sentiti libero di accettare la risposta più votata. Risponde correttamente alla tua domanda.
Mostafiz Rahman

Risposte:


180

Puoi pensare a Git come a un database avanzato di istantanee delle tue directory di lavoro.

Una caratteristica molto interessante di Git è la capacità di riscrivere la cronologia dei commit.
Il motivo principale per farlo è che molta di tale cronologia è rilevante solo per lo sviluppatore che l'ha generata, quindi deve essere semplificata, o resa più piacevole, prima di inviarla a un repository condiviso.

Schiacciare un commit significa, da un punto di vista idiomatico, spostare le modifiche introdotte in detto commit nel suo genitore in modo da finire con un commit invece di due (o più).
Se ripeti questo processo più volte, puoi ridurre n commit a uno solo.

Visivamente, se hai iniziato il tuo lavoro al commit contrassegnato con Start , lo vuoi

Git si impegna a schiacciare

Potresti notare che il nuovo commit ha una tonalità di blu leggermente più scura. Questo è intenzionale.

In Git lo squashing si ottiene con un Rebase , di una forma speciale chiamata Interactive Rebase .
Semplificando quando ribasate un insieme di commit in un ramo B , applicate tutte le modifiche introdotte da quei commit così come sono stati fatti, a partire da B invece del loro antenato originale.

Un indizio visivo

inserisci qui la descrizione dell'immagine

Nota ancora le diverse tonalità di blu.

Un rebase interattivo ti consente di scegliere come rebase i commit. Se esegui questo comando:

 git rebase -i branch

Ti ritroverai con un file che elenca i commit che verranno ribasati

 pick ae3...
 pick ef6...
 pick 1e0...
 pick 341...

Io non nomino i commit, ma questi quattro quelli destinati ad essere i commit da Inizio a capo

La cosa bella di questo elenco è che è modificabile .
Puoi omettere i commit oppure puoi eliminarli .
Tutto quello che devi fare è cambiare la prima parola in squash .

 pick ae3...
 squash ef6...
 squash 1e0...
 squash 341...

Se chiudi l'editor e non vengono trovati conflitti di unione, ti ritroverai con questa cronologia:

inserisci qui la descrizione dell'immagine

Nel tuo caso, non vuoi rebase in un altro ramo, ma piuttosto in un commit precedente.
Per trasformare la cronologia come mostrato nel primo esempio, devi eseguire qualcosa di simile

git rebase -i HEAD~4

cambia i "comandi" in squash per tutti i commit tranne il primo, quindi chiudi l'editor.


Nota sull'alterazione della storia

In Git, i commit non vengono mai modificati. Possono essere potati, resi non raggiungibili, clonati ma non modificati.
Quando si rebase, si stanno effettivamente creando nuovi commit.
I vecchi non sono più raggiungibili da nessun ref, quindi non vengono mostrati nella cronologia ma sono ancora lì!

Questo è ciò che ottieni effettivamente per un rebase:

inserisci qui la descrizione dell'immagine

Se li hai già spinti da qualche parte, riscrivere la cronologia creerà effettivamente un ramo!


Bello, cosa succede ai commenti di commit originali, tuttavia, vengono combinati in un unico grande commento di commit o vengono persi?
Paul R,

Da man git rebase: Il messaggio di commit suggerito per il commit piegato è la concatenazione dei messaggi di commit del primo commit e di quelli con il comando "squash"
Margaret Bloom

1
Questa è la migliore spiegazione che ho visto del ribasamento, grazie.
Kerry Jones

Informazioni sullo schiacciamento nella tua prima immagine: puoi fare un esempio in cui HEAD ha una directory di lavoro diversa prima (blu chiaro) e dopo (blu scuro) lo schiacciamento? In tutti gli esempi che ho provato a schiacciare sembrava che avesse appena cancellato i tre commit tra START e HEAD.
actual_panda

@actual_panda Non sono sicuro di seguire. HEAD è un riferimento a un commit. Impegna i delta del negozio. La directory di lavoro è una proprietà del repository nel suo insieme, dipende da quale commit hai estratto. In generale, due ref (come HEAD e START) danno sempre due diverse workdirs al momento del check out. Se ribasate squash sullo stesso ramo, l'effetto è di "perdere" i commit intermedi ma in realtà git ne ha creato uno nuovo con tutti i delta. git diffpuò aiutarti a mostrare cosa è successo.
Margaret Bloom

22

Il comando rebase ha alcune fantastiche opzioni disponibili nella sua modalità --interactive(o -i) e una delle più utilizzate è la possibilità di schiacciare i commit. Ciò che fa è prendere impegni più piccoli e combinarli in quelli più grandi, il che potrebbe essere utile se stai concludendo il lavoro della giornata o se vuoi semplicemente impacchettare le tue modifiche in modo diverso. Vedremo come puoi farlo facilmente.

Una parola di cautela: fallo solo su commit che non sono stati inviati a un repository esterno. Se altri hanno basato il lavoro al di fuori dei commit che stai per eliminare, possono verificarsi molti conflitti. Basta non riscrivere la tua cronologia se è stata condivisa con altri.

Quindi diciamo che hai appena fatto alcuni piccoli commit e vuoi farne uno più grande. La cronologia del nostro repository attualmente è simile a questa:

inserisci qui la descrizione dell'immagine

Gli ultimi 4 commit sarebbero molto più felici se fossero racchiusi insieme, quindi facciamolo attraverso il rebase interattivo:

$ git rebase -i HEAD~4

pick 01d1124 Adding license
pick 6340aaa Moving license into its own file
pick ebfd367 Jekyll has become self-aware.
pick 30e0ccb Changed the tagline in the binary, too.

# Rebase 60709da..30e0ccb onto 60709da
#
# Commands:
#  p, pick = use commit
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
#

Quindi, qui sono successe alcune cose. Prima di tutto, ho detto a Git che volevo rebase usando gli ultimi quattro commit da dove si trova HEAD con HEAD ~ 4. Git ora mi ha inserito in un editor con il testo sopra e una piccola spiegazione di cosa si può fare. Hai molte opzioni a tua disposizione da questa schermata, ma al momento stiamo solo per ridurre tutto in un commit. Quindi, cambiare le prime quattro righe del file in questo farà il trucco:

pick 01d1124 Adding license
squash 6340aaa Moving license into its own file
squash ebfd367 Jekyll has become self-aware.
squash 30e0ccb Changed the tagline in the binary, too.

Fondamentalmente questo dice a Git di combinare tutti e quattro i commit nel primo commit nell'elenco. Una volta fatto e salvato, si apre un altro editor con quanto segue:

# This is a combination of 4 commits.
# The first commit's message is:
Adding license

# This is the 2nd commit message:

Moving license into its own file

# This is the 3rd commit message:

Jekyll has become self-aware.

# This is the 4th commit message:

Changed the tagline in the binary, too.

    # Please enter the commit message for your changes. Lines starting
    # with '#' will be ignored, and an empty message aborts the commit.
    # Explicit paths specified without -i nor -o; assuming --only paths...
    # Not currently on any branch.
    # Changes to be committed:
    #   (use "git reset HEAD <file>..." to unstage)
    #
    #   new file:   LICENSE
    #   modified:   README.textile
    #   modified:   Rakefile
    #   modified:   bin/jekyll
    #

Dato che stiamo combinando così tanti commit, Git ti consente di modificare il messaggio del nuovo commit in base al resto dei commit coinvolti nel processo. Modifica il messaggio come meglio credi, quindi salva ed esci. Una volta fatto, i tuoi commit sono stati annullati con successo!

Created commit 0fc4eea: Creating license file, and making jekyll self-aware.
 4 files changed, 27 insertions(+), 30 deletions(-)
  create mode 100644 LICENSE
    Successfully rebased and updated refs/heads/master.

E se guardiamo di nuovo alla storia ... inserisci qui la descrizione dell'immagine

Quindi, finora è stato relativamente indolore. Se ti imbatti in conflitti durante il rebase, di solito sono abbastanza facili da risolvere e Git ti guida il più possibile. Le basi di questo è risolvere il conflitto in questione, git addil file e quindi git rebase --continueriprendere il processo. Ovviamente, se git rebase --abortlo desideri, tornerai al tuo stato precedente. Se per qualche motivo hai perso un commit nel rebase, puoi usare il reflog per recuperarlo.

I dettagli possono essere trovati questo link .


3
Grazie! Nel caso in cui qualcuno si trovi a disagio con VIM come me ... quando arrivi al punto in cui inserisci le azioni da modificare, premi "i" per accedere alla modalità di modifica. Quando hai finito con le modifiche, premi Esc, quindi digita ": wq" e ti sposterà in avanti. (Mac)
Farasi78

+1 per la cautela e il contesto di quando farlo. Nella mia esperienza personale, è positivo che tu stia lavorando da solo su un ramo di funzionalità e hai un sacco di commit banali come "readme aggiornato", "ha fatto qualcosa di cui nessuno si preoccupa" e sei pronto per unire il ramo, potrebbe beh, schiacciali tutti in un unico commit. Nessuno vorrà tornare al tuo "ha fatto qualcosa di cui nessuno si preoccupa" e inquina la cronologia dei commit
Adam Hughes,

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.