commit改変時の複数PCのリポジトリの合わせ方
個人開発では会社用と自宅用にそれぞれPCを置いて同じリポジトリを用いながら編集を継続する運用方法がある(私もそう)。その場合、どちらかのPCでリモートからpull&更新&commit&pushを行い、もう1台のPCを使う際には再びリモートからpull&更新&commit&pushを行う形で、違うPCでもよどみなく編集を継続出来る。
但しcommitを整理する目的でgit rebase
,git reset
やgit commit --amend
などを使用するとcommit IDが変わるため、リモートリポジトリやもう1台でのローカルリポジトリでgit pull
を行う際にconflictする。この記事は個人で複数台のPC使用時にreset使用等でconflictした場合のcommit履歴の整理方法を記載する。
git reset使用例
個人開発で複数台のPCを利用するケースで、git reset
などcommit IDの改変が行われる作業を行うと、複数人での開発同様に各ローカルリポジトリやリモートリポジトリとのconflictが生じる。
以下の例では、ある1台のPCで一気に最初のcommitまでresetする極端なgit reset
を行ったと仮定する。その段階での作業ディレクトリにはa.sh, b.sh, c.sh, d.shの4fileだけがあると仮定し、reset直後にstaging&commitを行うとする(2回目のcommitになる)。
$ git reset HEAD~10 Unstaged changes after reset: M a.sh M b.sh M c.sh M d.sh $ git log commit f0f8ac18d38bf64ff3cab023fb7330280fdadcf4 (HEAD -> main) Author: ************ Date: Mon Sep 20 15:39:36 2021 +0900 最初のcommit $ git add . $ git commit -m "2回目のcommit" [main 736ed4a] 2回目のcommit 2 files changed, 6 insertions(+) $ git log --oneline 736ed4a (HEAD -> main, origin/main) 2回目のcommit # - 1台目PCのcommit履歴 f0f8ac1 最初のcommit
リモートリポジトリへ強制push
次にresetした後のcommit(新2回目)をGitHubなどのリモートリポジトリにpushする。但し、reset前の2回目のcommitからIDが変化しているので普通にpushするとマッチしないためエラーで停止する。そこでpushコマンドに強制オプションの-fを付けて実行することで、resetを行ったローカルリポジトリの内容をリモートリポジトリに強制的に更新する。
この記事では自分のPC間のリポジトリ共有を対象としているので納得した上で行えるが、複数人で開発している場合は事前許諾を得てから行わなければならない。
$ git push -f Enumerating objects: 9, done. Counting objects: 100% (9/9), done. Delta compression using up to 8 threads Compressing objects: 100% (5/5), done. Writing objects: 100% (5/5), 788 bytes | 788.00 KiB/s, done. Total 5 (delta 4), reused 0 (delta 0) remote: Resolving deltas: 100% (4/4), completed with 4 local objects.
conflict発生
さてリモートリポジトリにreset後のcommitが無事に登録出来たので、もう1台のPCでこのリモートリポジトリからgit pull
すると、ここでもconflictが発生して本当なら2つだけのcommitのはずが4つcommitが見えており、マージ作業が求められる。これはもう1台の2番目のcommit(reset前のcommit:旧IDとする)とreset&push&pullした新しいcommitのID(新ID)が一致しないためで、このconflictも当然の結果だ。
$ git pull remote: Enumerating objects: 9, done. remote: Counting objects: 100% (9/9), done. remote: Compressing objects: 100% (1/1), done. remote: Total 5 (delta 4), reused 5 (delta 4), pack-reused 0 Unpacking objects: 100% (5/5), done. From https://github.com/********* + e0a9a2d...0fccb57 main -> origin/main (forced update) Merge made by the 'recursive' strategy. $ git log --oneline 9739d8f (HEAD -> main) Merge branch 'main' of https://github.com/********** into main 0fccb57 (origin/main) 2回目のcommit # - git pull後のcommit(新ID) e0a9a2d 2回目のcommit # - git pull前のcommit(旧ID) f0f8ac1 最初のcommit
しかし本来commitの整理のためにgit reset
やgit commit --amend
を使ったのに、その都度余分なマージcommitが生成される様では本末転倒である。ここでやりたいのはgit push -f
の様に強制的なgit pull
なのでその方法を以下説明する。
git-pullの強制conflictの解決方法
作業対象をmainブランチとする。なお下記作業を行うとstaging、作業ディレクトリにある変更が全て消失する点に注意。
$ git checkout main # - mainブランチにcheckoutする $ git fetch origin main # - リモートの最新状態を取り込む $ git reset --hard origin/main # - hardモードでリセット $ git log --oneline 736ed4a (HEAD -> main, origin/main) 2回目のcommit # - 1台目PCと同じcommit履歴になる f0f8ac1 最初のcommit
既にgit pullでconflictしてしまった場合も以下で対応出来る。
$ git merge --abort # - mergeを取り消す $ git fetch origin main # - リモートの最新状態を取り込む $ git reset --hard origin/main # - hardモードでリセット $ git log --oneline 736ed4a (HEAD -> main, origin/main) 2回目のcommit # - 1台目PCと同じcommit履歴になる f0f8ac1 最初のcommit
上記の様に2台目のPCも1台目のPCと同じく整理されたcommit状態になる