リモートとローカルでリポジトリの始祖が異なると不具合が起きる話とgitの復習

知り合いのエンジニアの方にRailsで簡単なAPIを作るという課題を出していただいており、やや久しぶりにgitを触った(add、commit、push以外のことをした)。その際初っ端から不具合が出たので、復習を兼ねたgitのコマンド・概念の整理とともに記事にする。

リモートとローカルでリポジトリの始祖が異なると・・・

※簡易な記事なのでGithubで表示される画面のスクショは省略する。

したこととしては、まずgithubリポジトリを作成した。その際にReadmeも一緒に作成するオプションを選択した。 結論としてはここでReadmeを一緒に作成したことが面倒の原因となった。

その後ローカルでAPIモードでRailsの新規アプリケーションを作成し、ローカルリポジトリを作成。続いてadd・commit・pushを実施する。 ここでgithubを確認すると、本来であればローカルのmasterブランチからのinitial commitなのでするっとリモートのmainブランチに反映されるはずだが、なぜか反映されていない。pull requestボタンが表示されているので押すと以下のメッセージが表示されている。

There isn't anything to compare. main and master are entirely different commit histories.

なんじゃそりゃ・・・という感じだが、メッセージの訳とググった内容を見ると、この不具合はリモートレポジトリとローカルレポジトリの始祖が一致していないために発生するもののようだった。

つまりリモートでReadmeを作成したので勝手に1度目のコミットがなされ、ローカルはローカルでもちろんコミットをしているので、2つのリポジトリ間で始まりとなるコミットが一致しておらず、そのため「コミットの経歴の異なるブランチ同士ではマージができないよ」みたいな怒られが発生していた。

一旦pullしたらなんか一緒くたにできんかと思い実行。しかし以下のエラーが表示される。

fatal: refusing to merge unrelated histories

おそらく経歴的に関連していないブランチ同士をマージすることはできないと言われている。

調べるとgit mergeに--allow-unrelated-historiesのオプションを付与すると、この状態でもマージが可能なようなので以下のように叩く。

git merge --allow-unrelated-histories origin/main

その後リモートとローカルでmasterの役割を果たすブランチの名前が異なっていてはややこしいので、ローカルにmainブランチを作成する。そちらに移動してmasterをmainにマージしたのち、再度push。

Githubを確認すると、ローカルのファイルが正常に反映されていた!これでOK。 次回からgithubで新しくリポジトリを作る際は空コミットにするよう気をつけたい。

以下からは復習を兼ねて、gitのコマンドといくつかの概念を簡単に整理する。

gitの基本的なコマンド
  • git init
  • git add
    • 選択したファイルをステージングエリアに上げる
  • git commit
    • ステージングエリアにあるファイルをローカルリポジトリに上げる
  • git push
    • ローカルリポジトリにあるファイルをリモートリポジトリに上げる
    • git push origin ブランチA というコマンドはリモートリポジトリのブランチAに対してpushを行うということ。originは登録したリモートリポジトリのURLを指している
  • git fetch
    • リモートのmainブランチからローカルのorigin/mainブランチにアプリのファイルデータを取り込む
  • git merge
    • ローカルのorigin/mainブランチからmain(名前を修正していない場合はmaster)ブランチへアプリのファイルデータを取り込む
  • git pull
    • fetchとmergeを同時に行う
  • git rebase
    • developブランチなどを切った元となるコミットを、元々の地点からmasterブランチの今一番新しいコミットの地点に変更する
gitに関する基本的な概念
  • ステージングエリア
    • ワーキングディレクトリで修正したファイルの中でローカルレポジトリにコミットをしたいものを置く場所
  • コミット
    • ステージングエリアにあるファイルをローカルリポジトリに上げること
    • コミットはIDを持っている
    • コミットは自身の1つ前のコミットのIDを参照するポインタを持っており、これにより各コミットは一つの繋がりを作ることができる
  • ブランチ
    • 実体としてのブランチは、コミットに参照をするポインタ
    • 概念としてのブランチは、参照しているコミットから一番初めのコミットまでの繋がりのこと
gitの基礎の図解

ローカル・リモートリポジトリの関係を表した図。 f:id:rinda_1994:20210725221501p:plain

コミットとブランチについて表した図。 f:id:rinda_1994:20210725221600p:plain

参照記事

Githubのデフォルトブランチがmainであることに気付かず、ローカルのブランチをmasterのままpushし、エラーが出たときの対処法 - Qiita

[Git] fatal: refusing to merge unrelated historiesを解決する話 - Qiita

Git - ブランチとは

git add ってなんのためにやるの? Gitの「ステージング」をイラストで解説します! – KRAY Inc.

【Git】新人エンジニア、git pushまでの道 - Qiita

【Git】基本コマンド - Qiita