git push が rejected になったときの直し方|pull --rebase とコンフリクト解消、--force-with-lease の使いどころ

リールで見た方へ。ここでは、リールでは収まりきらなかった「なぜ rejected になるのか」と「コンフリクトが出たときの直し方」まで、ゆっくり書いていきます。

git push したら、赤っぽい文字で ! [rejected] と出て止まった。焦りますよね。「何か壊した?」と思って、よく分からないまま --force を付けて押し通したくなりがちですが、それをすると、ほかの場所で作ったコミットを消してしまうことがあります(この記事の後半で、実際に消える様子を確かめています)。

安心してください。rejected は「壊れた」ではなく、「リモートに、あなたの手元にない変更があるよ」というGitからのお知らせです。この記事では、意味を読み解くところから、git pull --rebase での直し方、コンフリクトの直し方、そして --force を使ってよい場面まで、一緒にやってみましょう。

💡
この記事でわかること
  • まず試す流れ(確認 → git fetch → git pull --rebase → git push)
  • --rebase なしの git pull が止まる理由
  • コンフリクトが出たときの直し方(マーカーの読み方、git add → git rebase --continue、やめたいときの git rebase --abort)
  • rejected(fetch first / non-fast-forward)の意味
  • --force と --force-with-lease の違いと、使ってよい場面(実際にコミットが消える様子も見せます)
🧭
この記事の前提

この記事でできるようになること:rejected の意味を読み取り、--force に頼らず git pull --rebase とコンフリクト解消で push を通せる

🖥️
この記事の実行例について

実行例は、Debian 13 / git 2.47.3 で実際に動かした結果です。GitHub の代わりに、同じPCの中に作ったリモートリポジトリ(/home/kaito/remote/demo.git)を使い、2つのフォルダ(clone-a と clone-b)を「自分」と「別のPC(または別の人)」に見立てています。GitHub を使っている場合は、To の後ろがGitHubのURLになるなど、表示が少し変わります。


まずはこれだけ:rejected を直す流れ(確認してから取り込む)

! [rejected] の行の最後が (fetch first) か (non-fast-forward) なら、次の流れで直ることが多いです。コマンドはまとめて打たず、1つずつ実行して、出力を見てから次へ進みます。

  1. git push のエラーを読む:! [rejected] の行の最後が (fetch first) か (non-fast-forward) かを確認します。それ以外(ブランチの保護、権限、認証など)なら、この手順は使わずに、remote: の行の理由を読みます
  2. git status:最後が nothing to commit, working tree clean ならOK。変更が残っていたら、下の「1. 手元に未コミットの変更がないか確認する」の手順で守ってから進みます
  3. git fetch:リモートの新しいコミットを取ってきて、origin/main などのリモート追跡ブランチ(手元にあるリモートの写し)を更新します。手元のブランチや作業中のファイルは変わりません
  4. git status と git log --oneline --graph --all -n 10:Your branch is behind か have diverged と出ていること、今のブランチの送り先(upstream)が想定どおり(例:origin/main)であることを確かめます。どちらかが違ったら、ここで止まります。リモート側に見覚えのないコミットや大量のコミットがあるときも、チームに確認してから進みましょう
  5. git pull --rebase:リモートの新しいコミットを取り込んで、自分のコミットをその後ろに付け直します。Successfully rebased and updated refs/heads/main.(手元に未pushのコミットがない behind のときは Fast-forward)と出たときだけ次へ進みます。CONFLICT やエラーが出たら、そこで止まり、pushはしません(「コンフリクトが出たときの直し方」へ。分からなければ git rebase --abort)
  6. git push:もう一度送ります。main -> main のような行が出れば解決です(ブランチ名が main 以外なら、その名前で表示されます)

git pull --rebase で付け直されるのは、まだpushしていない自分のコミットです。もし、すでに別のブランチなどで誰かと共有しているコミットを rebase で付け直す場合は、ほかの人の履歴とずれるので、事前にチームの合意をとってください。

--force は、この流れを飛ばして「リモートを自分の内容で上書きする」コマンドです。rejected が出たときに最初に試すものではありません。みんなで使うブランチでは使わないでください。

