Git のコンフリクト解消と rebase をブラウザで練習する

0 / 22 手順

キーボードで操作するので、パソコンで開いてください。手順の内容は、このページの下で読めます。

このチュートリアルについて

Git のコンフリクト(衝突)は、2 人が同じファイルの同じ行を別々に変えたときに起きます。このページでは、ブラウザの中の Git で実際にコンフリクトを起こし、直して、merge と rebase のどちらでも最後まで進める練習ができます。インストールも登録も要りません。

舞台はパン屋のサイトのリポジトリです。あなたは feature/sale ブランチでキャッチコピーをセールの案内に変え、そのあいだにチームの人が main の同じ行に開店時間を足しました。右のエディタで直して、ターミナルで git add と続きのコマンドを打ちます。 コマンドは本物の Git と同じように動き、git status の表示や、git rebase --continue・git rebase -i で開くエディタ(Vim)も本物に合わせてあります。

コンフリクトマーカーの読み方

Git は、自動でまとめられなかったところに 3 種類の印を書き込みます。<<<<<<< から ======= までが片方の変更、======= から >>>>>>> までがもう片方の変更です。 どちらかを選ぶだけでなく、両方を活かして書き直してかまいません。最後に 3 つの印の行を消し、git add します。

    <h1>Leaf &amp; Loaf</h1>
<<<<<<< HEAD
    <p class="tagline">Spring sale: 20% off all bread</p>
=======
    <p class="tagline">Fresh bread every morning, from 7am</p>
>>>>>>> main
  </header>
merge(git merge main)rebase(git rebase main)
<<<<<<< HEAD の側あなたのブランチ(feature/sale)付け替え先(main)
>>>>>>> の側取り込んだ main当て直しているあなたのコミット
直したあとgit add して git commitgit add して git rebase --continue

rebase は main の上にあなたのコミットを 1 つずつ当て直すので、HEAD が main の側になり、上下が merge と逆になります。

チュートリアルの課と手順

  1. 1. コンフリクトを起こして、状況を読む

    あなたは feature/sale ブランチで、サイトのキャッチコピーをセールの案内に変えました。そのあいだに、チームの人が main の同じ行に開店時間を足しています。main を取り込むと、同じ行を 2 人が変えたので Git は自動でまとめられず、コンフリクト(衝突)になります。

    1. git log --oneline --graph --all で、2 つのブランチが分かれている様子を見ます。
    2. git merge main で、main の変更を feature/sale に取り込みます。CONFLICT と出ます。
    3. git status で、どのファイルがぶつかったかを見ます。Unmerged paths に both modified: index.html と出ます。
  2. 2. マーカーを読んで直し、マージを終える

    ぶつかったところには、Git が印(コンフリクトマーカー)を書き込んでいます。<<<<<<< HEAD から ======= までがあなたの変更、======= から >>>>>>> main までが main の変更です。残したい形に書き直して印を消し、git add で「直した」と伝えてから、コミットしてマージを終えます。

    1. 左のファイルの一覧で index.html を押して、エディタで開きます。3 つの印に挟まれた 2 行が見えます。
    2. セールの案内と開店時間を両方残します。たとえば開店時間の行を残し、その下にセールの行を <p class="sale"> で足して、3 つの印の行を消します。Ctrl+S(Mac は ⌘+S)で保存します。
    3. git add index.html で、このファイルは直したと Git に伝えます。
    4. git commit -m "Merge main into feature/sale" で、メッセージを付けてコミットし、マージを終えます。
    5. git log --oneline --graph で、2 本の線が 1 つのコミットに合わさったのを確かめます。
  3. 3. マージをやめて、取り込む前に戻す

    もう一度 main を取り込んで、コンフリクトになったところです。直し方に迷ったときや、先にチームの人と相談したいときは、マージをやめて取り込む前に戻せます。

    1. git merge --abort で、マージをやめます。
    2. git status で、取り込む前の状態(nothing to commit, working tree clean)に戻ったのを確かめます。
  4. 4. rebase で、最新の main の上に載せ直す

    merge の代わりに rebase を使うと、あなたのコミットを最新の main の上に 1 つずつ当て直し、履歴を一直線にできます。当て直す途中で同じ行がぶつかると、そのコミットで止まります。直し方は merge と同じで、最後だけ git commit ではなく git rebase --continue で続けます。

    1. git rebase main で、feature/sale の 2 つのコミットを main の上に当て直します。1 つ目でぶつかって止まります。
    2. git status で状況を見ます。rebase の途中で、済んだコミットと残りのコミット、ぶつかったファイルが出ます。
    3. index.html を開いて、両方を残す形に直して保存します(Ctrl+S・⌘+S)。rebase では上下が merge と逆で、<<<<<<< HEAD の側が main、>>>>>>> の側があなたのコミットです。
    4. git add index.html で、直したと伝えます。
    5. git rebase --continue で続けます。元のコミットメッセージが入ったエディタが開くので、そのまま :wq で保存します。残りのコミットはぶつからずに当たり、rebase が終わります。
    6. git log --oneline --graph で、あなたの 2 つのコミットが main の「Show opening time」の上に一直線に並んだのを確かめます。
  5. 5. rebase をやめて、元のブランチに戻す

    もう一度 rebase して、1 つ目のコミットで止まったところです。rebase も途中でやめられます。やめると、ブランチは rebase を始める前のコミットに戻ります。

    1. git rebase --abort で、rebase をやめます。
    2. git log --oneline --graph --all で、feature/sale が main から分かれたままの、始める前の形に戻ったのを確かめます。
  6. 6. push の前に、コミットを 1 つにまとめる(rebase -i)

    feature/sale には、セールの案内を足したコミットのあとに、打ち間違いの修正と作業中のコミットが残っています。プルリクエストに出す前に、git rebase -i で 1 つのコミットにまとめます。rebase -i は履歴を書き換えるので、まだ push していないコミットだけに使います。

    1. git log --oneline で、まとめたい 3 つのコミットを見ます。
    2. git rebase -i HEAD~3 で、最新の 3 つのコミットの一覧をエディタ(Vim)で開きます。上から古い順に pick が並びます。
    3. 2 行目と 3 行目の pick を fixup に変えて、:wq で保存します。fixup は「1 つ前のコミットにまとめ、自分のメッセージは捨てる」です。j で下の行へ移り、cw で単語を書き換え、Esc で戻ります。
    4. もう一度 git log --oneline で、セールの案内のコミットが 1 つだけになったのを確かめます。

