Git
Chapters ▾ 2nd Edition

7.3 Git Tools - Stashen und Bereinigen

Stashen und Bereinigen

Es passiert manchmal, dass dein Projekt nach einer Bearbeitung in einem unordentlichen Zustand. Stelle dir vor du willst in einen anderen Branch wechseln, um an etwas anderem zu arbeiten. Das Problem ist, dass du keinen Commit mit halbfertiger Arbeit machen willst, nur um später an diesen Punkt zurückkehren zu können. Die Antwort auf dieses Problem ist der Befehl git stash.

Stashing nimmt den unsauberen Zustand deines Arbeitsverzeichnisses – das heißt, deine geänderten, getrackten Dateien und gestagte Änderungen – und speichert ihn in einem Stapel unvollendeter Änderungen, die du jederzeit (auch auf einen anderen Branch) wieder anwenden kannst.

Anmerkung
Migrieren zu git stash push

Ende Oktober 2017 gab es eine ausführliche Diskussion innerhalb der Git-Mailingliste, bei der der Befehl git stash save zugunsten der bestehenden Alternative git stash push als veraltet eingestuft wurde. Der Hauptgrund dafür ist, dass git stash push die Möglichkeit bietet, ausgewählte pathspecs zu speichern, was git stash save nicht unterstützt.

git stash save wird in naher Zukunft nicht verschwinden, also mache dir keine Sorgen, dass es plötzlich nicht mehr vorhanden ist. Wir empfehlen dir, wegen der neuen Funktionalität, zu der neuen push Alternative zu migrieren.

Deine Arbeit stashen

Um das Stashen zu demonstrieren, gehe in dein Projekt und beginne mit der Arbeit an ein paar Dateien. Du kannst dann eine der Änderungen der Staging-Area hinzufügen. Wenn du git status ausführst, kannst du den aktuellen Status sehen:

$ 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

Du möchtest nun den Branch wechseln, aber du willst das bisherige noch nicht committen, also solltest du die Änderungen stashen. Um einen neuen Stash zu erstellen, führe git stash oder git stash push aus:

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

Anschliessend siehst du, dass dein Arbeitsverzeichnis bereinigt ist:

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

An dieser Stelle kannst du die Branches wechseln und anderswo arbeiten. Deine Änderungen werden solange gespeichert. Um zu sehen, welche Stashes du gespeichert hast, kannst du git stash list verwenden:

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

Hier wurden vorher schon zwei Stashes gespeichert, so dass du Zugriff auf drei verschiedene gestashte Arbeiten hast. Du kannst den soeben versteckten Stash erneut aufrufen, indem du den Befehl verwendest, der in der Hilfe-Anzeige des ursprünglichen Stash-Befehls angezeigt wird: git stash apply. Wenn du einen der früheren Stashes anwenden möchtest, kannst du ihn durch einen Namen angeben, etwa so: git stash apply stash@{2}. Wenn du keinen Stash angibst, nimmt Git den neuesten Stash und versucht, ihn zu übernehmen:

$ 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")

Du kannst sehen, dass Git die Dateien, die du beim Speichern des Stashes zurückgesetzt hast, erneut modifiziert. In diesem Fall hattest du ein sauberes Arbeitsverzeichnis, als du versucht hast, den Stash anzuwenden den du auf den gleichen Branch anwenden wolltest, aus dem du ihn erzeugt hast. Ein sauberes Arbeitsverzeichnis und dessen Anwendung auf denselben Branch sind nicht nötig, um einen Stash erfolgreich anzulegen. Du kannst einen Stash in einem Branch speichern, später in einen anderen Branch wechseln und erneut versuchen, die Änderungen zu übernehmen. Du kannst auch geänderte und nicht übertragene Dateien in deinem Arbeitsverzeichnis haben, wenn du einen Stash anwendest. Git meldet dir Merge-Konflikte, wenn etwas nicht mehr sauber funktioniert.

Die Änderungen an deinen Dateien wurden erneut angewendet, aber die Datei, die du zuvor bereitgestellt hast, wurde nicht neu eingestellt. Um das zu erreichen, musst du den Befehl git stash apply mit der Option --index ausführen und so dem Befehl anweisen, dass er versuchen soll, die gestagten Änderungen erneut anzuwenden. Hättest du stattdessen diesen Befehl ausgeführt, wärst du an deine ursprüngliche Position zurückgekehrt:

$ 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

Die apply-Option versucht nur, die gestashte Arbeit zu übernehmen. Du hast sie weiterhin in deinem Stack. Um sie zu entfernen, kann man git stash drop mit dem Namen des zu entfernenden Stash ausführen:

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

Man kann auch git stash pop ausführen, um den Stash einzubringen und ihn dann sofort vom Stack zu entfernen.

Kreatives Stashing

Es gibt ein paar Stash-Varianten, die ebenfalls nützlich sein können. Die erste, recht beliebte Option ist die --keep-index Option zum git stash Befehl. Diese weist Git an, nicht nur alle bereitgestellten Inhalte in den zu erstellenden Stash aufzunehmen, sondern sie gleichzeitig im Index zu belassen.

$ 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

