Kapitel ▾ 2. Auflage

7.3 Git-Tools - Stashing und Aufräumen

Stashing und Aufräumen

Oftmals, wenn Sie an einem Teil Ihres Projekts gearbeitet haben, ist der Zustand unübersichtlich und Sie möchten kurz zu einem anderen Branch wechseln, um an etwas anderem zu arbeiten. Das Problem ist, dass Sie keine halbfertige Arbeit committen möchten, nur um später zu diesem Punkt zurückkehren zu können. Die Antwort auf dieses Problem ist der Befehl git stash.

Stashing nimmt den schmutzigen Zustand Ihres Arbeitsverzeichnisses – das heißt, Ihre geänderten verfolgten Dateien und gestaged Änderungen – und speichert ihn auf einem Stapel unfertiger Änderungen, den Sie jederzeit (auch auf einem anderen Branch) wieder anwenden können.

Hinweis
Umstellung auf git stash push

Ende Oktober 2017 gab es eine ausführliche Diskussion in 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 Option zum Stashing ausgewählter *Pfadspezifikationen* einführt, was git stash save nicht unterstützt.

git stash save wird nicht so schnell verschwinden, also machen Sie sich keine Sorgen, dass es plötzlich verschwindet. Sie sollten jedoch vielleicht beginnen, auf die push-Alternative für die neue Funktionalität umzusteigen.

Ihre Arbeit stashen

Um Stashing zu demonstrieren, gehen Sie in Ihr Projekt und beginnen Sie, an ein paar Dateien zu arbeiten und möglicherweise eine der Änderungen zu stagen. Wenn Sie git status ausführen, können Sie Ihren schmutzigen Zustand 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

Jetzt möchten Sie den Branch wechseln, aber Sie möchten Ihre bisherige Arbeit noch nicht committen, also werden Sie die Änderungen stashen. Um einen neuen Stash auf Ihren Stapel zu legen, 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 können jetzt sehen, dass Ihr Arbeitsverzeichnis sauber ist.

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

An diesem Punkt können Sie den Branch wechseln und woanders arbeiten; Ihre Änderungen sind auf Ihrem Stapel 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

In diesem Fall wurden zuvor zwei Stashes gespeichert, sodass Sie Zugriff auf drei verschiedene gestashte Arbeiten haben. Sie können den gerade gestashten wieder anwenden, indem Sie den Befehl verwenden, der in der Hilfeausgabe des ursprünglichen Stash-Befehls angezeigt wird: git stash apply. Wenn Sie einen älteren Stash anwenden möchten, können Sie ihn durch Benennung angeben, wie hier: git stash apply stash@{2}. Wenn Sie keinen Stash angeben, geht Git davon aus, dass es sich um den neuesten Stash handelt, und versucht, ihn anzuwenden.

$ 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 sehen, dass Git die Dateien, die Sie beim Speichern des Stashes zurückgesetzt haben, wieder modifiziert. In diesem Fall hatten Sie ein sauberes Arbeitsverzeichnis, als Sie versuchten, den Stash anzuwenden, und Sie versuchten, ihn auf demselben Branch anzuwenden, von dem Sie ihn gespeichert haben. Ein sauberes Arbeitsverzeichnis und die Anwendung auf demselben Branch sind nicht notwendig, um einen Stash erfolgreich anzuwenden. Sie können einen Stash auf einem Branch speichern, später zu einem anderen Branch wechseln und versuchen, die Änderungen wieder anzuwenden. Sie können auch modifizierte und uncommittete Dateien in Ihrem Arbeitsverzeichnis haben, wenn Sie einen Stash anwenden – Git gibt Ihnen Merge-Konflikte, wenn etwas nicht mehr sauber angewendet werden kann.

Die Änderungen an Ihren Dateien wurden wiederhergestellt, aber die zuvor gestagte Datei wurde nicht erneut gestaged. Um dies zu tun, müssen Sie den Befehl git stash apply mit der Option --index ausführen, um dem Befehl mitzuteilen, dass er versuchen soll, die gestagten Änderungen wieder anzuwenden. Wenn Sie stattdessen diesen Befehl ausgeführt hätten, wären Sie zu Ihrer ursprünglichen 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 Option apply versucht nur, die gestashte Arbeit anzuwenden – sie bleibt auf Ihrem Stapel. Um sie zu entfernen, können Sie 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)

Sie können auch git stash pop ausführen, um den Stash anzuwenden und ihn dann sofort von Ihrem Stapel zu entfernen.

Kreatives Stashing

Es gibt ein paar Stash-Varianten, die ebenfalls hilfreich sein können. Die erste Option, die ziemlich beliebt ist, ist die Option --keep-index für den Befehl git stash. Dies weist Git an, nicht nur den gesamten gestagten Inhalt in den zu erstellenden Stash aufzunehmen, sondern ihn 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 gängige Sache, die Sie mit Stash tun möchten, ist, auch die ungetrackten Dateien neben den getrackten zu stashen. Standardmäßig stasht git stash nur modifizierte und gestagte *getrackte* Dateien. Wenn Sie --include-untracked oder -u angeben, werden ungetrackte Dateien in den zu erstellenden Stash aufgenommen. Das Einbeziehen von ungetrackten Dateien in den Stash schließt jedoch immer noch keine explizit *ignorierten* Dateien ein; um ignorierten Dateien zusätzlich einzubeziehen, verwenden Sie --all (oder einfach -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 Flag --patch angeben, wird Git nicht alles modifizierte stashen, sondern Sie interaktiv abfragen, welche der Änderungen Sie stashen und welche Sie 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 erstellen

Wenn Sie etwas Arbeit stashen, es eine Weile dort belassen und auf dem Branch fortfahren, von dem Sie die Arbeit gestasht haben, können Sie Probleme beim erneuten Anwenden der Arbeit haben. Wenn der Versuch, eine Datei zu ändern, die Sie seitdem geändert haben, zu einem Merge-Konflikt führt, müssen Sie diesen auflösen. Wenn Sie eine einfachere Möglichkeit wünschen, die gestashten Änderungen erneut zu testen, können Sie git stash branch <neuer Branchname> ausführen. Dies erstellt für Sie einen neuen Branch mit Ihrem ausgewählten Branchnamen, checkt den Commit aus, auf dem Sie sich befanden, als Sie Ihre Arbeit gestasht haben, wendet Ihre Arbeit dort wieder an und verwirft dann den Stash, 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)

Dies ist eine nette Abkürzung, um gestashte Arbeiten einfach wiederherzustellen und an ihnen in einem neuen Branch zu arbeiten.

Ihr Arbeitsverzeichnis aufräumen

Schließlich möchten Sie vielleicht nicht einige Arbeiten oder Dateien in Ihrem Arbeitsverzeichnis stashen, sondern sie einfach loswerden. Dafür ist der Befehl git clean da.

Einige übliche Gründe für das Aufräumen Ihres Arbeitsverzeichnisses könnten sein: das Entfernen von überflüssigem Code, der durch Merges oder externe Tools generiert wurde, oder das Entfernen von Build-Artefakten, um einen sauberen Build durchzuführen.

Sie sollten mit diesem Befehl sehr vorsichtig sein, da er dazu dient, nicht verfolgte Dateien aus Ihrem Arbeitsverzeichnis zu entfernen. Wenn Sie Ihre Meinung ändern, ist es oft nicht möglich, den Inhalt dieser Dateien wiederherzustellen. Eine sicherere Option ist, git stash --all auszuführen, um alles zu entfernen, aber in einem Stash zu speichern.

Angenommen, Sie möchten überflüssige Dateien entfernen oder Ihr Arbeitsverzeichnis aufräumen, können Sie dies mit git clean tun. Um alle nicht verfolgten Dateien in Ihrem Arbeitsverzeichnis zu entfernen, können Sie git clean -f -d ausführen, was sowohl Dateien als auch Unterverzeichnisse entfernt, die dadurch leer werden. Das -f bedeutet "force" (erzwingen) oder "wirklich tun" und ist erforderlich, wenn die Git-Konfigurationsvariable clean.requireForce nicht explizit auf false gesetzt ist.

Wenn Sie jemals sehen möchten, was es tun würde, können Sie den Befehl mit der Option --dry-run (oder -n) ausführen, was bedeutet: "Führen Sie einen Trockenlauf durch und sagen Sie mir, was Sie entfernt *hätten*".

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

Standardmäßig entfernt der Befehl git clean nur nicht verfolgte Dateien, die nicht ignoriert werden. Jede Datei, die einem Muster in Ihrer .gitignore oder anderen Ignore-Dateien entspricht, wird nicht entfernt. Wenn Sie auch diese Dateien entfernen möchten, z. B. um alle .o-Dateien zu entfernen, die aus einem Build generiert wurden, damit Sie einen vollständig sauberen Build durchführen können, können Sie der clean-Anweisung 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 Befehl git clean tun wird, führen Sie ihn immer zuerst mit -n aus, um ihn doppelt zu überprüfen, bevor Sie -n in -f ändern und ihn tatsächlich ausführen. Eine andere Möglichkeit, vorsichtig zu sein, ist, ihn mit dem Flag -i oder "interactive" auszuführen.

Dies führt den clean-Befehl im interaktiven Modus aus.

$ 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 Muster für die Löschung interaktiv festlegen.

Hinweis

Es gibt eine merkwürdige Situation, in der Sie möglicherweise extra stark Git bitten müssen, Ihr Arbeitsverzeichnis aufzuräumen. Wenn Sie sich in einem Arbeitsverzeichnis befinden, unter dem Sie andere Git-Repositories kopiert oder geklont haben (möglicherweise als Submodule), weigert sich selbst git clean -fd, diese Verzeichnisse zu löschen. In solchen Fällen müssen Sie eine zweite -f-Option zur Betonung hinzufügen.