Gitコマンドの最小限|1日の作業で本当に使う分だけ
「git コマンド」で検索して早見表を開き、翌日また同じ言葉で検索している。これは記憶力の問題ではなく、一覧の並び順の問題である。早見表はGitができることの順に並んでいて、1日の作業で手が動く順には並んでいない。この記事では、毎日叩く数個、履歴を読むための数個、取り消しのための数個に分けて、どれを先に体に入れればよいのかを決められるところまで整理する。
git コマンドは159本あるが、1日で触るのは十数個
手元のMacに入っているGitのマニュアルページを数えると、git- で始まるものが159本ある。Git 2.48.1 の構成で、内部処理のための実行ファイルまで含めると173本になる。この数を前にして「よく使う19選」のような記事を読み、19個を覚えようとするところから、覚え直しの循環が始まる。
実際の1日を振り返ると、状態を見る、差分を読む、選んで記録する、送る、この4つの動作しか繰り返していない。つまり必要なのは網羅ではなく、並べ替えである。並べ替えの軸は2つある。1つは頻度で、もう1つは危険度だ。頻度の高いものは手が覚えるまで繰り返す価値があり、危険度の高いものは、叩く前に何が失われるのかを1回だけ正確に理解しておく価値がある。この2つを混ぜて「よく使うコマンド一覧」にすると、頻度は低いが取り返しのつかないコマンドが、頻度の高いコマンドと同じ字の大きさで並ぶことになる。
Gitの用語そのものが曖昧なままだと、どのコマンドがどこを触っているのか判断できない。作業ツリーとインデックスの区別は、最初に固めておく価値がある。
作業ツリー(ワーキングツリー)とは、プログラマーが実際に変更するファイルの格納されたディレクトリです。前述のgit initコマンドでリポジトリを作成したディレクトリとも言い換えられます。 出典: sejuku.net
作業ツリーとインデックス、そしてコミット済みの履歴。この3つの場所のどこからどこへ内容が動くのかという形で覚えると、コマンド名の暗記が要らなくなる。
1日の作業を回す5つのコマンド
順番まで含めて固定されているのは、次の5つである。
git status
git diff
git add -p src/checkout.ts
git commit -m "期限切れクーポンを決済時に弾く"
git push
git status は、どのファイルが変わり、どれがインデックスに入っているかを並べる。git diff を引数なしで叩いたときに出るのは、まだインデックスに入っていない変更だけである。公式のマニュアルはこの形を「インデックス、つまり次のコミットのための待機領域に対して、自分が加えた変更を見るための形」だと説明している。ここが実務で最もよく詰まる点で、git add を済ませてしまうと git diff は何も表示しなくなる。ステージに入った分を見たいときは git diff --staged で、これは --cached と同じ意味である。
git add -p は、この5つの中で唯一、速さではなく結果の質を変えるコマンドだ。1つの作業ツリーに修正とリネームと消し忘れのデバッグ行が混ざっているとき、-p は差分の塊ごとに入れるかどうかを聞いてくる。結果として、きれいな2つのコミットと、捨てた1行が残る。この形で積んだコミットだけが、あとで単独で取り消したり、別のブランチへ移したりできる。逆に git add . で全部まとめて記録した履歴は、あとから1つの変更だけを引き抜けない。
割り込みが入ったときの退避も、この輪の中に入れておきたい。git stash push -m "クーポン検証の途中" で退避し、git stash list で待っている分を確認し、git stash pop で戻す。新規作成したファイルまで含めたいときは -u を付ける。退避したはずの新しいファイルが見つからないという相談のほとんどは、この -u の話に行き着く。
履歴を読むコマンドは、変える前に叩く
このグループは何も変更しない。だから、迷ったときに最も安く試せる。
git log --oneline --graph --decorate -20
git log -p -- src/checkout.ts
git show 9f2c1ab --stat
git blame -L 40,80 src/checkout.ts
git log --oneline --graph は、マージやリベースの前に確かめておきたい「いま履歴はどんな形をしているか」に答える。-p と -- でパスを絞ると、そのファイルを触った各コミットの差分だけが並ぶ。挙動の変化を人ではなくコミットに結び付けられるので、原因の切り分けが早い。
git blame -L 40,80 は、ファイル全体ではなく行の範囲だけを対象にする。-L :関数名 の形で関数を名前で追うこともできる。長いファイルで全体に対して叩くと数百行が流れるので、範囲を指定できるかどうかで読めるかどうかが変わる。
検索の2つのオプションは、似た形で違う質問に答える。-S は、ある文字列の出現回数が変わった差分を探すので、導入された箇所と削除された箇所が出る。-G は、追加または削除された行がパターンに一致する差分を探すので、その行に触ったコミットが全部出る。公式のマニュアルは、呼び出し側だけを書き換えたコミットを例に、-G では出るが -S では出ないと説明している。出現回数が変わっていないからである。「導入された時点を知りたい」のか「触った履歴を全部知りたい」のかで使い分ける。
取り消しは、失うものの大きさで並べ替える
ここだけは、名前順ではなく結果順に並べる価値がある。
| コマンド | 変わる場所 | 失われうるもの |
|---|---|---|
git restore --staged <file> |
インデックスだけ | なし |
git reset <commit> |
インデックスとブランチの位置 | 作業ツリーのものは残る |
git commit --amend |
直前のコミットを置き換える | 元のコミットメッセージ |
git revert <commit> |
新しいコミットが1つ増える | なし |
git restore <file> |
作業ツリーのファイル | そのファイルの未コミットの変更 |
git checkout -- <file> |
作業ツリーのファイル | そのファイルの未コミットの変更 |
git reset --hard |
インデックスと作業ツリー | 未コミットの変更すべて |
境目は表の下から3行目にある。git restore <file> より上は、記録済みの状態を触っているので、あとから戻せる。削除したブランチのコミットも、--amend で置き換えられた元のコミットも、履歴の中には残っている。表の下3行は、どこにも記録されたことのない作業を消す可能性がある。この差は「危ないコマンド」という言い方ではなく、記録済みか未記録かという1本の線で覚えるほうが間違えにくい。
上半分が戻せるのは git reflog があるからだ。これは HEAD がいた位置を並べるもので、いまどのブランチからも指されていない位置まで残っている。1時間前に消したブランチや、--amend で置き換えられたコミットは、ハッシュを指定すれば取り出せる。git log -g は同じ記録をログの形で読む。反対に、作業ツリーにあっただけの編集は、git stash もコミットも通していなければ、どこにも控えがない。
checkoutとswitchとrestoreの使い分け
git checkout は、ブランチを移る仕事とファイルを戻す仕事という、性質の違う2つを兼ねている。これを分けるために git switch と git restore が用意された。ただし2026年9月時点の Git 2.48.1 に付いてくるマニュアルでも、両方に「このコマンドは実験的であり、挙動は変わる可能性がある」という注意書きが大文字で残っている。導入から数年が経っても残っているので、新しい2つに置き換えて古い1つを忘れる、という覚え方は勧めにくい。3つとも読めるようにしておくのが実務的な答えになる。
git switch main
git switch -c fix/expired-coupons
git switch -
git restore src/checkout.ts
git restore --staged src/checkout.ts
git switch はブランチを移るだけで、それ以外のことをしない。インデックスと作業ツリーがきれいである必要はないが、移動によってローカルの変更が失われる場合は中断する。--discard-changes か --merge を付けたときだけ進む。-c は作成して移動、- だけを渡すと直前にいたブランチへ戻る。
git restore は、パスの中身を戻す。何も付けなければインデックスの内容で作業ツリーを戻し、--staged を付けると HEAD の内容でインデックスを戻す。後者がいわゆる「ステージから降ろす」操作にあたる。--source で別のコミットから取ってくることもでき、-p を付ければ塊ごとに選べる。
古い書き方も現役である。ブランチを移る git checkout <branch>、ファイルを捨てる git checkout -- <file>、降ろす git reset HEAD <file> は、既存のスクリプトや過去の記事の中に大量に残っている。どれも廃止されていない。初心者が最初に触るなら git switch と git restore のほうが意味が明快だが、他人の手順書を読む段になると git checkout の2つの顔を知らないと詰まる。
壊れた場所を二分探索で見つける
名前は知られているのに使われないコマンドが git bisect である。壊れていると分かっているコミットと、問題がなかったコミットの間を二分探索して、境目を見つける。
git bisect start
git bisect bad
git bisect good v3.2.0
git bisect run npm test -- src/checkout.test.ts
git bisect reset
マニュアルは、これを「不具合を持ち込んだコミットを二分探索で見つけるためのコマンド」だと説明し、さらに広い使い方も示している。good と bad の代わりに old と new を使えるので、不具合を直したコミットや、処理速度が変わったコミットを探すのにも同じ探索が使える。1,000個のコミットの範囲でも、およそ10回の判定で終わる。
git bisect run は、判定をスクリプトに任せる形である。この契約は正確に覚えておく価値がある。良いときは終了コード0、悪いときは1から127のいずれかを返す。125は予約されていて、このコミットは試せないという意味になり、判定せずに飛ばす。それ以外の値を返すと探索そのものが止まる。exit(-1) で終わるスクリプトは255を返すため、悪い側として扱われる点も見落としやすい。終わったら git bisect reset で元の位置に戻す。古いコミットに居座ったまま作業を続けてしまう事故は、この1行の忘れから起きる。
探すコマンドはもう2つある。git grep は、作業ツリーの追跡対象のファイル、インデックスに登録された内容、あるいは指定したツリーの内容からパターンを探す。つまりタグや過去のコミットを、チェックアウトせずに検索できる。git ls-files はインデックスの一覧と実際のディレクトリの一覧を突き合わせて出すので、そのファイルが追跡されているのか、無視されているのか、ただ置いてあるだけなのかがはっきりする。
Macで一度だけ見ておく設定
同じコマンドでも、Macでは挙動が変わる設定が4つある。読むだけで、原因不明に見えていた挙動のいくつかが説明できる。
git config --get core.ignoreCase
git config --get core.precomposeUnicode
git config --get core.protectHFS
core.ignoreCase は、APFSやHFS+のように大文字と小文字を区別しないファイルシステムでGitを動かすための内部変数だと説明されている。既定は false だが、git clone と git init がファイルシステムを調べて、必要なら true に設定する。マニュアルはこの値が環境に対して正しいことを前提にGitが動いているため、変更すると予期しない挙動になると書いている。読む対象であって、書き換える対象ではない。大文字小文字だけを変えるリネームが素直に通らないのも、ここが理由である。git mv README.md tmp を挟んでから目的の名前に変える、という二段構えが要る。
core.precomposeUnicode は、macOS版のGitだけが使う設定である。true のとき、macOSが行うファイル名のUnicode分解を元に戻す。濁点や半濁点を含む日本語のファイル名が、MacとLinuxのビルドサーバで別物として扱われる問題は、ここで説明がつく。多言語のファイル名を日常的に扱う環境なら、最初に確認しておきたい。どの言語の表示に対応しているかという観点は対応言語のページに整理されていて、ファイル名の扱いと画面の言語は別の話だという前提を掴んでおくと切り分けが楽になる。
core.protectHFS は macOS で既定が true で、HFS+ 上で .git と同じものと見なされるパスのチェックアウトを拒む。安全のための設定なので、そのままにしておく。core.fsmonitor は、Gitに内蔵されたファイルシステム監視の常駐処理を有効にする設定で、外部の道具を入れる必要がない。git status のようにインデックスを読み直すコマンドが、ファイル数の多い作業ツリーでも速くなる。対応するのは現時点で Windows と macOS に限られる。
認証情報の保管も最後に触れておく。macOS版のGitには git-credential-osxkeychain が同梱されていて、これを認証情報の補助として指定しておけば、HTTPSのリモートが毎回トークンを聞いてこなくなる。
時間が消えているのは、コマンドの数ではなく窓の数
1日に叩くコマンドを数えると、多くても十数個にしかならない。ところが、その周りの往復を数えると桁が変わる。ターミナルで状態を読む、ファイルの一覧から目的のファイルを探す、別のディレクトリを開いているエディタに切り替える、テストを走らせる、差分を読む、また戻る。この往復はGitの知識では短くならない。窓の配置の問題だからである。
ファイルの一覧とターミナルとAIの応答が同じ窓で同じ作業ディレクトリを共有している状態なら、git status の出力と、そこに並んだファイル名が同時に視野に入る。切り替えが作業の一部でなくなる、という形の短縮になる。どこまでを1つの窓に収める設計なのかはできることのページに並んでいて、2画面型のファイル管理アプリやターミナル中心の道具と設計がどう違うのかは他のファイル管理との比較で軸ごとに整理されている。
長い処理を走らせたまま席を離れる場合も同じ構図になる。テストが途中で止まって入力を待っているとき、手元にMacがなければ待ち時間がそのまま失われる。外出先から続きを返せるかどうかという観点はiPhone・iPadから続きをにまとめられている。導入にいくらかかるのかは料金に、対応環境や移行時の疑問はよくある質問に載っているので、比べる前に前提を揃えておくと判断が速い。
決める順番としては、まず git add . を1週間だけ git add -p に置き換える。次に、共有ブランチへ送る前に git log --oneline origin/main..HEAD を読む癖を付ける。この2つはあとのすべての操作の効きを変える。きれいなコミットが積まれていることが、取り消しも移植も二分探索も使える状態の前提になるからだ。そこまで整えて、それでも往復の時間が残るなら、残っているのは知識の問題ではなく配置の問題である。
よくある質問
git コマンドは何個覚えれば仕事になりますか?
毎日の作業に必要なのは git status、git diff、git add -p、git commit、git push の5つと、git stash、git log、git switch を加えた8つ程度です。残りは必要になった時点でマニュアルを読めば足ります。覚える数を増やすより、作業ツリーとインデックスと履歴という3つの場所の区別を固めるほうが効きます。
git add したあとに git diff を叩いても何も出ないのはなぜですか?
引数なしの git diff は、作業ツリーとインデックスの差分を表示します。git add を済ませた時点で両者は同じ内容になるため、表示するものがなくなります。ステージに入った分を確認したいときは git diff --staged を使います。--cached も同じ意味です。
git switch と git restore に置き換えて、git checkout は忘れてよいですか?
意味は明快になりますが、Git 2.48.1 のマニュアルでも両方に実験的であるという注意書きが残っており、git checkout は廃止されていません。過去のスクリプトや手順書には git checkout が大量に残っているため、3つとも読めるようにしておくのが実務的です。
git reset --hard で消した変更は戻せますか?
コミットしていた分なら git reflog に位置が残っているので、ハッシュを指定して取り戻せます。一方、作業ツリーにあっただけで一度もコミットも git stash も通していない編集は、どこにも記録がないため戻せません。危ない操作の前に git stash push -u を通しておくのが安全側の手順です。