プログラミング
 Computer >> コンピューター >  >> プログラミング >> プログラミング

【Git入門】stashコマンドで変更を一時保存する方法を実例付きで徹底解説

Gitの「スタッシュ(stash)」機能を使うと、リポジトリ内のコード変更を一時的に保存し、後で取り出すことができます。

Gitリポジトリで作業していると、ファイルに加えた変更を、後ほどコミットに含めたいケースがよくあります。そんなときに役立つのが git stash コマンドです。スタッシュを利用すれば、作業中ブランチ上のコード変更を一時退避させ、別の作業に集中できます。本記事では、実例を交えながらGitにおけるスタッシュの基本と git stash コマンドの使い方を解説します。

Gitスタッシュとは?

スタッシュとは、作業ディレクトリとインデックスにある変更を一時的に保存するための機能です。コードを書いている途中で、「変更は残したいけれど、まだリポジトリにはコミットしたくない」という場面は少なくありません。

典型的なシナリオがバグ修正です。ある機能を実装している最中にバグ報告が届いたとしましょう。多くの場合、バグ対応は新機能の開発よりも優先されます。そこで、機能開発のための変更をいったんスタッシュに退避させ、先にバグを修正する、という流れになります。

バグ修正と機能追加のコードが同じコミットに混在してしまうと、コミットが大きく複雑になり、他の開発者がリポジトリの履歴を読み解きにくくなります。そのため、機能関連のコードをコミットする前に、バグ修正だけを独立して行いたいわけです。

つまりスタッシュを使えば、既存の変更を破棄したりコミットしたりすることなく、別の作業に取り掛かることができます。

スタッシュは、ファイルに加えた変更を「退避(スタッシュ)」して後で使えるようにする仕組みです。変更をスタッシュすれば、その間に別の作業を進められ、後から戻ってきて退避しておいた変更をコードに再適用できます。

また、スタッシュを実行すると作業ディレクトリは直近のコミットの状態に戻ります。つまり、クリーンな作業ディレクトリを手に入れられるのです。クリーンな状態であれば、マージコンフリクトを気にせず変更を加えられますし、コミット間での変更の整理についても考える必要がなくなります。

Gitで変更をスタッシュする方法

git stash コマンドは、コードを後のために退避させるために使います。このコマンドを実行すると、現在の作業ディレクトリでまだコミットされていない変更がすべて保存されます。対象となるのは、ステージ済みの変更(git add でステージングエリアに追加したもの)と、未ステージの変更の両方です。

コマンドの構文は以下の通りです。

git stash

ここでは、Webサイトを開発していて、そのコードがGitリポジトリで管理されている状況を想定しましょう。

index.htmlindex.js の2つのファイルに変更を加えたあと、index.html のデザインバグを修正するために、これらの変更を一時退避させたいとします。

まず git status コマンドで現在の変更内容を確認してみましょう。この例では次のような結果が返されます。

On branch master
Your branch is up to date with 'origin/master'.

Changes to be committed:

        new file:   index.js

Changes not staged for commit:

        modified:   index.html

新しいファイル index.js を作成し、既存の index.html を編集したことがわかります。これらの変更を退避させて、コードベースの別の部分に取り組むことにしましょう。git stash コマンドを実行します。

git stash

実行結果は次の通りです。

Saved working directory and index state WIP on master: 3b16026 feat: Launch new homepage

git stash コマンドによって、リポジトリへの変更が保存されました。この状態でもう一度 git status を実行すると、コミットすべきものが何もないことが確認できます。

On branch master
Your branch is up to date with 'origin/master'.

nothing to commit, working tree clean

コードがスタッシュされると、リポジトリは加えた変更を除いた直前のコミットの状態に戻ります。あなたが加えた変更は後のために保存されており、準備が整ったタイミングで git stash pop コマンドを使ってコードベースに再適用できます。

未追跡ファイルをスタッシュする

git stash コマンドがデフォルトで退避するのは、すでにGitリポジトリで追跡されているファイルに対するステージ済み・未ステージの変更のみです。未追跡(untracked)の変更は、デフォルトでは含まれません。

ステージ済みの変更とは、ステージングエリアに追加された変更のことです。スタッシュされる未ステージの変更は、Gitが追跡しているファイルに対するものに限られます。Gitに追跡されていない新しいファイルを変更しても、それはスタッシュに含まれません。同様に、無視(ignore)指定されたファイルもスタッシュの対象外です。

これらは git stash のデフォルトの挙動です。もし未追跡のファイル(まだステージしていない新規ファイルなど)も一緒に退避したい場合は、-u オプションを使用します。

git stash -u

さらに、無視指定されたファイルまでスタッシュに含めたい場合は、-a オプションを使います。

git stash -a

-a オプションを付けると、無視指定されたファイルへの変更もスタッシュされます。これは、リポジトリ内の .gitignore ファイルに定義されたファイル群(存在する場合)を指します。

git stash popでスタッシュした変更を適用する

