コンテンツへスキップ
6分で読めます

Git応用ワークフロー:毎週何時間も節約できるコマンドたち

インタラクティブリベース、cherry-pick、bisect、worktree、reflogレスキュー、実際に機能するブランチ戦略。ほとんどの開発者が知らない、毎日使うGitコマンド。

シェア:X / TwitterLinkedIn

ほとんどの開発者は5つのGitコマンドを覚えたらそこで止まる。addcommitpushpullmerge。冒険心があればcheckoutbranchも。最初の1年はそれで乗り切れる。そしてブランチに「fix」や「wip」や「please work」のようなメッセージのコミットが47個溜まり、リセットすべきでないものを誤ってリセットし、失敗したマージを元に戻そうとStack Overflowで40分過ごすことになる。

私はGitを何年も使ってきた。カジュアルにではなく — ヘビーに。複数のブランチ、複数のリポジトリ、複数の共同作業者、毎日、一日中。以下は私が実際に使っているコマンドとワークフローだ。チュートリアルで見栄えのするものではなく、毎週、本当の時間を節約してくれるものだ。

インタラクティブリベース:誰かに見られる前に散らかりを片付ける#

ブランチに12個のコミットがある。半分は「fix typo」。1つは「undo previous commit」。もう1つは「actually fix it this time」。PRを開こうとしている。誰もその履歴を見る必要はない。

インタラクティブリベースは、共有する前にブランチの履歴を書き換える方法だ。コミットを統合したり、メッセージを書き直したり、並び替えたり、完全に削除したりできる。

基本コマンド#

bash
git rebase -i HEAD~5

これはエディタを開き、最後の5つのコミットを古い順に表示する:

bash
pick a1b2c3d Add user authentication endpoint
pick d4e5f6g Fix typo in auth middleware
pick h7i8j9k Add rate limiting
pick l0m1n2o Fix rate limit bug
pick p3q4r5s Update auth tests