途中で止まったら:どこを読めばいい?
出てきた表示 読むところ
git pull --rebase で CONFLICT (content): Merge conflict in ... 「コンフリクトが出たときの直し方」
--rebase を付け忘れて fatal: Need to specify how to reconcile divergent branches. 「--rebase なしの git pull が止まるとき(divergent branches)」
何をしているのか分からなくなった 「やめたいときは git rebase --abort」
カッコの中が fetch first / non-fast-forward 以外 「なぜ rejected になるのか(fetch first / non-fast-forward)」 の最後の注意
自分だけのブランチで rebase や amend をしたあと 「--force と --force-with-lease の違い」

💡
それぞれの手順で、実際にどんな表示が出るかは、次の「詳しい手順」で実行例つきで見ていきます。

詳しい手順:git pull --rebase で直す

この流れを、実際の表示と一緒にもう少し丁寧に見ていきます。一緒にやってみましょう。

1. 手元に未コミットの変更がないか確認する
  • 1
git status

表示の最後が nothing to commit, working tree clean になっていれば、未コミットの変更はありません。

未コミットの変更が残っていると、git pull --rebase が止まることがあります。先にコミットするか、いったん git stash で退避しておきましょう。

  • 1
git stash

退避した変更は、あとで git stash pop で戻せます。ただし、git stash だけでは、まだ一度も git add していない新しいファイル(未追跡ファイル)は退避されません(git stash 公式ドキュメント)。

未コミットの変更をどう守るかは、中身を見てから選びます。

  1. git status と git diff で、何が変わっているかを見る
  2. パスワードやAPIキー、.env などの秘密が入っていないか確かめる(入っていたら、コミットもpushもしない)
  3. 問題がなければ、残したい変更はコミットする。まだ途中の変更は、git stash(未追跡ファイルも含めるなら git stash -u)で退避する

rebase の前に、今のコミットに名前(ブランチ)を付けて残しておくこともできます。

  • 1
git branch backup-before-rebase

うまくいかなかったときに、このブランチから元のコミットを確かめられます。このブランチで守れるのは、コミット済みの履歴だけです。未コミットの変更や未追跡ファイルは守れないので、上の手順で別に守ってください。rebase のあとも、このブランチは付け直す前のコミットを指したまま残ります。残しておいて問題ありません。あとから消したいときは、git log --oneline main などで自分の変更が残っていることを十分確かめてから、git branch -d backup-before-rebase を実行してください。not fully merged と出て消えないことがあります。そのときは強制削除(git branch -D)は使わず、バックアップを残したまま止めてください。

2. リモートの変更を取り込んで、自分のコミットを付け直す
  • 1
git pull --rebase

Debian 13 / git 2.47.3 での実行例

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
kaito@demo:~/work/clone-a$ git pull --rebase
remote: Enumerating objects: 4, done.
remote: Counting objects: 100% (4/4), done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0 (from 0)
Unpacking objects: 100% (3/3), 253 bytes | 253.00 KiB/s, done.
From /home/kaito/remote/demo
   826dcaa..a1fcee5  main       -> origin/main
Successfully rebased and updated refs/heads/main.

最後の Successfully rebased and updated refs/heads/main. が、付け直しに成功したという意味です。--rebase を付けると、Gitは次のように動きます。

  1. リモートの最新のコミットを取ってくる
  2. 自分のコミットをいったん横によける
  3. リモートの最新の後ろに、自分のコミットを1つずつ付け直す

git pull --rebase のしくみ(自分のコミットを付け直す)

--rebase を付けずに git pull すると、メッセージが出て止まることがあります。これは次の章で説明しますね。

3. 履歴を確認して、もう一度 push する
  • 1
git log --oneline --graph -n 4

Debian 13 / git 2.47.3 での実行例

  • 1
  • 2
  • 3
  • 4
kaito@demo:~/work/clone-a$ git log --oneline --graph -n 4
* 9ef4f08 (HEAD -> main) A: add a.txt
* a1fcee5 (origin/main) B: add b.txt
* 826dcaa first commit

リモートのコミット(origin/main の付いた B: add b.txt)の上に、自分のコミット(A: add a.txt)が乗っていればOKです。コミットのIDは、rebase で付け直したので元の 84936ae から 9ef4f08 に変わっています。付け直すと別のコミットとして作り直される、というのが rebase の特徴です。

  • 1
git push

Debian 13 / git 2.47.3 での実行例

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
kaito@demo:~/work/clone-a$ git push
Enumerating objects: 4, done.
Counting objects: 100% (4/4), done.
Delta compression using up to 8 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 298 bytes | 298.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0), pack-reused 0 (from 0)
To /home/kaito/remote/demo.git
   a1fcee5..9ef4f08  main -> main

