Git実践② ブランチを作って安全に変更しよう
ブランチを作って編集し、mainへ統合する流れを、前後のファイル内容で確認します。未コミット変更の注意、fast-forward、競合の直し方、練習と解答を解説します。
「見出しを変えてみたい。でも、今の状態も残したい」。そんなとき、フォルダーを丸ごと複製すると、後でどちらが最新版か分からなくなりがちです。
Gitのブランチを使えば、履歴の流れに名前を付けて、変更を分けて進められます。今回は最初のコミットを作った練習用リポジトリで、見出しを変え、最後に main へ取り込みます。
ブランチを確認する
先に次を実行してください。
git status
git branch
未記録の変更がないことを確かめ、* main と表示されるか見ます。* は現在のブランチです。別の名前を使っている場合は、記事中の main をその名前へ読み替えます。
ブランチは、新しい物理フォルダーを作る機能ではありません。どのコミットを先頭として作業するかを示す名前です。切り替えると、同じ作業フォルダーの内容がその履歴に合わせて変わります。
未保存・未コミットの変更は別扱い
「ブランチを作れば、保存前の作業も自動で隔離される」と思いがちですが、そうではありません。未コミットの変更は切り替え先へ持ち越されることがあり、上書きされる場合はGitが切り替えを止めることもあります。
練習では、開始前に変更を保存し、必要な内容をコミットしておきます。見慣れない変更があれば、消さずに何の作業かを確認してください。
作業用ブランチを作る
git switch -c feature/header
git branch
switch は切り替え、-c は新しく作る指定です。feature/header を作り、そのブランチへ移ります。次の表示では * が feature/header 側に付けば成功です。
feature は機能追加でよく使う名前ですが、特別な機能を持つ予約語ではありません。header は見出しの作業であることを示しています。
新しいブランチを作った直後は、main と同じコミットを指しています。まだ内容が違わなくて大丈夫です。これから作業用ブランチでコミットを追加すると、履歴の先端が分かれます。
変更をコミットする
前回作った README.md の最初の行を、次へ変えて保存します。説明文は残します。
# わたしの学習記録
Gitの基本操作を練習するプロジェクトです。
次を順に実行します。
git diff
git add README.md
git diff --staged
git commit -m "READMEの見出しを学習記録へ変更"
git diff は編集内容、git diff --staged は今回記録する内容を確認するコマンドです。1行の見出しだけが変わっているか読みます。
ここで main に戻ってみましょう。
git switch main
エディターの内容が、以前の「はじめてのプロジェクト」に戻ります。変更が消えたのではありません。さっきのコミットは feature/header が指す履歴に残っています。
もう一度 git switch feature/header とすれば「わたしの学習記録」へ戻ります。自分で切り替えてみると、「ブランチは別フォルダーではない」が実感しやすいですね。
mainへ統合する
見出しの変更を採用するなら、受け取り先の main にいる状態で実行します。
git switch main
git status
git merge feature/header
マージは、別の履歴の変更を現在のブランチへ統合する操作です。git merge feature/header は「今いるブランチへ、feature/header の変更を取り込む」と読みます。
今回のfast-forwardとは
今回、ブランチを分けた後に main 側は変更していません。そのため、main の指す先を新しいコミットまで進めれば取り込めます。この進め方がfast-forward(早送り)です。
履歴: A ── B(新しいコミット)
変更前: main は A、feature/header は B を指す
変更後: main も feature/header も B を指す
AとBは説明用のコミット名です。今回のような統合では、新しいマージ専用コミットが必ず作られるわけではありません。分岐後に両方へ別々のコミットが増えた場合は、履歴を合わせる別の処理になります。
確認します。
git log --oneline --graph --all
git status
--graph は履歴のつながり、--all はほかのブランチも含めた表示です。main で新しい見出しが見えれば、今回の統合は完了です。
なお、ここまでの操作はローカル、つまり自分のパソコン内だけです。公開サイトへの反映はまだ行っていません。
コンフリクトが起きたら
コンフリクトは、変更をどの内容へまとめるか、Gitだけでは決められない状態です。同じ部分を別々に変更したときなどに起きます。
例えば、一方が見出しを「学習ノート」、もう一方が「制作メモ」にした場合、どちらが望ましいかは人が判断する必要があります。
1. 印の意味を読む
競合したファイルには、次のような印が入ることがあります。
<<<<<<< HEAD
# 学習ノート
=======
# 制作メモ
>>>>>>> feature/header
HEAD は、ここでは現在のブランチ側を示します。======= で相手側と区切られています。この表示は「どちらかを勢いで消して」という意味ではありません。変更の目的を確認します。
2. 残したい内容へ編集する
両方の意味を含めたいなら、例えば次の1行へ整えます。
# 学習と制作のメモ
<<<<<<< などの印もすべて取り除き、実際に残す完成形だけにします。今回のREADMEなら文章を読み直し、プログラムなら動作確認も行います。
3. 解決したことを記録する
git status
git add README.md
git diff --staged
git commit -m "READMEの見出しの競合を解決"
add は、競合時には「このファイルを解決済みとして扱う」意味も持ちます。印を残したまま add しないよう、内容を先に確認してください。
この競合例は読み方の説明です。前の練習をそのまま進めた場合、main が変更されていないので通常は競合しません。
やり直す場合も、いきなり強制しない
現在進行中のマージを取りやめるコマンドには git merge --abort があります。ただし、マージ前に未コミット変更があると元に戻せない場合もあるので、まず git status とファイルを確認します。
「分からないから強制」「変更を全部破棄」は避けましょう。競合はデータが壊れた合図ではなく、残したい内容を決めるために止まっている状態です。
よくあるつまずき
ブランチを変えたのに内容が変わらない
両方が同じコミットを指している、または変更をまだコミットしていない可能性があります。git status と履歴を確認します。
「already exists」と出る
同じ名前のブランチがすでにあります。作成用の -c を付けず、git switch feature/header で切り替えます。別の作業のブランチを勝手に使わないよう、中身も確認します。
mainへ戻る操作が止まった
未記録の変更を上書きするおそれがある場合があります。必要な編集を保存・記録してから再度確認します。
マージの向きを間違えそう
先に git branch を見ます。「取り込み先へ移動してから、取り込む名前を指定する」と声に出して読むと整理しやすいです。
練習:説明追加用のブランチを作る
main から feature/description を作り、README.md に「使い方は、このファイルを開いて読みます。」を追加してください。コミット後、main へ統合します。
模範解答
最初に変更がないことを確認して進めます。
git switch main
git status
git switch -c feature/description
READMEの説明の下へ1行追加して保存した後、実行します。
git diff
git add README.md
git diff --staged
git commit -m "READMEに使い方を追加"
git switch main
git merge feature/description
git log --oneline --graph --all
解説
作業用ブランチで変更を記録するまでは、main の履歴は変わりません。マージした段階で main からも新しい説明をたどれます。
ブランチ名とコミットメッセージは別です。前者は作業の流れの名前、後者はその1件で何を変えたかの説明です。この2つを分けて考えると、作業が増えても探しやすくなります。
次はGitHubへpushし、pullで受け取るところへ進みましょう。