stash - guida git pdf




Stash un solo file di più file modificati con Git? (18)

Come posso nascondere solo uno dei vari file modificati sul mio ramo?


Risposta rapida

Per ripristinare uno specifico file modificato in git, puoi eseguire la seguente riga:

git checkout <branch-name> -- <file-path>

Ecco un esempio reale:

git checkout master -- battery_monitoring/msg_passing.py

Soluzione

Cambiamenti locali:

  • file_A (modificato) non messo in scena
  • file_B (modificato) non messo in scena
  • file_C (modificato) non messo in scena

Per creare uno stash "my_stash" con solo le modifiche su file_C :

1. git add file_C
2. git stash save --keep-index temp_stash
3. git stash save my_stash
4. git stash pop [email protected]#{1}

Fatto.

Spiegazione

  1. aggiungi file_C all'area di staging
  2. crea uno stash temporaneo chiamato "temp_stash" e mantieni le modifiche su file_C
  3. crea lo stash desiderato ("my_stash") con solo le modifiche su file_C
  4. applica le modifiche in "temp_stash" (file_A e file_B) sul tuo codice locale ed elimina la scorta

È possibile utilizzare lo stato git tra i passaggi per vedere cosa sta succedendo.


Aggiornamento (14/02/2015) - Ho riscritto un po 'lo script, per gestire meglio il caso dei conflitti, che dovrebbero ora essere presentati come conflitti non uniti piuttosto che come file .rej.

Trovo spesso più intuitivo fare l'inverso dell'approccio di @ bukzor. Cioè, per mettere in scena alcune modifiche e quindi memorizzare solo quelle modifiche graduali.

Sfortunatamente, git non offre un git stash --only-index o simile, quindi ho montato uno script per farlo.

#!/bin/sh

# first, go to the root of the git repo
cd `git rev-parse --show-toplevel`

# create a commit with only the stuff in staging
INDEXTREE=`git write-tree`
INDEXCOMMIT=`echo "" | git commit-tree $INDEXTREE -p HEAD`

# create a child commit with the changes in the working tree
git add -A
WORKINGTREE=`git write-tree`
WORKINGCOMMIT=`echo "" | git commit-tree $WORKINGTREE -p $INDEXCOMMIT`

# get back to a clean state with no changes, staged or otherwise
git reset -q --hard

# Cherry-pick the index changes back to the index, and stash.
# This cherry-pick is guaranteed to succeed
git cherry-pick -n $INDEXCOMMIT
git stash

# Now cherry-pick the working tree changes. This cherry-pick may fail
# due to conflicts
git cherry-pick -n $WORKINGCOMMIT

CONFLICTS=`git ls-files -u`
if test -z "$CONFLICTS"; then
    # If there are no conflicts, it's safe to reset, so that
    # any previously unstaged changes remain unstaged
    #
    # However, if there are conflicts, then we don't want to reset the files
    # and lose the merge/conflict info.
    git reset -q
fi

Puoi salvare lo script di cui sopra come git-stash-index da qualche parte sul tuo percorso, e quindi puoi invocarlo come git stash-index

# <hack hack hack>
git add <files that you want to stash>
git stash-index

Ora lo stash contiene una nuova voce che contiene solo le modifiche che hai messo in scena e la tua struttura di lavoro contiene ancora modifiche non modificate.

In alcuni casi, le modifiche alla struttura di lavoro possono dipendere dalle modifiche dell'indice, pertanto quando si eseguono le modifiche all'indice, le modifiche alla struttura di lavoro presentano un conflitto. In questo caso, otterrai i soliti conflitti non raggruppati che puoi risolvere con git merge / git mergetool / etc.


Dato che git è fondamentalmente sulla gestione di un contenuto e di un indice di tutti i repository (e non di uno o più file), non sorprende con la directory di tutti i lavori .

In realtà, da Git 2.13 (2 ° trim. 2017), puoi archiviare singoli file, con git stash push :

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