a1fcee5..9ef4f08 main -> main と出れば、送れています。これで rejected は解決です。コミットの基本(git add と git commit)があやふやな方は、先にGit入門|init・add・commit を1回やってみるを読んでおくと、ここからの話がぐっと分かりやすくなります。

詰まったらAIに聞こう①:エラーの意味が分からないときは、全文をそのまま貼って聞いてみましょう。

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
git push をしたら、次のエラーが出ました。
初心者にも分かるように、何が起きているのかと、安全な直し方を順番に教えてください。
--force は使いたくありません。

【git push のエラー全文】

ブランチ名:【ブランチ名(例:main)】
このブランチを使っている人:【自分だけ/チームで共有】
git log --oneline --graph --all -n 10 の結果:【出力】
(URLにトークンが含まれる行や、社内のURLは消してから貼ります)

⚠️
AIに貼る前に

エラー文やログに、パスワード・アクセストークン・APIキー・社内のURLなどが含まれていないか確認してください。含まれていたら、その部分は消すか「***」に置き換えてから貼りましょう。


--rebase なしの git pull が止まるとき(divergent branches)

手元とリモートがそれぞれ別のコミットを持っている(分かれている)状態で、--rebase を付けずに git pull すると、こう出て止まりました。

Debian 13 / git 2.47.3 での実行例

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
kaito@demo:~/work/clone-a$ git pull
hint: You have divergent branches and need to specify how to reconcile them.
hint: You can do so by running one of the following commands sometime before
hint: your next pull:
hint:
hint:   git config pull.rebase false  # merge
hint:   git config pull.rebase true   # rebase
hint:   git config pull.ff only       # fast-forward only
hint:
hint: You can replace "git config" with "git config --global" to set a default
hint: preference for all repositories. You can also pass --rebase, --no-rebase,
hint: or --ff-only on the command line to override the configured default per
hint: invocation.
fatal: Need to specify how to reconcile divergent branches.

Debian 13での実行画面

最後の fatal: は「分かれた履歴をどうまとめるか決めてね」という意味です。エラーっぽく見えますが、何も壊れていません。Gitが勝手に決めずに、あなたに選ばせてくれているんですね。

選び方は2通りあります。

やり方 コマンド 向いている人
その都度指定する git pull --rebase まずはこれ。毎回「rebase で取り込む」と意識できる
設定しておく git config pull.rebase true いつも rebase で取り込むと決めている。設定後は git pull だけで rebase になる

git config pull.rebase true はそのリポジトリだけの設定です。メッセージにあるとおり、git config --global pull.rebase true にすると、すべてのリポジトリで同じ動きになります。

ほかの選択肢も、メッセージに書かれています。pull.rebase false はマージで取り込む設定で、分かれているときはマージコミットが1つ増えます。pull.ff only は「早送り(fast-forward)できるときだけ取り込む」設定なので、今回のように分かれているときは取り込まずに止まります。チームで決まりがあるなら、それに合わせてくださいね。この記事では、履歴が一直線になって読みやすい --rebase を使います。


コンフリクトが出たときの直し方

git pull --rebase の途中で、こんな表示が出て止まることがあります。

Debian 13 / git 2.47.3 での実行例

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
kaito@demo:~/work/clone-a$ git pull --rebase
remote: Enumerating objects: 5, done.
remote: Counting objects: 100% (5/5), done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0 (from 0)
Unpacking objects: 100% (3/3), 304 bytes | 304.00 KiB/s, done.
From /home/kaito/remote/demo
   9ef4f08..9ddb534  main       -> origin/main
Auto-merging README.md
CONFLICT (content): Merge conflict in README.md
error: could not apply 903b40e... A: edit README title
hint: Resolve all conflicts manually, mark them as resolved with
hint: "git add/rm <conflicted_files>", then run "git rebase --continue".
hint: You can instead skip this commit: run "git rebase --skip".
hint: To abort and get back to the state before "git rebase", run "git rebase --abort".
hint: Disable this message with "git config advice.mergeConflict false"
Could not apply 903b40e... A: edit README title

これが「コンフリクト(衝突)」です。ここでは、clone-b と clone-a の両方で README.md の1行目を書き換えたので、ぶつかりました。リモートのコミットと自分のコミットが、同じファイルの同じあたりを別々に書き換えたので、Gitがどちらを残すか決められなくなった状態です。ここで手を止めてくれるのは、Gitの優しさなんですよね。

