Combinazione di più commit in uno prima di spingere


132

Questa domanda non riguarda solo come eseguire questo compito, ma anche se farlo sia una buona o cattiva pratica con Git.

Considera che localmente lavoro principalmente sul ramo master, ma ho creato un ramo d'attualità che chiamerò "topical_xFeature". Nel processo di lavorare su "topical_xFeature" e passare avanti e indietro per fare altri lavori sul ramo principale, si scopre che ho fatto più di un commit sul ramo "topical_xFeature", ma tra ogni commit non ho fatto spingere.

Innanzitutto , considereresti questa cattiva pratica? Non sarebbe più saggio attenersi a un commit per ramo per push? In quali casi sarebbe bene avere più commit su una filiale prima che venga effettuata una spinta?

In secondo luogo , come posso realizzare al meglio portando i commit multipli sul ramo topical_xFeature nel ramo principale per una spinta? È una seccatura non preoccuparsene e fare semplicemente la spinta in cui vengono spinti più commit, o è meno fastidioso unire in qualche modo i commit in uno e poi spingere? Ancora una volta, come fare questo?

Risposte:


140

Per la tua prima domanda, no, non c'è niente di sbagliato nello spingere più commit contemporaneamente. Molte volte, potresti voler suddividere il tuo lavoro in alcuni piccoli e logici commit, ma spingili verso l'alto solo quando ritieni che l'intera serie sia pronta. Oppure potresti fare diversi commit localmente mentre sei disconnesso e li spingi tutti una volta che sei di nuovo connesso. Non c'è motivo di limitarsi a un commit per push.

Trovo generalmente che sia una buona idea mantenere ogni commit un singolo, logico, coerente cambiamento, che includa tutto ciò di cui ha bisogno per funzionare (quindi, non lascia il tuo codice in uno stato non funzionante). Se hai due commit, ma causerebbero la rottura del codice se tu applicassi solo il primo, potrebbe essere una buona idea schiacciare il secondo commit nel primo. Ma se hai due commit in cui ognuno apporta una modifica ragionevole, spingendoli come commit separati va bene.

Se vuoi mettere insieme più commit, puoi usare git rebase -i. Se sei sul ramo topical_xFeature, corri git rebase -i master. Questo aprirà una finestra dell'editor, con un mucchio di commit elencati con il prefisso pick. Puoi cambiare tutto tranne il primo squash, che dirà a Git di mantenere tutti quei cambiamenti, ma schiacciali nel primo commit. Dopo averlo fatto, controlla mastere unisci nel tuo ramo delle caratteristiche:

git checkout topical_xFeature
git rebase -i master
git checkout master
git merge topical_xFeature

In alternativa, se si desidera solo a tutto ciò squash in topical_xFeaturein master, si può solo effettuare le seguenti operazioni:

git checkout master
git merge --squash topical_xFeature
git commit

Quale scegli dipende da te. In generale, non mi preoccuperei di avere più piccoli commit, ma a volte non vuoi preoccuparti di commit minori, quindi li schiacci in uno solo.


1
Dopo essermi unito a --squash, non riesco a eliminare il ramo dell'argomento con git branch -d topic. Perché git non è in grado di identificare che tutte le modifiche sono state unite?
Balki,

7
@balki Perché Git rileva se le patch vengono unite in base alla loro comparsa nella cronologia di un determinato ramo. La compressione li commuta li cambia; diventano un nuovo commit e mentre quel nuovo commit fa la stessa cosa degli altri, Git non può dirlo, può solo dire se i commit sono gli stessi se hanno lo stesso ID commit (SHA-1) . Quindi, una volta che lo schiacci, devi dire a git di eliminare il vecchio ramo git branch -D topicper eliminarlo forzatamente.
Brian Campbell,

66

Questo è il modo in cui seguo generalmente per combinare più commit in un singolo commit prima di inviare il codice.