git stash pop コマンドは、スタッシュしておいた変更をリポジトリに再適用するために使います。先ほどの例で退避した変更を適用したくなった場合、次のコマンドを実行します。

git stash pop

実行結果は以下の通りです。

On branch master
Your branch is up to date with 'origin/master'.

Changes to be committed:

        new file:   index.js

Changes not staged for commit:

        modified:   index.html

Dropped refs/stash@{0} (48afd55381cf43f2332f771349c7233fb99f80a6)

git stash pop を実行すると、スタッシュ内の変更がローカルの作業コピーに適用されます。popコマンドはスタッシュ内のコードをリポジトリに反映したうえで、そのスタッシュを削除します。

なお、git stash pop は適用した変更の一覧も表示してくれるため、スタッシュの中身が何だったのかを確認できます。

一方、変更を適用しつつスタッシュ自体は保持しておきたい場合は、git stash apply コマンドを使います。

git stash apply

実行結果:

On branch master
Your branch is up to date with 'origin/master'.

Changes to be committed:

        new file:   index.js

Changes not staged for commit:

        modified:   index.html

複数ブランチへの適用

同じスタッシュの変更を、コードベース内の複数のブランチに適用したい場合にも、スタッシュコマンドは便利です。たとえば、あるブランチにスタッシュを適用したあと、別のブランチに移動して同じスタッシュを再度適用する、といった使い方が可能です。

複数のスタッシュを管理する

Gitでは複数のスタッシュを作成できます。あるファイルへの変更を1つ目のスタッシュに退避させ、別のファイルへの変更を別のスタッシュに保存する、ということも可能です。

スタッシュの一覧を確認する

リポジトリ内のスタッシュ一覧を取得するには、次のコマンドを実行します。

git stash list

作成済みのすべてのスタッシュを確認したい場合は、上記のコマンドを実行します。実行結果の例:

stash@{0}: WIP on master: 3b16026 feat: Launch new homepage
stash@{1}: WIP on master: 3b16026 feat: Launch new homepage

git stash list コマンドにより、スタッシュの一覧が返されました。

ただし、これらのスタッシュにはデフォルトのコミットメッセージしか付いていません。どのスタッシュにどの変更が含まれているのかを把握するのが難しい状態です。これは、スタッシュ作成時に説明文を指定していなかったためです。

そこで、新しいスタッシュに説明文を付けてみましょう。次のコマンドを使用します。

git stash save "add new change to website"

実行結果:

Saved working directory and index state On master: add new change to website

直近のコミットに関連付けられたデフォルトメッセージではなく、スタッシュ固有のメッセージが割り当てられました。

特定のスタッシュを適用する

git stash pop コマンドは、常に最新のスタッシュ(タグ stash@{0} のもの)をリポジトリに適用します。しかし、複数のスタッシュを扱っている場合、特定のスタッシュだけを適用したいこともあるでしょう。

その場合は、適用したいスタッシュに紐づく一意のIDを指定します。たとえば stash@{1} に保存されたスタッシュを適用したい場合、次のように実行します。

git stash pop stash@{2}

このコマンドは stash@{2} に保存された変更をリポジトリに適用し、そのスタッシュを削除します。

スタッシュからブランチを作成する

スタッシュは、別の変更作業を行っている間にコードベースへの変更を手軽に保管しておく手段です。

しかし、スタッシュとして保持し続けるのではなく、そのコードを独立したブランチに移したい場合もあります。

こうした状況が生じるのは、たとえばマージ時にコンフリクトが発生したときです。また、大規模な変更を行っている場合にも、コードを別のブランチへ移動したくなることがあります。そんなときに活躍するのが git stash branch コマンドです。

git stash branch コマンドを使うと、新しいブランチを作成し、そこにスタッシュ内の変更を適用できます。構文は以下の通りです。

git stash branch <new-branch> <stash-id>

「new-branch」パラメータには作成するブランチ名を、「stash-id」パラメータには新しいブランチに適用したいスタッシュのIDを指定します。

たとえば、stash@{2} の変更を「update-site」という新しいブランチに適用したい場合は、次のコマンドを実行します。

git stash branch update-site stash@{2}

実行結果:

Switched to a new branch 'update-site'
On branch update-site
Changes to be committed:

        new file:   index.js

Changes not staged for commit:

        modified:   index.html

Dropped stash@{2} (9a15b9cd20f8988937134d1267fafbea4c6a8647)

まず git stash branch コマンドが、退避していた変更のための新しいブランチを作成します。続いて、リポジトリへの変更がその新しいブランチに適用され、カレントブランチがそのブランチに切り替わります。最後に、退避していたコードが専用のブランチに保存されたため、元のスタッシュは削除されます。

Gitスタッシュを削除する

スタッシュ内のコードを使い終えたら、git stash drop コマンドでそのスタッシュを削除できます。構文は以下の通りです。

git stash drop <stash>

たとえば、IDが stash@{2} のスタッシュを削除したい場合は、次のように実行します。