Quando viene assegnato pathspec a ' git stash push ', la nuova memoria registra gli stati modificati solo per i file che corrispondono al pathspec

Vedi " Stash changes to specifici files " per altro.

Il test case è auto-esplicativo:

test_expect_success 'stash with multiple pathspec arguments' '
    >foo &&
    >bar &&
    >extra &&
    git add foo bar extra &&

    git stash push -- foo bar &&   

    test_path_is_missing bar &&
    test_path_is_missing foo &&
    test_path_is_file extra &&

    git stash pop &&
    test_path_is_file foo &&
    test_path_is_file bar &&
    test_path_is_file extra

La risposta originale (di seguito, giugno 2010) riguardava la selezione manuale di ciò che si desidera memorizzare.

Commenti di Casebash :

Questa (la soluzione originale stash --patch ) è carina, ma spesso ho modificato molti file, quindi usare la patch è fastidioso

La answer bukzor (upvoted, novembre 2011) suggerisce una soluzione più pratica, basata su
git add + git stash --keep-index .
Vai a vedere e invertire la sua risposta, che dovrebbe essere quella ufficiale (invece della mia).

A proposito di questa opzione, chhh indica un flusso di lavoro alternativo nei commenti:

dovresti " git reset --soft " dopo una tale operazione per ottenere il tuo chiaro allestimento:
Per arrivare allo stato originale - che è una chiara area di staging e con solo alcune modifiche non programmate, è possibile ripristinare l'indice per ottenere (senza commettere nulla come te - ha fatto bukzor).

(Risposta originale giugno 2010: scorta manuale)

Eppure, git stash save --patch potrebbe permetterti di ottenere lo stashing parziale che stai git stash save --patch :

Con --patch , è possibile selezionare in modo interattivo gli hunk nella differenza tra HEAD e l'albero di lavoro da nascondere.
La voce stash è costruita in modo tale che lo stato dell'indice sia lo stesso dello stato dell'indice del repository e il suo worktree contenga solo le modifiche selezionate in modo interattivo. Le modifiche selezionate vengono quindi ripristinate dal tuo albero di lavoro.

Comunque questo salverà l'indice completo (che potrebbe non essere quello che vuoi dato che potrebbe includere altri file già indicizzati), e un albero di lavoro parziale (che potrebbe sembrare quello che vuoi mettere da parte).

git stash --patch --no-keep-index

potrebbe essere una misura migliore.

Se --patch non funziona, un processo manuale potrebbe:

