Git 🌙
Chapters ▾ 2nd Edition

7.3 Git Tools - Stashen en opschonen

Stashen en opschonen

Vaak, als je aan het werk bent geweest aan een deel van je project, zijn zaken in een rommelige staat en wil je naar andere branches omschakelen om wat aan iets anders te werken. Het probleem hier is dat je geen commit van half-compleet werk wilt doen, met als enige reden om later hiernaar terug te kunnen keren. Het antwoord op dit probleem is het git stash commando.

Stashen (even opzij leggen) pakt de onafgewerkte status van je werk directory — dat wil zeggen: je gewijzigde tracked bestanden en gestagede wijzigingen — en bewaart dit op een stapel met incomplete wijzigingen die je op elk willekeurig moment kunt afspelen.

Noot
Migreren naar git stash push

Per achterin oktober 2017 was er een uitvoerige discussie op de Git maillijst, waarbij het commando git stash save achterhaald (deprecated) is verklaard ter faveure van het bestaande alternatief git stash push. De belangrijkste reden hiervoor is dat git stash push de optie introduceert van het stashen van geselecteerde _pathspecs~, iets wat git stash save niet ondersteund.

git stash save blijft voorlopig nog wel rondhangen, dus maak je er geen zorgen over dat het plotseling verdwijnt. Maar je kunt erover nadenken om te beginnen migreren naar het push alternatief voor de nieuwe functionaliteit.

Je werk stashen

Om de werking te laten zien, ga je naar je project en begint te werken aan een aantal bestanden en mogelijk stage je een van de wijzigingen. Als je git status gebruikt, kan je je vuile status zien:

$ git status
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

	modified:   index.html

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

	modified:   lib/simplegit.rb

Nu wil je naar een andere branch omschakelen, maar je wilt hetgeen waar je aan hebt gewerkt nog niet committen; dus ga je de wijzigingen stashen. Om een nieuwe stash op de stapel (stack) te duwen, roep je git stash of git stash push aan:

$ git stash
Saved working directory and index state \
  "WIP on master: 049d078 added the index file"
HEAD is now at 049d078 added the index file
(To restore them type "git stash apply")

Je nu zien dat je werk directory schoon is:

$ git status
# On branch master
nothing to commit, working directory clean

Op dit moment kan je eenvoudige branches switchen en werk elders doen; je wijzigingen zijn bewaard op je stack. Om te zien welke stashes je bewaard hebt, kan je git stash list gebruiken:

$ git stash list
stash@{0}: WIP on master: 049d078 added the index file
stash@{1}: WIP on master: c264051 Revert "added file_size"
stash@{2}: WIP on master: 21d80a5 added number to log

In dit voorbeeld, zijn er twee stashes eerder bewaard, dus je hebt toegang tot drie verschillende gestashde werken. Je kunt die ene die je zojuist gestasht hebt weer afspelen door het commando zoals getoond in de help uitvoer van het oorspronkelijke stash commando: git stash apply. Als je een van de oudere stashes wilt afspelen, kan je deze aangeven door de naam gebruiken, zoals dit: git stash apply stash@{2}. Als je geen stash aangeeft, gebruikt Git de meest recente stash en probeert deze af te spelen:

$ git stash apply
On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

	modified:   index.html
	modified:   lib/simplegit.rb

no changes added to commit (use "git add" and/or "git commit -a")

Je kunt zien dat Git de bestanden weer wijzigt die je teruggedraaid had toen je de stash bewaarde. In dit geval had je een schone werk directory toen je de stash probeerde af te spelen en je probeerde deze af te spelen op dezelfde branch als waarvan je het had bewaard. Maar het hebben van een schone werk directory en deze op dezelfde branch af te spelen zijn niet nodig om een stash succesvol af te spelen. Je kunt een stash op de ene branch bewaren, later overschakelen naar een andere branch en daar de wijzigingen proberen opnieuw af te spelen. Je kunt ook gewijzigde en ongecommitte bestanden in je werkdirectory hebben als je een stash afspeelt — Git geeft je merge conflicten als iets niet meer netjes kan worden toegepast.