1. どのファイルがぶつかっているか見る
  • 1
git status

Debian 13 / git 2.47.3 での実行例

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
kaito@demo:~/work/clone-a$ git status
interactive rebase in progress; onto 9ddb534
Last command done (1 command done):
   pick 903b40e A: edit README title
No commands remaining.
You are currently rebasing branch 'main' on '9ddb534'.
  (fix conflicts and then run "git rebase --continue")
  (use "git rebase --skip" to skip this patch)
  (use "git rebase --abort" to check out the original branch)

Unmerged paths:
  (use "git restore --staged <file>..." to unstage)
  (use "git add <file>..." to mark resolution)
        both modified:   README.md

no changes added to commit (use "git add" and/or "git commit -a")

both modified: と表示されているファイルが、直す対象です。git pull --rebase で止まったのに interactive rebase in progress(rebase の途中)と出ますが、これで合っています。内部では rebase が動いているからです。

2. コンフリクトマーカーを読む

対象のファイルを開くと、次のような記号が入っています。実際の README.md の中身はこうでした。

Debian 13 / git 2.47.3 での実行例

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
kaito@demo:~/work/clone-a$ cat README.md
<<<<<<< HEAD
# demo (edited in B)
=======
# demo (edited in A)
>>>>>>> 903b40e (A: edit README title)

Debian 13での実行画面

記号 意味
<<<<<<< HEAD から ======= まで rebase の土台になっている側(リモートから取り込んだ内容)
======= から >>>>>>> まで いま付け直している自分のコミットの内容
>>>>>>> の後ろ 付け直しているコミットのIDとメッセージ

ここ、つまずきポイントです。rebase 中は HEAD 側が「リモート側」になります。実際に、HEAD 側には先に push された clone-b の内容(edited in B)が入っていますよね。普段の git merge のときと感覚が逆なので、「HEAD=自分」と思い込まないでくださいね。迷ったら、>>>>>>> の後ろのコミットメッセージ(ここでは A: edit README title)を見て「こっちが自分の変更だな」と確認するのが確実です。

コンフリクトマーカーの読み方(rebase 中は HEAD=リモート側)
3. 残したい内容に書き換えて、マーカーを消す

どちらか一方を残すことも、両方を合わせることもできます。大事なのは、<<<<<<<・=======・>>>>>>> の3種類の行をすべて消すことです。1つでも残ると、その記号ごとコミットされてしまいます。

4. git add → git rebase --continue

直したファイルを git add して、rebase を先に進めます。

  • 1
  • 2
git add README.md
git rebase --continue

README.md のところは、直したファイル名に置き換えてください。実行例では、両方の変更を合わせた1行に書き換えてから進めました。

Debian 13 / git 2.47.3 での実行例

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
kaito@demo:~/work/clone-a$ echo "# demo (edited in A and B)" > README.md
kaito@demo:~/work/clone-a$ cat README.md
# demo (edited in A and B)
kaito@demo:~/work/clone-a$ git add README.md
kaito@demo:~/work/clone-a$ GIT_EDITOR=true git rebase --continue
[detached HEAD d7fa34b] A: edit README title
 1 file changed, 1 insertion(+), 1 deletion(-)
Successfully rebased and updated refs/heads/main.
kaito@demo:~/work/clone-a$ git push
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Delta compression using up to 8 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 333 bytes | 333.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0), pack-reused 0 (from 0)
To /home/kaito/remote/demo.git
   9ddb534..d7fa34b  main -> main
kaito@demo:~/work/clone-a$ git log --oneline --graph -n 5
* d7fa34b (HEAD -> main, origin/main) A: edit README title
* 9ddb534 B: edit README title
* 9ef4f08 A: add a.txt
* a1fcee5 B: add b.txt
* 826dcaa first commit

Debian 13での実行画面

実行例の GIT_EDITOR=true は、記録のためにエディタを開かずに進めるための指定です。普段は付けなくて大丈夫です。付けずに git rebase --continue を実行すると、コミットメッセージを編集する画面が開くことがあります。そのままでよければ、保存して閉じればOKです。

自分のコミットが複数あると、次のコミットでまたコンフリクトが出ることもあります。そのときは、同じ手順(直す → git add → git rebase --continue)を繰り返します。Successfully rebased と出たら、さっきと同じく git push で送りましょう。