各行はコマンドで始まる。pickを以下のいずれかに変更する:

  • squash(またはs — このコミットを上のコミットにマージし、メッセージを結合
  • fixup(またはf — squashと同じだが、このコミットのメッセージを破棄
  • reword(またはr — コミットを保持するがメッセージを変更
  • drop(またはd — このコミットを完全に削除
  • edit(またはe — このコミットでリベースを一時停止して修正可能にする

実際のクリーンアップセッション#

実際に私がやること。上の散らかった履歴はこうなる:

bash
pick a1b2c3d Add user authentication endpoint
fixup d4e5f6g Fix typo in auth middleware
pick h7i8j9k Add rate limiting
fixup l0m1n2o Fix rate limit bug
pick p3q4r5s Update auth tests

保存して閉じる。5つの代わりに3つのクリーンなコミットになった。タイポ修正は認証コミットに折り込まれる。レート制限のバグ修正はレート制限コミットに折り込まれる。PRレビュアーはクリーンで論理的な進行を見ることになる。

コミットの並び替え#

文字通り行を並び替えることができる。テストコミットがレート制限コミットの前に来るべきなら、行を移動するだけだ:

bash
pick a1b2c3d Add user authentication endpoint
pick p3q4r5s Update auth tests
pick h7i8j9k Add rate limiting

Gitはこの新しい順序でコミットをリプレイする。コンフリクトがあれば一時停止して解決させてくれる。

オートスカッシュのショートカット#

コミットが前のコミットの修正だとわかっている場合、コミット時にマークしておく:

bash
git commit --fixup=a1b2c3d

これはfixup! Add user authentication endpointというメッセージのコミットを作成する。そしてリベース時に:

bash
git rebase -i --autosquash HEAD~5

Gitが自動的にfixupコミットをターゲットの直下に並び替え、fixupとしてマークする。保存して閉じるだけだ。手動編集不要。

私はこれを常に使っている。最終的な履歴をクリーンに保ちながらブランチで反復作業する最速の方法だ。

ゴールデンルール#

共有ブランチにプッシュ済みのコミットは絶対にリベースしない。 他の人がそれらのコミットに基づいて作業している場合、履歴の書き換えは本当の問題を引き起こす。マージ前に自分のフィーチャーブランチをリベースする。mainは絶対にリベースしない。

フィーチャーブランチをすでにプッシュしていてリベースが必要な場合:

bash
git push --force-with-lease

--force-with-leaseフラグは--forceより安全だ。最後のフェッチ以降に他の誰かが同じブランチにプッシュしていれば、プッシュを拒否する。すべての問題を防ぐわけではないが、最も一般的な問題をキャッチする。

Cherry-Pick:外科的なコミット転送#

Cherry-pickは特定のコミットをあるブランチから取り出して別のブランチに適用する。マージでもリベースでもない。1つのコミットだけを、きれいに適用する。

実際に使うとき#

最も一般的なシナリオ:フィーチャーブランチでバグを修正したが、その修正をmainまたはリリースブランチにも今すぐ入れる必要がある。フィーチャーブランチ全体をマージしたくない。その1つの修正だけが欲しい。

bash
# 修正のコミットハッシュを見つける
git log --oneline feature/user-auth
# a1b2c3d Fix null pointer in session validation
 
# mainに切り替えてcherry-pick
git checkout main
git cherry-pick a1b2c3d

完了。修正は同じ変更を持つ新しいコミットとしてmainにある。

コミットなしのCherry-Pick#

変更を適用するがまだコミットしたくないことがある。複数のcherry-pickを1つのコミットにまとめたい場合や、変更を少し修正したい場合:

bash
git cherry-pick --no-commit a1b2c3d

変更はステージされているがコミットされていない。修正し、さらに変更を加え、準備ができたらコミットできる。

範囲Cherry-Pick#

連続する複数のコミットが必要な場合、範囲構文を使用:

bash
git cherry-pick a1b2c3d..f6g7h8i

これはa1b2c3dの後からf6g7h8iを含むまですべてをcherry-pickする。a1b2c3d自体は除外されることに注意。含めたい場合:

bash
git cherry-pick a1b2c3d^..f6g7h8i

コンフリクトの処理#

Cherry-pickのコンフリクトはマージコンフリクトと同じように機能する。発生した場合:

bash
# Gitがコンフリクトを通知する
# コンフリクトしたファイルを修正し、以下を実行:
git add .
git cherry-pick --continue

気が変わった場合:

bash
git cherry-pick --abort

注意点が1つ:cherry-pickされたコミットは新しいコミットハッシュを作成する。後で元のブランチをマージする場合、Gitは通常重複をうまく処理する。しかし最終的にマージされるブランチ間で積極的にcherry-pickすると、予期しないコンフリクトが発生する可能性がある。マージ戦略としてではなく、外科的に使用すること。

Git Bisect:バグのバイナリサーチ#

何かが壊れた。2週間前には動いていたことを知っている。それ以降200コミットがある。どれが壊したのか?

各コミットを手動でチェックすることもできる。またはgit bisectを使えば、バイナリサーチで正確な原因コミットをlog2(n)ステップで見つけられる。200コミットの場合、200の代わりに約7〜8回のチェックだ。

手動の方法#

bash
# bisectを開始
git bisect start
 
# 現在のコミットをbad(バグがここに存在する)としてマーク
git bisect bad
 
# 既知のgoodコミット(バグがここには存在しなかった)をマーク
git bisect good v2.1.0

GitがgoodとbadのMiddleのコミットをチェックアウトする。テストする。そして:

bash
# このコミットにバグが存在する場合:
git bisect bad
 
# このコミットにバグが存在しない場合:
git bisect good

Gitは毎回範囲を半分に狭める。7〜8ステップ後、教えてくれる:

bash
a1b2c3d4e5f6g7h is the first bad commit
commit a1b2c3d4e5f6g7h
Author: Some Developer <dev@example.com>
Date: Tue Feb 18 14:23:01 2026 +0300
 
    Refactor session handling to use async middleware

バグを導入した正確なコミットがわかった。完了したら:

bash
git bisect reset

これで元の場所に戻る。

自動化された方法(これが本当の力)#

バグを検出できるテストがある場合、全体を自動化できる:

bash
git bisect start
git bisect bad HEAD
git bisect good v2.1.0
git bisect run npm test -- --grep "session validation"

Gitが自動的にコミットをチェックアウトし、テストを実行し、終了コードに基づいてgoodまたはbadとマークする。ゼロはgood、非ゼロはbad。席を離れて、戻ってくれば正確なコミットを教えてくれる。

任意のスクリプトを使用できる:

bash
git bisect run ./test-regression.sh

test-regression.shの内容:

bash
#!/bin/bash
npm run build 2>/dev/null || exit 125  # 125は「このコミットをスキップ」を意味する
npm test -- --grep "session" || exit 1  # 1は「bad」を意味する
exit 0                                   # 0は「good」を意味する

終了コード125は特別だ。bisectにそのコミットをスキップするよう指示する(コンパイルできないコミットに有用)。必要になるまでニッチに見えるが、必要になると午後全体を節約する機能だ。

実際のBisectセッション#

実際にはこのように見える:

bash
$ git bisect start
$ git bisect bad HEAD
$ git bisect good abc1234
Bisecting: 97 revisions left to test after this (roughly 7 steps)
[def5678...] Commit message here
 
$ npm test -- --grep "login flow"
# テスト失敗
 
$ git bisect bad
Bisecting: 48 revisions left to test after this (roughly 6 steps)
[ghi9012...] Another commit message
 
$ npm test -- --grep "login flow"
# テスト成功
 
$ git bisect good
Bisecting: 24 revisions left to test after this (roughly 5 steps)
...
 
# 約7回の反復後:
abc1234def5678ghi is the first bad commit

200コミットの山の中から針を見つけるのに7ステップ。

Git Worktree:複数ブランチ、スタッシュゼロ#

これは最も活用されていないGit機能だ。ほとんどの開発者がその存在を知らないと確信している。

問題:フィーチャーブランチの深いところで作業中。あちこちでファイルが変更されている。すると誰かが「この本番バグをちょっと見てくれない?」と言う。3つの選択肢がある:

  1. すべてをスタッシュし、ブランチを切り替え、修正し、戻って、スタッシュをポップ。何も問題が起きないことを祈る。
  2. 半完成の作業を「wip」メッセージでコミット。不格好だが機能的。
  3. リポジトリを別のディレクトリにもう一度クローン。

または選択肢4:worktree

Worktreeとは#

Worktreeは同じリポジトリにリンクされた2つ目(または3つ目、4つ目)の作業ディレクトリだ。各worktreeは独自のチェックアウトされたブランチ、独自の作業ファイル、独自のインデックスを持つ。しかし同じ.gitデータを共有するので、リポジトリ全体を複製しているわけではない。

Worktreeの追加#

bash
# feature/user-authで深く作業中
# mainでバグ修正が必要:
 
git worktree add ../hotfix-session main

これはmainがチェックアウトされた新しいディレクトリ../hotfix-sessionを作成する。現在のディレクトリはそのまま。スタッシュなし、コミットなし、何も中断なし。

bash
cd ../hotfix-session
# バグを修正
git add .
git commit -m "Fix null pointer in session validation"
git push origin main
cd ../my-project
# 何事もなかったかのようにフィーチャーの作業を続行

Worktreeで新しいブランチを作成#

bash
git worktree add ../hotfix-nav -b hotfix/nav-crash main

これはworktreeを作成し、同時にmainに基づく新しいブランチhotfix/nav-crashを作成する。

Worktreeの管理#

bash
# すべてのworktreeをリスト
git worktree list
# /home/dev/my-project         abc1234 [feature/user-auth]
# /home/dev/hotfix-session     def5678 [main]
 
# 完了したらworktreeを削除
git worktree remove ../hotfix-session
 
# ディレクトリがすでに削除されている場合:
git worktree prune

スタッシュより優れている理由#

スタッシュは素早いコンテキスト切り替えには問題ない。しかしworktreeは5分以上かかるものには優れている:

  • IDEはフィーチャーブランチを開いたまま。再インデックスなし、スクロール位置を失わない。
  • 両方のディレクトリで同時にテストを実行できる。
  • スタッシュコンフリクトやスタッシュし忘れのリスクがない。
  • 一方のworktreeで長時間実行のdevサーバー、もう一方でクリーンビルドを持てる。

私は通常2つか3つのworktreeをアクティブにしている:メインのフィーチャーブランチ、素早いチェック用のmainworktree、そして時には他の人のPRをチェックアウトするレビューworktree。

1つの注意点#

同じブランチを2つのworktreeでチェックアウトすることはできない。設計上の意図だ。同じブランチに2つの場所で競合する変更を加えることを防ぐ。試みると、Gitは拒否する。

Reflog:すべてのアンドゥボタン#

ハードリセットしてコミットを失った。ブランチを削除した。リベースしたら恐ろしいことになった。作業が消えたと思っている。

消えていない。Gitは実際にはほとんど何も削除しない。reflogがセーフティネットだ。

Reflogとは#

HEADが移動するたびに — コミット、チェックアウト、リベース、リセット、マージのたびに — Gitはreflogに記録する。HEADがどこにいたかのログで、順番に記録される。

bash
git reflog

出力:

bash
a1b2c3d (HEAD -> main) HEAD@{0}: reset: moving to HEAD~3
f4e5d6c HEAD@{1}: commit: Add payment processing
b7a8c9d HEAD@{2}: commit: Update user dashboard
e0f1g2h HEAD@{3}: commit: Fix auth token refresh
i3j4k5l HEAD@{4}: checkout: moving from feature/payments to main

各エントリにはインデックス(HEAD@{0}HEAD@{1}など)と何が起こったかの説明がある。

ハードリセット後の復旧#

誤ってgit reset --hard HEAD~3を実行して3つのコミットを失った。reflogにちゃんとある:

bash
# 失ったものを確認
git reflog
 
# リセット前のコミットはHEAD@{1}
git reset --hard f4e5d6c

3つのコミットがすべて戻った。危機回避。

削除されたブランチの復旧#

マージされていない作業があるブランチを削除した:

bash
git branch -D feature/experimental
# しまった、2週間分の作業があった

コミットはまだ存在する。見つける:

bash
git reflog | grep "feature/experimental"
# またはreflogを見てそのブランチの最後のコミットを探す
 
# 見つけた。そのコミットでブランチを再作成:
git branch feature/experimental a1b2c3d

ブランチが戻った、すべてのコミットとともに。

悪いリベース後の復旧#

リベースしたらすべてがおかしくなった。どこにでもコンフリクト、間違ったコミット、カオス:

bash
# reflogにリベース前の位置が表示される
git reflog
# a1b2c3d HEAD@{0}: rebase (finish): ...
# ...
# f4e5d6c HEAD@{5}: rebase (start): checkout main
# b7a8c9d HEAD@{6}: commit: Your last good commit
 
# リベース前に戻る
git reset --hard b7a8c9d

リベースが始まる前の正確な状態に戻った。何も起こらなかったかのように。

30日間のセーフティネット#

デフォルトでは、Gitはreflogエントリを30日間保持する(到達可能なコミットは90日間)。その後、ガベージコレクションされる可能性がある。つまり、ミスに気づくまで1ヶ月ある。実際には、これで十分すぎる。

有効期限を確認できる:

bash
git config gc.reflogExpire
# default: 90.days.ago(到達可能なもの)
git config gc.reflogExpireUnreachable
# default: 30.days.ago(到達不可能なもの)

心配性なら増やす:

bash
git config --global gc.reflogExpireUnreachable "180.days.ago"

個人ルール#

破壊的な操作 — ハードリセット、フォースプッシュ、ブランチ削除 — の前に、まずgit log --oneline -10を実行する。現在のHEADを心に留める。2秒で済み、必要のないパニックから何度も救ってくれた。

正しいスタッシュ:git stashだけじゃない#

ほとんどの人はスタッシュをこう使う:

bash
git stash
# 何かをする
git stash pop

これは機能するが、すべてを「もの」とラベルされた箱に放り込むのと同じだ。3つのスタッシュがあると、どれがどれかわからない。

スタッシュに名前を付ける#

bash
git stash push -m "WIP: user auth form validation"

スタッシュをリストすると:

bash
git stash list
# stash@{0}: On feature/auth: WIP: user auth form validation
# stash@{1}: On main: Quick fix attempt for nav bug
# stash@{2}: On feature/payments: Experiment with Stripe webhooks

各スタッシュが何を含むか正確にわかる。

追跡されていないファイルを含める#

デフォルトでは、git stashは追跡されたファイルのみをスタッシュする。まだ追加していない新しいファイルは残される:

bash
# 新しいファイルを含め、すべてをスタッシュ
git stash push --include-untracked -m "WIP: new auth components"
 
# 無視されたファイルも含める(まれに必要)
git stash push --all -m "Full workspace snapshot"

私はほぼ毎回--include-untrackedを使う。ブランチ切り替え時に新しいファイルを残すと混乱を招く。

部分的なスタッシュ#

ほとんどの人が知らないもの。特定のファイルをスタッシュできる:

bash
# 特定のファイルのみをスタッシュ
git stash push -m "Just the auth changes" src/auth/ src/middleware.ts

またはパッチモードを使ってファイル内の特定のハンクをスタッシュ:

bash
git stash push --patch -m "Partial: only the validation logic"

Gitが各変更を対話的に確認し、スタッシュするか尋ねる。yはyes、nはno、sはハンクをさらに小さく分割。

ApplyとPopの違い#

bash
# Pop: 適用してスタッシュリストから削除
git stash pop stash@{2}
 
# Apply: 適用するがスタッシュリストに残す
git stash apply stash@{2}

スタッシュがきれいに適用されるか不明な場合はapplyを使う。コンフリクトがあればスタッシュは保持される。popの場合、コンフリクトがあるとスタッシュはリストに残る(多くの人はこれを知らない)が、applyの明示的な意図の方が好みだ。

スタッシュ内容の確認#

bash
# スタッシュで変更されたファイルを確認
git stash show stash@{0}
 
# 完全なdiffを確認
git stash show -p stash@{0}

スタッシュからブランチを作成#

スタッシュがより本格的なものに成長した場合:

bash
git stash branch feature/auth-validation stash@{0}

これは元々スタッシュしたコミットから新しいブランチを作成し、スタッシュを適用して削除する。クリーン。

ブランチ戦略:実際に機能するのはどれか#

主流のブランチ戦略は3つある。それぞれに輝くコンテキストと痛みを引き起こすコンテキストがある。

Gitflow#

クラシック。maindevelopfeature/*release/*hotfix/*。2010年にVincent Driessenが作成。

bash
# フィーチャーブランチ
git checkout -b feature/user-auth develop
# ... 作業 ...
git checkout develop
git merge --no-ff feature/user-auth
 
# リリースブランチ
git checkout -b release/2.1.0 develop
# ... 最終修正 ...
git checkout main
git merge --no-ff release/2.1.0
git tag -a v2.1.0 -m "Release 2.1.0"
git checkout develop
git merge --no-ff release/2.1.0
 
# ホットフィックス
git checkout -b hotfix/session-fix main
# ... 修正 ...
git checkout main
git merge --no-ff hotfix/session-fix
git checkout develop
git merge --no-ff hotfix/session-fix

機能する場合: モバイルアプリ、デスクトップソフトウェア、名前付きバージョンリリースがあり複数バージョンが同時にサポートされるもの。v2.1とv3.0をリリースして両方にパッチを当てる必要がある場合、Gitflowはそれを処理する。

機能しない場合: 継続的デプロイメントのWebアプリケーション。本番環境に1日5回デプロイする場合、リリースブランチやdevelopブランチの儀式は純粋なオーバーヘッドだ。Gitflowを採用するほとんどのWebチームは、永続的に壊れたdevelopブランチと誰も理解しないリリースブランチを抱えることになる。

GitHub Flow#

シンプル。mainがある。フィーチャーブランチを作成する。PRを開く。mainにマージする。mainをデプロイする。

bash
git checkout -b feature/user-auth main
# ... 作業 ...
git push origin feature/user-auth
# PRを開き、レビューされ、マージ
# mainは常にデプロイ可能

機能する場合: Webアプリを出荷する小〜中規模チーム。継続的デプロイメント。mainが常にデプロイされているなら、これだけで十分。このサイトが使っているのがこれだ。

機能しない場合: 複数のリリースバージョンを維持する必要がある場合、またはデプロイ前に長いQAサイクルがある場合。GitHub Flowはmainがすぐに本番に行くことを前提としている。

トランクベース開発#

全員がmain(「トランク」)に直接コミットするか、非常に短命なブランチ(1日未満)を経由する。長期的なフィーチャーブランチなし。

bash
# 短命ブランチ(当日マージ)
git checkout -b fix/auth-token main
# ... 小さく焦点を絞った変更 ...
git push origin fix/auth-token
# PRは数時間以内にレビューされマージ、数日ではなく

機能する場合: 良いCI/CD、包括的なテストスイート、フィーチャーフラグを持つ高パフォーマンスチーム。Google、Meta、ほとんどの大手テック企業がトランクベース開発を使用している。小さく段階的な変更を強制し、マージ地獄を排除する。

機能しない場合: テストカバレッジやCIが不十分なチーム。トランクへのマージがテストされていないコードのデプロイを意味する場合、常に本番を壊すことになる。1日以上かかるものにはフィーチャーフラグも必要:

bash
# コード内のフィーチャーフラグ
if (featureFlags.isEnabled('new-checkout-flow')) {
  renderNewCheckout();
} else {
  renderLegacyCheckout();
}

私の推奨#

ほとんどのWeb開発チームには:GitHub Flowから始める。シンプルで、機能し、GitHub/GitLabがすでに提供しているもの以上のツールを必要としない。

チームが15〜20人のエンジニアを超え、1日に複数回デプロイしている場合、フィーチャーフラグ付きトランクベース開発を検討する。フィーチャーフラグインフラへの投資は、マージコンフリクトの削減と反復の高速化で元が取れる。

バージョン管理されたソフトウェア(モバイルアプリ、CLIツール、ライブラリ)を出荷する場合:Gitflowまたはその簡略版。リリースブランチが実際に必要だ。

ブログ記事が最良だと言ったからという理由で戦略を選ぶな。実際にどう出荷しているかに合うものを選べ。

Gitフック:忘れがちなことを自動化する#

Gitフックは、Gitワークフローの特定のポイントで自動的に実行されるスクリプトだ。マシンにローカル(リモートにプッシュされない)なので、チームと共有する方法が必要だ。

実際に重要なフック#

pre-commit — すべてのコミット前に実行。リンティングとフォーマットに使用:

bash
#!/bin/bash
# .git/hooks/pre-commit
 
# ステージされたファイルのみでESLintを実行
STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep -E '\.(js|ts|tsx)$')
 
if [ -n "$STAGED_FILES" ]; then
  echo "Running ESLint on staged files..."
  npx eslint $STAGED_FILES --quiet
  if [ $? -ne 0 ]; then
    echo "ESLint failed. Fix errors before committing."
    exit 1
  fi
fi
 
# ステージされたファイルでPrettierを実行
if [ -n "$STAGED_FILES" ]; then
  echo "Running Prettier..."
  npx prettier --check $STAGED_FILES
  if [ $? -ne 0 ]; then
    echo "Prettier check failed. Run 'npx prettier --write' first."
    exit 1
  fi
fi

commit-msg — コミットメッセージのフォーマットを検証。Conventional Commitsの強制に最適:

bash
#!/bin/bash
# .git/hooks/commit-msg
 
COMMIT_MSG=$(cat "$1")
PATTERN="^(feat|fix|docs|style|refactor|test|chore|perf|ci|build|revert)(\(.+\))?: .{1,72}"
 
if ! echo "$COMMIT_MSG" | grep -qE "$PATTERN"; then
  echo "Invalid commit message format."
  echo "Expected: type(scope): description"
  echo "Example: feat(auth): add session refresh endpoint"
  echo ""
  echo "Valid types: feat, fix, docs, style, refactor, test, chore, perf, ci, build, revert"
  exit 1
fi

pre-push — プッシュ前に実行。テストに使用:

bash
#!/bin/bash
# .git/hooks/pre-push
 
echo "Running tests before push..."
npm test
if [ $? -ne 0 ]; then
  echo "Tests failed. Push aborted."
  exit 1
fi

Husky vs ネイティブフック#

ネイティブフックは.git/hooks/に存在する。問題:.gitディレクトリはGitによって追跡されないので、リポジトリ経由でフックを共有できない。全員が手動でセットアップする必要がある。

Huskyはこれを解決する。フック設定をリポジトリに保存し、npm install時に自動的に.git/hooksをセットアップする:

bash
npx husky init

これは.husky/ディレクトリを作成する。ファイルとしてフックを追加:

bash
# .husky/pre-commit
npx lint-staged

lint-stagedと組み合わせると、高速で的を絞ったpre-commitチェックが得られる:

json
{
  "lint-staged": {
    "*.{js,ts,tsx}": ["eslint --fix", "prettier --write"],
    "*.css": ["prettier --write"],
    "*.json": ["prettier --write"]
  }
}

これは実際にコミットしているファイルのみでESLintとPrettierを実行する。コードベース全体ではない。高速。

フックをスキップするとき#

フックを実行せずにコミットする必要がある場合もある。緊急ホットフィックス、自分のブランチでのwork-in-progressコミット:

bash
git commit --no-verify -m "WIP: debugging production issue"

控えめに使用すること。定期的にフックをスキップしているなら、フックが遅すぎるか厳しすぎる可能性がある。

時間を節約するエイリアス#

私の.gitconfigは何年もかけて進化してきた。これらは生き残ったエイリアスだ。実際に毎日使うもので、賢く見えるから追加したものではない。

きれいなログ#

デフォルトのgit logは冗長だ。これはクリーンでカラフルなグラフベースのビューを提供する:

bash
git config --global alias.lg "log --oneline --graph --all --decorate"

使用法:

bash
git lg
# * a1b2c3d (HEAD -> main) Fix session validation
# | * d4e5f6g (feature/payments) Add Stripe integration
# | * h7i8j9k Update payment models
# |/
# * l0m1n2o Merge PR #42
# * p3q4r5s Add user dashboard

1日に20回実行する。リポジトリの状態を理解する最速の方法だ。

最後のコミットを元に戻す#

変更は保持し、コミットだけを元に戻す:

bash
git config --global alias.undo "reset HEAD~1 --mixed"

使用法:

bash
git undo
# コミットは消えたが、すべての変更はまだ作業ディレクトリにある

コミットが早すぎたとき、ファイルを忘れたとき、変更を再構成したいときに使う。

すべてをアンステージ#

bash
git config --global alias.unstage "reset HEAD --"

使用法:

bash
git unstage src/auth/session.ts
# ファイルはアンステージされるが変更は保持される

すべてのエイリアスをリスト#

設定したものを忘れるから:

bash
git config --global alias.aliases "config --get-regexp ^alias\\."

その他使用しているエイリアス#

bash
# コミットしようとしているものを表示
git config --global alias.staged "diff --staged"
 
# 短いステータス
git config --global alias.st "status -sb"
 
# メッセージを変更せずにamend
git config --global alias.amend "commit --amend --no-edit"
 
# 最後のコミットを表示
git config --global alias.last "log -1 HEAD --stat"
 
# マージの代わりにリベースでpull
git config --global alias.up "pull --rebase --autostash"
 
# mainにマージ済みのブランチを削除
git config --global alias.cleanup "!git branch --merged main | grep -v '^[ *]*main$' | xargs git branch -d"

upエイリアスは特に良い。pull --rebaseはプルのたびにマージコミットを作成する代わりに履歴をリニアに保つ。--autostashはダーティファイルがある場合に自動的に変更をスタッシュ・復元する。pullのデフォルトであるべきだった。

cleanupエイリアスはmainにマージされたローカルブランチを削除する。しばらくすると古いブランチが数十個溜まる。毎週実行すること。

毎日使うコマンド#

これらはエイリアスや高度な機能ではない。多くの開発者が知らないようだが、私が常に実行するコマンドだ。

究極のログコマンド#

bash
git log --oneline --graph --all

これはすべてのブランチ、すべてのマージ、リポジトリのトポロジー全体をコンパクトなビューで表示する。変更をプルした時に最初に実行する。「このリポジトリで今何が起きているか?」に答える。

ステージされた変更のDiff#

bash
git diff --staged

これはコミットされようとしているものを表示する。作業ディレクトリで変更されたものではなく、実際にステージされたものだ。コミット前に必ず実行する。必ず。誤った包含、デバッグ文、あるべきでないconsole.logをキャッチする。

bash
# ステージされていない変更を確認
git diff
 
# ステージされた変更を確認
git diff --staged
 
# 両方を確認
git diff HEAD

特定のコミットを表示#

bash
git show a1b2c3d

単一コミットの完全なdiffを表示する。履歴のレビュー、コミットが実際に何を変更したかの理解に有用。

bash
# 変更されたファイルのみを表示
git show --stat a1b2c3d
 
# 特定のコミットの特定のファイルを表示
git show a1b2c3d:src/auth/session.ts

最後のものは非常に有用だ。そのコミットをチェックアウトせずに、履歴の任意の時点の任意のファイルを表示できる。

行範囲のBlame#

bash
git blame -L 42,60 src/auth/session.ts

行42〜60を最後に修正した人を表示する。ファイル全体をblameするより有用で、通常は圧倒的だ。

bash
# ハッシュだけでなくコミットメッセージも表示
git blame -L 42,60 --show-name src/auth/session.ts
 
# 空白変更を無視(非常に有用)
git blame -w -L 42,60 src/auth/session.ts
 
# blameされたものの前のコミットを表示(さらに掘り下げ)
git log --follow -p -- src/auth/session.ts

-wフラグは重要だ。これがないと、blameはファイルを最後にリフォーマットした人に帰属させるが、それは探している人であることはまれだ。

すべての履歴で文字列を検索#

bash
# 関数がいつ追加または削除されたかを検索
git log -S "validateSession" --oneline
 
# 正規表現パターンがいつ現れたかを検索
git log -G "session.*timeout" --oneline

-S(「pickaxe」)は文字列の出現回数が変化したコミットを見つける。-Gはdiffが正規表現にマッチするコミットを見つける。両方とも考古学に強力だ。何かがいつ導入または削除されたかを理解するために。

2つのポイント間の変更を表示#

bash
# 2つのブランチ間で何が変わったか
git diff main..feature/auth
 
# mainから分岐してからこのブランチで何が変わったか
git diff main...feature/auth
 
# 変更されたファイルのみをリスト
git diff main...feature/auth --name-only
 
# 統計ビュー(ファイル + 挿入/削除)
git diff main...feature/auth --stat

2つのドットと3つのドットは重要な違い。2つのドットは両ブランチの先端間の差分を表示する。3つのドットは左側から分岐してから右側で何が変わったかを表示する。フィーチャーブランチをレビューする場合、通常は3つのドットが必要なものだ。

追跡されていないファイルのクリーンアップ#

bash
# 何が削除されるかを確認(ドライラン)
git clean -n
 
# 追跡されていないファイルを削除
git clean -f
 
# 追跡されていないファイルとディレクトリを削除
git clean -fd
 
# 追跡されていないファイルと無視されたファイルを削除(核オプション)
git clean -fdx

必ず最初に-nで実行する。git clean -fdxnode_modules.env、ビルド成果物 — Gitによって追跡されていないすべてを削除する。真に新鮮なスタートに有用だが、破壊的。

別のブランチから単一ファイルを復元#

bash
# ブランチを切り替えずにmainブランチのファイルバージョンを取得
git restore --source main -- src/config/database.ts

特定のコミットから:

bash
git restore --source a1b2c3d -- src/config/database.ts

これは古いgit checkout main -- path/to/file構文よりクリーンで、HEADに影響しない。

すべてを合わせる:実際のワークフロー#

これらのツールを使った典型的な1日はこのように見える:

bash
# 朝:何が起きているかチェック
git lg
git fetch --all
 
# フィーチャーを開始
git checkout -b feature/session-refresh main
 
# 作業、段階的にコミット
git add -p                        # ファイル全体ではなく特定のハンクをステージ
git commit -m "Add token refresh endpoint"
git commit -m "Add refresh token rotation"
git commit -m "Fix: handle expired refresh tokens"
git commit -m "Add integration tests"
 
# 中断:本番バグの修正が必要
git worktree add ../hotfix main
cd ../hotfix
# ... 修正、コミット、プッシュ、PRマージ ...
cd ../my-project
git worktree remove ../hotfix
 
# フィーチャー作業に戻る
git commit -m "Fix edge case in token validation"
 
# PR準備完了:履歴をクリーンアップ
git rebase -i main
# 修正コミットをスカッシュ、明確に書き直し
 
# プッシュしてPRを開く
git push -u origin feature/session-refresh
 
# ステージングで何か壊れた?どのコミットかを特定:
git bisect start
git bisect bad HEAD
git bisect good main
git bisect run npm test
 
# おっと、間違ったものをhard-resetした
git reflog
git reset --hard HEAD@{2}

これらのコマンドはそれぞれ数秒で完了する。合わせると何時間もの節約になる。仮定の時間ではなく、毎週の実際の時間だ。そうでなければGitの混乱を解きほぐしたり、手動でバグを探したり、コンテキストスイッチで作業を失ったりするのに費やされていたはずの時間だ。

Gitは深さが報われるツールだ。基本は1日を乗り切れる。しかしこの記事のコマンドは「Gitを使っている」と「Gitが実際に自分を速くしている」を分けるものだ。段階的に学べ。今週1つの新しいテクニックを選んで、筋肉記憶になるまで使え。そして次のを選べ。

午後11時に本番バグを見つめている未来の自分は、git bisectを知っていたことに感謝するだろう。

関連記事