git stash drop stash@{2}

実行結果:

Dropped stash@{2} (82079798c950b053fac0efb7b1d5693864dc96e7)

また、git stash clear コマンドを使うと、リポジトリに関連付けられたすべてのスタッシュを一括で削除できます。

git stash clear

このコマンドを実行すると、リポジトリ内の全スタッシュが削除されます。

特定のファイルだけをスタッシュする

git stash コマンドは、デフォルトでは現在の作業ディレクトリ内の追跡対象ファイルすべてを退避します。しかし、場合によっては、特定のファイル(または特定のファイル群)だけをスタッシュしたいこともあるでしょう。

そのような場合は、git stash push コマンドを使用します。構文は以下の通りです。

git stash push -m "<message>" <file>

「message」パラメータにはスタッシュに関連付けるメッセージを、「file」パラメータにはスタッシュしたいファイル名を指定します。

たとえば、index.html への変更だけをスタッシュに含めたい場合は、次のコマンドを実行します。

git stash push -m "feat: changed index.html file" index.html

実行結果:

Saved working directory and index state On master: feat: changed index.html file

このコマンドにより、index.html ファイルだけが「feat: changed index.html file」というメッセージ付きでスタッシュされました。それ以外の変更はスタッシュに含まれていません。

スタッシュ間の差分を確認する

スタッシュを活用していると、退避しておいたコードに戻るまでに、複数のコミットにわたってさまざまな変更を加えることがあります。

たとえば、コードをスタッシュしてバグ修正に取り掛かった場合、退避したコードに戻る前に、バグ修正のためのいくつかのコミットをプッシュすることになるでしょう。そのようなとき、スタッシュと最新のコミットとの差分の概要を確認できれば、スタッシュ以降にどんな変更を加えたのかがひと目でわかります。

スタッシュと最新のコミットの差分を表示するには、git stash show コマンドを使用します。構文は以下の通りです。

git stash show <stash-id>

「stash-id」パラメータには、ブランチの最新コミットと比較したいスタッシュのIDを指定します。たとえば stash@{1} の内容と現在のコードの状態を比較したい場合は、次のように実行します。

git stash show stash@{1}

実行結果:

 index.html | 2 +-
 index.js   | 0
 2 files changed, 1 insertion(+), 1 deletion(-)

この出力から、コードに2つの変更(1行の追加と1行の削除)を加えたことがわかります。ファイル間の詳細な差分を確認したい場合は、-p オプションを使用します。

git stash show -p stash@{1}

このコマンドを実行すると、次のような出力が得られます。

diff --git a/index.html b/index.html
index 4dd1ef7..e859c68 100644
--- a/index.html
+++ b/index.html
@@ -1 +1 @@
-</div>
+</html>
diff --git a/index.js b/index.js
new file mode 100644
index 0000000..e69de29

この出力により、スタッシュ内のコードとブランチの最新コミットとの詳細な比較が可能になります。上記の例では、index.html から </div> の行を削除し(– 記号で示される)、代わりに </html> の行を追加した(+ 記号で示される)ことが読み取れます。

まとめ

git stash コマンドは、コードベースへの変更を一時的に保存するための機能です。準備が整ったタイミングで、退避しておいた変更をいつでもコードベースに再適用できます。

本記事では、Gitにおけるスタッシュの基本と、git stash コマンドを使ったスタッシング機能の活用方法を解説しました。これであなたも、Gitのプロのようにコードをスタッシュできるはずです!

  1. Gitリセットでコミットを取り消す!基本から実践テクニックまで徹底解説

    個人でもチームでも、プロジェクトを進めていると「コミットを取り消したい」という場面に遭遇することがあります。そんなときに頼りになるのが git reset コマンドです。まさに命綱とも言えるツールの一つでしょう。 Gitの追跡メカニズム git reset を理解する前に、まずGitの内部構造について押さえておきましょう。Gitは、ノードとポインタで構成されるツリー構造によってファイルを管理・追跡しています。 ローカルのGitリポジトリには、基本的に次の3つの「ツリー」が存在します。 作業ディレクトリ(Working Directory):ワーキングツリーとも呼ばれ、ローカルディレク

  2. 【初心者向け】git pushコマンドの使い方を徹底解説!リモートリポジトリへのプッシュ方法

    git pushコマンドは、ローカルリポジトリの内容をリモートリポジトリへアップロードするためのコマンドです。プッシュは、変更内容をリモートリポジトリに反映させるための仕組みであり、一度プッシュを行えば、プロジェクトに参加しているすべてのメンバーがその変更をダウンロードできるようになります。 コードをリモートリポジトリにプッシュすることは、Gitリポジトリに対して行った変更を「保存」する最終段階にあたります。 プッシュの処理では、コードがローカルリポジトリ(自分のPC)から、ローカルのコードと紐づけられたリモートリポジトリへと転送されます。これにより、コードベースへの変更内容をプロジェクト