1.8 Gitの基本操作(clone, add/remove, commit, push/pull, branch, merge/conflict, diff)

Git は、いま最も広く使われているバージョン管理ツールである。1.7 で「なぜバージョン管理が必要か」を見たので、ここでは Git の基本操作が「作業の流れの中でそれぞれ何のためにあるのか」に徹する。コマンドの暗記ではなく、役割で理解するのが目標だ。

基本操作の全体像

まず各操作の役割を一覧で押さえる。Git の操作は、大きく「取ってくる」「記録する」「共有する」「枝分かれと合流」「差分を見る」に分類できる。

操作何をするか役割
cloneリポジトリ(履歴ごとの保管庫)を丸ごと複製する作業を始める起点
add変更を記録の候補に載せる(ステージング)次の記録に何を含めるか選ぶ
removeファイルを管理対象から外す不要になったものを記録から除く
commitステージした変更を履歴に刻む「ある時点」を確定し記録する
push手元の履歴を共有の保管庫へ送る自分の変更を他者と共有
pull共有の保管庫から最新を取り込む他者の変更を手元へ反映
branch履歴の枝を作る本流を汚さず並行作業
merge枝を合流させる並行作業の成果を1つに統合
diff差分を表示するどこがどう変わったかを確認

基本の流れ ―― clone から push まで

日々の作業は、おおむね「複製 → 編集 → 記録の候補に載せる → 記録する → 共有する」という一本の流れで進む。まずこの幹を押さえると、各操作の居場所が分かる。

flowchart LR
    A["clone
保管庫を複製"] --> B["編集
ファイルを変更"] B --> C["add
記録候補に載せる"] C --> D["commit
履歴に刻む"] D --> E["push
共有先へ送る"] F["pull
最新を取り込む"] --> B
  • clone は、リモート(共有の保管庫)にあるリポジトリを、履歴ごと手元に丸ごと複製する。これで作業の出発点ができる。最初の1回だけ行う操作だ。
  • 手元でファイルを 編集 する。この時点では、まだ変更はどこにも記録されていない。
  • add で、変更を「次に記録する候補」として登録する(後述のステージング)。
  • commit で、候補に載せた変更を履歴に刻む。ここで初めて「ある時点」が確定し、後から戻れる節目になる。
  • push で、手元の履歴を共有の保管庫へ送り、他のメンバーが受け取れるようにする。
  • pull は逆向きで、他の人が共有先に入れた変更を手元へ取り込む。共同作業では、編集を始める前に pull して最新に合わせるのが基本だ。

なぜ add(ステージング)という一段階が挟まるのか

commit の前に add があるのは、「変更したこと」と「変更を記録すること」を別の操作に分け、何を記録に含めるかを自分で選べるようにするためである。この中間の置き場を ステージング(ステージングエリア) と呼ぶ。

なぜ分かれていると嬉しいのか。実際の作業では、一度にあちこちを編集することが多い。だが履歴は「意味のあるまとまり」ごとに刻むほうが後で読みやすい。add があれば、「今あるすべての変更」のうち、関係するものだけを選んで1つの commit にまとめられる。たとえばバグ修正と無関係な設定変更を同時にいじってしまっても、バグ修正の分だけ add して commit し、設定変更は別の commit に分けられる。

flowchart LR
    W["作業場所
編集中の変更"] -->|add| S["ステージング
記録する候補"] S -->|commit| H["履歴
確定した記録"]

もし add がなく、編集が即 commit になっていたら、こうした「記録の選り分け」ができない。add という一段階は、履歴を意図どおりに整える余地を与えるためにある。

branch と merge ―― なぜ並行作業ができるのか

branch(ブランチ=枝)は、本流の履歴から枝を分けて、独立した作業場を作る仕組みである。これにより、本流の安定を保ったまま、複数の作業を並行して進め、後で合流(merge)させられる。

flowchart LR
    A["共通の出発点"] --> B["本流: そのまま安定"]
    A --> C["枝: 新機能を試す"]
    C --> D["枝で commit を重ねる"]
    B --> M["merge
枝を本流へ合流"] D --> M

枝の上での変更は、合流させるまで本流に影響しない。だから新機能の開発や設定変更の試行を枝で安全に行え、失敗すれば枝を捨てればよい。複数人がそれぞれの枝で同時に作業し、完成したものから本流へ merge していけば、互いの作業を踏みつぶさずに共同作業できる。これが「並行作業」を支える仕組みだ。merge は、枝で重ねた変更を本流の変更と1つに統合する操作である。

conflict(コンフリクト)はなぜ起きるのか

コンフリクト(衝突)は、合流しようとする2つの変更が「同じ箇所を別々に変えていて、Git にはどちらが正しいか判断できない」ときに起きる。エラーではなく、「人間が判断してください」という Git からの申告である。

Git は merge のとき、2つの枝の変更を自動で組み合わせようとする。変更箇所が重なっていなければ、自動でうまく統合できる。問題は、同じファイルの同じ行を、両方の枝が違う内容に変えた場合だ。Git にはどちらを採用すべきか分からないので、自動では決められず、その箇所を「コンフリクト」として印をつけて止まる。

たとえば、共通の出発点で次の1行があったとする。

speed = 1000

枝Aはこれを speed = 10000 に、枝Bは同じ行を speed = 100 に変えた。merge しようとすると、Git は同じ行への食い違う2つの変更を前にして、該当箇所に両者を併記した印を残し、判断を人間に委ねる。解決は、人がどちらを採るか(または両方を踏まえた正しい内容)を決めて1つに書き直し、それを add して commit することで完了する。

コンフリクトは「悪いこと」ではなく、並行作業の必然的な副産物だ。複数人が同じ場所を同時に触れば、いつかは食い違う。バージョン管理がない世界では、この食い違いは「後から保存した人が前の人の変更を黙って上書きする」形で表面化せず失われていた。Git はそれを隠さず止めて見せてくれる。コンフリクトが見えること自体が、安全装置として働いている。

diff ―― 変更を確認する

diff は、2つの時点のあいだで「どこがどう変わったか」を行単位で示す操作である。これは特定のタイミングに縛られず、作業のあらゆる場面で使う確認の道具だ。

たとえば commit する前に diff を見れば、「これから記録しようとしている変更」が意図どおりかを確認できる。過去の2つの時点を比べれば、「いつのまにか壊れた」原因の箇所を絞り込める。1.7 で見た「手作業のコピーでは目で見比べるしかなかった差分」を、Git は正確に・瞬時に示してくれる。記録する前、共有する前、原因を追うとき——節目で diff を挟む習慣が、誤った変更を早い段階で食い止める。

Note

push と commit を混同しやすいが、別の操作だ。commit は「手元の履歴に記録する」操作で、ここまではネットワークにつながっていなくてもできる。push は「手元の履歴を共有先へ送る」操作で、初めて他者に見える。つまり commit しただけでは自分の手元に留まっている。共有するには push が要る、と分けて覚えるとよい。


本ページはCisco DevNet Associate(200-901) Exam Topics v1.1を学習範囲の根拠として参照。文章・図表はすべて新規作成。