Eine weitere gebräuchliche Funktion von stash ist die Ablage der nicht getrackten sowie der getrackten Dateien. Standardmäßig wird git stash nur modifizierte und gestagte, getrackte Dateien aufnehmen. Wenn du --include-untracked oder -u angeben, wird Git ungetrackte Dateien in den zu erstellenden Stash einschließen. Trotzdem wird das Einfügen von nicht getrackten Dateien in den Stash weiterhin keine explizit zu ignorierenden Dateien enthalten, Um zusätzlich ignorierte Dateien einzubeziehen, verwende --all (oder nur -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
$

Wenn du das --patch Flag angibst, wird Git nicht alles, was modifiziert wurde, in den Stash aufnehmen, sondern dich fragen, welche der Änderungen du sicher verwahren willst und welche du noch in deinem Arbeitsverzeichnis behalten möchtest.

$ 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

Einen Branch aus einem Stash erzeugen

Wenn Du etwas Arbeit stashen, sie eine Weile dort belassen und dann auf dem Branch weiter machen willst, aus dem du die Arbeit gestashet hast, könntest du ein Problem bekommen, die Änderungen wieder anzuwenden. Wenn man versucht, eine Datei zu ändern, die man zwischenzeitlich schon bearbeitet hatte, erhält man einen Merge-Konflikt und muss versuchen, diesen aufzulösen. Wenn du einen einfacheren Weg bevorzugst, um die gespeicherten Änderungen noch einmal zu testen, kannst du git stash branch <new branchname> ausführen. Damit wird ein neuer Branch mit dem gewählten Branch-Namen erzeugt, der Commit, an der du gerade gearbeitet hast, auscheckt, deine Arbeit dort wieder eingesetzt und dann den Stash verworfen, wenn er erfolgreich angewendet wurde:

$ 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)

Das ist ein interessanter Weg, mit dem man die gestashten Arbeiten leicht wiederherstellen und in einem neuen Branch bearbeiten kann.

Bereinigung des Arbeitsverzeichnisses

Letztendlich möchtest du vielleicht einige Arbeiten oder Dateien nicht in deinem Arbeitsverzeichnis ablegen, sondern sie einfach nur loswerden. Dafür ist der Befehl git clean gedacht.

Einige gängige Beispiele, in denen du dein Arbeitsverzeichnis bereinigen musst, sind das Entfernen von überflüssigem Programmcode, der durch Merges oder externe Tools erzeugt wurde oder das Entfernen von Build-Artefakten, um einen sauberen Build zu ermöglichen.

Du solltest mit diesem Befehl sehr vorsichtig sein, da er darauf ausgelegt ist, Dateien aus deinem Arbeitsverzeichnis zu entfernen, die nicht getrackt werden. Wenn du deine Absicht änderst, gibt es oft keine Möglichkeit mehr, den Inhalt dieser Dateien wiederherzustellen. Eine bessere Option ist, git stash --all auszuführen um alles zu entfernen, aber es in einem Stash zu speichern.

Angenommen, du willst unerwünschte Dateien entfernen oder dein Arbeitsverzeichnis bereinigen, dann könntest du das mit git clean erledigen. Um alle ungetrackten Dateien in deinem Arbeitsverzeichnis zu entfernen, kannst du git clean -f -d ausführen, das alle Dateien entfernt, auch aus Unterverzeichnissen, die dadurch leer werden könnten. Das -f bedeutet „force“ (dt. „erzwingen“ oder „unbedingt ausführen“) und wird benötigt, falls die Git-Konfigurationsvariable clean.requireForce explizit nicht auf false gesetzt ist.

Wenn du wissen willst, was der Befehl bewirken könnte, dann führe ihn mit der Option --dry-run (oder -n) aus. Das bedeutet: „Mach einen Probelauf und berichte mir, was du gelöscht hättest“.

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

Standardmäßig entfernt der Befehl git clean nur die ungetrackten Dateien, die nicht ignoriert werden. Jede Datei, die mit einem Suchmuster in deiner .gitignore oder anderen Ignore-Dateien übereinstimmt, wird nicht entfernt. Wenn du diese Dateien ebenfalls entfernen willst, z.B. um alle .o Dateien zu entfernen, die von einem Build erzeugt wurden, kannst du dem clean-Befehl ein -x hinzufügen.

$ 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/

Wenn du nicht weißt, was der git clean Befehl bewirken wird, führe ihn immer mit einem -n aus, um ihn zu überprüfen, bevor du das -n in ein -f änderst und ihn dann wirklich auszuführen. Der andere Weg, wie du dich vorsehen kannst, ist den Prozess mit dem -i oder „interactive“ Flag auszuführen.

Dadurch wird der Clean-Befehl im interaktiven Modus ausgeführt.

$ 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>

Auf diese Weise kannst du jede Datei einzeln durchgehen oder interaktiv das zu entsprechende Lösch-Pattern festlegen.

Anmerkung

Es gibt eine ungewöhnliche Situation, in der man Git besonders energisch auffordern muss, das Arbeitsverzeichnis zu bereinigen. Wenn du dich in einem Arbeitsverzeichnis befindest, unter dem du andere Git-Repositorys (vielleicht als Submodule) kopiert oder geklont hast, wird selbst git clean -fd sich weigern, diese Verzeichnisse zu löschen. In solchen Fällen musst du eine zweite -f Option zur Verstärkung hinzufügen.

scroll-to-top