Per uno o più file, una soluzione intermedia sarebbe:

  • copialo fuori dal repository Git
    (In realtà, eleotlecram propone un'interessante alternativa )
  • git stash
  • copiarli indietro
  • git stash # questa volta, solo i file che vuoi sono nascosti
  • git stash pop [email protected]{1} # applica nuovamente tutte le modifiche ai file
  • git checkout -- afile # resetta il file al contenuto HEAD, prima di qualsiasi modifica locale

Alla fine di questo processo piuttosto ingombrante, avrai solo uno o più file nascosti.


Ho esaminato le risposte e i commenti per questo e un numero di thread simili. Tieni presente che nessuno dei seguenti comandi è corretto allo scopo di poter archiviare file specifici tracciati / non tracciati :

  • git stash -p (--patch) : seleziona manualmente hunk, esclusi i file non tracciati
  • git stash -k (--keep-index) : archivia tutti i file tracciati / non tracciati e li tiene nella directory di lavoro
  • git stash -u (--include-untracked) : archivia tutti i file tracciati / non tracciati
  • git stash -p (--patch) -u (--include-untracked) : comando non valido

Attualmente, il metodo più ragionevole per essere in grado di memorizzare qualsiasi file tracciato / non tracciato specifico è:

  • Impegna temporaneamente i file che non desideri memorizzare
  • Aggiungi e conserva
  • Inserisci il commit temporaneo

Ho scritto un semplice script per questa procedura in una risposta ad un'altra domanda , e qui ci sono dei passaggi per eseguire la procedura in SourceTree .


Il problema con la soluzione "intermedia" di VonC di copiare i file all'esterno del repository Git è che si perdono le informazioni sul percorso, il che rende la copia di un gruppo di file in un secondo momento piuttosto complicata.

A trovare più facile usare tar (strumenti simili probabilmente lo faranno) invece di copiare:

  • tar cvf /tmp/stash.tar percorso / a / alcuni / percorso file / a / alcuni / altro / file (... ecc.)
  • git checkout path / to / some / file path / to / some / other / file
  • git stash
  • tar xvf /tmp/stash.tar
  • ecc. (vedi il suggerimento "intermedio" di VonC)

Nel caso in cui si intendano effettivamente le modifiche di eliminazione ogni volta che si utilizza git stash (e in realtà non si utilizza git stash per riporlo temporaneamente), in tal caso è possibile utilizzare

git checkout -- <file>

[ NOTA ]

Quella git stash è solo un'alternativa più semplice e veloce alla ramificazione e al fare cose.


Non ho trovato nessuna risposta per essere quello di cui avevo bisogno e questo è facile come:

git add -A
git reset HEAD fileThatYouWantToStash
git commit -m "committing all but one file"
git stash

Questo contiene esattamente un file.


Ogni risposta qui è così complicata ...

Che dire di questo per "riporre":

git diff /dir/to/file/file_to_stash > /tmp/stash.patch
git checkout -- /dir/to/file/file_to_stash

Questo per far saltare il file cambia:

git apply /tmp/stash.patch

Esatto stesso comportamento come la memorizzazione di un file e il suo inserimento di nuovo.


Per memorizzare un singolo file usa git stash --patch [file] .

Questo richiederà: Stash this hunk [y,n,q,a,d,j,J,g,/,e,?]? ? Stash this hunk [y,n,q,a,d,j,J,g,/,e,?]? ? . Basta digitare a (riponi questo pezzo e tutti gli hunk successivi nel file) e stai bene.


Puoi anche usare git stash save -p "my commit message" . In questo modo puoi selezionare quali hunk devono essere aggiunti alla stash, anche interi file possono essere selezionati.

Ti verrà richiesto un paio di azioni per ogni hunk:

   y - stash this hunk
   n - do not stash this hunk
   q - quit; do not stash this hunk or any of the remaining ones
   a - stash this hunk and all later hunks in the file
   d - do not stash this hunk or any of the later hunks in the file
   g - select a hunk to go to
   / - search for a hunk matching the given regex
   j - leave this hunk undecided, see next undecided hunk
   J - leave this hunk undecided, see next hunk
   k - leave this hunk undecided, see previous undecided hunk
   K - leave this hunk undecided, see previous hunk
   s - split the current hunk into smaller hunks
   e - manually edit the current hunk
   ? - print help

Quando git stash -p (o git add -p con stash --keep-index ) sarebbe troppo macchinoso, ho trovato più facile usare diff , checkout e apply :

Per "memorizzare" solo un file / dir particolare:

git diff path/to/dir > stashed.diff
git checkout path/to/dir

Quindi dopo

git apply stashed.diff

Questo può essere fatto facilmente in 3 passaggi utilizzando SourceTree.

  1. Impegna temporaneamente tutto ciò che non vuoi nascosto.
  2. Git aggiungi tutto il resto, quindi nascondilo.
  3. Inserisci il tuo commit temporaneo eseguendo git reset, mirando al commit prima del tuo temporaneo.

Tutto questo può essere fatto in pochi secondi in SourceTree, dove puoi semplicemente fare clic sui file (o anche sulle singole linee) che vuoi aggiungere. Una volta aggiunti, basta impegnarli in un commit temporaneo. Quindi, fai clic sulla casella di controllo per aggiungere tutte le modifiche, quindi fai clic su nascondi per nascondere tutto. Con le modifiche nascoste, getta uno sguardo alla tua lista di commit e annota l'hash per il commit prima del commit temporaneo, quindi esegui 'git reset hash_b4_temp_commit', che è fondamentalmente come "scoppiare" il commit reimpostando il tuo ramo sul impegnarsi proprio prima. Ora, sei rimasto solo con le cose che non volevi nascondere.


Salvare il seguente codice in un file, ad esempio, denominato stash . L'utilizzo è stash <filename_regex> . L'argomento è l'espressione regolare per il percorso completo del file. Ad esempio, per riporre un / b / c.txt, stash a/b/c.txt o stash .*/c.txt , ecc.

$ chmod +x stash
$ stash .*.xml
$ stash xyz.xml

Codice da copiare nel file:

#! /usr/bin/expect --
log_user 0
set filename_regexp [lindex $argv 0]

spawn git stash -p

for {} 1 {} {
  expect {
    -re "diff --git a/($filename_regexp) " {
      set filename $expect_out(1,string)
    }
    "diff --git a/" {
      set filename ""
    }
    "Stash this hunk " {
      if {$filename == ""} {
        send "n\n"
      } else {
        send "a\n"
        send_user "$filename\n"
      }
    }
    "Stash deletion " {
      send "n\n"
    }
    eof {
      exit
    }
  }
}

Un altro modo per farlo:

# Save everything
git stash 

# Re-apply everything, but keep the stash
git stash apply

git checkout <"files you don't want in your stash">

# Save only the things you wanted saved
git stash

# Re-apply the original state and drop it from your stash
git stash apply [email protected]{1}
git stash drop [email protected]{1}

git checkout <"files you put in your stash">

L'ho inventato dopo che io (ancora una volta) sono arrivato a questa pagina e non mi sono piaciute le prime due risposte (la prima risposta non risponde alla domanda e non mi piaceva lavorare con la modalità interattiva -p ) .

L'idea è la stessa di quanto suggerito da @VonC usando i file al di fuori del repository, si salvano le modifiche che si desidera da qualche parte, si rimuovono le modifiche che non si desiderano nella memoria e poi si applicano nuovamente le modifiche che si sono spostate. Tuttavia, ho usato il git stash come "da qualche parte" (e come risultato, c'è un passo in più alla fine: rimuovere i cahnge che hai messo nella scorta, perché li hai spostati anche fuori strada).