このチュートリアルで使うコマンド

コマンドすること
git merge mainmain の変更を今のブランチに取り込む
git statusどのファイルがぶつかったか(both modified)、merge や rebase の途中かを見る
git add <file>直したファイルを「直した」と Git に伝える
git commit -m "…"メッセージを付けてコミットし、merge を終える(-m を付けないとメッセージのエディタが開く)
git merge --abortmerge をやめて、取り込む前に戻す
git rebase main自分のコミットを、最新の main の上に当て直す
git rebase --continue衝突を直したコミットを作り、残りのコミットへ進む
git rebase --abortrebase をやめて、始める前のブランチに戻す
git rebase -i HEAD~3最新の 3 つのコミットを並べ替え・まとめる(pick・fixup・squash・reword・drop)
git log --oneline --graph --allブランチの分かれ方と合わさり方を線で見る

よくある質問

コンフリクトはどんなときに起きますか?

2 つのブランチで、同じファイルの同じ行(または隣り合う行)を別々に変えたときです。違う場所の変更なら、Git が自動でまとめます。merge でも rebase でも、cherry-pick や git stash pop でも起きます。

merge と rebase は、どちらで取り込めばいいですか?

チームの決まりに合わせます。rebase は自分のコミットを作り直すので、ほかの人も使っているブランチには使いません。自分だけの作業ブランチを最新の main に追いつかせるときに rebase、ブランチの分かれた跡を残したいときに merge、と使い分けるチームが多いです。

rebase したあと、git push が拒否されました。

一度 push したブランチを rebase すると、コミットが作り直されて手元とリモートの履歴が食い違うので、ふつうの push は拒否されます。自分だけのブランチなら git push --force-with-lease で上書きします。ほかの人が使っているブランチでは上書きしないでください。

git commit で Vim が開いて、抜け方が分かりません。

Esc を押してから :wq と打って Enter で、保存して閉じます。merge を終えるときにメッセージを書き換えないなら、git commit --no-edit でエディタを開かずにコミットできます。いつものエディタを使いたいときは、git config --global core.editor で変えられます(VS Code なら "code --wait")。

VS Code でもコンフリクトを直せますか?

直せます。VS Code は印の上に Accept Current Change(HEAD の側)・Accept Incoming Change(もう片方)・Accept Both Changes のボタンを出します。押すだけで選べますが、rebase のときは HEAD の側が main になるので、どちらが自分の変更かに気をつけてください。直したら、ターミナルと同じく git add して続けます。

このページの Git は本物ですか?

ブラウザの中で動く Git のシミュレーターです。コマンドと表示は本物の Git に合わせてあり、あなたのパソコンのファイルには触れません。

続きを練習する

WebTerm Learn の Git 基礎編では、ブランチの作り方からプルリクエスト、コンフリクト、rebase、cherry-pick までを、スライドと演習でゲームのように 1 つずつ進めます。 Git が初めてなら、先に Git とは を読むと流れがつかめます。