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, 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.

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 onderhanden 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 save 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 werk directory is schoon:

$ 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 gedaan, dus je hebt toegang tot drie verschillende gestashde werken. Je kunt die ene die je zojuist gestashed 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
# Changed but not updated:
#   (use "git add <file>..." to update what will be committed)
#
#      modified:   index.html
#      modified:   lib/simplegit.rb
#

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 proberde 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
#
# Changed but not updated:
#   (use "git add <file>..." to update what will be committed)
#
#      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 stash save commando. Dit vertelt Git niets te stashen wat je al gestaged hebt met het git add commando.

Dit kan erg handig zijn als je al een aantal wijzigingen gemaakt hebt, maar alleen een paar daarvan wilt committen om later de overige wijzigingen af te maken.

$ 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 bestanden opslaan die al in de index staan. Als je --include-untracked of `-u gebruikt zal git ook alle niet getrackte bestanden die je gemaakt hebt stashen.

$ 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 gestashed 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 makkelijjker manier wilt om de gestashde wijzigingen weer te testen, kan je git stash branch gebruiken, wat een nieuwe branch voor je aanmaakt, checked 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
Switched to a new branch "testchanges"
# On branch testchanges
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#      modified:   index.html
#
# Changed but not updated:
#   (use "git add <file>..." to update what will be committed)
#
#      modified:   lib/simplegit.rb
#
Dropped refs/stash@{0} (f0dfc4d5dc332d1cee34a634182e168c4efc3359)

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. Het git clean commando zal dit voor je doen.

Een eenvoudige reden hiervoor 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 gewoon".

Als je ooit wilt zien wat het zou gaan doen, kan je het commando met de -n optie draaien, 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.