Un modo complicato sarebbe di commettere prima tutto:

git add -u
git commit // creates commit with sha-1 A

Ripristina il commit originale ma checkout the_one_file dal nuovo commit:

git reset --hard HEAD^
git checkout A path/to/the_one_file

Ora puoi nascondere the_one_file:

git stash

Pulisci salvando il contenuto impegnato nel tuo file system mentre resetti al commit originale:

git reset --hard A
git reset --soft HEAD^

Sì, un po 'imbarazzante ...


Vorrei usare git stash save --patch . Non trovo l'interattività fastidiosa perché ci sono delle opzioni durante l'applicazione per applicare l'operazione desiderata a interi file.


avvertimento

Come notato nei commenti, questo mette tutto nella scorta, sia messa in scena che non. --Keep-index lascia solo l'indice dopo che è stato eseguito lo stash. Ciò può causare conflitti di unione quando si apre la scorta.

Questo immagazzinerà tutto ciò che non hai precedentemente aggiunto. Basta git add le cose che vuoi mantenere, quindi eseguirlo.

git stash --keep-index

Ad esempio, se si desidera dividere un vecchio commit in più di un changeset, è possibile utilizzare questa procedura:

  1. git rebase -i <last good commit>
  2. Contrassegna alcune modifiche come edit .
  3. git reset HEAD^
  4. git add <files you want to keep in this change>
  5. git stash --keep-index
  6. Risolvi le cose se necessario. Non dimenticare di git add modifiche.
  7. git commit
  8. git stash pop
  9. Ripeti, dal n. 5, se necessario.
  10. git rebase --continue




git-stash