Per raggiungere questo obiettivo, ti suggerisco di utilizzare il concetto di " squash " fornito da GIT.

Seguire i passaggi seguenti.

1) git rebase -i master (invece di master puoi anche usare un commit specifico)

apri l'editor interattivo rebase, dove mostrerà tutti i tuoi commit. Fondamentalmente dove è necessario identificare i commit che si desidera unire in un singolo commit.

Immagina che questi siano i tuoi impegni e mostrino qualcosa di simile nell'editor.

pick f7f3f6d changed my name a bit    
pick 310154e updated README formatting and added blame   
pick a5f4a0d added cat-file  

È importante notare che questi commit sono elencati nell'ordine opposto di quanto normalmente li vedi usando il comando log. Significa che verrà mostrato per primo il commit più vecchio.

2) Cambia 'pick' in 'squash' per gli ultimi cambiamenti impegnati. qualcosa come mostrato di seguito. In questo modo, i tuoi ultimi 2 commit verranno uniti al primo.

pick f7f3f6d changed my name a bit         
squash 310154e updated README formatting and added blame   
squash a5f4a0d added cat-file

Puoi anche usare la forma abbreviata se hai molti commit da combinare:

p f7f3f6d changed my name a bit         
s 310154e updated README formatting and added blame   
s a5f4a0d added cat-file

per la modifica usa 'i', abiliterà l'editor per l'inserimento. Tieni presente che la maggior parte dei commit (meno recenti) non può essere eliminata poiché non esiste un commit precedente da combinare. Quindi deve essere scelto o 'p'. Utilizzare 'Esc' per uscire dalla modalità di inserimento.

3) Ora, salva l'editor con il seguente comando. : wq

Quando lo salvi, hai un unico commit che introduce le modifiche di tutti e tre i commit precedenti.

Spero che questo ti possa aiutare.


5
Forse questo è ovvio per gli altri ma, quando dici "git rebase -i", devi anche specificare da quale commit inizi. Questo è qualcosa che non ho realizzato quando ho provato a seguire questo esempio. Quindi, in questo esempio, sarebbe "git rebase -i xxxxx" in cui xxxxx è il commit proprio prima di f7f3f6d in ordine cronologico. Una volta capito, tutto ha funzionato esattamente come descritto sopra.
nukeguy,

È interessante @nukeguy, non ho avuto problemi a non specificare un commit specifico. Ha semplicemente fallito ciò che c'era.
JCrooks

Forse come @nukeguy, è git rebase -i HEAD~2stato un posto utile per iniziare. Quindi questa risposta è stata utile. Quindi, ho git statusmostrato "La tua filiale e 'origin / feature / xyz' sono divergenti e hanno rispettivamente 1 e 1 commit diversi ciascuno". Quindi avevo bisogno di git push origin feature/xyz --force-with-leasevedere stackoverflow.com/a/59309553/470749 e freecodecamp.org/forum/t/...
Ryan

11

Primo : nulla ti dice di avere un solo commit per ramo per push: un push è un meccanismo di pubblicazione che ti consente di pubblicare una cronologia locale (cioè una raccolta di commit) su un repository remoto.

Secondo : a git merge --no-ff topical_xFeatureregistrerebbe sul master come un unico commit il tuo argomento di lavoro, prima di spingere master.
(In questo modo, tieni in topical_xFeaturegiro per ulteriori evoluzioni, che puoi registrare mastercome un singolo nuovo commit alla prossima unione --no-ff.
Se topical_xFeaturel'obiettivo è quello di sbarazzarsi di questo, allora git merge --squashè l'opzione giusta, come dettagliato in Brian Campbell la risposta .)


Penso che --squashnon --no-ffsia quello che vuoi. --no-ffcreerebbe un commit unione, ma lascerebbe anche tutti i commit da topical_xFeature.
Brian Campbell,

