git stash
git stash
を使用すると、作業コピーに加えた変更を一時的に棚上げし (または stash して)、他の作業をした後で戻って再適用することができます。コード変更が完了しておらず、まだコミットできない状態で素早くコンテキストを切り替えて別の作業を行う場合には stash が便利です。
- git 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
の実行時に渡します。
複数の 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 @@ +
部分的に隠す
単一のファイルや複数のファイル、ファイル内の個別の変更を選んで 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 @@ + Stash this hunk [y,n,q,a,d,/,e,?]? n
? を入力すると、ハンクコマンドの全リストを取得できます。よく使用されるコマンドは次のとおりです。
コマンド | 説明 |
---|---|
/ | 正規表現を使用してハンクを検索 |
? | ヘルプ |
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 する前に、作業ツリーに追跡対象ファイル、追跡対象外ファイル、および無視されたファイルに対する変更が含まれている場合があります。これらの変更の一部は、インデックスにステージングされている場合もあります。
-
git stash
を呼び出すと、DAG の2つの新しいコミットとして、追跡対象ファイルへの変更がすべてエンコードされます。1 つはステージングされていない変更で、もう 1 つはインデックスにステージングされた変更です。特別なrefs/stash
ref が更新され、これらのコミットをポイントします。 -
--include-untracked
オプションを使って追跡対象外ファイルへの変更を追加コミットとしてエンコードすることもできます。 -
--all
オプションでは、無視されたファイルと追跡対象外のファイルへの変更を同じコミットに含めることができます。
git stash pop
を実行すると、上記のコミットの変更が作業コピーとインデックスの更新に使用されます。stash reflog がシャッフルされて、ポップされたコミットは削除されます。ポップされたコミットはすぐには削除されませんが、将来のガベージコレクションの候補になります。