詰まったらAIに聞こう②:マーカーの中身をどう合わせればいいか迷ったら、こう聞いてみましょう。

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
git pull --rebase の途中でコンフリクトが起きました。
次のコンフリクト部分について、
・どちらがリモート側で、どちらが自分の変更か
・両方の意図を残すなら、どう書けばよいか
を教えてください。マーカー(<<<<<<< ======= >>>>>>>)を消した完成形も見せてください。

ファイル名:【ファイル名】
コンフリクト部分:
【<<<<<<< から >>>>>>> までの全文】

自分がこの変更でやりたかったこと:【一言で】
やめたいときは git rebase --abort

「よく分からなくなった」「一回やり直したい」というときは、慌てずにこれです。

  • 1
git rebase --abort

別のコンフリクトが起きたところで git rebase --abort を実行し、そのあと git status と履歴を確認しました。

Debian 13 / git 2.47.3 での実行例

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
kaito@demo:~/work/clone-a$ git rebase --abort
kaito@demo:~/work/clone-a$ git status
On branch main
Your branch and 'origin/main' have diverged,
and have 1 and 1 different commits each, respectively.
  (use "git pull" if you want to integrate the remote branch with yours)

nothing to commit, working tree clean
kaito@demo:~/work/clone-a$ git log --oneline --graph --all -n 5
* c8b3edd (HEAD -> main) A: README v2
| * f24d707 (origin/main) B: README v2
|/
* d7fa34b A: edit README title
* 9ddb534 B: edit README title
* 9ef4f08 A: add a.txt

Debian 13での実行画面

git rebase --abort 自体は、何も表示せずに終わります。手元のブランチとファイルは git pull --rebase を始める前の状態に戻り、直しかけていた内容も元に戻ります。

ただし、git status を見ると have diverged(分かれている)と出ます。これは、pull の最初に取ってきたリモートの情報(origin/main)は手元に残っているからです。履歴の図でも、自分の A: README v2 とリモートの B: README v2 が枝分かれしていますね。この状態のまま push すると、non-fast-forward(このあとの「なぜ rejected になるのか」で説明します)で拒否されます。落ち着いてから、もう一度 git pull --rebase からやり直せば大丈夫です。

詰まったらAIに聞こう③:いまどういう状態なのか分からなくなったら、git status の結果を貼って聞いてみましょう。

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
git の作業中に、いまどういう状態なのか分からなくなりました。
次の git status の結果から、
・いま何の途中なのか(rebase 中、merge 中など)
・次に打つべきコマンド
・やめて元に戻したい場合のコマンド
を、初心者向けに教えてください。

【git status の出力全文】

なぜ rejected になるのか(fetch first / non-fast-forward)

直し方が分かったところで、「そもそも何が起きていたのか」を見ておきましょう。次に同じ表示が出たとき、落ち着いて読めるようになります。まず、実際のエラーです。

  • 1
git push

📝
clone してきたブランチなら、送り先(origin の main など)が設定済みなので、git push だけで送れます。git push origin main のように送り先を書いても、やっていることは同じです。この記事の実行例は、どちらも省略した形で打っています。

Debian 13 / git 2.47.3 での実行例

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
kaito@demo:~/work/clone-a$ git push
To /home/kaito/remote/demo.git
 ! [rejected]        main -> main (fetch first)
error: failed to push some refs to '/home/kaito/remote/demo.git'
hint: Updates were rejected because the remote contains work that you do not
hint: have locally. This is usually caused by another repository pushing to
hint: the same ref. If you want to integrate the remote changes, use
hint: 'git pull' before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

Debian 13での実行画面

これは、別のフォルダ(clone-b)から先に push したあとで、clone-a から push したときの表示です。メッセージの中で見てほしいのは、! [rejected] の行の最後にあるカッコの中です。

表示 意味
(fetch first) リモートに、手元にまだ取り込んでいないコミットがある。まず取ってきてね、という意味
(non-fast-forward) 手元のブランチが、リモートのブランチより遅れている(または分かれている)。そのまま送るとリモートの履歴を消してしまうので拒否した、という意味

non-fast-forward のほうは、リモートの新しいコミットを一度取ってきた(fetch した)けれど、まだ自分のブランチに取り込んでいない状態で push すると出ました。

Debian 13 / git 2.47.3 での実行例

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
kaito@demo:~/work/clone-a$ git push
To /home/kaito/remote/demo.git
 ! [rejected]        main -> main (non-fast-forward)