@Brian: sono d'accordo e ho votato a favore della tua risposta, ma per la prima volta ho pensato all'opzione --no-ff perché volevo mantenere il topical_featureramo intorno e registrare un singolo commit sul masterramo.
VonC,

8

Passa al ramo principale e assicurati di essere aggiornato.

git checkout master

git fetch questo potrebbe essere necessario (a seconda della configurazione di git) per ricevere aggiornamenti su origin / master

git pull

Unire il ramo della funzione nel ramo principale.

git merge feature_branch

Ripristina il ramo principale allo stato di origine.

git reset origin/master

Git ora considera tutte le modifiche come modifiche non messe in scena. Possiamo aggiungere queste modifiche come un unico commit. Aggiunta aggiungerà anche file non tracciati.

git add --all

git commit

Rif: https://makandracards.com/makandra/527-squash-several-git-commits-into-a-single-commit


3
questa risposta è facile da seguire e davvero facile da visualizzare.
jokab il

6
  1. Per prima cosa scegli quale impegno vuoi che tutto venga dopo.

    git reflog
    5976f2b HEAD@{0}: commit: Fix conflicts
    80e85a1 HEAD@{1}: commit: Add feature
    b860ddb HEAD@{2}: commit: Add something
    
  2. Ripristina la testa selezionata (ho scelto HEAD@{2})

    git reset b860ddb --soft
    
  3. git status (giusto per essere sicuro)

  4. Aggiungi il tuo nuovo commit

    git commit -m "Add new commit"
    

Nota: HEAD@{0}& HEAD@{1}Ora sono uniti in 1 commit, questo può essere fatto anche per più commit.

git reflog di nuovo dovrebbe visualizzare:

git reflog
5976f2b HEAD@{0}: commit: Add new commit
b860ddb HEAD@{1}: commit: Add something

0

Uno strumento per automatizzare più commit in uno

come dice Kondal Kolipaka . Usando "git rebase -i"

La logica di "git rebase"

Quando si utilizza "git rebase -i", git genera il file git-rebase-todo nella directory .git / rebase-merge corrente, quindi richiama l'editor git per consentire agli utenti di modificare il file git-rebase-todo per l'elaborazione. Quindi lo strumento deve soddisfare:

  1. Modifica l'editor git allo strumento che abbiamo fornito;
  2. Lo strumento elabora il file git-rebase-todo.

Modifica l'editor git predefinito

git config core.editor #show current default git editor
git config --local --replace-all  core.editor NEW_EDITOR # set the local branch using NEW_EDITOR as git editor

Quindi, lo strumento deve modificare l'editor git ed elaborare il file git-rebase-todo. Lo strumento che utilizza Python di seguito:

#!/usr/bin/env python3
#encoding: UTF-8

import os
import sys

def change_editor(current_file):
    os.system("git config --local --replace-all  core.editor " + current_file) # Set current_file as git editor
    os.system("git rebase -i") # execute the "git rebase -i" and will invoke the python file later with git-rebase-todo file as argument
    os.system("git config --local --replace-all core.editor vim") # after work reset the git editor to default

def rebase_commits(todo_file):
    with open(todo_file, "r+") as f:
        contents = f.read() # read git-rebase-todo's content
        contents = contents.split("\n")
        first_commit = True
        f.truncate()
        f.seek(0)
        for content in contents:
            if content.startswith("pick"):
                if first_commit:
                    first_commit = False
                else:
                    content = content.replace("pick", "squash") # replace the pick to squash except for the first pick
            f.write(content + "\n")

def main(args):
    if len(args) == 2:
        rebase_commits(args[1]) # process the git-rebase-todo
    else:
        change_editor(os.path.abspath(args[0])) # set git editor

if __name__ == "__main__":
    main(sys.argv)

Rif: https://liwugang.github.io/2019/12/30/git_commits_en.html


4
Riduci la promozione del tuo sito web. Vedi anche Come non essere uno spammer.
Tripleee,
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.