De wijzigingen aan je bestanden werden opnieuw gemaakt, maar het bestand dat je voorheen gestaged had wordt niet opnieuw gestaged. Om dat te doen, moet de het git stash apply commando met een --index optie gebruiken om het commando te vertellen om de gestagede wijzigingen opnieuw af te spelen. Als je deze variant had gebruikt, zou je je oorspronkelijke situatie terug hebben gekregen:

$ git stash apply --index
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

	modified:   index.html

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

	modified:   lib/simplegit.rb

De apply optie probeert alleen het gestashde werk opnieuw af te spelen — je blijft het behouden op je stack. Om het weg te halen, kan je git stash drop gebruiken met de naam van de stash die moet worden verwijderd:

$ git stash list
stash@{0}: WIP on master: 049d078 added the index file
stash@{1}: WIP on master: c264051 Revert "added file_size"
stash@{2}: WIP on master: 21d80a5 added number to log
$ git stash drop stash@{0}
Dropped stash@{0} (364e91f3f268f0900bc3ee613f9f733e82aaed43)

Je kunt ook git stash pop gebruiken om de stash af te spelen en dan meteen te verwijderen uit je stack.

Creatief stashen

Er zijn een paar stash varianten die ook handig kunnen zijn. De eerste variant die redelijk populair is, is de --keep-index optie bij het git stash commando. Dit vertelt Git niet alleen alle gestagede inhoud mee te nemen in de te creeren stash, en tegelijkertijd het in de index te laten.

$ git status -s
M  index.html
 M lib/simplegit.rb

$ git stash --keep-index
Saved working directory and index state WIP on master: 1b65b17 added the index file
HEAD is now at 1b65b17 added the index file

$ git status -s
M  index.html

Een ander veel voorkomende toepassing van stash is om de niet getrackte bestanden te stashen zowel als de getrackte. Standaard zal git stash alleen tracked bestanden die gewijzigd en staged zijn opslaan. Als je --include-untracked of `-u gebruikt zal git ook alle niet getrackte bestanden die je gemaakt hebt stashen. Echter, niet getrackte bestanden in de stash betrekken zal nog steeds niet expliciet ignored bestanden meenemen; om ignored bestanden mee te nemen, gebruik je --all (of alleen -a).

$ git status -s
M  index.html
 M lib/simplegit.rb
?? new-file.txt

$ git stash -u
Saved working directory and index state WIP on master: 1b65b17 added the index file
HEAD is now at 1b65b17 added the index file

$ git status -s
$

Als laatste, als je de --patch optie gebruikt, zal Git niet alles wat is gewijzigd stashen maar zal in plaats je daarvan interactief vragen welk van de wijzigingen je wilt stashen en welke je in je werk directory wilt behouden.

$ git stash --patch
diff --git a/lib/simplegit.rb b/lib/simplegit.rb
index 66d332e..8bb5674 100644
--- a/lib/simplegit.rb
+++ b/lib/simplegit.rb
@@ -16,6 +16,10 @@ class SimpleGit
         return `#{git_cmd} 2>&1`.chomp
       end
     end
+
+    def show(treeish = 'master')
+      command("git show #{treeish}")
+    end

 end
 test
Stash this hunk [y,n,q,a,d,/,e,?]? y

Saved working directory and index state WIP on master: 1b65b17 added the index file

Een branch vanuit een stash maken

Als je wat werk gestashed hebt, dit daar een tijdje bewaard, en doorgaat op de branch van waaruit je het werk gestasht hebt, kan je een probleem krijgen bij het weer afspelen hiervan. Als het afspelen een bestand wil wijzigen wat je daarna weer gewijzigd hebt, zal je een merge conflict krijgen en zul je het moeten proberen op te lossen. Als je een makkelijker manier wilt om de gestashde wijzigingen weer te testen, kan je git stash branch <nieuwe branchnaam> gebruiken, wat een nieuwe branch voor je aanmaakt, checkt de commit uit waar je op stond toen je je werk stashde, speelt de wijzigingen af op je werk daar en verwijdert de stash als het succesvol heeft kunnen toepassen:

$ git stash branch testchanges
M	index.html
M	lib/simplegit.rb
Switched to a new branch 'testchanges'
On branch testchanges
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

	modified:   index.html

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

	modified:   lib/simplegit.rb

Dropped refs/stash@{0} (29d385a81d163dfd45a452a2ce816487a6b8b014)

Dit is een mooie manier om gestashde werk eenvoudig terug te halen en eraan te werken in een nieuwe branch.

Je werk directory opschonen

Als laatste wil je misschien bepaald werk of sommige bestanden in je werk directory stashen, maar dit simpelweg verwijderen. Dit is waar het git clean commando voor bedoeld is.

Een simpele reden voor het opschonen van je werk directory kan zijn dat het verwijderen van restjes zijn die zijn gegenereerd door merges of andere tools of om bouw-producten te verwijderen om een nette build te maken.

Je moet wel erg voorzichtig zijn met dit commando, het is namelijk gemaakt om bestanden van je werk directory weg te halen die niet worden getracked. Als je je later bedenkt, is er vaak niet mogelijk om de inhoud van die bestanden terug te halen. Een veiligere optie is om git stash --all te gebruiken om alles weg te halen, en dit alles in een stash te bewaren.

Aangenomen dat je de overbodige bestanden wilt verwijderen of je werk directory wilt opschonen, kan je dat doen met git clean. Om alle ongetrackte bestanden uit je werk directory te verwijderen kan je het git clean -f -d commando aanroepen, die alle bestanden verwijdert en ook alle subdirectories die leeg zijn geraakt als gevolg hiervan. De -f betekent forceer of "doe dit nou maar echt", en is verplicht als de Git configuratie variabele clean.requireForce niet expliciet op false gezet is.

Als je ooit wilt zien wat het zou gaan doen, kan je het commando met de --dry-run (of -n) optie aanroepen, wat “doe een generale repetitie en vertel me wat je zou hebben verwijderd”.

$ git clean -d -n
Would remove test.o
Would remove tmp/

Stamdaard zal het git clean commando alleen ongetrackte bestanden verwijderen die niet genegeerd worden. Elk bestand waarvan de naam overeenkomt met een patroon in je .gitignore of andere negeer (ignore) bestanden zullen niet verwijderd worden. Als je ook deze bestanden wilt verwijderen, bijvoorbeeld om alle .o bestanden te verwijderen die zijn gegenereerd tijdens een build zodat je een compleet schone build kunt doen kan je een -x toevoegen aan het opschoon-commando.

$ git status -s
 M lib/simplegit.rb
?? build.TMP
?? tmp/

$ git clean -n -d
Would remove build.TMP
Would remove tmp/

$ git clean -n -d -x
Would remove build.TMP
Would remove test.o
Would remove tmp/

Als je niet weet wat het git clean commando zal gaan doen, roep deze dan altijd eerst aan met een -n voor de zekerheid voordat je de -n in een -f wijzigt en het definitief doet. Een alternatief voor het voorzichtig uitvoeren van dit proces is om het aan te roepen met de -i of “interactive” optie.

Dit zal het opschoon-commando in een interactieve modus aanroepen.

$ git clean -x -i
Would remove the following items:
  build.TMP  test.o
*** Commands ***
    1: clean                2: filter by pattern    3: select by numbers    4: ask each             5: quit
    6: help
What now>

Op deze manier kan je alle bestanden een voor een afgaan of bepaalde patronen voor verwijdering aangeven.

Noot

Er is een specifiek vreemde situatie waarin je extra overtuigend moet zijn om Git te vragen je werk directory schoon te maken. Als je in een werk directory zit waaronder je een andere Git repository hebt gekopieerd of gekloond (misschien als submodules), zal zelfs git clean -fd weigeren deze directories te verwijderen. In zulke gevallen zal je een tweede -f optie moeten toevoegen voor extra nadruk.

scroll-to-top