このヘージと関連するページ一覧

  • 一人でイチから始めるGIT 導入編
  • 一人でイチから始めるGIT gitignore
  • 一人でイチから始めるGIT push
  • 一人でイチから始めるGIT merge
  • 一人でイチから始めるGIT conflict

  • このページの最終更新日 2020/10/25

    一人でイチから始めるGIT merge

     このページは一人でイチから始めるGIT push の続きのページです。

    1、pull

     複数人でプログラムの開発作業を行うときは、それぞれの作業者のPCにローカルリポジトリを作成し、1つのリモートリポジトリを介在して、ソースコードの結合などの管理を行うことが多いかと思います。一人でプログラムを管理している場合には、複数ローカルリポジトリを作って作業することはあまりないかもしれませんが、ここでは練習をすることを目的に、2つのローカルリポジトリを作成してみます。ここでは、デスクトップに「git_test」というフォルダを作成し、その中に「local1」「local2」「remote」の3つのフォルダを作成します。
    一人でイチから始めるGIT pushのページと同様に「remote」はリモートリポジトリを置くフォルダとします。まず「remote」フォルダ内で「git init --bare」とコマンドを打ってリモートリポジトリを作成しましょう。また、「local1」に 「1~5」までの数字を1行ずつCRLF改行区切りで記載した「f1.txt」ファイルを作成しておきます。

    - local1 first commit - 「f1.txt」ファイル
    1
    2
    3
    4
    5
    

     その後「local1」フォルダ内で「git init」「git add f1.txt」「git commit -m "local1 first commit"」とコマンドを実行して作業ツリーのファイルをローカルリポジトリに登録します。さらに、「git remote add origin ~/Desktop/git_test/remote」とコマンドを実行してリモートリポジトリの参照先を指定し、「git push origin master」と打ってリモートリポジトリへ反映させます。


     次に「local2」フォルダへ移動して、「git init」「git remote add origin ~/Desktop/git_test/remote」「git pull origin master」と順にコマンドを実行していきます。「git pull origin master」で「remote」フォルダのリモートリポジトリのファイルを「local2」フォルダへ取り込んでいます。「local2」フォルダに「f1.txt」ファイルが作成されていることを確認して、最後に「git log --oneline」とコマンドを打ち pic.1のように 「local1 first commit」コミットが取り込まれていることも確認しておきましょう。

    ローカルでPullの練習

    - pic.1 -

    2、merge(First forward)

     「1、pull」の項目で、local1で作成したファイルを、local2へ反映させることが出来ました。引き続きlocal1でファイルを編集し、編集した内容をlocal2へ反映させる方法を学習していきます。

     まず、local1フォルダでf1.txtをテキストエディタで開き、1と2の間に「aaaaa」と書き込んで保存して下さい。その後「git add f1.txt」「git commit -m "local1 second commit"」 「git push origin master」とコマンドを実行します。

    - local1 second commit - 「f1.txt」ファイル
    1
    aaaaa
    2
    3
    4
    5
    

     次にlocal2フォルダへ移動し、「git fetch」コマンドを実行します。このコマンドによってリモートリポジトリの内容が取り入れられますが、作業ツリーのファイルは何も変化しません。ローカルリポジトリがどうなっているかは「git log --oneline --all」コマンドを実行して確認します。後に説明しますが「git log --oneline」では全てのコミットを見ている訳ではなく、ここでは「--all」オプションを付けていないとfetchしたコミットを見ることが出来ないことに注意して下さい。
     前のページでも説明したように「HEAD -> master」が現在の作業ツリーのファイルがどのコミットの内容なのかを示しており、「origin/master」がリモートリポジトリのコミットを示しています。この段階では、「HEAD -> master」が「origin/master」の前にあるので、まだ現在の作業ツリーはリモートレポジトリを反映出来ていないことが解かります。

     ここで「git merge origin/master」とコマンドを打つと、リモートリポジトリのファイルがlocal2フォルダに反映されるので、local2のf1.txtファイルを開いて「aaaaa」が追記されていることを確認して下さい。また「git merge origin/master」コマンドを実行したときに、「Fast-forward」というメッセージが表示されていることも覚えておきましょう。「Fast-forward」については後ほど説明します。
     基本的に「1、Pull」で行った「git pull origin master」と「2、Fetch/Merge」で行った「git fetch」「git merge origin/master」は同様の動作をしますが、執筆者としては「fetch」の後に「log」などでリモートリポジトリの内容の確認を行ってから、「merge」を行うようにしています。

    ローカルでMerge(First-forward)の練習

    - pic.2 -

    3、merge(3-way merge)

     今度は「local1」と「local2」で同時にf1.txtファイルを編集し、同時に行われた変更の両方を不整合なく取り入れることを考えます。まず、「local1」フォルダで「f1.txt」ファイルの「3」の行を消して「b」と文字を追加し、「git add f1.txt」「git commit -m "local1 third commit"」「git push origin master」とコマンドを実行します。

    - local1 third commit - 「f1.txt」ファイル
    1
    aaaaa
    2
    b
    4
    5
    

     次は「local2」フォルダで「f1.txt」ファイルの「5」の行を消して、「c」と書かれた行を追加します。「git add f1.txt」「git commit -m "local2 first commit"」とコマンドを打って、その後さらに「git push origin master」とコマンドを打ちます。

    - local2 first commit - 「f1.txt」ファイル
    1
    aaaaa
    2
    3
    4
    c
    

     pushを行おうとすると pic.3のようなエラーが表示されます。「local1」が既にリモートリポジトリに反映したファイルを、local2が取り込まないままにlocal2の編集内容をリモートリポジトリへ反映することは出来ません。

    ローカルでMerge(3-way)の練習

    - pic.3 -

     そこで、まず local2では fetch を行ってリモートリポジトリの内容を取り込むことにします。「git fetch」コマンドを打ったあと、「git log --oneline --all」コマンドを打ってコミットを確認します。ただし、「git log --oneline --all」では分岐したコミットを判断することが出来ないため --graphオプションを追加して「git log --oneline --all --graph」とコマンドを打ちます。pic.4 のように表示されることを確認しましょう。
     「local2 first commit」も「local1 third commit」も、「local1 second commit」の後に行われたコミットであり、グラフもその通りになっています。


    logのgraphオプション

    - pic.4 -

     次に「git merge origin/master」をコマンドを打つと、pic.5のようなvi画面が起動されるため、「Esc」キーを押してviのコマンドラインモードへ切り替えて「:wq」と打って「Enter」キーを押します。

    Auto Merge時の編集画面

    - pic.5 -

     pic.6のようにコミットグラフが書かれていることを確認しましょう。

    Auto Merge時のgit更新画面

    - pic.6 -



     また、「local2」での「f1.txt」ファイルは次のようになっていることも確認してみて下さい。local1 で「3」から「b」へ変更した行と、local2 で「5」から「c」へ変更した行が共に適切に反映されていることが解ります。

    - local2 first commit - 「f1.txt」ファイル
    1
    aaaaa
    2
    b
    4
    c
    

     このようなマージの仕方は3-wayマージというように呼ばれます。3-wayとは、変更があった2つのコミットと、その2つのコミットの共通祖先を比較してマージを行うことを言います。何故、共通の先祖を含めて比較する必要があるのかを考えてみます。
     local1とlocal2で変更を行ったファイルの中身は次のとおりでした。

    - local1 third commit - 「f1.txt」ファイル
    1
    aaaaa
    2
    b
    4
    5
    
    - local2 first commit - 「f1.txt」ファイル
    1
    aaaaa
    2
    3
    4
    c
    

     この2つを比較してみると、local1で「b」となっている箇所がlocal2では「3」となっていて、local1で「5」となっている箇所がlocal2では「c」となっていますね。けれどもこの2つを較べただけでは、「3」と「b」のどちらの情報が最後に更新されたものなのか解りませんし、「5」と「c」のどちらが最新のものなのかも解りません。
     この2つのコミットと、その共通祖先である「local1 second commit」の情報を比較することで、元々「3」だった箇所がlocal1で「b」に変更され、元々「5」だった箇所がlocal2で「c」に変更されたのだ、ということが初めて判断出来る訳です。それ故に、マージを行うときには共通祖先の情報も用いられるのです。

    - local1 second commit - 「f1.txt」ファイル
    1
    aaaaa
    2
    3
    4
    5
    

     これでようやく、local1で変更したファイルとlocal2で変更したファイルを整合性のある状態で統合させることが出来ました。最後に「local2」フォルダで「git push origin master」「git log --oneline --all --graph」とコマンドを打って、リモートレポジトリにマージした情報を反映させましょう。「origin/master」の位置が、マージしたコミットの位置に来ていることを確認してみて下さい。

    mergeした後にpushする

    - pic.7 -

     3-wayマージについての説明はひと段落しました。ここでは「local1 second commit」を祖先として、local1では「3」を「b」に変更、local2では「5」を「c」に変更しました。では、local2で「5」を「c」に変更するところを、「3」を「d」に変更した場合は、マージしたときにどうなるのでしょうか。この場合は、「3」を「b」に変更した情報を優先させれば良いのか、「3」を「d」に変更した情報を優先させれば良いのか、gitは自動的には判断出来ません。このような状態をgitの世界では競合(conflict)が発生した、というように表現されます。conflictについては次のページで説明します。