git mergeとは?gitのコミットやブランチの仕組み・マージの発生するpullやpush

git mergeとは?
gitではローカルにリポジトリを作り開発の殆どがローカル環境で行われ、ブランチと呼ばれる単位で作業を進めます。
マージとはブランチを統合することで基本的にはそれぞれの編集を足し合わせたものになります。
大まかに新たに作業用のブランチを作って編集し、問題がないことを確認したら本流のブランチにマージさせる流れになります。
またネットワークを利用して複数のローカルリポジトリからそれぞれのブランチをネットワーク上に共用するリモートリポジトリのブランチに足し合わせ開発を進めていくことができます。
リモートリポジトリとローカルリポジトリの間でブランチを更新するgit push、git pullでも内部でマージが行われています。
簡単なgit mergeの使い方
gitを使用しての作業はブランチの中で行われます。ブランチとは開発の本流から分岐して、本流の開発を邪魔することなく作業を続ける機能のことです。
1
2
3
|
$ git init #新規のgitリポジトリを作成する
$ git add . #管理対象のファイルを登録する
$ git commit -m "Initialize repository" #最初のコミットでmasterブランチが作られる
|
または
1
|
$ git clone (ネットワーク上などにあるリモートリポジトリ)
|
などのコマンドで自端末にローカルリポジトリを作成すると最初はmasterブランチのみが存在します。このmasterブランチが開発の本流となるブランチです。
以下リモートリポジトリをリモート、ローカルリポジトリをローカル、masterブランチをマスターと呼び、新しく作成したブランチはブランチ名で呼ぶことにします。
HEADとは
ブランチを確認します。ここでブランチとHEADの意味を確認します。
1
|
$ git branch #最初はmasterのみが存在して、HEADはmasterを指している
|
gitはコミットを差分で管理しません。gitではコミットはツリーオブジェクトと一つ前のコミットへのポインタ(データの位置を示すデータ)などを格納したオブジェクトです。ツリーオブジェクトは変更ごとに圧縮されたファイルの参照とディレクトリの構造などを格納しています。
ブランチとは一連のコミットオブジェクトの先頭を指すポインタです。コミットをするとブランチは自動的に進みます。一番先頭から以前のコミットのポインタをたどり開始点まで戻ることができます。
ブランチが切られると元のブランチの先頭と同一のコミットオブジェクトを指したポインタが作られ、そのブランチでコミットすることで元のブランチとは別に進みます。
git checkout (ブランチ)と入力することで編集するブランチの切り替えができ、編集しているブランチの先頭を指すポインタがHEADです。
新しくブランチを作る(ブランチを切る)
新しくブランチを切って作業を進めます。
1
2
|
$ git checkout -b ' add-modify1(ブランチ名)'
$ git branch #ブランチが新たに作られ、HEADがadd-modify1を指しています
|
このブランチは作業のために短期間使う一時的なブランチでトピックブランチと呼ばれます。ブランチの運用方法は複数あって今回はgit-flowを簡略化したmasterブランチとtopicブランチで構成されるGitHub Flowを採用しています。
移動したブランチadd-modify1内で編集を進めます。
トピックブランチで行われた編集をコミットする
区切りの良いところでtopicブランチで行われた変更をコミットします。gitはコミットする度にディレクトリ下のファイルを圧縮して.gitに格納するので、たとえ一文字編集してコミットしたとしてもコミットの度にデータ量は肥大します。
追跡する必要のないファイルは.gitignoreに記入することをおすすめします。
1
2
|
$ git add . #ディレクトリ内のファイルをgitの管理下に置きます。
$ git commit -m "added some code1" # メッセージを付けてコミットします
|
マスターブランチにマージする
add-modify1からマスターに移動します。
1
|
$ git checkout master #HEADをマスターに移動します
|
add-modify1をマスターにマージします。
1
|
$ git merge add-modify1
|
以下同様に編集するためのブランチを切り、マスターにマージするという作業を繰り返します。既にマージされているトピックブランチは削除しても構いません。
1
2
|
$ git branch --merged #マージ済みのブランチを表示する
$ git branch -d add-modify1 # add-modify1を削除する
|
このコマンドは既にマージされているブランチにしか適用されません。マージされていないブランチを削除する場合は次のように入力します。
1
|
git branch -D add-modify2 #まだマージされていないadd-modify2を削除する
|
複数のブランチで開発する
同じマスターから複数のトピックブランチを作って異なる機能などを並列に開発することがあります。
1
2
|
$ git branch add-modify2 #add-modify2ブランチが作られます
$ git branch add-modify3 #add-modify3ブランチが作られます
|
それぞれ交互にブランチに入って作業を進めていきます。
1
2
|
$ git checkout add-modify2
$ git checkout add-modify3
|
先にadd-modify2をマージします。
1
2
|
$ git checkout master
$ git merge add-modify2
|
次にadd-modify3をマージします。
1
|
$ git merge add-modify3 -m "merge add-modify3"
|
ディレクトリ内のファイルで重複した場所を編集していなければ後て解説するコンフリクトが発生せず無事にマージは成功します。
fast-forwardマージ、non fast-forwardマージとは
add-modify2をマージする時点で、マスターはまだコミットされておらず、コミットの履歴はadd-some2と同じものになります。マスターのHEADはadd-modify2の先頭のコミットオブジェクトに移動するだけで何も作成されません。このようなマージをfast-forwardマージとよびます。
これに対してブランチadd-modify3をマージした時、ブランチadd-modify3が切られた時点からマスターとadd-modify3ブランチのそれぞれの変更の差分を取った新しいファイルが作成されます。
この状態はマスターのポインタを進めるだけでは実現できず、直前のコミットへのポインタを二つ格納した新しいコミットオブジェクトが生成されます。これがnon fast-forwardマージです。
non fast-forwardマージはコミットが発生するのでmerge -m “merge some branch”のようにコミットメッセージを通常のコミットと同じように入力します。
non fast-forwardマージを強制する
ここでadd-modify2をマージするようなケースでも作業上のルールでマスターのマージをコミットとして履歴に残して管理していることがあります。fast-forwardマージが適用されるケースでもnon fast-forwardマージを強制する書き方があります。
1
|
$ git merge --no-ff add-modify2 -m "marge add-modify2 "
|
このように書くとadd-modify2のマージにもマージコミットとして残ります。
リモートリポジトリからPullする
複数人での開発ではリモートに最新のコミットを登録して、ローカル側はリモートと同期を取る必要があります。ここではリモートのマスターを取得します。マスター以外のブランチを取得することも可能です。
リポートのブランチを取得しローカル側に置きます。
1
|
$ git fetch origin master #originはリモートを意味します
|
ローカルのブランチにマージします。ここでマージを行うブランチはローカルのマスターです。
1
2
3
|
$ git checkout master #マージを行うブランチに入る
$ git marge origin/master
#origin/masterはローカル側に作られたリモートのマスターブランチを追跡するブランチです
|
git pullとは
以上の操作はこちらのコマンドと等価です。
1
2
|
$ git checkout master #ローカルのマスターブランチに入ります
$ git pull origin master #取得とマージが行われます
|
一回の操作で済むので便利ですが、中止したい場合や元に戻したい場合などの処理は煩雑になります。
ローカルのブランチをリモートにPushする前には必ずリモートのブランチをローカルのブランチにPullして同期させ、その後ローカルのトピックブランチをマージしてリモート側がfast-forwardマージの形になるようにします。
ここでローカルのマスターをリモートのマスターにPushすることを考えます。ローカルのマスターには編集がコミットされたブランチadd-modifyがあります。まずリモートのマスターと同期させます。
1
2
|
$ git checkout master
$ git pull origin master
|
次にadd-modifyをマージします。
1
2
|
$ git merge add-modify
$ git push origin master
|
rebaseとは
先程のgit pullコマンドによって同期させたローカルのマスターにadd-modifyブランチをマージする際、add-modifyのマージコミットが履歴として残ります。
add-modifyブランチのマージコミット履歴はリモートのログに記録すべきローカルで実装したファイルの追加や編集のコミットとは違い開発の流れに関係ない履歴であるとして、リモートにPushする際には取り除くように作業ルールに決められていることもあります。
そこで先程のadd-modifyをマージする代わりにadd-modifyを作り変えます。ローカルのマスターはマージをするだけで自身を編集してコミットしないものとします。
1
2
|
$ git checkout add-modify
$ git rebase master
|
または次のコマンドでここまでのPullからrebaseまでを行えます。
1
2
|
$ git checkout add-modify
$ git pull --rebase origin master
|
これによってマスターの後にadd-modifyが一連のコミットとなり、先程のマージコミットは生成されません。ただしrebase後のadd-modifyはリベース前とは異なるコミットオブジェクトです。あとはこのブランチをマスターにfast-forwardマージさせPushします。
1
2
3
|
$ git checkout master
$ git merge add-modify #fast-forwardでマージされる
$ git push origin master
|
リモートリポジトリにPushする
複数人でリモートリポジトリを共有して開発を進める際、作業者それぞれがPush(リモートのブランチにローカルのブランチをアップロードしてマージする操作)を繰り返して最終的な成果物になります。
まずローカルのトピックブランチadd-modifyをリモートのマスターでリベースします。
1
2
|
$ git checkout add-modify
$ git pull --rebase origin master #リモートのマスターと同期させリベースする
|
次にローカルのマスターでadd-modifyをマージします。
1
2
|
$ git checkout master
$ git merge add-modify #fast-forwardマージになります(マージ履歴が残りません)
|
現在のローカルのマスターはリモートのマスターからadd-modifyのコミットの分だけ進んでいるのでfast-forwardマージの形でリモートリポジトリにPushできます。git pushはfast-forwardマージの形でない場合エラーになり強制的にgit push -fによってPushすることになります。
1
|
$ git push origin master #リモートのマスターブランチにPushします
|
git mergeを自由自在に使う方法
これまでマージする際にコンフリクトが発生しないものとして説明してきましたが複数人の関わる実際の開発ではコンフリクトは頻繁に発生します。
また間違ったブランチをマージしたり、マージした後でバグが判明するなどマージを取り消す必要が出てくることがあります。
マージをなるべく頻繁に行うことで、コンフリクトの発生やバグなどを早い段階で発見できることになります。
コンフリクトが発生したら
ブランチ内の異なる箇所で変更があった場合にはコンフリクトは発生しません。コンフリクトが発生するのは同一のブランチから切られたブランチで編集された内容が、同一の箇所を編集し内容が異なっている場合です。
また編集だけではなく片方のブランチで編集されたファイルがもう一方では削除されている場合もコンフリクトが発生します。
もちろんもう既にマージされているブランチで行った変更箇所を、新たにブランチを切って編集してもコンフリクトは発生しません。
コンフリクトが発生するのはgit mergeコマンドだけでなくrebaseでも同様に発生しますが対処方法は基本的には同じです。またPushやPullでも発生する可能性があります。
事前に確認する
git diffを使うと重複して編集しているファイルと場所を確認することができます。これからマージするローカルブランチ同士を比較するには
1
|
$ git diff ブランチA..ブランチB #ブランチAからブランチBへの変更が表示されます
|
左側のブランチが変更前、右側のブランチが変更後として表示されます。現在いるブランチとリモート(origin)ブランチ(master)と比較するには( origin/master はgit fetchやgut pullで最新の状態に更新されます)
1
|
$ git diff HEAD origin / master #リモートの方が進んでいる場合
|
または
1
2
|
$ git diff origin / master HEAD
#ローカルのほうが進んでいる場合
|
もし既存の編集したファイルが片方では存在しない場合や、同一のファイル内の同じ箇所にマイナス記号とプラス記号が隣接して表示されている場合は同一箇所を重複して編集している可能性が高くなるのでコンフリクトの有無を確認してローカルのファイルを編集し直すか、他の作業者のリモートリポジトリの操作ミスを確認したほうがよいでしょう。
状態を確認するコマンド
git diffの表示を見ると注目すべき箇所は—+++ファイル名と書かれている行で2つのファイルの間で異なっている箇所が存在することを示しています。
1番目の引数が変更前で2番目の引数が変更後と見なされます。@@で囲まれた数字は二つのファイルのそれぞれ表示を開始する行と行数を示しています。+が追加された箇所で-が削除された箇所です。
git logはこれまでのリポジトリのコミットの履歴が全て表示され、冒頭の長い英数字はコミットIDと呼ばれリポジトリ内のコミットを一意に識別します。またgit logブランチ名を入力してこのブランチで行われたコミットだけを表示することができます。
先程のgit diffでも二つの時点のコミットIDを指定することによって二つのコミット間の差分を調べることができます(コミットIDは最初の7桁ぐらいで全て入力する必要はありません)。
コミットを引数に取る他のコマンドでもコミットIDでコミット指定することができます。
優先するブランチを指定する
コンフリクトが発生しているファイルでどちらかのブランチを指定することによって、コンフリクトを解決できます。
file1でコンフリクトが発生し現在いるブランチのファイルを取り込む場合は
1
2
|
$ git checkout --ours file1
$ git add .
|
もう一方のブランチのファイルを取り込む場合は
1
2
|
$ git checkout --theirs file1
$ git add .
|
最後に
1
|
$ git commit
|
直接コンフリクトを起こしている箇所を編集する
コンフリクトが発生したらgit statusを入力するとマージに失敗したファイルがUnmerged pathsに表示されます。
直接コンフリクトを起こしているファイルをエディタで開くとマージしようとした重複して編集した箇所が次のように表示されています。
1
|
<<<<<<< HEAD マージする側の編集した内容 ======= マージされる側の編集した内容 >>>>>>> ブランチ名
|
この部分を直接編集してもコンフリクトを解決できます。修正したらコミットします。注意としてこの状態のファイルでも次のコマンドを実行すればコミットは通ってしまいますので必ず編集してコンフリクトを解決してください。
1
2
|
$ git add .
$ git commit -m "ブランチを修正してマージ"
|
コミットするとマージコミット(non fast-forwardマージ)が生成されます。
git mergetoolを使う
gitではコンフリクトが発生した際の編集ツールとしてマージツールが多数用意されています。編集しながらdiffを確認することができます。
1
2
3
|
$ git mergetool --tool-help #使用できるマージツールを確認する
$ git mergetool --tool=vimdiff #例としてマージツールにvimdiffを使う
$ git config --global merge.tool (マージツール )#デフォルトで使用するマージツールを設定する
|
git mergeを取り消す
マージする際またマージした後の何らかの不具合でマージを取り消す方法について紹介します。
マージの段階でコンフリクトが発生し、その時点でファイルの編集せずにマージを中止する場合には次のように入力します。
1
|
$ git merge --abort #マージを中止する
|
マージの段階でコンフリクトが発生し、ファイルの編集をしたが結局中止する場合には次のように入力します。
1
|
$ git reset --hard HEAD # マージを中止しファイルはマージの時点のまま(編集した内容は破棄する)
|
マージを完了した後に取り消す
次にマージを完了しましたが何らかの理由でマージする前の状態に戻すことを考えます。
1
2
3
|
$ git show #もしくはgit logでマージしたコミットIDを調べる
$ git revert -m 1 (マージコミットのID)
#数字はどちらの親コミットに戻すのか二つのコミットIDの左端の数字を指定
|
fast-forwardでマージコミットのないマージをした場合はgit logでマージしたブランチで行われたコミットを全て抽出します。
1
|
$ git revert コミットID#マージしたブランチで行われた全てのコミットを打ち消します
|
この方法でマージコミットの取り消しをしてもそれ以降に行われたマージコミットは取り消しされません。またこの方法では取り消したマージコミットとマージコミットを取り消すコミットが二つ履歴に残ります。
この方法だと取り消した同じマージを再び行おうとするとマージができなくなります。一度コミットしてコミットIDを変える必要があります。
次に述べるマージの取り消し方法と比べると手間がかかりますが複数人が使用する共用リポジトリにPushした場合にはこの方法を取るべきです。
完了したマージを履歴ごと無かったことにする
完全にマージとそのコミット履歴を消したい場合に使います。
1
|
$ git log #ログを確認する fast-forwardでマージした場合はマージコミットは生成されません
|
ORIG_HEADはHEADが移動するコマンドを実行した時、コマンド実行する時点のHEADの位置を記録しています。代わりにログに記載されているコミットのハッシュ値(最初の七桁ぐらい)を引数としてハッシュ値のコミットをした時点に戻ります。また移動させるHeadの位置の指定は^の数でも指定でき、HEAD~数字でも同様の指定が出来ます。例えばHead^^^(Head~3)は3つ前のコミットの直前です。
1
|
$ git reset --hard ORIG_HEAD #HEADがマージコマンドを実行する時点の位置に戻る
|
この方法はローカルリポジトリで行う場合は便利ですが、変更したブランチを他の開発者と共有するリポジトリにgit push -fで強制的にPushすることは避けたほうがいいでしょう。
修正前のブランチをPullしていた他の開発者はローカルに修正前のブランチを取り込んだまま編集を進めてしまい、次のPullで打ち消すことができなくなります。リモートへ取り消しの変更をPushする場合はgit revertを使うことをおすすめします。
まとめ
ここまで読んでいただき誠にありがとうございました。今回はgit mergeを中心にしたgitの操作について解説してきました。
gitはコマンドとオプションが豊富に用意されていますので詳細は
1
2
3
4
5
|
$ git help -a
$ git help <gitのコマンド名>
$ git <gitのコマンド名> --help
$ man git-<gitのコマンド名>
|
などでコマンドで確認してください。
公式のサイトのドキュメントもお読みください。
ネプラス株式会社はサービス開始から10年以上
『エンジニアの生涯価値の向上』をミッションに掲げ、
多くのインフラエンジニア・ネットワークエンジニアの就業を支援してきました。
ネプラス株式会社はこんな会社です
秋葉原オフィスにはネプラス株式会社をはじめグループのIT企業が集結!
数多くのエンジニアが集まります。

