Unisci (con squash) tutte le modifiche da un altro ramo come un unico commit


479

In Git, c'è un modo per unire tutte le modifiche da un ramo all'altro, ma comprimere contemporaneamente un singolo commit?

Lavoro spesso su una nuova funzionalità in un ramo separato e commetto / spingo regolarmente, principalmente per il backup o per trasferire ciò su cui sto lavorando su un'altra macchina. Per lo più quei commit dicono "Feature xxx WIP" o qualcosa di ridondante.

Una volta che quel lavoro è finito e voglio unire nuovamente il ramo WIP al master, mi piacerebbe scartare tutti quei commit intermedi e avere un solo commit pulito.

C'è un modo semplice per farlo?

In alternativa, che ne dici di un comando che schiaccia tutti i commit su un ramo dal punto in cui è stato ramificato?

Risposte:


601

Un'altra opzione è git merge --squash <feature branch>infine fare un git commit.

Da Git Unisci

--squash

--no-squash

Produrre l'albero di lavoro e lo stato dell'indice come se si verificasse un'unione reale (ad eccezione delle informazioni sull'unione), ma non effettuare effettivamente un commit o spostare il HEADrecord, né registrare $GIT_DIR/MERGE_HEADin modo che il git commitcomando successivo crei un commit di unione. Ciò consente di creare un singolo commit sopra il ramo corrente il cui effetto è lo stesso della fusione di un altro ramo (o più nel caso di un polpo).


1
Funzionalità interessante! Adoro git. Anche se lo userò sicuramente in futuro ora, consiglierei comunque di conoscere il tuo modo di aggirare rebase -i. È una buona abilità avere, nel caso in cui volessi davvero renderli più di un solo commit.
Buck Buck

4
Un avvertimento: funziona, ma il messaggio di commit predefinito include il registro dal ramo che viene unito. Il problema è che sembra simile al formato che normalmente vedi dove l'intero testo mostrato non diventa effettivamente parte del messaggio di commit, ma in questo caso lo fa. Quindi, se non vuoi tutto ciò, devi rimuoverlo manualmente dal tuo messaggio di commit. Avrei dovuto testarlo prima di usarlo ...
still_dreaming_1

23
Quello, e, essere avvisato che il ramo non sarà visto come unito. stackoverflow.com/questions/19308790/…
Ryan,

3
IMHO questo avrebbe dovuto essere chiamatorebase --squash
Andy,

quindi (poiché non unisce realmente il ramo della funzione) questo sarebbe appropriato se avessi intenzione di eliminare il ramo della funzione dopo il commit. È corretto? (Non sono un esperto di git)
Andrew Spencer,

214

Trovato! Il comando Unisci ha --squashun'opzione

git checkout master
git merge --squash WIP

a questo punto tutto viene unito, forse in conflitto, ma non impegnato. Quindi ora posso:

git add .
git commit -m "Merged WIP"

2
cosa fa git add .?
Michael Potter,

1
@MichaelPotter Aggiunge tutti i file e le modifiche
Daksh Shah

2
git add .aggiunge tutti i file non ignorati nella directory corrente, sarei diffidente nel raccogliere file indesiderati in questo modo.
Jake Cobb,

7
In alternativa git add .puoi usare git add -usolo per aggiungere file che sono già stati aggiunti all'albero.
Brandon Ogle,

19
Suggerendo che un "git add". anche fare è confuso. Quando eseguo il "git merge --squash WIP", ha già le modifiche schiacciate nell'indice. Tutto ciò che serve è impegnarli. Fare un "git add". aggiungerà le modifiche che si trovano nella directory di lavoro, ma che non facevano parte del ramo delle funzionalità. La domanda era: come eseguire il commit delle modifiche nel ramo funzione come un unico commit.
John Pankowicz,

30

Prova git rebase -i masteril ramo delle funzionalità. È quindi possibile modificare tutti tranne uno "pick" in "squash" per combinare i commit. Vedi i commit di schiacciamento con rebase

Infine, puoi quindi eseguire l'unione dal ramo principale.


8
Sì, funziona, ma non voglio il fastidio di rebase interattivo. Voglio solo tutto da quando il ramo si è appiattito.
Brad Robinson

2
+1 Questo crea una cronologia pulita. È molto più facile identificare e gestire i commit come singole patch, carte, storie, ecc.
Ryan,

