「困った」状況:コードに夢中になっていると、上司から緊急の電話!
午前2時、サーバー画面は真っ赤なエラーで、上司からのSlackメッセージが鳴り止まない。心臓がドキドキする。私はfeature/abcブランチで新機能を開発中で、コードはまだ散らかり放題で未完成、コミットできない状態だ。しかし、本番環境のエラーは待ってくれない。この未完成のコードを迅速に「片付け」、mainブランチに戻ってバグを修正し、これまでの作業を無駄にしないためにはどうすればよいだろうか?
もしあなたがこの状況、または次のような同様の状況に陥ったことがあるなら:
mainから新しいコードをプルする必要があるが、現在のブランチには未コミットの変更がある。- 何かを確認するために別のブランチに切り替え、後で作業に戻りたい。
- 新しいアイデアを試しているが、その実験的な変更をすぐにコミットしたくない。
これこそ、未完成の変更を効率的に管理するための解決策が必要な時です。あなたの作業スペース (working directory) は、修正されたファイル (modified) や新しく追加されたファイル (staged) で「汚れて」おり、それらを一時的に「隠す」方法が必要なのです。
緊急の「応急処置」方法の比較
最も効果的な解決策にたどり着く前に、多くの開発者がよく採用するいくつかの緊急的な解決策を見てみましょう。思わず笑ってしまうような状況もあるはずです:
アプローチ1:とりあえずコミットする (WIPコミット)
方法: 手早くgit add .を実行し、<a href="https://itfromzero.com/ja/git-ja/gpg%e3%82%ad%e3%83%bc%e3%81%a7commit%e3%81%ab%e7%bd%b2%e5%90%8d%e3%81%99%e3%82%8b%ef%bc%9agit%e3%81%ab%e3%81%8a%e3%81%91%e3%82%8b%e6%88%90%e3%82%8a%e3%81%99%e3%81%be%e3%81%97%e3%81%8b%e3%82%89.html">git commit</a> -m "WIP: XXXXX"として作業を完了させます。これにより作業は保存されたとみなし、mainに戻ってバグを修正します。
利点:
- 非常に迅速で、考える時間が不要。
- コードがGitの履歴に保存されるため、失われる心配がない。
欠点::
- コミット履歴を汚す: 意味のない、未完成のコミットを自分のブランチに追加してしまったことになります。後で
git rebase -iを使ってクリーンアップする手間がかかり、慣れていないと混乱を招きやすい。 - 管理が難しい: このようなWIPコミットが多数ある場合、どのコミットがどの機能に属し、どこまで作業が進んだかを追跡するのが非常に困難になる。
- マージ/リベース時のリスク: これらの未完成のコミットは、後でブランチをマージまたはリベースする際に、不必要な競合を引き起こす可能性がある。
アプローチ2:コードを保持するために新しいブランチを作成する
方法: 新しいブランチ (例:git branch fix-prod-issue) を作成し、現在の変更をそのブランチにコミットします。main側での作業が完了した後、このブランチに戻って作業を続行します。
利点::
- 履歴がきれい:
feature/abcブランチが未完成のコミットで汚れることはない。 - 安全: コードは別のブランチに安全に保存される。
欠点::
- 少々面倒: 数分または数時間の一時的な保存のためだけに新しいブランチを作成し、後でマージやリベースを行うのは、少々やりすぎに思える。
- 多数のブランチ管理: この方法を頻繁に行うと、後でクリーンアップする必要がある一時的なブランチが多数発生する。
アプローチ3:コードを別の場所にコピー&ペーストする (まさか!)
方法: ファイルエクスプローラー/Finderを開き、プロジェクトフォルダ全体または修正したファイルをデスクトップや別のドライブ上の一時フォルダにコピーします。その後、git reset --hardを使用してクリーンな状態に戻します。
利点::
- 処理する必要があるファイルが数個しかない場合、非常に迅速。
- 複雑なGitコマンドを覚える必要がない。
欠点::
- コード紛失のリスクが極めて高い: あなたは運命に賭けています!ファイルのコピーを忘れた?ドライブをフォーマットした?誤って削除した?台無しです!
- バージョン管理なし: これはGitではありません!すべての変更はばらばらのコピーであり、バージョンを追跡したり復元したりすることはできません。
- 大規模プロジェクトでの大惨事: 数GBのプロジェクト (例えば、巨大な
node_modulesディレクトリを持つフロントエンドプロジェクト) をコピーするのは拷問であり、後で古いプロジェクトにコピーし直す際に誤って上書きしたり、ファイルが不足したりするリスクが非常に高い。
私自身、誤ってブランチをforce pushして重要なコードを失ったことがあります。それ以来、git push --forceには常に慎重になりました。その経験から、コードベースをクリーンに保つことと、git stashのように未完成の作業に対して安全なバックアップ手段を持つことの重要性を痛感しました。このツールは、性急にコミットするのを避け、不必要なリスクを軽減するのに役立ちます。
アプローチ4:Git stash – あなたが必要とするツール
これこそが、Gitがこの問題を正確に解決するために提供する機能です。作業途中のものを一時的に保存し、作業スペースをクリーンにし、コミットすることなく簡単にコンテキストを切り替えるのに役立ちます。
Git stashの利点と欠点の分析
Git stashの利点
- 作業スペースをクリーンに保つ: 未コミットの変更を即座に「隠し」、ブランチを最新のHEADコミットの状態に戻します。
- 容易なコンテキスト切り替え: 別のブランチに素早く切り替えたり、新しいコードをプルしたり、バグを修正したりすることができ、未完成の作業に影響を与える心配がありません。
- コミット履歴を汚さない: 変更はGitの「引き出し」に個別に保存され、ブランチのコミット履歴には表示されません。
- 複数のstashを管理: Gitでは、複数の異なる「引き出し」(stash)を保存でき、それぞれが一度の作業保存を表します。
- 未追跡ファイルも保存 (オプション使用時): デフォルトでは、Git stashは追跡済みファイルのみを保存しますが、
git addされていない新しく作成されたファイルも保存するように要求できます。
Git stashの欠点
- stashを忘れやすい: stashに名前を付けなかったり、頻繁に確認しなかったりすると、何をどこにstashしたかを忘れがちになる。
- 競合が発生する可能性: stashを適用(
stash applyまたはstash pop)する際に、stash時と異なる変更がブランチにある場合、マージ競合(merge conflict)が発生することがある。 - 異なるリポジトリ間では適用できない: stashは現在のリポジトリ内にのみローカルに存在します。リモートにpushしたり、別のリポジトリに移動したりすることはできません。
適切な方法を選択:Git stashはいつ使用すべきか?
上記の分析から、git stashはコミットしたくない作業を一時的に中断する必要がある場合に理想的な解決策であることがわかります。次のような場合にgit stashを使用すべきです:
- 新しいコードをプル/マージする必要がある: 現在のブランチに未コミットの変更があり、
mainから新しいコードをプルする必要がある場合。未コミットの変更がある場合、git pullはエラーを報告します。 - 緊急の作業のために別のブランチに切り替える必要がある: 記事の冒頭で述べた午前2時の本番環境のバグ修正のような状況。
- 元のコードでテストを実行したい: 新しい機能をテスト中で、クリーンなコードバージョンで以前のテストがまだパスするかどうかを確認したい場合。
- アイデアを試す: 小さな変更を試したいが、確信がなく、すぐにコミットしたくない場合。stashしておき、試してみて、問題なければstashを破棄します。
要するに、作業途中の努力を失うことなくクリーンな作業スペースが必要な場合、git stashが適切な選択肢です。
Git stashのAからZまでの実装ガイド
それでは、この便利なツールを習得するためにgit stashコマンドを練習しましょう。
git stash save "message" または git stash: 作業を保存する
これは、現在の作業ディレクトリとステージングエリアにある変更をstashに保存するための最も基本的なコマンドです。後で思い出しやすいようにメッセージを添えることをお勧めします。
# 変更済みおよびステージング済みのすべてのファイルを保存
# 後で思い出しやすいようにメッセージを添えることを推奨
git stash save "機能XYZ作業中、緊急の本番バグ修正が必要"
# メッセージなしのより短い書き方
# GitはHEADコミットに基づいてメッセージを自動生成します
git stash
このコマンドを実行すると、作業ディレクトリはプロジェクトをクローンした直後やコミットをチェックアウトした直後のようにきれいになります。変更はGitによってパッケージ化され、引き出しに保管されています。
git stash list: stashのリストを表示する
何をstashしたかを確認するには、このコマンドで保存したすべてのstashのリストが表示されます。各stashはstash@{n}の形式のインデックスを持ち、nはシーケンス番号(最新のstashが0)です。
git stash list
# 結果は次のようになる場合があります:
# stash@{0}: On feature/abc: 機能XYZ作業中、緊急の本番バグ修正が必要
# stash@{1}: On main: Hotfix: try out new logic for auth
git stash apply [stash@{n}]: stashを再適用し、リストに残す
このコマンドはstashから変更を取得し、現在の作業ディレクトリに適用します。そのstashはstashリストに残ります。
# 最新のstash (stash@{0}) を適用
git stash apply
# 特定のstash (例: stash@{1}) を適用
git stash apply stash@{1}
applyは、stashを適用しようとするが、後で再利用したり、別のブランチに適用したりするためにリストに残しておきたい場合に使用します。
git stash pop [stash@{n}]: stashを再適用し、リストから削除する
applyと同様ですが、popは正常に適用された直後にそのstashをリストから削除します。これは、そのstashがもう必要ないと確信している場合に最も一般的な使用方法です。
# 最新のstashを適用して削除
git stash pop
# 特定のstashを適用して削除
git stash pop stash@{1}
pop時に競合が発生した場合、Gitはそのstashをリストから削除しません。最初に競合を解決し、その後手動でgit stash dropすることができます。
git stash drop [stash@{n}]: 特定のstashを削除する
特定のstashがもう不要だと判断した場合は、このコマンドを使用してリストから削除します。
# 最新のstashを削除
git stash drop
# 特定のstashを削除
git stash drop stash@{1}
git stash clear: すべてのstashをクリアする
このコマンドを使用する際は注意してください!確認なしですべてのstashが削除されます。
git stash clear
もうどのstashも必要ないと確信している場合にのみ使用してください。
git stash show [stash@{n}]: stashの内容を表示する
stashの内容を忘れてしまった場合、showコマンドは元のコミットとの差分(diff)を表示します。
# 最新のstashの内容を表示
git stash show
# 最新のstashの詳細な内容 (完全なdiff) を表示
git stash show -p
# 特定のstashの内容を表示
git stash show stash@{1}
git stash branch <branchname> [stash@{n}]: stashから新しいブランチを作成する
このコマンドは非常に便利です。変更のセットをstashし、それらを独立したブランチとして開発したい場合、このコマンドは新しいブランチを作成し、そこにstashを適用し、その後リストからstashを削除します。
# 最新のstashから「new-feature-from-stash」ブランチを作成
git stash branch new-feature-from-stash
# stash@{1}から「hotfix-from-old-stash」ブランチを作成
git stash branch hotfix-from-old-stash stash@{1}
Git stashを使用する際の重要な注意点
- 競合の解決:
git stash applyまたはgit stash popを実行する際、Gitが変更を自動的にマージできない場合、競合が発生します。通常のマージ/リベース時と同様に競合を解決し、その後git add .とgit commitで完了します。 - 未追跡ファイル: デフォルトでは、
git stashはGitによって追跡されているファイル(tracked files)、変更されたファイル(modified)、またはステージングエリアにあるファイル(staged)のみを保存します。新しく作成され、まだgit addされていないファイルはstashしません。未追跡ファイルもstashするには、git stash -uまたはgit stash --include-untrackedを使用できます。未追跡ファイルとGitが無視するファイル(ignored files、例:.env、node_modules)の両方をstashするには、git stash -aまたはgit stash --allを使用してください。 - stashリストの定期的な管理: stashリストが長くなり、管理が困難になるのを避けるため、不要になったstashは
git stash dropで削除してください。 - stashはローカルのみ: stashはあなたのコンピュータ上にのみ存在し、リモートリポジトリにはプッシュされないことを忘れないでください。したがって、長期的なバックアップソリューションではありません。
Git stashは単なるコマンドではなく、クリーンな開発環境を維持し、緊急事態に常に対応できるようするためのツールです。個人的には、スピードと柔軟性が求められるプロジェクトで作業する際に、格段に自信を持つことができるようになりました。システムが問題に直面しているときに、未完成の変更があなたを遅らせることのないように。git stashをマスターしましょう!

