git stash を使用すると、作業コピーに加えた変更を一時的に棚上げし (または stash して)、他の作業をした後で戻って再適用することができます。コード変更が完了しておらず、まだコミットできない状態で素早くコンテキストを切り替えて別の作業を行う場合には stash が便利です。

作業を隠す

git stash コマンドは、コミットされていない変更 (ステージングされたものとされていないもの) を取り出し、後で使用するために保存した後、作業コピーから打ち消します。例:

$ git status
On branch master
Changes to be committed:
new file: style.css
Changes not staged for commit:
modified: index.html
$ git stash
Saved working directory and index state WIP on master: 5002d47 our new homepage
HEAD is now at 5002d47 our new homepage
$ git status
On branch master
nothing to commit, working tree clean

この時点で、自由に変更を加えたり、新しいコミットを作成したり、ブランチを切り替えたり、その他の Git 操作を実行することができます。準備が整ったら、stash しておいた変更に戻って、再適用します。

stash は作業しているローカルの Git リポジトリにのみ適用されます。プッシュ時にサーバーには転送されません。

隠していた変更を再適用する

以前に stash した変更は、git stash pop を使用して再適用できます。

$ git status
On branch master
nothing to commit, working tree clean
$ git stash pop
On branch master
Changes to be committed:
new file: style.css
Changes not staged for commit:
modified: index.html
Dropped refs/stash@{0} (32b3aa1d185dfe6d57b3c3cc3b32cbf3e380cc6a)

stash をポップすると、stash から変更が削除され、作業コピーに再適用されます。

または、作業コピーに変更を再適用したうえでgit stash apply を使用してその変更を stash に維持することも可能です。

$ git stash apply
On branch master
Changes to be committed:
new file: style.css
Changes not staged for commit:
modified: index.html

これは、stash した変更を複数のブランチに適用するのに便利です。

stash の基本を理解したところで、git stash について注意することが 1 つあります。既定では、Git は追跡対象外のファイルや無視されたファイルに対する変更を stash しません

未追跡または無視されたファイルを隠す

既定では、git stash を実行すると以下の変更が stash されます。

  • インデックスに追加された変更 (ステージングされた変更)
  • Git によって現在追跡されているファイルに対する変更 (ステージングされていない変更)

ただし、以下は stash されません

  • まだステージングされていない作業コピー内の新しいファイル
  • 無視されたファイル

したがって、上記の例に 3 番目のファイルを追加しても、それをステージングしないと (つまり、git add を実行しない場合)、git stash はそのファイルを stash しません。

$ script.js
$ git status
On branch master
Changes to be committed:
new file: style.css
Changes not staged for commit:
modified: index.html
Untracked files:
script.js
$ git stash
Saved working directory and index state WIP on master: 5002d47 our new homepage
HEAD is now at 5002d47 our new homepage
$ git status
On branch master
Untracked files:
script.js

-u オプション (または --include-untracked) を追加すると、追跡対象外のファイルも git stash で stash できます。

$ git status
On branch master
Changes to be committed:
new file: style.css
Changes not staged for commit:
modified: index.html
Untracked files:
script.js
$ git stash -u
Saved working directory and index state WIP on master: 5002d47 our new homepage
HEAD is now at 5002d47 our new homepage
$ git status
On branch master
nothing to commit, working tree clean

無視されたファイルへの変更を含めることもできます。それには、-a オプション (または --all) を git stash の実行時に渡します。

Git Stash のオプション

複数の stash の管理

使用できる stash は 1 つに限定されているわけではありません。git stash を数回実行して複数の stash を作成し、git stash list を使用して作成した stash を表示できます。stash は既定で、stash の作成元のブランチやコミット上にある "WIP" (進行中の作業) として識別されます。しばらくすると、各 stash に何が含まれているか覚えておくことが難しくなる場合があります。

$ git stash list
stash@{0}: WIP on master: 5002d47 our new homepage
stash@{1}: WIP on master: 5002d47 our new homepage
stash@{2}: WIP on master: 5002d47 our new homepage

内容をわかりやすくするため、git stash save "message" を使用して stash に説明文を付けることをお勧めします。

$ git stash save "add style to our site"
Saved working directory and index state On master: add style to our site
HEAD is now at 5002d47 our new homepage
$ git stash list
stash@{0}: On master: add style to our site
stash@{1}: WIP on master: 5002d47 our new homepage
stash@{2}: WIP on master: 5002d47 our new homepage

既定では、git stash pop は直近に作成された stash を再適用します: stash@{0}

最後の引数として識別子を渡すことで、再適用する stash を選択できます。たとえば、次のようになります。

$ git stash pop stash@{2}

stash diffs の表示

git stash show を使用すると、stash の要約を表示できます。

$ git stash show
index.html | 1 +
style.css | 3 +++
2 files changed, 4 insertions(+)

stash のすべての差分を表示するには -p オプション (または --patch) を渡します。

$ git stash show -p
diff --git a/style.css b/style.css
new file mode 100644
index 0000000..d92368b
--- /dev/null
+++ b/style.css
@@ -0,0 +1,3 @@
+* {
+ text-decoration: blink;
+}
diff --git a/index.html b/index.html
index 9daeafb..ebdcbd2 100644
--- a/index.html
+++ b/index.html
@@ -1 +1,2 @@
+<link rel="stylesheet" href="style.css"/>