2

Usando git merge --squash <feature branch>come risposta accettata suggerisce la fa il trucco ma non mostrerà il ramo unito come effettivamente unito.

Pertanto una soluzione ancora migliore è:

  • Crea un nuovo ramo dall'ultimo master
  • Unisci <feature branch>a quanto sopra usandogit merge --squash
  • Unire il ramo appena creato in master

Questa wiki spiega la procedura in dettaglio.


0

Ho creato il mio alias git per fare esattamente questo. Lo sto chiamando git freebase! Prenderà il tuo ramo di funzionalità disordinato e non riferibile esistente e lo ricrea in modo che diventi un nuovo ramo con lo stesso nome con i suoi commit schiacciati in un commit e riordinati sul ramo specificato (master per impostazione predefinita). Alla fine, ti consentirà di utilizzare qualsiasi messaggio di commit che ti piace per il tuo nuovo ramo "freebased".

Installalo posizionando il seguente alias nel tuo .gitconfig:

[alias]
  freebase = "!f() { \
    TOPIC="$(git branch | grep '\\*' | cut -d ' ' -f2)"; \
    NEWBASE="${1:-master}"; \
    PREVSHA1="$(git rev-parse HEAD)"; \
    echo "Freebaseing $TOPIC onto $NEWBASE, previous sha1 was $PREVSHA1"; \
    echo "---"; \
    git reset --hard "$NEWBASE"; \
    git merge --squash "$PREVSHA1"; \
    git commit; \
  }; f"

Usalo dal tuo ramo di funzionalità eseguendo: git freebase <new-base>

L'ho provato solo poche volte, quindi leggilo prima e assicurati di volerlo eseguire. Come piccola misura di sicurezza stampa lo sha1 iniziale, quindi dovresti essere in grado di ripristinare il tuo vecchio ramo se qualcosa va storto.

Lo manterrò nel mio repository dotfiles su github: https://github.com/stevecrozz/dotfiles/blob/master/.gitconfig


ha funzionato come una felicità! potresti anche dare un'occhiata a evernote.com/shard/s52/sh/7f8f4ff1-9a68-413f-9225-c49e3ee2fafd/…
Ilya Sheershoff

-1

git merge --squash <feature branch> è una buona opzione. Il "git commit" ti dice tutti i messaggi di commit del ramo funzione con la tua scelta di tenerlo.

Per unire meno commit.

git merge do x times --git reset HEAD ^ --soft quindi git commit.

Rischio: i file eliminati potrebbero tornare indietro.


-5

Puoi farlo con il comando "rebase". Chiamiamo i rami "principale" e "caratteristica":

git checkout feature
git rebase main

Il comando rebase riprodurrà tutti i commit su "feature" come un commit con un genitore uguale a "main".

Potresti voler eseguire git merge mainprima git rebase mainse "main" è cambiato da quando è stata creata "feature" (o dalla fusione più recente). In questo modo, hai ancora la tua storia completa nel caso in cui avessi un conflitto di unione.

Dopo il rebase, puoi unire il tuo ramo a main, il che dovrebbe tradursi in una fusione in avanti veloce:

git checkout main
git merge feature

Vedi il pagina rebase di Comprensione di Git concettualmente per una buona panoramica


Questo non ha funzionato per me. Ho appena creato un semplice repository di prova, con un ramo WIP e ho provato quanto sopra e ho riscontrato conflitti di unione (anche se non avevo apportato modifiche al master).
Brad Robinson,

Se la funzione è stata creata da main (git checkout -b feature main) e hai avuto una recente fusione da main, non dovresti ottenere conflitti dal rebase
NamshubWriter

OK, riprovato. Questa volta non abbiamo avuto conflitti, ma la storia non è stata schiacciata.
Brad Robinson,

Dando un'altra occhiata alla documentazione di git-merge, hai ragione, alcuni degli impegni rimarranno. Se hai effettuato precedenti fusioni da "principale" a "caratteristica", il rebase ne rimuoverà alcune, ma non tutte.
NamshubWriter,

Ricorda solo che il rebasing può essere piuttosto pericoloso se il ramo delle funzionalità è stato precedentemente pubblicato. Maggiori informazioni su questa domanda SO .
Robert Rossmann,
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.