push前なら、新しいブランチへ移ってから、mainだけを送信先の位置へ戻す
別のブランチで作るはずだった変更を、mainのままコミットしてしまった場合の直し方です。まだpushしていなければ、次の2行で直せます。1行目のgit switch -cで、今のコミットの位置に新しいブランチfeature/loginを作って移ります。2行目のgit branch -fで、mainだけを送信先にpush済みの位置(origin/main)へ戻します。
間違えて入れたコミットは消えずにfeature/loginに残り、コミットIDも変わりません。まだコミットしていない変更や未追跡ファイルも、作業フォルダーにそのまま残ります。feature/loginは例の名前で、実際には作業内容に合う名前にします。
mainがpush済みの場合は、この方法を使いません(後の節)。main以外のブランチに間違えてコミットした場合も、mainとorigin/mainをそのブランチ名に置き換えれば同じ考え方です。
git switch -c feature/login
git branch -f main origin/main移す前に、pushしていないコミットを確かめる
git log --oneline origin/main..mainは、mainにあってorigin/mainにないコミット、つまりまだpushしていないコミットだけを新しい順に表示します。ここに出た行が移す対象です。この中にmainに残すべきコミットがある場合は、後の手順でorigin/mainの代わりに、mainに残す最後のコミットのIDを指定します(後の節「送信先がない場合」と同じ指定方法)。git statusの2行目には、Your branch is ahead of 'origin/main' by 2 commits.のように件数が出ます。
何も表示されず、git statusがYour branch is up to date with 'origin/main'.なら、間違えたコミットは既にpush済みです。この場合、git branch -f main origin/mainを実行してもmainは動かず、エラーも出ないまま、コミットはmainに残ります。コミットIDやHEAD~2で戻すと、送信先より古い位置になってpushが拒否されます。後の節「mainをpush済みなら、戻さずにrevertで打ち消す」へ進んでください。
origin/mainは、最後にpushまたはfetchしたときに記録された送信先の位置です。fetchしていなくても、この手順には使えます。fatal: ambiguous argument 'origin/main..main'と表示される場合は、送信先が設定されていないか、mainをまだ一度もpushしていません。その場合は後の節「送信先がない場合」を参照してください。
git status
git log --oneline origin/main..main練習用フォルダーを作り、そこへ移動する
以下はGitが使えるBash(WSLやGit Bashなど)用です。PowerShellへそのまま貼る例ではありません。実務のリポジトリではなく、まだ存在しないgit-wrong-branch-practiceというフォルダーで試します。次の2行は1行ずつ実行し、mkdirとcdの両方が成功したことを確かめてから次へ進みます。エラーが出たらそこで止めてください。既に同名フォルダーがある場合は、別の新しい名前を選びます。
mkdir git-wrong-branch-practice
cd git-wrong-branch-practice送信先と、mainに間違えて入れた2件を用意する
同じフォルダー内に送信先用のリポジトリremote.gitを作り、作業用のworkへ移動します。ネットワーク上のサービスには接続しません。名前とメールアドレスは、この練習用リポジトリ内だけに設定する例示値です。git init -bはGit 2.28以降で使えます。git init --bareの後に、初期ブランチ名についてのhint:が何行か表示されることがありますが、この練習には影響しません。
Add READMEをmainに、Add dev notesをdevelopに入れてpushした後、mainへ戻ってAdd login form・Add login checkの2件をコミットします。この2件が、本来は別のブランチで作るはずだったコミットという想定です。最後にlogin.txtへ1行足し、コミットしていない変更も残します。
最後のgit log --oneline origin/main..mainはAdd login check・Add login formの2件を表示し、git status --shortは M login.txt(右の列のMは未ステージの変更)です。左端の7桁ほどの英数字がコミットIDで、環境ごとに値が変わります。
git init --bare remote.git
git init -b main work
cd work
git config user.name 'Practice'
git config user.email 'practice@example.invalid'
git remote add origin ../remote.git
printf '# app\n' > README.md
git add -- README.md
git commit -m 'Add README'
git push -u origin main
git switch -c develop
printf 'dev\n' > dev.txt
git add -- dev.txt
git commit -m 'Add dev notes'
git push -u origin develop
git switch main
printf 'login form\n' > login.txt
git add -- login.txt
git commit -m 'Add login form'
printf 'login check\n' >> login.txt
git add -- login.txt
git commit -m 'Add login check'
printf 'wip\n' >> login.txt
git log --oneline origin/main..main
git status --short新しいブランチを作って移る
git switch -c feature/loginは、今のコミット(Add login check)を先頭にしたfeature/loginを作り、そこへ移ります。Switched to a new branch 'feature/login'と表示され、git log --oneline -3はAdd login check、Add login form、Add READMEの3件です。
コミットしていない変更は付いてきます。git status --shortは移る前と同じ M login.txtで、login.txtの中身も変わりません。新しいブランチは移る前と同じコミットから始まるため、変更がぶつかって止まることはありません。
git switch -c feature/login
git log --oneline -3
git status --shortmainだけを、送信先にpush済みの位置へ戻す
git branch -f main origin/mainは、mainが指すコミットをorigin/mainと同じ位置へ付け替えます。今いるのはfeature/loginなので、作業フォルダーのファイルとステージは変わりません。実行するとbranch 'main' set up to track 'origin/main'.と表示されます。mainの送信先の設定が、元と同じorigin/mainに設定し直されたという意味です。
git log --oneline mainはAdd READMEの1件になり、git branch -vvのmainの行に[origin/main]が表示されます。ahead(未送信の件数)は付きません。feature/loginの行は、Add login checkのままです。
mainにいるままgit branch -f main origin/mainを実行すると、fatal: cannot force update the branch 'main' used by worktree at(フォルダーのパス)と表示されて何も変わりません。先にgit switch -cで移ってから実行します。
git branch -f main origin/main
git log --oneline main
git branch -vv移したブランチで作業を続け、pushする
feature/loginで残りの変更をコミットし、git push -u origin feature/loginで送信先に新しいブランチとして送ります。* [new branch] feature/login -> feature/loginと表示されます。mainは送信先と同じ状態のままなので、mainを強制pushする必要はありません。
この後、feature/loginからmainへ取り込む方法は、チームのやり方(プルリクエストなど)に従います。
git add -- login.txt
git commit -m 'Add login wip'
git push -u origin feature/login
git status --short移し先のブランチが既にある場合は、cherry-pickしてからmainを戻す
既にあるdevelopへ入れるはずだったコミットをmainに入れてしまった場合は、developへ移ってgit cherry-pickで同じ変更を取り込み、その後でmainを戻します。順番が大事で、先にmainを戻すと、取り込む範囲をorigin/main..mainで指定できなくなります。
練習では、mainにAdd logout buttonをコミットしてから、developへ取り込みます。git cherry-pick origin/main..mainは、pushしていないコミットを古い順に1件ずつdevelopの先頭へ積みます。git log --oneline -2はAdd logout button、Add dev notesの2件です。続くgit branch -fでmainを戻した後、git log --oneline -1 mainはAdd READMEです。
cherry-pickは同じ変更を持つ新しいコミットを作るので、developに入ったコミットのIDは、mainにあったときと違う値になります。新しいブランチへ移す前の方法では、IDは変わりません。
コミットしていない変更があると、git switch developがerror: Your local changes to the following files would be overwritten by checkout:と表示して止まることがあります。移る先のdevelopとmainで中身が違うファイルに変更がある場合です。先にその変更をコミットするか、関連記事「作業途中の変更をgit stashで退避する:未追跡ファイルは-u、ステージは--indexで戻す」の方法で退避してから移ります。
git switch main
printf 'logout\n' > logout.txt
git add -- logout.txt
git commit -m 'Add logout button'
git switch develop
git cherry-pick origin/main..main
git log --oneline -2
git branch -f main origin/main
git log --oneline -1 maincherry-pickが競合で止まったら、--abortで取り込む前に戻す
developに同じファイルを変える別のコミットがあると、cherry-pickはCONFLICTと表示して途中で止まります。git statusにはYou are currently cherry-picking commitと表示されます。git cherry-pick --abortを実行すると、cherry-pickを始める前のdevelopに戻ります。競合で止まっていないときに実行すると、error: no cherry-pick or revert in progressと表示されるだけで、何も変わりません。
このときmainはまだ戻していないので、間違えて入れたコミットはmainに残っています。競合を解消して取り込みたい場合は、ファイルを直してgit addし、git cherry-pick --continueで続けます。取り込みが全部終わったことをgit statusで確かめてから、git branch -f main origin/mainでmainを戻してください。
git cherry-pick --abort
git statusほかの方法との違い
Git公式のgit-resetの説明には、同じ場面の例として、git branchでブランチを作り、git reset --hardでmainを戻し、git switchで移る手順が載っています。git reset --hardは、追跡中のファイルのコミットしていない変更も消します(未追跡ファイルは残ります)。この記事でgit branch -fを使うのは、mainにいないときに実行するため、作業フォルダーに手を触れないからです。
git reset --hardの代わりに--keepを使うと、コミットしていない変更が消えるのは防げます。ただし練習のlogin.txtのように、戻す前後で中身が違うファイルに変更があると、error: Entry 'login.txt' not uptodate. Cannot merge.と表示されて止まり、mainは戻りません。
表は横にスクロールできます →
| 方法 | コミットID | コミットしていない変更 | 止まる場合 |
|---|---|---|---|
| git switch -c と git branch -f(この記事) | 変わらない | そのまま残る | mainにいるままbranch -fを実行したとき |
| git branch と git reset --hard と git switch(公式の例) | 変わらない | 追跡中のファイルの変更は消える | 止まらない |
| git branch と git reset --keep と git switch | 変わらない | 残る | 戻す前後で違うファイルに変更があるとき |
| git cherry-pick(既にあるブランチへ) | 新しいIDになる | 残る(移る前にコミットか退避が要る場合あり) | 移る先へswitchできないとき・競合したとき |
送信先がない場合は、コミットIDで戻す位置を指定する
git initだけで作ったリポジトリなど、origin/mainがない場合は、git log --onelineで間違えたコミットより1つ前のコミットIDを探し、origin/mainの代わりに指定します。下のabc1234は例の値です。自分のgit logに出たIDに置き換えてください。HEAD~2のように件数で数える指定もできますが、数え間違えると必要なコミットまでmainから外れます。
どちらの場合も、git branch -fで外れたコミットは消えていません。間違えて戻した場合は、git reflog mainでmainの以前の位置を探せます(関連記事「git reset --hardで消したコミットを戻す:git reflogで元の位置を探す手順」)。
git log --oneline
git switch -c feature/login
git branch -f main abc1234mainをpush済みなら、戻さずにrevertで打ち消す
送信先のmainにも入ってしまったコミットは、手元のmainだけを戻しても送信先には残ります。手元を戻した後でpushしても、送信先より古い位置なので拒否されます。強制pushで送信先を戻すと、そのコミットを既に取り込んだ他の人の履歴と食い違います。
他の人も使うmainなら、git revertでその変更を打ち消すコミットを足すのが安全です。手順は関連記事「push済みのコミットを打ち消す:git revertで履歴を残したまま元に戻す手順」にあります。
push済みと気づく前に、コミットIDなどでmainを戻してしまった場合は、feature/loginにいるままgit branch -f main origin/mainを実行すると、mainが送信先と同じ位置に戻ります。
関連する操作と、確認した環境
コミットしていない変更だけを別のブランチへ持っていく場合は、コミットせずにgit switch -cで移るか、関連記事「作業途中の変更をgit stashで退避する:未追跡ファイルは-u、ステージは--indexで戻す」の方法を使います。直前のコミットのメッセージだけを直す場合は「直前のコミットメッセージを直す:git commit --amendと、push済みのときの戻し方」が対象です。
2026年10月30日、WSLのLinux・Git 2.53.0・非rootユーザーで掲載例を実行しました。origin/main..mainの表示、git switch -cで付いてくる変更、mainにいるときのgit branch -fの拒否、git branch -f後のgit log・git branch -vv、新しいブランチのpush、cherry-pickでの取り込みとID、未コミットの変更でswitchが止まる場合、競合で止まったときの--abort、git reset --keepが止まる場合、送信先がないときの表示、push済みのときの表示を照合しています。Git公式の説明は2026年10月30日時点の版(git-branch・git-cherry-pick・gitrevisionsは2.56.0、git-switchは2.55.0、git-resetは2.54.0で更新)を参照しました。Git for Windows(Git Bash)・コマンドプロンプト・PowerShellでは実行していません。