Usi pratici di git reset --soft?


136

Lavoro con Git da poco più di un mese. In effetti ho usato il reset per la prima volta solo ieri, ma il soft reset non ha ancora molto senso per me.

Capisco di poter utilizzare il soft reset per modificare un commit senza alterare l'indice o la directory di lavoro, come farei git commit --amend.

Questi due comandi sono davvero gli stessi ( reset --softvs commit --amend)? Qualche motivo per usare l'uno o l'altro in termini pratici? E, soprattutto, ci sono altri usi reset --softoltre a modificare un commit?

Risposte:


110

git resetè tutto in movimento HEAD, e in generale il ramo rif .
Domanda: che dire dell'albero di lavoro e dell'indice?
Se utilizzato con --soft, si sposta HEAD, aggiornando il ref spesso e solo ilHEAD .
Questo differisce da commit --amendcome:

  • non crea un nuovo commit.
  • può effettivamente spostare HEAD su qualsiasi commit (poiché si commit --amendtratta solo di non spostare HEAD, pur consentendo di ripetere il commit corrente)

Ho appena trovato questo esempio di combinazione:

  • una fusione classica
  • un'unione di sottostruttura

tutti in uno (polpo, poiché ci sono più di due rami uniti) commettono unione.

Tomas "wereHamster" Carnecky spiega nel suo articolo "Sottotree Octopus merge" :

  • La strategia di unione delle sottostrutture può essere utilizzata se si desidera unire un progetto in una sottodirectory di un altro progetto e successivamente mantenere aggiornato il sottoprogetto. È un'alternativa ai sottomoduli git.
  • La strategia di unione polpo può essere utilizzata per unire tre o più rami. La normale strategia può unire solo due rami e se si tenta di unire più di questo, git ricade automaticamente sulla strategia del polpo.

Il problema è che puoi scegliere solo una strategia. Ma volevo combinare i due per ottenere una cronologia chiara in cui l'intero repository viene atomicamente aggiornato a una nuova versione.

Ho un superprogetto, chiamiamolo projectA, e un sottoprogetto projectB, che ho unito in una sottodirectory di projectA.

(questa è la parte di unione dei sottotree)

Mantengo anche alcuni impegni locali.
ProjectAviene regolarmente aggiornato, projectBha una nuova versione ogni paio di giorni o settimane e di solito dipende da una particolare versione di projectA.

Quando decido di aggiornare entrambi i progetti, non mi limito a estrarre projectAe projectB poiché ciò creerebbe due commit per quello che dovrebbe essere un aggiornamento atomico dell'intero progetto .
Invece, creo un unico merge commit che unisce projectA, projectBe le mie commit locali .
La parte difficile qui è che questa è una fusione di polpo (tre teste), ma projectBdeve essere unita alla strategia di sottostruttura . Quindi questo è quello che faccio:

# Merge projectA with the default strategy:
git merge projectA/master

# Merge projectB with the subtree strategy:
git merge -s subtree projectB/master

Qui l'autore ha usato a reset --hard, e quindi read-treeper ripristinare ciò che le prime due fusioni avevano fatto sull'albero e sull'indice di lavoro, ma è qui che reset --softpuò aiutare:
come rifare quelle due fusioni , che hanno funzionato, ovvero il mio albero di lavoro e l'indice sono bene, ma senza dover registrare quei due commit?

# Move the HEAD, and just the HEAD, two commits back!
git reset --soft HEAD@{2}

Ora, possiamo riprendere la soluzione di Tomas:

# Pretend that we just did an octopus merge with three heads:
echo $(git rev-parse projectA/master) > .git/MERGE_HEAD
echo $(git rev-parse projectB/master) >> .git/MERGE_HEAD

# And finally do the commit:
git commit

Quindi, ogni volta:

  • sei soddisfatto di ciò che finisci (in termini di albero di lavoro e indice)
  • siete non soddisfatto con tutti i commit che hanno avuto voi per arrivarci:

git reset --soft è la risposta.


8
Nota per sé: semplice esempio di schiacciamento con git reset --soft: stackoverflow.com/questions/6869705/...
VonC

3
è utile anche se ti sei impegnato nel ramo sbagliato. tutte le modifiche tornano all'area di gestione temporanea e si spostano con te durante il checkout del ramo giusto.
Smoebody

44

Caso d'uso: combina una serie di commit locali

"Oops. Quei tre commit potrebbero essere solo uno."

Quindi, annulla gli ultimi 3 (o qualunque altro) commit (senza influenzare l'indice né la directory di lavoro). Quindi eseguire il commit di tutte le modifiche come una.

Per esempio

> git add -A; git commit -m "Start here."
> git add -A; git commit -m "One"
> git add -A; git commit -m "Two"
> git add -A' git commit -m "Three"
> git log --oneline --graph -4 --decorate

> * da883dc (HEAD, master) Three
> * 92d3eb7 Two
> * c6e82d3 One
> * e1e8042 Start here.

> git reset --soft HEAD~3
> git log --oneline --graph -1 --decorate

> * e1e8042 Start here.

Ora tutte le modifiche sono conservate e pronte per essere impegnate come una sola.

Brevi risposte alle tue domande

Questi due comandi sono davvero gli stessi ( reset --softvs commit --amend)?

  • No.

Qualche motivo per usare l'uno o l'altro in termini pratici?

  • commit --amend per aggiungere file / rm dall'ultimo commit o per cambiarne il messaggio.
  • reset --soft <commit> per combinare diversi commit sequenziali in uno nuovo.

E, soprattutto, ci sono altri usi reset --softoltre a modificare un commit?

  • Vedi altre risposte :)

2
Vedi altre risposte per "ci sono altri usi a reset --softparte la modifica di un commit - No"
Antony Hatchkins

In risposta all'ultimo commento, concordato in parte, ma direi che modificare un singolo commit è troppo specifico. Ad esempio, potresti voler, una volta scritta una funzione, eliminare tutto e ritagliare nuovi commit più utili per i revisori. Direi che i soft reset (compresi quelli semplici git reset) sono buoni quando tu (a) vuoi riscrivere la storia, (b) non ti preoccupi dei vecchi commit (quindi puoi evitare la confusione del rebase interattivo), e (c) avere più di un commit per cambiare (altrimenti, commit --amendè più semplice).
johncip,

18

Lo uso per modificare più del solo ultimo commit.

Diciamo che ho commesso un errore nel commit A e poi ho fatto il commit B. Ora posso solo modificare B. Quindi lo faccio git reset --soft HEAD^^, correggo e commetto di nuovo A e poi commetto di nuovo B.

Certo, non è molto conveniente per commit di grandi dimensioni ... ma non dovresti fare comunque commit di grandi dimensioni ;-)


3
git commit --fixup HEAD^^ git rebase --autosquash HEAD~Xfunziona anche bene.
Niklas,

3
git rebase --interactive HEAD^^dove si sceglie di modificare entrambi i commit A e B. In questo modo vengono conservati i messaggi di commit di A e B, se necessario è comunque possibile modificarli.
Paul Pladijs,

2
Come posso ripetere il commit di B dopo aver reimpostato su A?
Northben,

1
Un altro modo che è probabilmente meno lavoro: controllare il registro git, ottenere l'hash del B commettere, poi git reset A, fanno e aggiungere le modifiche, git commit --amend, git cherry-pick <B-commit-hash>.
naught101

15

Un altro potenziale utilizzo è un'alternativa allo stashing (che ad alcune persone non piace, vedi ad esempio https://codingkilledthecat.wordpress.com/2012/04/27/git-stash-pop-considered-harmful/ ).

Ad esempio, se sto lavorando su un ramo e ho bisogno di riparare qualcosa di urgente sul master, posso solo fare:

git commit -am "In progress."

quindi controlla master e fai la correzione. Quando ho finito, torno al mio ramo e lo faccio

git reset --soft HEAD~1

per continuare a lavorare da dove avevo interrotto.


3
Aggiornamento (ora che ho capito meglio git): --softqui in realtà non è necessario, a meno che non ti interessi davvero che le modifiche vengano immediatamente messe in scena. Ora uso solo git reset HEAD~quando faccio questo. Se ho alcune modifiche messe in scena quando ho bisogno di cambiare ramo e voglio mantenerlo in quel modo, allora lo faccio git commit -m "staged changes", quindi git commit -am "unstaged changes", successivamente, git reset HEAD~seguito da git reset --soft HEAD~un ripristino completo dello stato di funzionamento. Anche se, ad essere sincero, faccio entrambe queste cose molto meno ora che ne so git-worktree:)
deltacrux,

7

È possibile utilizzare git reset --softper modificare la versione che si desidera avere come genitore per le modifiche apportate all'indice e all'albero di lavoro. I casi in cui ciò è utile sono rari. A volte potresti decidere che le modifiche che hai nell'albero di lavoro debbano appartenere a un ramo diverso. Oppure puoi usarlo come un modo semplice per comprimere più commit in uno (simile a squash / fold).

Vedi questa risposta di VonC per un esempio pratico: schiacciare i primi due commit in Git?


Questa è una buona domanda che non avevo mai trovato prima. Ma non sono sicuro di poter vedere come inserire le modifiche su un altro ramo usando reset soft. Quindi ho checkout Branch, quindi uso git reset --soft anotherBranche quindi mi impegno lì? Ma non cambi davvero il ramo di ckeckout, quindi ti impegni con Branch o con un altro Branch ?
AJJ

Quando lo fai, è importante non usare git checkout perché questo modificherà il tuo albero. Pensa a git reset --soft come un modo per manipolare la revisione a cui punta HEAD.
Johannes Rudolph,

7

Un possibile utilizzo sarebbe quando si desidera continuare il lavoro su una macchina diversa. Funzionerebbe così:

  1. Acquista un nuovo ramo con un nome simile a una scorta,

    git checkout -b <branchname>_stash
    
  2. Spingi il tuo ramo di stash verso l'alto,

    git push -u origin <branchname>_stash
    
  3. Passa all'altra tua macchina.

  4. Abbassa sia la tua scorta che i rami esistenti,

    git checkout <branchname>_stash; git checkout <branchname>
    
  5. Dovresti essere sul tuo ramo esistente ora. Unisci le modifiche dal ramo stash,

    git merge <branchname>_stash
    
  6. Ripristina soft il ramo esistente su 1 prima della tua unione,

    git reset --soft HEAD^
    
  7. Rimuovi il tuo ramo di scorta,

    git branch -d <branchname>_stash
    
  8. Rimuovi anche il ramo di stash dall'origine,

    git push origin :<branchname>_stash
    
  9. Continua a lavorare con le modifiche come se le avessi riposte normalmente.

Penso, in futuro, GitHub e co. dovrebbe offrire questa funzionalità "stash remota" in meno passaggi.


2
Voglio sottolineare che il primo stash e pop sul tuo primo computer sono completamente inutili, puoi semplicemente creare un nuovo ramo direttamente da una copia di lavoro sporca, eseguire il commit e quindi inviare le modifiche al telecomando.

7

Un uso pratico è se ti sei già impegnato nel tuo repository locale (es. Git commit -m), puoi invertire l'ultimo commit eseguendo git reset --soft HEAD ~ 1

Anche per tua conoscenza, se hai già messo in scena le tue modifiche (ad es. Con git add.) Allora puoi invertire la messa in scena facendo git reset - HEAD MISTO o comunemente ho anche usatogit reset

infine, git reset --hard cancella tutto, comprese le modifiche locali. Il ~ after head ti dice a quanti si impegna ad andare dall'alto.


1
git reset --soft HEAD ~1mi fatal: Cannot do soft reset with paths.sembra che dovremmo rimuovere lo spazio dopo HEAD così sarebbegit reset --soft HEAD~1
Andrew Lohr

6

Un ottimo motivo per usare " git reset --soft <sha1>" è spostarsiHEAD in un repository nudo.

Se si tenta di utilizzare il --mixedo--hard opzione , verrà visualizzato un errore poiché si sta tentando di modificare e lavorare l'albero e / o l'indice che non esiste.

Nota: sarà necessario farlo direttamente dal repository nudo.

Nota di nuovo: sarà necessario assicurarsi che il ramo che si desidera ripristinare nel repository nudo sia il ramo attivo. In caso contrario, seguire la risposta di VonC su come aggiornare il ramo attivo in un repository nudo quando si ha accesso diretto al repository.


1
Come menzionato nella risposta precedente ( stackoverflow.com/questions/4624881/… ), questo è vero quando si ha un accesso diretto al repository nudo (di cui si parla qui). +1 però.
VonC,

@VonC Sì, assolutamente corretto e grazie per aver aggiunto la nota! Continuo a dimenticare di aggiungere che dato che suppongo che i ripristini vengano eseguiti direttamente dal repository. Inoltre, suppongo che il ramo che la persona desidera reimpostare nel repository nudo sia il ramo attivo. Se il ramo non è il ramo attivo, è necessario aggiornare in base alla risposta ( stackoverflow.com/questions/3301956/… ) su come aggiornare il ramo attivo per i repository nudi. Aggiornerò anche la risposta con le informazioni sul ramo attivo. Grazie ancora!!!
Hazok,

2

SourceTree è una GUI git che ha un'interfaccia abbastanza conveniente per mettere in scena solo i bit che desideri. Non ha nulla di simile a distanza per modificare una revisione corretta.

Quindi git reset --soft HEAD~1è molto più utile dicommit --amend in questo scenario. Posso annullare il commit, ripristinare tutte le modifiche nell'area di gestione temporanea e riprendere a modificare i bit di gestione temporanea utilizzando SourceTree.

Davvero, mi sembra che commit --amendsia il comando più ridondante dei due, ma git è git e non evita comandi simili che fanno cose leggermente diverse.


1

Mentre mi piacciono molto le risposte in questa discussione, io uso git reset --soft per uno scenario leggermente diverso, ma molto pratico.

Uso un IDE per lo sviluppo che ha un buon strumento diff per mostrare i cambiamenti (messi in scena e non messi in scena) dopo il mio ultimo commit. Ora, la maggior parte dei miei compiti prevede più commit. Ad esempio, supponiamo che esegua 5 commit per completare una determinata attività. Uso lo strumento diff nell'IDE durante ogni commit incrementale da 1-5 per esaminare le mie modifiche dall'ultimo commit. Trovo che sia un modo molto utile per rivedere le mie modifiche prima di impegnarmi.

Ma alla fine del mio compito, quando voglio vedere tutte le mie modifiche insieme (da prima del primo commit), per fare una revisione del codice di auto prima di fare una richiesta pull, vedrei solo le modifiche dal mio precedente commit (dopo il commit 4) e non cambia da tutti gli impegni del mio compito attuale.

Quindi uso git reset --soft HEAD~44 commit. Questo mi permette di vedere tutte le modifiche insieme. Quando sono fiducioso delle mie modifiche, posso quindi farlo git reset HEAD@{1}e spingerlo verso il telecomando con fiducia.


1
... poi fai git add --patche git commitripetutamente per costruire la serie di commit che avresti costruito se sapessi cosa stavi facendo da sempre. I tuoi primi impegni sono come le note sulla tua scrivania o la prima bozza di un memo, sono lì per organizzare il tuo pensiero, non per la pubblicazione.
jillill

Ehi, non ho capito bene cosa stai suggerendo.
Swanky Coder,

1
Solo che invece di git reset @{1}ripristinare le serie della prima bozza, puoi invece creare una serie per la pubblicazione da git add -pe git commit.
jillill

Si corretto. Un altro modo è (quello che di solito seguo) di rifare su upstream / master e comprimere i commit in uno solo.
Swanky Coder,

1

Un altro caso d'uso è quando si desidera sostituire l'altro ramo con il proprio in una richiesta pull, ad esempio, supponiamo che tu abbia un software con funzionalità A, B, C in fase di sviluppo.

Stai sviluppando con la prossima versione e tu:

  • Funzione rimossa B

  • Aggiunta funzione D

Nel processo, sviluppare aggiornamenti rapidi appena aggiunti per la funzione B.

Puoi unire sviluppare in successivo, ma a volte può essere disordinato, ma puoi anche usare git reset --soft origin/develop e creare un commit con le tue modifiche e il ramo è unificabile senza conflitti e mantieni le tue modifiche.

Si scopre che git reset --softè un comando utile. Personalmente lo uso molto per schiacciare i commit che non hanno "lavoro completato" come "WIP", quindi quando apro la richiesta pull, tutti i miei commit sono comprensibili.

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.