error: failed to push some refs to '/home/kaito/remote/demo.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. If you want to integrate the remote changes,
hint: use 'git pull' before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

どちらも、言っていることはほぼ同じです。「リモートの履歴」と「手元の履歴」がずれているんですね。hint: の行にも「push し直す前に git pull してね」と書かれています。

リモートが先に進んでいると push は止まる(履歴のずれ)

たとえば、GitHubの画面でREADMEをちょっと直した。別のPCでコミットしてpushした。チームの誰かが先にpushした。こういうときに起こります。Gitは、リモートにある他人(や別の自分)のコミットを黙って消さないように、pushを止めてくれているわけです。

📝
fetch first / non-fast-forward 以外で拒否されたら

カッコの中が別の理由(ブランチの保護設定など)のときは、この記事の手順では直らないことがあります。メッセージの remote: から始まる行に理由が書かれていることが多いので、そこを読んでみてください。


--force と --force-with-lease の違い

ネットで調べると、「git push --force で解決」と書かれていることもあります。でも、ここは本当に気をつけてほしいところです。

オプション 何をするか リスク
--force リモートの状態に関係なく、手元の内容で上書きする 他の人(や別のPCの自分)が先にpushしたコミットを、気づかないまま消してしまう
--force-with-lease 「最後に取得したときからリモートが変わっていなければ」上書きする。変わっていたら拒否する --force よりは安全。ただし万能ではない(下の注意を参照)

--force と --force-with-lease の違い
実際に試してみた:--force で他の人のコミットが消える

言葉だけだとピンとこないので、実際にやってみました。clone-b 側で B: important work というコミットを push したあと、それを取り込んでいない clone-a 側でコミットを作り、まず --force-with-lease で push してみます。

Debian 13 / git 2.47.3 での実行例

  • 1
  • 2
  • 3
  • 4
kaito@demo:~/work/clone-a$ git push --force-with-lease
To /home/kaito/remote/demo.git
 ! [rejected]        main -> main (stale info)
error: failed to push some refs to '/home/kaito/remote/demo.git'

(stale info) は「手元が知っているリモートの情報が古い」という意味です。clone-a が最後に取得したときから、リモートが進んでいたので、--force-with-lease は上書きせずに止めてくれました。

次に、同じ状態で --force を使います。

Debian 13 / git 2.47.3 での実行例

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
kaito@demo:~/work/clone-a$ git push --force
Enumerating objects: 21, done.
Counting objects: 100% (21/21), done.
Delta compression using up to 8 threads
Compressing objects: 100% (13/13), done.
Writing objects: 100% (21/21), 1.76 KiB | 1.76 MiB/s, done.
Total 21 (delta 1), reused 0 (delta 0), pack-reused 0 (from 0)
To /home/kaito/remote/demo.git
 + 1afe5fd...fef010c main -> main (forced update)
kaito@demo:~/work/clone-a$ git log --oneline -n 4
fef010c (HEAD -> main, origin/main) A: rewrite
f24d707 B: README v2
d7fa34b A: edit README title
9ddb534 B: edit README title

Debian 13での実行画面

今度は止まらず、(forced update) で上書きされました。リモートの履歴に、B: important work がありません。clone-b 側から確認してみます。

Debian 13 / git 2.47.3 での実行例

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
kaito@demo:~/work/clone-b$ git log --oneline -n 3
1afe5fd (HEAD -> main, origin/main, origin/HEAD) B: important work
f24d707 B: README v2
d7fa34b A: edit README title
kaito@demo:~/work/clone-b$ git fetch
remote: Enumerating objects: 4, done.
remote: Counting objects: 100% (4/4), done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 1), reused 0 (delta 0), pack-reused 0 (from 0)
Unpacking objects: 100% (3/3), 256 bytes | 256.00 KiB/s, done.
From /home/kaito/remote/demo
 + 1afe5fd...fef010c main       -> origin/main  (forced update)
kaito@demo:~/work/clone-b$ git status
On branch main
Your branch and 'origin/main' have diverged,
and have 1 and 1 different commits each, respectively.
  (use "git pull" if you want to integrate the remote branch with yours)

