Git
Chapters ▾ 2nd Edition

7.3 Git Tools - Stashen und Bereinigen

Stashen und Bereinigen

Oft, wenn Sie an einem Teil Ihres Projekts gearbeitet haben, ist es in einem unordentlichen Zustand. Sie wollen jetzt die Branch für eine gewisse Zeit wechseln, um an etwas anderem zu arbeiten. Das Problem ist, dass Sie keinen Commit mit halbfertiger Arbeit machen wollen, 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 Ihres Arbeitsverzeichnisses – das heißt, Ihre geänderten getrackten Dateien und gestagten Änderungen – und speichert ihn in einem Stapel unvollendeter Änderungen, die Sie jederzeit (auch auf einen anderen Branch) wieder anwenden können.

Note
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 abgelöst, also machen Sie sich keine Sorgen, dass es plötzlich verschwinden wird. Aber Sie sollten, wegen der neuen Funktionalität, mit der Migration zu der push Alternative anfangen.

Ihre Arbeit stashen

Um das Stashen zu demonstrieren, gehen Sie in Ihr Projekt und beginnen Sie mit der Arbeit an ein paar Dateien. Sie können dann eine der Änderungen der Staging-Area hinzufügen. Wenn Sie git status ausführen, können Sie den schlechten 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

Sie möchten nun den Branch wechseln, aber Sie wollen das bisherige noch nicht committen, also werden Sie die Änderungen stashen. Um einen neuen Stash in Ihren Stack zu verschieben, führen Sie 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")

Sie sehen dann, dass Ihr Arbeitsverzeichnis bereinigt ist:

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

An dieser Stelle können Sie die Branches wechseln und anderswo arbeiten. Ihre Änderungen werden auf Ihrem Stack gespeichert. Um zu sehen, welche Stashes Sie gespeichert haben, können Sie 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 Sie Zugriff auf drei verschiedene gestashte Arbeiten haben. Sie können den soeben versteckten Stash erneut aufrufen, indem Sie den Befehl verwenden, der in der Hilfe-Anzeige des ursprünglichen Stash-Befehls angezeigt wird: Git stash apply. Wenn Sie einen der früheren Stashes anwenden möchten, können Sie ihn durch einen Namen angeben, etwa so: git stash apply stash@{2}. Wenn Sie keinen Stash angeben, 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")

Sie können feststellen, dass Git die Dateien, die Sie beim Speichern des Stashes zurückgesetzt haben, erneut modifiziert. So hatten Sie ein sauberes Arbeitsverzeichnis, als Sie versuchten, den Stash anzuwenden den Sie auf den gleichen Branch anwenden wollten, aus dem Sie ihn erzeugt hatten. Ein sauberes Arbeitsverzeichnis und dessen Anwendung auf denselben Branch sind nicht nötig, um einen Stash erfolgreich anzulegen. Sie können einen Stash in einem Branch speichern, später in einen anderen Branch wechseln und erneut versuchen, die Änderungen zu übernehmen. Sie können auch geänderte und nicht übertragene Dateien in Ihrem Arbeitsverzeichnis haben, wenn Sie einen Stash anwenden – Git meldet Ihnen Merge-Konflikte, wenn etwas nicht mehr sauber funktioniert.

Die Änderungen an Ihren Dateien wurden erneut angewendet, aber die Datei, die Sie zuvor bereitgestellt haben, wurde nicht neu eingestellt. Um das zu erreichen, müssen Sie 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ätten Sie stattdessen diesen Befehl ausgeführt, wären Sie an Ihre 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 – Sie haben sie weiterhin in Ihrem Stack. Um sie zu entfernen, kann man git stash drop mit dem Namen des zu entfernenden Stashes 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 Sie --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, verwenden Sie --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
$

Schließlich, wenn Sie das --patch Flag angeben, wird Git nicht alles, was modifiziert wurde, in den Stash aufnehmen, sondern Sie interaktiv fragen, welche der Änderungen Sie sicher verwahren wollen und welche Sie noch in Ihrem Arbeitsverzeichnis behalten möchten.

$ 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 Sie etwas Arbeit stashen, sie eine Weile dort belassen und dann auf dem Branch weiter machen wollen, aus dem Sie die Arbeit gebunkert haben, könnten Sie ein Problem bekommen, die Arbeit wieder aufzunehmen. 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 Sie einen einfacheren Weg bevorzugen, um die gespeicherten Änderungen noch einmal zu testen, könnten Sie git stash branch <new branchname> ausführen, der einen neuen Branch mit dem gewählten Branch-Namen für Sie erzeugt, die Übertragung, an der Sie gerade waren, auscheckt, Ihre Arbeit dort wieder einsetzt und dann den Stash verwirft, wenn er erfolgreich angewendet wird:

$ 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öchten Sie vielleicht einige Arbeiten oder Dateien nicht in Ihrem Arbeitsverzeichnis ablegen, sondern sie einfach nur loswerden; dafür ist der Befehl git clean gedacht.

Einige gängige Fälle, in denen Sie Ihr Arbeitsverzeichnis bereinigen müssen, sind das Entfernen von überflüssigem Programmkode, der durch Merges oder externe Tools erzeugt wurde oder das Entfernen von Build-Artefakten, um einen sauberen Aufbau zu ermöglichen.

Sie sollten mit diesem Befehl sehr vorsichtig sein, da er darauf ausgelegt ist, Dateien aus Ihrem Arbeitsverzeichnis zu entfernen, die nicht getrackt werden. Wenn Sie Ihre Absicht ändern, 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, Sie wollen unerwünschte Dateien entfernen oder Ihr Arbeitsverzeichnis bereinigen, dann können Sie das mit git clean erledigen. Um alle ungetrackten Dateien in Ihrem Arbeitsverzeichnis zu entfernen, können Sie git clean -f -d ausführen, das alle Dateien entfernt, auch aus Unterverzeichnissen, die dadurch leer werden. 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 Sie einmal wissen wollen, was der Befehl bewirken könnte, dann führen Sie 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 Ihrer .gitignore oder anderen Ignore-Dateien übereinstimmt, wird nicht entfernt. Wenn Sie diese Dateien ebenfalls entfernen wollen, z.B. um alle .o Dateien zu entfernen, die von einem Build erzeugt wurden, damit Sie einen vollständig sauberen Build machen können, können Sie 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 Sie nicht wissen, was der git clean Befehl bewirken wird, führen Sie ihn immer mit einem -n aus, um ihn zu überprüfen, bevor Sie das -n in ein -f ändern und ihn dann wirklich ausführen. Der andere Weg, wie Sie sich vorsehen können, 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 können Sie jede Datei einzeln durchgehen oder interaktiv den zu löschenden Pattern festlegen.

Note

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