Gitは、ファイルの変更履歴を安全に管理するためのツールです。
しかし現場では、「エラーが出たらとりあえずコマンドを打つ」「何が起きているか分からないまま操作する」という使われ方になりがちです。
そのためエラーの解消に時間がかかったり、誤った操作によってより状況が悪化してしまうこともしばしば。
この講義では、Gitのエラーを入口に 「今、何が起きているか」「なぜこの操作が必要なのか」 を理解することを目的とします。
1. バージョン管理とは何か
バージョン管理とは、ファイルがどのように変更されてきたかを時系列で記録し続ける仕組みです。
簡易ファイル例(profile.txt)
最初の状態
名前:山田
言語:Java
修正後
名前:山田
言語:Java
経験年数:3年 ←new!!
Gitは「どこが・いつ・どう変わったか」を履歴として残します。
なぜ必要か
- 間違えた修正を元に戻したい
- 誰の修正で壊れたか(バグが混入したか)特定したい
- 複数人で安全に作業したい
👉 これを実現するのがGitです。
2. Gitとリポジトリ
Gitでは、履歴を保存する場所が大きく分けて2つあります。
- ローカルリポジトリ: 自分のPCにある履歴。
- リモートリポジトリ: ネットワーク越しに存在しチーム全体で共有する履歴。(GitHub, GitLab, Bitbucket, Azure DevOps, AWS CodeCommit, Backlog, Gitea, SourceForge など)
3. ファイルの流れとステージングエリア
Gitは、変更をいきなり保存するのではなく、段階的に保存します。
| 段階 | コマンド | 意味・状態 |
|---|---|---|
| ① 作業ディレクトリ | (編集のみ) | ファイルを編集しているだけ。まだ履歴には残らない。 |
| ② ステージングエリア | git add | 「この変更は保存していい」とGitに伝える場所。保存するファイルを選べる。 |
| ③ ローカルリポジトリ | git commit | 変更が履歴として自分のPC内で確定。まだ他人には見えない。 |
| ④ リモートリポジトリ | git push | チーム全体に共有される。 |
4. 基本コマンド(意味で理解)
コマンドを「呪文」として暗記するのではなく、その「役割」で捉えましょう。
| コマンド | 意味 |
|---|---|
| git status | 【最重要】 今、何が起きているか確認する |
| git add | 保存していい変更を選ぶ(ステージング) |
| git commit | 変更を確定して記録する |
| git push | 自分の履歴を共有する |
| git fetch | 最新情報を「見に行くだけ」 |
| git merge | 履歴を合体させる |
| git pull | 最新情報を取得して合体させる(fetch + merge) |
5. よくあるエラーと解決策
エラー①:pull時に「未コミットの変更がある」
【エラーメッセージ】
「Your local changes to the following files would be overwritten by merge」
【原因】 あなたがまだ保存(commit)していない編集中のファイルが、リモートの最新版と重なっているため「上書きされて消えちゃうよ!」とGitが警告しています。
【解決策】
ここでは解決策を3パターン紹介します。
今の「書きかけの変更」をどうしたいかに合わせて選んでください。
パターン① 【捨てる】今の変更は不要なので消したい
「色々書いたけど、全部やり直したい。最後にコミットした綺麗な状態に戻して pull したい」という場合。
指定したファイルを、最後にコミットした時の状態に強制的に復元するコマンドである git restore を使います。
「ファイルを編集したが、書きすぎて何が正解か分からなくなった。」「自分の修正は間違えていることがわかっているので一旦無しにして綺麗にしたい。」という時に使えます。
- 手順(変更の取り消し):
$ git restore ファイル名
対象のファイルの中身が、編集前の(最後にコミットした)状態にリセットされます。
これにより編集内容が完全に消え、綺麗な状態で pull できるようになります。
git restore を実行すると、そのファイルに加えた 「まだコミットしていない修正」は完全に消えてなくなります。 復活はできないので注意して使いましょう。
パターン② 【避ける】今は邪魔だが、後で続きを書きたい(推奨)
エラーを抑止するための暫定的なファイル修正をチーム内で行っているが、
クライアントに出せる正式な変更ではないのでコミットできないということがチーム開発ではあったりします。そんな「まだコミットできる状態じゃないけど、消したくもない。一旦脇に置いておいて、先に最新版を pull したい」というケースに使用します。
- 手順:
①退避
$ git stash push -m "どの退避かわかるようなメッセージ" ファイル名
特定のファイルだけを一時保存します。
②最新化
$ git pull
③ 確認
$ git stash list
実際のコマンド実行結果のイメージ
$ git stash list
stash@{0}: WIP on main: 4c2a1b3 feat: ログイン機能の実装
stash@{1}: WIP on develop: 9d8f7e2 fix: バグ修正中の作業
stash@{2}: On feature-ui: 1a5b3c4 デザイン調整のバックアップ
表示内容の解説
stash@{n}: スタッシュの識別番号です。0 が最新の退避データです。
WIP on [ブランチ名]: Work In Progress(作業中)の略で、どのブランチで作業していたかを示します。
4c2a1b3...: その作業のベースとなっていたコミットのハッシュ値とメッセージです。
④復元
$ git stash pop stash@{0}
最新を取り込んだ後に自分の変更を合体させます
パターン③ 【固める】一旦、現在の内容で保存してしまいたい
「キリが良いので、今の状態で一旦自分の履歴として確定させたい」という場合。
-
手順:
①ステージングに追加
$ git add .
②変更の確定
$ git commit -m "保存用メッセージ"
②最新化
$ git pull
自分の履歴が作られるため、Gitは安心してリモートの履歴と合流(merge)させることができます。
もしリモートと修正箇所が被っていれば、この後に「マージ競合(Conflict)」が発生しますが、データが消える心配はありません。
エラー②:マージ競合(Conflict)
【エラーメッセージ】
「CONFLICT (content): Merge conflict in ... 」
【原因】
同じファイルの同じ箇所を、自分と他人が別々に編集してしまったため、Gitがどちらを優先すべきか判断できない状態です。
競合が起きているファイルの実例(profile.txt)
名前:山田
<<<<<<< HEAD
言語:Python (←自分のローカル環境での修正点)
=======
言語:Java (←リモート/他人の修正点)
>>>>>>> origin/main
経験年数:3年
-
<<<<<<< HEAD から ======= まで: あなたの手元のPC(ローカル環境)で修正した内容です。
-
======= から >>>>>>> origin/main まで: 他人がリモートリポジトリにPushした内容です。
-
手順:
①競合の解決
ファイルを開くと「 <<<<<<< HEAD 」のような記号が出ているので、手動で正しい内容に書き換える。チーム内の他のメンバーと同じファイルを変更している場合は、そのメンバーと修正について相談しながら安全に進める。
②ステージングに追加(で解消したことを伝える。)
$ git add ファイル名
③変更の確定
$ git commit -m "保存用メッセージ"
エラー③:Pushできない(non fast-forward)
【エラーメッセージ】
「error: failed to push some refs to ... rejected (non-fast-forward)」
fast-forward と non fast-forward
本題に入る前にこの二つの違いについて説明します。
- fast-forward(一本道)
A ─ B ─ C ─ D
(履歴が分岐しておらず、相手の進んだ履歴をそのまま自分の先に繋げられる状態)
- non fast-forward(分岐あり)
C(他人)
/
A ─ B
\
D(自分)
(共通の過去から、自分と他人が別々の修正を行ったため、履歴が枝分かれしている状態)
履歴が分かれている場合、そのままPushすると他人の履歴を消す恐れがあるため、Gitは安全のために停止します。
【原因】
リモートリポジトリの履歴のローカルの履歴が分岐してしまっている。
【解決策】
①変更の取り込み
$ git pull
この際に競合が出たら解決します。
自分の最新コミットとリモートの最新コミットを繋ぎ合わせる、 新しい「マージコミット」を自動で作ります。(pullはfetch + merge)
これにより二股に分かれていた履歴が、一つのコミットで合流します。
②履歴の共有
再度pushを試します。
$ git push
6. 誤ったコミットを正す方法
「間違えた内容をコミットした」と気づいた時、焦って次の操作をするのが一番危険です。 状態に合わせた正しい手順を踏めば、安全にリカバリーできます。
状態別の対応方針:reset と revert の使い分け
| 状態 | 深刻度 | 推奨される対応 | 理由 |
|---|---|---|---|
| 未プッシュ | 低 | git reset | 歴史を「巻き戻して」消去できる。自分しか知らないので安全。 |
| プッシュ済み (未マージ) | 中 | git revert | すでに共有されているため、歴史を消さずに「打ち消し履歴」を重ねる。 |
| マージ済み (本番反映後) | 高 | git revert | 歴史の改ざんは厳禁。不整合を防ぐため必ず打ち消し用コミットを作る。 |
git reset(取り消し)と git revert(打ち消し)の違い
git reset (歴史を巻き戻す)
-
実行前: A ─ B ─ C (ミス)
-
実行後: A ─ B (Cは歴史から消滅)
-
具体的に何をするか: コミットCで「新しいファイルを追加」し、「既存のコードを5行削除」したとします。「git reset --hard B」を実行すると、Gitは「Cなど最初からなかった」かのように振る舞います。フォルダの中身はコミットBの状態に即座に戻り、追加したファイルは消え、削除した5行は復活します。
未プッシュの場合のみ使いましょう。
git revert (間違いを打ち消すコミットを追加)
- 実行前: A ─ B ─ C (ミス)
- 実行後: A ─ B ─ C ─ D (Cを打ち消す修正)
- 具体的に何をするか: ミスしたコミット(C)で「行を追加」していたら、revert(D)では「その行を削除」する処理をGitが自動で行い、新しいコミットを作ります。人間が手動で書き直す必要はありません。
プッシュ済み・マージ済みの場合は必ずこちらを使います。
未プッシュの直前コミットを無かったことにする手順
実行コマンド:
$ git reset --soft HEAD^
用語解説
- HEAD: 今自分がいるブランチの「最新のコミット」を指すポインタです。
- ^: 「一つ前」という意味。
- --soft: 歴史だけを戻し、編集内容は残す設定です。
「--hard」 を使うと編集内容もすべて消えるため、注意が必要です。
修正の手順:
- コマンド実行:
$ git reset --soft HEAD^
これで「コミットした」という事実だけが消え、ファイルは修正した状態(ステージング状態)に戻ります。自分しか知らない履歴なので、消去しても問題ありません。
-
ファイルを修正:
ファイルは編集中の状態で残っています。修正が必要な箇所を書き換えます。 -
やり直し:
再度 git add → git commit を行います。
プッシュ済み・マージ済みのミスを「安全に打ち消す」手順
すでにチームに共有された歴史を消すと、他の人のGitが壊れるため、ミスを相殺(打ち消し)する履歴を作ります。
修正の手順:
- コマンド実行:
$ git revert HEAD
Gitが自動的に「直前のコミットで行った変更と正反対の修正作業」を裏側で行い、新しいコミットとして記録まで完了してくれます。人間が手動で書き直す必要はありません。
- プッシュ:
$ git push
ミスした事実と、それを直した事実の両方が履歴に残りますが、これがチーム開発で最も安全な方法です。
パニック時の緊急停止ボタン:git merge --abort
コンフリクト解消作業中に収拾がつかなくなった際、「pull や merge コマンドを打つ直前の平和な状態」 に戻すために使います。
git merge --abort とは? コンフリクト解消のために自分で書き換えた内容や、Gitが自動で挿入した <<<<<<< HEAD などの記号をすべて消し去り、「pull や merge コマンドを打つ直前の平和な状態」 に戻すことです。
「作業」とは: 競合が発生した後の「ファイルを手動で修正する作業」を指します。
具体的な実例
git pull をしたら大量の競合が発生した。
修正を試みたが、だんだん整合性が取れなくなり、どこをどう直せばいいか分からなくなった。
「一旦今のこのぐちゃぐちゃな解消作業を全部やめて、pullする前の状態から落ち着いて考え直したい!」
ここで git merge --abort を実行。
結果: pull を実行する直前の、自分の最新コミット状態にリセットされます。
pull を実行する前に commit していた内容は消えません。
pull を実行した後に、競合を直そうとして書き換えた内容は消えます。
7. 競合の予防
複数人による同じファイルへの修正は競合が発生する可能性が高いため避けるべきですが、実際の現場ではそうもいかないことがあります。
ここでは、そんな状況下でも安全に作業を進められるための方法を紹介します。
- リモートの履歴の取得:
$ git fetch origin
リモートリポジトリ(サーバー側)の最新の更新情報を手元のカタログにダウンロードします。この段階では更新情報を取得しただけで自分の作成中のコードと合体(マージ)はされないため、安全に情報を取得できます。
- 更新ファイルの確認:
$ git diff HEAD origin/ブランチ名 --name-only
自分の現在の作業(HEAD)と、サーバー側の最新状態(origin/<ブランチ名>)を比較し、「修正が入ったファイル名」だけをリストアップします。自分が今触っているファイルがこのリストに含まれていたら、競合の要注意サインです。「3. 詳細な差分の確認:」に進んでください。
- 詳細な差分の確認:
$ git diff HEAD origin/ブランチ名> -- ファイル名
特定のファイルについて、どの行が具体的に書き換えられたかを1行ずつ表示します。自分の修正箇所と同じ行がリモートでも書き換わっている場合、そのままプルすると競合(コンフリクト)が発生することを事前に察知できます。
この手順を定期的に行うことで、誰かが同じファイルを触った瞬間に気づけるようになります。
8. トラブルを避けるために意識すること
ここまでトラブル発生時の解消法について紹介しましたが、トラブルの原因となる操作を避けることも有効な手段です。
最後に「トラブルを避けるために意識すること」としていくつか紹介します。
-
修正作業を始める直前と、コミットする直前に必ず pull する
常に「最新のリモート環境」と同じ状態にしてから作業を始めることで、履歴の分岐を最小限に抑えられます。 -
自分が修正していない箇所で「未コミットの修正」を放置しない
作業範囲外のファイルが編集状態にあるとエラーの元になります。極力リモート環境と同じ、クリーンな状態を保ちましょう。 -
同じファイルを複数人で同時に修正しない
根本的な競合対策です。作業分担を明確にし、チームでコミュニケーションを取りましょう。ただし、同じファイルに対して複数人で修正することは現場によってはあり得ます。その場合は、fetchで取得しリモートの更新情報を確認しながら作業をしましょう。 -
プッシュ済みの履歴を reset して強制プッシュ (--force) しない
すでにチームで共有されているコミット履歴を過去に巻き戻して書き換える操作です。
他のメンバーは、
• すでに「書き換え前の履歴」を元に作業しているため
• リモートの履歴が突然変わると、ローカル履歴と食い違いが発生します
その結果、
• git pull が fast-forward できなくなる
• マージやリベースを要求され、初心者では対処不能になる
• 最悪、ローカル変更を失ったり、一から環境構築をし直す必要が出る
など、チーム全体の作業が止まる原因になります。
5. 焦ってファイルを直接「ゴミ箱」に捨てない(git restore を使う)
OS上でファイルを削除すると、Gitでは「削除した変更」として扱われます。
意図しない削除やマージ競合の原因になるため、
元に戻したい場合は git restore を使いましょう。
6. トラブル発生時は落ちついて状況確認
エラーメッセージだけで無く、「git status」コマンドで現在どういう状況にあるのかをまず確認しましょう。
直近で自分が行った作業も振り返り、トラブルの原因を探った上で今回本記事で紹介したようなコマンドを実行したり、ファイルを修正して慎重に解決への歩みを進めましょう。
本記事が現場作業の助けになったら幸いです。
ありがとうございました。