nothing to commit, working tree clean
kaito@demo:~/work/clone-b$ git log --oneline -n 4 origin/main
fef010c (origin/main, origin/HEAD) A: rewrite
f24d707 B: README v2
d7fa34b A: edit README title
9ddb534 B: edit README title
kaito@demo:~/work/clone-b$ git log --oneline --all --graph -n 6
* 1afe5fd (HEAD -> main) B: important work
| * fef010c (origin/main, origin/HEAD) A: rewrite
|/
* f24d707 B: README v2
* d7fa34b A: edit README title
* 9ddb534 B: edit README title
* 9ef4f08 A: add a.txt

Debian 13での実行画面

B: important work は、clone-b の手元(HEAD -> main)にしか残っていません。リモート(origin/main)からは消えています。今回はたまたま手元に残っていましたが、もしそのPCのフォルダを消していたら、取り戻すのは大変です。--force の怖さは、この「黙って消える」ところなんです。

使ってよい場面、使わない場面

使わない:main など、チームで共有しているブランチ。rejected が出たら、上で紹介した git pull --rebase で取り込むのが基本です。

使ってよい:自分しか使っていない作業ブランチで、すでにpushしたコミットを、git rebase や git commit --amend で書き換えたとき。このときは履歴そのものを作り直しているので、普通の git push は rejected になります。そこで、--force ではなく --force-with-lease を使います。

  • 1
git push --force-with-lease origin 【自分の作業ブランチ名】

⚠️
--force-with-lease も万能ではありません

--force-with-lease は、手元が覚えている「リモートの最後の状態」と比べて判断します。上の実行例で止まったのは、clone-a がリモートの新しいコミットをまだ取得していなかったからです。pushの直前に git fetch を実行していたり、エディタが裏で自動的に fetch していたりすると、他の人の変更を「知っている」扱いになり、上書きしてしまうことがあります。迷ったら、push する前に git log --oneline --graph --all でリモート側のコミットを確認しましょう。

(補足)Git 2.30 以降には、--force-with-lease と一緒に使う --force-if-includes というオプションもあります。リモートの変更を手元に取り込まないまま上書きしてしまう危険を減らすためのものです(git push 公式ドキュメント)。rejected の解消には必要ないので、初心者のうちは使わなくてかまいません。

「戻し方」全般(reset や revert の使い分け)は、git reset と revert、どっち?ブランチの切り方と戻し方を実行例つきで整理にまとめています。共有ブランチで「なかったことにしたい」ときは、--force ではなく revert を選ぶ考え方も紹介しているので、あわせてどうぞ。

詰まったらAIに聞こう④:force を使うか迷ったら、使う前に一度確認してもらいましょう。

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
git push --force-with-lease を使おうか迷っています。
次の状況で使ってよいか、使うなら注意点を、使わないなら代わりの方法を教えてください。

ブランチ名:【ブランチ名】
このブランチを使っている人:【自分だけ/チームで共有】
直前にしたこと:【例:git rebase main をした/git commit --amend をした】
git log --oneline -n 5 の結果:
【出力】
git status の結果:
【出力】

よくあるつまずき

症状 原因 対処
git pull --rebase がすぐ止まる 未コミットの変更が残っている 差分と秘密を確かめてから、コミットするか git stash(未追跡ファイルもなら -u)で退避する
git pull で fatal: Need to specify how to reconcile divergent branches. 手元とリモートが分かれていて、取り込み方が決まっていない git pull --rebase を使う。いつも rebase なら git config pull.rebase true
コンフリクトを直したのに先に進めない git add を忘れている 直したファイルを git add してから git rebase --continue
push したら、ファイルに <<<<<<< が残っていた マーカーの行を消し忘れた ファイルを直して、新しいコミットとして push する
何度 push しても rejected になる 取り込んだ後に、また誰かが push した もう一度 git pull --rebase → git push
git rebase --abort のあと、status に have diverged と出る 取ってきたリモートの情報は残っているため 正常です。もう一度 git pull --rebase からやり直す
どこまで進んだか分からない rebase の途中で手を止めた git status で状態を確認。やめるなら git rebase --abort
git stash で退避した変更が見当たらない 退避したまま戻していない rebase と push が終わったら git stash pop で戻す

次の一歩

rejected が出たら、「確認(git status・git fetch・git log)→ git pull --rebase → git push」。コンフリクトが出たら「直す → git add → git rebase --continue」。この順番を覚えておけば、落ち着いて対処できます。

次は、「間違えてコミットしちゃった」「前の状態に戻したい」に備えて、戻し方を押さえておきましょう。git reset と revert、どっち?ブランチの切り方と戻し方を実行例つきで整理で、実際に動かしながら整理できます。


関連する本・記事

記事: