プログラミング

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で受け取るところへ進みましょう。

参考資料

← 記事一覧へ戻る