「別のブランチを作ったのに、作業中の変更がそのまま見える」「未コミット変更があると、ブランチを切り替えられないこともある」。この2つは、どちらもgit switchで起こり得ます。
未コミットの変更は、ブランチを作成しただけでそのブランチのコミットになるわけではありません。まず、現在の作業ファイル・ステージング領域・コミット済みの内容を区別すると、状況を確認しやすくなります。
この記事では、同じ作業ディレクトリでブランチを切り替え、変更が持ち越される場合と、上書きを避けるために切り替えが拒否される場合を再現します。そのあと、stashで退避し、ステージング状態も含めて戻します。
実案件の体験談ではなく、この記事用に作成・実行した最小例です。検証環境はmacOS、Git 2.53.0、bashです。
内容が異なる2つのブランチを用意する
空の検証用ディレクトリで実行してください。以下のdemoは検証専用です。
mkdir demo
cd demo
git init -b main
git config user.name "Example User"
git config user.email "example@example.com"
printf 'base\n' > app.txt
printf 'ignored.log\n' > .gitignore
git add app.txt .gitignore
git commit -m "Initial commit"
git switch -c other
printf 'other\n' > app.txt
git add app.txt
git commit -m "Change app on other"
git switch main
mainのapp.txtはbase、otherではotherという内容になっています。ユーザー名とメールは、この検証用リポジトリだけに設定しています。
ステージング済み・未ステージング・未追跡の変更を作る
printf 'staged\n' > app.txt
git add app.txt
printf 'unstaged\n' >> app.txt
printf 'draft\n' > draft.txt
printf 'ignored\n' > ignored.log
git status --short
status --shortの出力:
MM app.txt
?? draft.txt
app.txtは、stagedの行までをgit addしたあと、さらにunstagedを追加しました。MMの左側はステージング領域の変更、右側は作業ファイルの変更を示しています。
draft.txtは未追跡です。ignored.logは.gitignoreに一致するため、この出力には現れません。
新しいブランチを作っても変更は残る
git switch -c feature
git branch --show-current
git status --short
git show HEAD:app.txt
git show :app.txt
cat app.txt
ブランチ名はfeatureへ変わりましたが、status --shortは同じでした。
3つの内容を比較すると、次の状態でした。
| 確認方法 | app.txtの内容 |
|---|---|
git show HEAD:app.txt:現在のコミット |
base |
git show :app.txt:ステージング領域 |
staged |
cat app.txt:作業ファイル |
stagedとunstaged
|
featureはmainと同じコミットから作られています。変更が見えていても、まだコミット済みの内容はbaseです。
ブランチ作成によって作業ファイルが空になるわけではなく、今回の変更はその作業ディレクトリに残ったまま、新しいブランチへ切り替わりました。
上書きが必要な切り替えは拒否される
同じ状態で、内容が異なるotherへ切り替えます。
git switch other
検証では、ローカル変更が上書きされる旨のエラーで非ゼロ終了しました。ブランチはfeatureのままで、ステージング状態と作業ファイルも保持されていました。
Git公式のswitch資料では、作業ディレクトリやステージング領域がcleanでなくても切り替えられる一方、ローカル変更を失う操作になる場合は中止することが説明されています。
「未コミット変更があれば必ず切り替えできない」「切り替えできたので変更がコミットされた」と判断せず、ブランチ名とstatusを確認します。
未追跡ファイルも含めてstashへ退避する
git stash push -u -m "Before switching branches"
git status --short
cat app.txt
cat ignored.log
git switch other
cat app.txt
退避直後のstatus --shortは出力なしで、app.txtはbaseに戻りました。draft.txtは作業ディレクトリからなくなり、stashへ保存されていました。
一方、ignored.logは残っています。-uは未追跡ファイルも対象にしますが、今回のignore対象ファイルは含まれませんでした。ignore対象も含める-aとは対象が異なります。stashの公式資料にそれぞれの指定が説明されています。
退避後はotherへ切り替えられ、app.txtはotherになりました。
ステージング状態も含めて戻す
元のfeatureに戻ってから、退避した変更を適用します。
git switch feature
git stash apply --index
git status --short
git show :app.txt
cat app.txt
cat draft.txt
git stash list
検証では次が復元されました。
-
status --shortはMM app.txtと?? draft.txt。 - ステージング領域は
staged。 - 作業ファイルは
stagedとunstaged。 - 未追跡の
draft.txtはdraft。
--indexは、作業ファイルだけでなくステージング状態も復元しようとする指定です。今回の競合のない例では、git add済みの範囲とその後の変更を分けた状態が戻りました。
ここでは、適用後もstashを残して確認できるapplyを使用しました。stash listにも退避した1件が残っていました。
今回は元と同じコミットへ戻して適用した検証です。別の変更が加わったブランチへの適用や、競合した場合に同じ状態へ戻せることまで保証するものではありません。
確認した結果
| 操作 | 結果 |
|---|---|
switch -c feature |
ブランチを作成し、未コミット変更を保持 |
変更を抱えたままswitch other
|
拒否され、ブランチ・ファイル・ステージング状態を保持 |
stash push -u |
追跡済み変更と未追跡ファイルを退避し、ignore対象は残る |
退避後にswitch other
|
成功し、otherのコミットの内容を表示 |
stash apply --index |
今回はステージング状態も復元し、stash自体は保持 |
未コミット変更を持ち込まずに別ブランチへ切り替えたいときは、まずstatusで対象を確認し、必要な変更をstashへ退避する方法が使えます。戻すときにステージング済みの範囲も再現したいなら、apply --indexの結果まで確認します。
参照資料
確認日:2026年9月28日。