-
インフラ業界に特化
ネットワーク・サーバー・データベース等、ITインフラ業界に特化。Cisco Systemsプレミアパートナーをはじめ各種ベンダーのパートナー企業です。
業界を知り尽くしているからこそ大手の取引先企業、経験豊富なエンジニアに選ばれています。
-
正社員なのにフリーランスのような働き方
正社員の方でも希望を聞いたうえでプロジェクトをアサインさせていただいており、フリーランスのような働き方が可能。帰社日もありません。
プロジェクト終了後もすぐに次の案件をご紹介させていただきますのでご安心ください。
-
大手直取引の高額案件
案件のほとんどが大手SIerやエンドユーザーからの直取引のためエンジニアの皆様へに高く還元できています。
Ciscoをはじめ、Juniper、Azure、Linux、AWS等インフラに特化した常時300件以上の案件があります。
-
スキルアップ支援
不要なコストを削減し、その分エンジニアの方へのスキルアップ支援(ネットワーク機器貸出、合格時の受験費用支給など)や給与で還元しています。
受験費用例)CCNP,CCIE:6-20万円、JNCIS:3-4万円、AWS:1-3万円など
※業務に関連する一定の資格のみ。各種条件がありますので詳しくは担当者へにお尋ねください。
-
現給与を保証します!※
前職の給与保証しており、昨年度は100%の方が給与アップを実現。収入面の不安がある方でも安心して入社していただけます。
※適用にはインフラエンジニアの業務経験1年以上、等一定の条件がございます。
-
インセンティブ制度
ネットワーク機器の販売・レンタル事業等、売上に貢献いただいた方にはインセンティブをお支払いしています。
取引先企業とエンジニア側、双方にメリットがあり大変好評をいただいています。
-
社会保険・福利厚生
社員の方は、社会保険を完備。健康保険は業界内で最も評価の高い「関東ITソフトウェア健康保険組合」です。
さらに様々なサービスをお得に利用できるベネフィットステーションにも加入いただきます。
-
東証プライム上場企業グループ
ネプラスは東証プライム上場「株式会社オープンアップグループ」のグループ企業です。
安定した経営基盤とグループ間のスムーズな連携でコロナ禍でも安定した雇用を実現させています。
ネプラス株式会社に興味を持った方へ
ネプラス株式会社では、インフラエンジニアを募集しています。
年収をアップしたい!スキルアップしたい!大手の上流案件にチャレンジしたい!
オンライン面接も随時受付中。ぜひお気軽にご応募ください。

