エラー発生時の状況
作業中に同じブランチでほかの開発者がコミットしていた場合を想定し、挙動を確認する実験を行っていた。
①リモートのmainブランチにコミットAをpushで追加
(mainリモート追跡ブランチ origin/mainが更新される)
②ローカルのmainブランチからgit reset —hardでコミットAを削除
③ローカルのmainブランチに独自のコミットBを追加
④リモートがコミットA、ローカルがコミットBをもつ状態でpushを実行した
・エラー内容
$ git push
! [rejected] main -> main (non-fast-forward)
error: failed to push some refs to 'github.com:'
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.
原因調査
- non-fast-foward (ローカルブランチとそれに対応するリモートブランチがそれぞれ独自のコミット履歴を持ってしまっている)が原因
- 自身の実装中の問題個所(それぞれ特有のコミットは何か?)
//①最新のリモートリポジトリの状況を取得
$ git fetch
//②git logを使ってのリモート追跡ブランチとローカルブランチでそれぞれからたどれる特有のコミットが何かを確認する
// ローカルにあってリモートにないもの
$ git log origin/main..HEAD
commit 34a865277468fffe824d2eeef543ed2bfa06e025 (HEAD -> main)
// リモートにあってローカルにないもの
$ git log HEAD..origin/main
commit 9739bd3ce69d28b2975541841d987776aa7a8dc2 (origin/main)
解決仮説と実行
[ローカルブランチとリモートブランチが異なる]という状況について
大まかな方針:ローカルブランチとリモートブランチをそろえる
方針①:ローカルブランチのコミットを削除しリモートブランチのコミットをローカルブランチにFFマージする(リモートのコミットのみを残す)
方針②:シンプルにローカルブランチにリモートブランチを3wayマージする(ローカルの変更、リモートの変更どっちも残したい)
方針②を試してみる
具体的方法:
step1:最新のリモートブランチのコミットを取得してからリモート追跡ブランチをローカル ブランチに3wayマージ
$ git fetch
$ git merge origin/main
Auto-merging index.html
CONFLICT (content): Merge conflict in index.html
Automatic merge failed; fix conflicts and then commit the result.
$ git add .
$ git commit -m "ローカルにリモートのcommitをマージしてコミット履歴を揃えた"
[main e0dd16c] ローカルにリモートのcommitをマージしてコミット履歴を揃えた
step2:push可能かをgit logまたはstatusで確認=ff可能かを調査する(両方に特有のコミットがなければ〇)
//一応最新のリモートリポジトリの状態を取得
$ git fetch
$ git log origin/main..HEAD
commit e0dd16c9da714ce7b2f86e4aeee3d5a1ba2ba30f (HEAD -> main)
$ git log HEAD..origin/main
commit 34a865277468fffe824d2eeef543ed2bfa06e025
// have diversed でなければOK
$ git status
On branch main
Your branch is ahead of 'origin/main' by 2 commits.
(use "git push" to publish your local commits)
nothing to commit, working tree clean
ローカルが進んでいるだけなので〇、push可能
step3:pushしてみる
yuji1@LAPTOP-T546O2B1 MINGW64 ~/git-learn2 (main)
$ git push
Enumerating objects: 10, done.
Counting objects: 100% (10/10), done.
Delta compression using up to 8 threads
Compressing objects: 100% (5/5), done.
Writing objects: 100% (6/6), 839 bytes | 419.00 KiB/s, done.
Total 6 (delta 2), reused 0 (delta 0), pack-reused 0 (from 0)
remote: Resolving deltas: 100% (2/2), completed with 1 local object.
To github.com:yuji1123-hyogo/git-learn2.git
9739bd3..e0dd16c main -> main
git status/ logでpush結果を確認
//一応最新のリモートリポジトリの状態を取得
$ git fetch
//git statusでの確認(成功)
$ git status
On branch main
Your branch is up to date with 'origin/main'.
nothing to commit, working tree clean
//git logでの確認
yuji1@LAPTOP-T546O2B1 MINGW64 ~/git-learn2 (main)
$ git log --oneline --graph --all --decorate
* e0dd16c (HEAD -> main, origin/main, origin/HEAD) ローカルにリモートのcommitをマージしてコミット履歴を揃えた
|\
* | 34a8652 ローカルで独自コミットを作成
| | * bb7164c (non-related-branch) pushされないブランチを作成
| |/
| * 9739bd3 git restore実験してみた
|/
* def0b15 コンフリクトを解消
|\
| * d140b6a (origin/feature, feature) featureブランチのコミット履歴を進める
* | 9d7e5ef mainのコミット履歴も進める
|/
* dc5c0f0 2回目のコミットmainのみ
* 021f2f4 first commit
より良い解決の調査
大体pull/fetch merge/rebaseでローカルとリモートの同期をとる方法が挙げられていた。
rebase版試してみた。
コミット履歴の分岐なくマージできるのは少しだけ魅力的だがそれ以上に自分が行ったファイル編集内容とコミット履歴として残る変更内容が異なることに違和感があるためあまり使わないでおこうと思う。
回避策:push前のチェック
①今いるブランチは正しいか?
git branch
→ 「push するつもりのブランチにいるか?」を確認。
②ローカルの変更は最新のリモートに基づいているか?
git fetch
git status
→ ローカルがリモートと diverge(分岐)していないかを確認。
もしリモートに新しいコミットがあって、自分の変更が古いままだと、push 時に拒否される
③正しい変更内容か?
git diff origin/ブランチ..
→ 「実際に差分で何を push しようとしているのか」
- .. が重要(ローカルブランチとの比較)
④ブランチの対応関係は正しいか?
git branch -vv
②だけは毎回やる癖をつける。他は不安な時だけやる
まとめ
原因は?
- pushはFFマージ可能でないと失敗する(pullのようによしなにマージコミットを作成してくれない)
回避策は?
- リモートでのコミット履歴とローカルでのコミット履歴をそろえておく必要がある
- push前はfetchしておく
- 一応 git statusでリモート追跡ブランチとローカルブランチの差分を確認する
解決策は?
- push時エラーが起こったときはまずそれぞれのコミット履歴を確認して度のコミット履歴を残すか判断する。そもそもリモート追跡ブランチとローカルブランチが正しく対応しているかを確認する。
- 最新のリモートブランチを取り込んでマージ(場合によっては自分のコミットをgit reset hard HEAD~1で削除)
やっといてなんだがそもそも実務で同じブランチで開発することはあるんだろうか?
最近のclaude自分より人間味ある