部分的に隠す

単一のファイルや複数のファイル、ファイル内の個別の変更を選んで stash することもできます。-p オプション (または --patch) を git stash に渡すと、作業コピーで変更された各「ハンク」で処理を反復して、その変更を stash するかを確認するメッセージが表示されます:

$ git stash -p
diff --git a/style.css b/style.css
new file mode 100644
index 0000000..d92368b
--- /dev/null
+++ b/style.css
@@ -0,0 +1,3 @@
+* {
+ text-decoration: blink;
+}
Stash this hunk [y,n,q,a,d,/,e,?]? y
diff --git a/index.html b/index.html
index 9daeafb..ebdcbd2 100644
--- a/index.html
+++ b/index.html
@@ -1 +1,2 @@
+<link rel="stylesheet" href="style.css"/>
Stash this hunk [y,n,q,a,d,/,e,?]? n
Git Stash -p

? を入力すると、ハンクコマンドの全リストを取得できます。よく使用されるコマンドは次のとおりです。

コマンド 説明
/ 正規表現を使用してハンクを検索
? ヘルプ
n このハンクを stash しない
q 中断 (選択したすべてのハンクを stash する)
s このハンクを小さなハンクに分割
y このハンクを stash する

明示的な「中止」コマンドはありませんが、キーボードで CTRL+C (SIGINT) を押すと stash プロセスを中止します。

stash からのブランチの作成

ブランチでの変更が stash の変更と異なる場合、stash の変更を適用しようとすると競合が発生する可能性があります。代わりに、git stash branch を使用して新しいブランチを作成し、そこに stash しておいた変更を適用することができます。

$ git stash branch add-stylesheet stash@{1}
Switched to a new branch 'add-stylesheet'
On branch add-stylesheet
Changes to be committed:
new file: style.css
Changes not staged for commit:
modified: index.html
Dropped refs/stash@{1} (32b3aa1d185dfe6d57b3c3cc3b32cbf3e380cc6a)

これは、stash を作成したコミットをベースにした新しいブランチをチェックアウトし、stash しておいた変更をそのブランチに適用します。

stash のクリーンアップ

特定の stash が必要なくなった場合は、git stash drop で削除できます。

$ git stash drop stash@{1}
Dropped stash@{1} (17e2697fd8251df6163117cb3d58c1f62a5e7cdb)

または、次の方法ですべての stash を削除できます。

$ git stash clear

git stash の使用方法

git stash の使用方法のみ知りたい場合はここで読むのをやめてもかまいませんが、Git (と git stash) の仕組みに関心がある場合は、このまま読み進んでください。

stash はコミットオブジェクトとしてご使用のリポジトリ内に実際にエンコードされます。.git/refs/stash にある特別な ref は最近作成された最新の stash をポイントし、以前に作成された stash は stash ref の reflog により参照されます。このため、stash@{n}: で stash を参照すると、実際には stash ref の nth reflog エントリーを参照していることになります。stash は単なるコミットにすぎないため、git log で調べることができます。

$ git log --oneline --graph stash@{0}
*-. 953ddde WIP on master: 5002d47 our new homepage
|\ \
| | * 24b35a1 untracked files on master: 5002d47 our new homepage
| * 7023dd4 index on master: 5002d47 our new homepage
|/
* 5002d47 our new homepage

単一の git stash 操作では、stash の内容に応じて 2 つまたは 3 つのコミットが新たに作成されます。上の図のコミットに関する説明は以下のとおりです。

  • stash@{0}: git stash 実行時に作業コピー内の追跡対象ファイルを保存するための新しいコミット
  • stash@{0} の最初の親: git stash 実行時に HEAD にあった既存のコミット
  • stash@{0} の 2 番目の親: git stash 実行時にインデックスを表す新しいコミット
  • stash@{0} の 3 番目の親: git stash 実行時に作業コピー内にあった追跡対象外のファイルを表す新しいコミット。3 番目の親は次の場合のみ作成される:
    • 作業コピーが実際に追跡対象外のファイルを含んでいた
    • git stash 呼び出し時に --include-untracked または --all オプションを指定した

git stash でワークツリーとインデックスをコミットとしてエンコードする仕組み:

  • stash する前に、作業ツリーに追跡対象ファイル、追跡対象外ファイル、および無視されたファイルに対する変更が含まれている場合があります。これらの変更の一部は、インデックスにステージングされている場合もあります。

    Before stashing
  • git stash を呼び出すと、DAG の2つの新しいコミットとして、追跡対象ファイルへの変更がすべてエンコードされます。1 つはステージングされていない変更で、もう 1 つはインデックスにステージングされた変更です。特別な refs/stash ref が更新され、これらのコミットをポイントします。

    Git stash
  • --include-untracked オプションを使って追跡対象外ファイルへの変更を追加コミットとしてエンコードすることもできます。

    Git stash --include-untracked
  • --all オプションでは、無視されたファイルと追跡対象外のファイルへの変更を同じコミットに含めることができます。

    Git Stash --all

git stash pop を実行すると、上記のコミットの変更が作業コピーとインデックスの更新に使用されます。stash reflog がシャッフルされて、ポップされたコミットは削除されます。ポップされたコミットはすぐには削除されませんが、将来のガベージコレクションの候補になります。

Git を学ぶ準備はできていますか?

この対話式のチュートリアルをお試しください。

今すぐ始める