mainに間違えてコミットした:push前に新しいブランチへ移し、mainを戻す手順

push前なら、git switch -cで今の位置に新しいブランチを作って移り、git branch -f main origin/mainでmainだけを戻せます。コミットIDも未コミットの変更もそのまま。既にあるブランチへcherry-pickで移す方法、競合時の--abort、push済みの場合を実行例で掲載。

目次
  1. push前なら、新しいブランチへ移ってから、mainだけを送信先の位置へ戻す
  2. 移す前に、pushしていないコミットを確かめる
  3. 練習用フォルダーを作り、そこへ移動する
  4. 送信先と、mainに間違えて入れた2件を用意する
  5. 新しいブランチを作って移る
  6. mainだけを、送信先にpush済みの位置へ戻す
  7. 移したブランチで作業を続け、pushする
  8. 移し先のブランチが既にある場合は、cherry-pickしてからmainを戻す
  9. cherry-pickが競合で止まったら、--abortで取り込む前に戻す
  10. ほかの方法との違い
  11. 送信先がない場合は、コミットIDで戻す位置を指定する
  12. mainをpush済みなら、戻さずにrevertで打ち消す
  13. 関連する操作と、確認した環境
  14. 参考資料

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 --short

mainだけを、送信先に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 main

cherry-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 abc1234

mainを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では実行していません。

参考資料