|||||||||||||||||||||

なんぶ電子

- 更新: 

gitのコンフリクト解消

github

githubの使い方に慣れてきていろいろなPC経由で pushするようになったら、pullが必要だと言われ、pullしたら今度はコンフリクト(競合)が発生しました。今回はこれに対処します。

gitの基本操作のおさらい

コンフリクトはリモートの FETCH_HEAD とローカルブランチ間で起きる現象なので、まずはブランチについておさらいしておきます。まずは、ブランチの生成と選択方法です。「 git branch ブランチ名 」でブランチを生成します。このときブランチ名を指定しなければ既存のブランチの一覧が表示されます。ブランチを変更したい場合は checkout コマンドを利用します。

$ git branch branch1
...
$ git branch 
  branch1
* main
$ git checkout branch1
...
* branch1
  main

checkout コマンドの内容を言い換えると「ワークツリー」とよばれる作業ディレクトリの内容を変更するコマンドです。この時、「 HEAD 」とよばれる最新コミットへのポインタも合わせて変更されます。

マージしたりして役目を終えたブランチは -dオプションで削除することができます。

$ git checkout main
...
$ git branch -d branch1
...
$git branch
* main

ちなみにHEADポインタは HEAD~ や HEAD~2 というふうにN世代前の指定の仕方があり、コミットを取り消すような場合にも利用します。

$ git reset --hard HEAD~

pullとfetch

pull作業は、fetchというリモートレポジトリの変更を取得する過程と、リモートレポジトリのHEADをローカルレポジトリに取り込むmergeのふたつの作業に分解されます。

これは別々に行うことができます。切り離してみてみると仕組みがよく理解できます。

まず、fetchです。データを取得するだけなので、ここでは競合は表示されません。fetchしたリモートポジジトリは無名のブランチの扱いとなりますが、これを確認したい場合は git branch -r を使います。

$ git fetch 
...
$  git branch -r
  origin/HEAD -> origin/main
  origin/main

ローカルレポジトリから fetchで取得したブランチをマージするのですが、この際mergeに渡す引数として「 FETCH_HEAD 」というリモート側のHEADを意味する特殊なポインタを指定します。

merge はローカルレポジトリをコミットした状態でないと使えませんが、stash コマンドで一旦変更を退避させておくこともできます。

$ git merge FETCH_HEAD 
CONFLICT (add/add): Merge conflict in js/lib/focus-order.js
Auto-merging js/lib/focus-order.js
...
Automatic merge failed; fix conflicts and then commit the result.

通常はマージは自動で行われます。ただ自動で処理できずにマージに失敗する(コンフリクトが発生する)と人的な措置を求められます。

ブランチを作成してから(cloneしてから)ローカル側で変更した箇所と同じ箇所でリモート側も変更されていたりするとこのような状況になります。

コンフリクトの状況は「 $ git status 」コマンドにより状況を確認できます。

$ git status
On branch main
Your branch and 'origin/main' have diverged,
and have 2 and 1 different commits each, respectively.
  (use "git pull" to merge the remote branch into yours)

You have unmerged paths.
  (fix conflicts and run "git commit")
  (use "git merge --abort" to abort the merge)

Changes to be committed:
        new file:   test.php

Unmerged paths:
  (use "git add <file>..." to mark resolution)
        both modified:   sample.php
        both added:      QandA.pdf

Unmerget pathsにリストされた項目に対して修正が必要となります。

コンフリクトの確認と修正

コンフリクトが発生しているファイルがテキストファイルの場合は、任意のエディタからも確認や修正ができます。

その際、ファイル内のどこでコンフリクトが発生して修正が必要なのかは「マーカー」によって記されています。

「 <<<<<<< HEAD 」 から 「 ======= 」までの前半部が、ローカルレポジトリのデータで、「 ======= 」から「 >>>>>>> 」 までが後半でリモート側のデータです。

これは git diff コマンドによっても出力されます。

$ git diff
diff --cc sample.php
index 0267580,47ee72e..0000000
mode 100755,100644..100755
--- a/sample.php
+++ b/sample.php
@@@ -276,7 -276,7 +276,11 @@@
                <button id="btn-addFile" type="button" class="" onclick="addFileFromBotton(event);" style="margin-bottom: 20px; background:#ccf; color: #333;">
                追加
                </button>
++<<<<<<< HEAD
 +              <!-- <button type="button">dummy</button> -->
++=======
+               <button type="button">dummy</button>
++>>>>>>> 572219c4e257fd135470c9898cf6cd5bbb490002
...
diff --cc QandA.pdf
index e8fe411,e8fe411..0000000
mode 100755,100644..100755
Binary files differ
...

バイナリファイルの場合はマーカーが挿入できす、「 Binary files differ 」という表示のみにとどまります。これはエディタを使っての手動での修正も困難です。そのような場合は、特定のバージョンのファイルで上書きするなどの方法をとります。

変更後 git add を経て git commit することでマージが完了します。

修正不可能な競合を見つけたりして、マージを取り消したい場合は、「 git merge --abort 」を実行します。これによりマーカーは除去されます。

$ git merge --abort

コンフリクトをまとめて解決

ローカル側かリモート側のどちらかを選択してまとめてコンフリクトを解決させることができます。これは多くのデータでコンフリクトが発生しているような場合などに有効です。

git checkout --ours コマンドを使うとすべての競合をローカル側のデータで解決します。逆の場合(マージの引数で指定する側ここではFETCH_HEAD)で更新したい場合はは --theirs とします。

$ git checkout --ours
または
$ git checkout --theirs
...
$ git add -A
...
$ git commit -m "All conflicts resolved."
...
$ git push
...

筆者紹介


自分の写真
がーふぁ、とか、ふぃんてっく、とか世の中すっかりハイテクになってしまいました。プログラムのコーディングに触れることもある筆者ですが、自分の作業は硯と筆で文字をかいているみたいな古臭いものだと思っています。 今やこんな風にブログを書くことすらAIにとって代わられそうなほど技術は進んでいます。 生活やビジネスでPCを活用しようとするとき、そんな第一線の技術と比べてしまうとやる気が失せてしまいがちですが、おいしいお惣菜をネットで注文できる時代でも、手作りの味はすたれていません。 提示されたもの(アプリ)に自分を合わせるのでなく、自分の活動にあったアプリを作る。それがPC活用の基本なんじゃなかと思います。 そんな意見に同調していただける方向けにLinuxのDebianOSをはじめとした基本無料のアプリの使い方を紹介できたらなと考えています。

広告