Nascondere solo le modifiche temporanee in git - è possibile?


368

C'è un modo in cui posso nascondere solo le mie modifiche organizzate? Lo scenario con cui ho problemi è quando ho lavorato su diversi bug in un determinato momento e ho diverse modifiche non messe in scena. Mi piacerebbe essere in grado di mettere in scena questi file singolarmente, creare i miei file .patch e nasconderli fino all'approvazione del codice. In questo modo, una volta approvato, posso nascondere tutta la mia sessione (corrente), inserire quel bug e spingere il codice.

Sto andando in questo modo nel modo sbagliato? Sto fraintendendo come git può funzionare in altri modi per semplificare il mio processo?


Sì, probabilmente stai facendo cose sbagliate per entrare in questa situazione. Ancora una domanda utile. Dovresti davvero stash o branch prima di iniziare la prossima correzione. La risposta tangenziale stackoverflow.com/a/50692885 è probabilmente un modo migliore per gestirlo in git. Giocare con la scorta spesso fa cose strane nella mia area di lavoro se ho eseguito il commit da monte.
Samuel Åslund l'

Risposte:


472

Sì, è possibile con DOUBLE STASH

  1. Metti in scena tutti i tuoi file che devi conservare.
  2. Corri git stash --keep-index. Questo comando creerà uno stash con TUTTE le modifiche ( gestite e non gestite ), ma lascerà le modifiche gestite nella directory di lavoro (ancora nello stato gestita).
  3. Correre git stash push -m "good stash"
  4. Ora il vostro "good stash"è solo messo in scena i file .

Ora se hai bisogno di file non messi in scena prima dello stash, applica semplicemente il primo stash ( quello creato con--keep-index ) e ora puoi rimuovere i file a cui sei stato nascosto "good stash".

Godere


Blocca i cambiamenti nei sottomoduli anche se non vengono messi in scena. C'è un modo per aggirare questo?
Rluks

1
questo in qualche modo ha lasciato fuori tutti i nuovi file (anche messi in scena).
Aurimas,

8
@Aurimas, per nascondere nuovi file, è necessario utilizzare l' -uinterruttore.
Gyromite,

2
quando riapplicate la prima scorta e ottenete tutti i cambiamenti indietro mentre potreste essere interessati solo alle vostre modifiche di unstages utilizzare l' git stash apply --indexopzione. Questo tenterà di mantenere il tuo stato non (in scena). Ora è più facile rimuovere le modifiche indesiderate dall'albero di lavoro.
otomo,

Anche se non avevo bisogno di fare esattamente quello che diceva questa risposta, conoscere la bandiera --keep-index è stato molto utile.
Aaron Krauss,

129

Con l'ultimo git puoi usare l' --patchopzione

git stash push --patch  

git stash save --patch   # for older git versions

E git ti chiederà ogni modifica nei tuoi file da aggiungere o meno allo stash.
Hai appena risposto yon

UPD
Alias ​​per DOUBLE STASH :

git config --global alias.stash-staged '!bash -c "git stash --keep-index; git stash push -m "staged" --keep-index; git stash pop stash@{1}"'

Ora puoi mettere in scena i tuoi file ed eseguirli git stash-staged.
Di conseguenza, i file in fasi verranno salvati in uno stash .

Se non si desidera mantenere i file in fasi e si desidera spostarli in uno stash. Quindi puoi aggiungere un altro alias ed eseguire git move-staged:

git config --global alias.move-staged '!bash -c "git stash-staged;git commit -m "temp"; git stash; git reset --hard HEAD^; git stash pop"'

17
Tecnicamente non risponde alla domanda, ma una tecnica davvero piacevole che ottiene uno stash selettivo.
alexreardon,

6
D'accordo, questo va bene, ma l'idea qui con la domanda è che ho già fatto tutto questo lavoro di messa in scena dei cambiamenti con cui voglio fare qualcosa (apparentemente originariamente da impegnare, ma ora voglio mettere da parte), non guardare solo rifallo da capo.
Steven Lu

4
non funziona per i file appena creati (funziona solo con i file modificati)
Derek Liang

@DerekLiang: i file appena creati non vengono tracciati affatto. Probabilmente dovresti controllare l' -u|--include-untrackedopzione digit-stash
Eugen Konkov,

2
Dai documenti : " save : questa opzione è deprecata a favore di git stash push . Si differenzia da" stash push "in quanto non può accettare pathspecs e qualsiasi argomento non-opzione costituisce il messaggio."
Borjovsky,

53

Ho realizzato una sceneggiatura che nasconde solo ciò che è attualmente in scena e lascia tutto il resto. Questo è fantastico quando inizio a fare troppe modifiche non correlate. Metti semplicemente in scena ciò che non è correlato al commit desiderato e nascondi proprio quello.

(Grazie a Bartłomiej per il punto di partenza)

#!/bin/bash

#Stash everything temporarily.  Keep staged files, discard everything else after stashing.
git stash --keep-index

#Stash everything that remains (only the staged files should remain)  This is the stash we want to keep, so give it a name.
git stash save "$1"

#Apply the original stash to get us back to where we started.
git stash apply stash@{1}

#Create a temporary patch to reverse the originally staged changes and apply it
git stash show -p | git apply -R

#Delete the temporary stash
git stash drop stash@{1}

7
Aggiungo che puoi trasformare lo script in un comando git seguendo thediscoblog.com/blog/2014/03/29/custom-git-commands-in-3-steps
Petr Bela,

3
Questo è fantastico! L'ho ottimizzato per richiedere all'utente una descrizione stash se non ne inserisce una sulla riga di comando: gist.github.com/brookinc/e2589a8c5ca33f804e4868f6bfc18282
brookinc

1
Questo non funziona affatto con git 2.23.0.
thnee

Grazie, ho effettuato l'upgrade e l'ho trasformato in un alias qui: stackoverflow.com/a/60875067/430128 .
Raman,

36

TL; DR Basta aggiungere -- $(git diff --staged --name-only)per il <pathspec>parametro git

Ecco un semplice one-liner:

git stash -- $(git diff --staged --name-only)

E per aggiungere un messaggio semplicemente:

git stash push -m "My work in progress" -- $(git diff --staged --name-only)

Testato su v2.17.1 e v2.21.0.windows.1

limitazioni:

  • Si prega di essere consapevoli del fatto che questo riporterà ogni singola cosa, se non ci sono file messi in scena.
  • Inoltre, se si dispone di un file messo in scena solo parzialmente (ovvero solo alcune linee modificate, vengono messe in scena mentre alcune altre linee modificate non lo sono), l'intero file verrà bloccato (comprese le linee non messe in scena).

6
Penso che questa sia l'opzione migliore sulla situazione descritta: facile da capire e nessuna magia nera coinvolta!
Luis

1
Questo è abbastanza pulito. Ho finito per creare un alias da esso!
Kalpesh Panchal,

continuate a votare in arrivo
Somo S.

@KalpeshPanchal puoi condividere il tuo alias? Non sono sicuro di come evitarlo, quindi non lo sta interpretando correttamente.
Igor Nadj


15

Per realizzare la stessa cosa ...

  1. Metti in scena solo i file su cui vuoi lavorare.
  2. git commit -m 'temp'
  3. git add .
  4. git stash
  5. git reset HEAD~1

Boom. I file che non vuoi sono nascosti. I file che desideri sono tutti pronti per te.


3
Questa è facilmente la risposta migliore e la più semplice da ricordare
Kevin

9

In questo scenario, preferisco creare nuove filiali per ogni problema. Uso un prefisso temp / quindi so che posso eliminare questi rami in seguito.

git checkout -b temp/bug1

Metti in scena i file che correggono bug1 e li commetti.

git checkout -b temp/bug2

È quindi possibile selezionare i commit dalle rispettive filiali in base alle esigenze e inviare una richiesta pull.


2
Mentre i suoni fantasiosi di stashing sono belli da sapere, in pratica questo sembra un approccio che ho meno probabilità di bork.
ryanjdillon,

1
Utilizzare "git cherry-pick tmpCommit" per ripristinare il commit temporaneo con un merge-commit o "git merge tmpCommit" + "git reset HEAD ^" per ottenere le modifiche senza il commit.
Samuel Åslund l'

1
Come questa risposta mostra a volte, è meglio chiedere direttamente cosa vuoi ottenere invece di come raggiungerlo con una determinata tecnica. I rami temporanei e la scelta della ciliegia sono utili in situazioni complicate.
Guney Ozsan,

se hai messo in scena parzialmente un file, dovrai riporre le modifiche prima di tornare al ramo originale e
saltarle di

6

Perché non commetti la modifica per un determinato bug e crei una patch da quel commit e dal suo predecessore?

# hackhackhack, fix two unrelated bugs
git add -p                   # add hunks of first bug
git commit -m 'fix bug #123' # create commit #1
git add -p                   # add hunks of second bug
git commit -m 'fix bug #321' # create commit #2

Quindi, per creare le patch appropriate, utilizzare git format-patch:

git format-patch HEAD^^

Questo creerà due file: 0001-fix-bug-123.patche0002-fix-bug-321.patch

Oppure puoi creare rami separati per ogni bug, così puoi unire o riformulare individualmente correzioni di bug, o anche eliminarle, se non risolvono.


2

git stash --keep-index è una buona soluzione ... tranne per il fatto che non ha funzionato correttamente sui percorsi che sono stati rimossi, che è stato risolto in Git 2.23 (3 ° trimestre 2019)

Vedi commit b932f6a (16 lug 2019) di Thomas Gummerer ( tgummerer) .
(Unito da Junio ​​C Hamano - gitster- in commit f8aee85 , 25 lug 2019)

stash: risolve la gestione dei file rimossi con --keep-index

git stash push --keep-index dovrebbe mantenere tutte le modifiche che sono state aggiunte all'indice, sia nell'indice che sul disco.

Attualmente questo non si comporta correttamente quando un file viene rimosso dall'indice.
Invece di tenerlo eliminato sul disco, ** - keep-index attualmente ripristina il file. **

Correggi quel comportamento usando ' git checkout' in modalità no-overlay che può ripristinare fedelmente l'indice e l'albero di lavoro.
Questo semplifica anche il codice.

Si noti che ciò sovrascriverà i file non tracciati se il file non tracciato ha lo stesso nome di un file che è stato eliminato nell'indice.


2

Riporre solo l'indice (cambiamenti graduali) in Git è più difficile di quanto dovrebbe essere. Ho trovato la risposta di @ Joe per funzionare bene e ne ho trasformato una piccola variante in questo alias:

stash-index = "!f() { \
  git stash push --quiet --keep-index -m \"temp for stash-index\" && \
  git stash push \"$@\" && \
  git stash pop --quiet stash@{1} && \
  git stash show -p | git apply -R; }; f"

Spinge sia la messa in scena e cambiamenti unstaged in una scorta temporanea, lasciando la messa in scena cambia da solo. Spinge quindi le modifiche organizzate nella scorta, che è la scorta che vogliamo mantenere. Gli argomenti passati all'alias, come --message "whatever"verranno aggiunti a questo comando stash. Infine, visualizza lo stash temporaneo per ripristinare lo stato originale e rimuovere lo stash temporaneo, quindi infine "rimuove" le modifiche stash dalla directory di lavoro tramite un'applicazione di patch inversa.

Per il problema opposto di stashing solo le modifiche non messe in scena (alias stash-working) vedere questa risposta .


1

È assolutamente necessario lavorare su più bug contemporaneamente? E per "subito" intendo "avere file modificati per più bug contemporaneamente". Perché a meno che tu non ne abbia assolutamente bisogno, lavorerei solo su un bug alla volta nel tuo ambiente. In questo modo è possibile utilizzare filiali e rebase locali, che trovo molto più facili rispetto alla gestione di una scorta / fase complessa.

Supponiamo che il master sia in commit B. Ora lavora al bug n. 1.

git checkout -b bug1

Ora sei sul branch bug1. Apporta alcune modifiche, esegui il commit, attendi la revisione del codice. Questo è locale, quindi non stai influenzando nessun altro, e dovrebbe essere abbastanza facile creare una patch da git diffs.

A-B < master
   \
    C < bug1

Ora stai lavorando su bug2. Andare di nuovo a letto con git checkout master. Fare un nuovo ramo, git checkout -b bug2. Apporta modifiche, esegui il commit, attendi la revisione del codice.

    D < bug2
   /
A-B < master
   \
    C < bug1

Facciamo finta che qualcun altro impegna E&F sul master mentre stai aspettando la revisione.

    D < bug2
   /
A-B-E-F < master
   \
    C < bug1

Quando il tuo codice è stato approvato, puoi rifarlo su Master per procedere come segue:

git checkout bug1
git rebase master
git checkout master
git merge bug1

Ciò comporterà quanto segue:

    D < bug2
   /
A-B-E-F-C' < master, bug1

Quindi è possibile inviare, eliminare il ramo bug1 locale e il gioco è fatto. Un bug alla volta nell'area di lavoro, ma con l'utilizzo di filiali locali il repository può gestire più bug. E questo evita un complicato ballo di scena / scorta.

Rispondi alla domanda di ctote nei commenti:

Bene, puoi tornare allo stashing per ogni bug e lavorare solo con un bug alla volta. Atleast che ti salva il problema della messa in scena. Ma dopo averlo provato, lo trovo personalmente problematico. Gli stash sono un po 'disordinati in un grafico di log git. E, soprattutto, se rovini qualcosa non puoi tornare indietro. Se hai una directory di lavoro sporca e fai scoppiare una scorta, non puoi "annullare" quel pop. È molto più difficile rovinare i commit già esistenti.

Così git rebase -i.

Quando si ribatte un ramo su un altro, è possibile farlo in modo interattivo (il flag -i). Quando lo fai, hai la possibilità di scegliere cosa vuoi fare con ogni commit. Pro Git è un libro fantastico che è anche online in formato HTML e ha una bella sezione su rebasing e squashing:

http://git-scm.com/book/ch6-4.html

Ruberò il loro esempio alla lettera per comodità. Fai finta di avere la seguente cronologia di commit e desideri rifare e schiacciare il bug1 sul master:

    F < bug2
   /
A-B-G-H < master
   \
    C-D-E < bug1

Ecco cosa vedrai quando digiti git rebase -i master bug1

pick f7f3f6d changed my name a bit
pick 310154e updated README formatting and added blame
pick a5f4a0d added cat-file
#
# 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.
#

Per schiacciare tutti i commit di una diramazione in un singolo commit, mantieni il primo commit come "pick" e sostituisci tutte le successive voci "pick" con "squash" o semplicemente "s". Avrai anche l'opportunità di cambiare il messaggio di commit.

pick f7f3f6d changed my name a bit
s 310154e updated README formatting and added blame
s a5f4a0d added cat-file
#
# Commands:
#  p, pick = use commit
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit

Quindi sì, schiacciare è un po 'una seccatura, ma lo consiglierei comunque su un uso pesante delle scorte.


1
Grazie per il post dettagliato! Questo risolve sicuramente molti dei miei problemi - l'unico problema che vedo è che il nostro team attuale ha richiesto di mantenere tutte le consegne in un unico commit. :(
MrDuk,

1
Se non hanno bisogno o vogliono la tua cronologia di lavoro nel repository di produzione va bene: rendi il tuo tracking master meno storico applicando diff piuttosto che unendo filiali. Puoi capire come mantenere un ramo master decorato che ha la cronologia reale unita e fare il tuo vero lavoro da quello, in questo modo sarà facile automatizzare la generazione delle differenze corrette.
jill

2
Si noti che git checkout master; git checkout -b bug2può essere abbreviato in git checkout -b bug2 master. Lo stesso vale per git checkout bug1; git rebase master; git checkout master; git merge bug1, che è identico a git rebase master bug1; git push . bug1:master(scontato, il pushtrucco non è ovvio)
knittl

1
Ho fornito una guida dettagliata per riporlo nella risposta principale in modo da poter usare una formattazione elaborata
Mike Monkiewicz

6
Ho effettuato il downgrade perché non risponde alla domanda originale. Sono in una filiale che sta lavorando a qualcosa e ho appena apportato una modifica che penso debba essere impegnata separatamente nel ramo di integrazione. Tutto quello che voglio fare è mettere in scena quel cambiamento e riporlo in modo che io possa passare a un altro ramo e impegnarmi separatamente, invece del mio attuale ramo "work in progress". (Attenzione, git rant in avanti.) È assurdo che sia così difficile da fare; Devo immaginare che questo è un evento comune . (Lavorare in un ramo e individuare un rapido cambiamento che deve essere fatto e dimenticare di cambiare prima.)
jpmc26

0

Tra i tuoi commenti alla risposta di Mike Monkiewicz ti suggerisco di usare un modello più semplice: usa i rami di sviluppo regolari, ma usa l'opzione di compressione della fusione per ottenere un singolo commit nel ramo principale:

git checkout -b bug1    # create the development branch
* hack hack hack *      # do some work
git commit
* hack hack hack *
git commit
* hack hack hack *
git commit
* hack hack hack *
git commit
git checkout master     # go back to the master branch
git merge --squash bug1 # merge the work back
git commit              # commit the merge (don't forget
                        #    to change the default commit message)
git branch -D bug1      # remove the development branch

Il vantaggio di questa procedura è che puoi usare il normale flusso di lavoro git.


Non riesco a vedere come questa risposta possa aiutare. Non è correlato alla domanda originale.
frapen

0

TL; DR ;git stash-staged

Dopo aver creato un alias:

git config --global alias.stash-staged '!bash -c "git stash -- \$(git diff --staged --name-only)"'

Qui git diffritorna un elenco di --stagedfile --name-only
E poi passiamo questo elenco come pathspecal git stashcomando.

Da man git stash:

git stash [--] [<pathspec>...]

<pathspec>...
   The new stash entry records the modified states only for the files
   that match the pathspec. The index entries and working tree
   files are then rolled back to the state in HEAD only for these
   files, too, leaving files that do not match the pathspec intact.


-1

Per eliminare una modifica accidentale, in particolare l'eliminazione di più file, procedi come segue:

git add <stuff to keep> && git stash --keep-index && git stash drop

in altre parole, riponi la merda e gettala via con la scorta.

Testato in versione git 2.17.1


un voto negativo senza un commento non è utile né a me né al prossimo lettore ... zaenks scontroso anon. Anche se posso immaginare un problema con questo one-liner: bisogna stare molto attenti a non dimenticare di aggiungere tutte le modifiche desiderate all'indice, altrimenti verranno eliminate anche quelle importanti. Ma ancora una volta, l'uso non attento di qualsiasi strumento cli può essere molto pericoloso per il prezioso tempo e il lavoro nel peggiore dei casi.
wmax,
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.