Cursor で差分が崩れる/意図しない大量変更が入るときの復旧プレイブック

AI 駆動型エディタの Cursor は、コード補完や自動生成機能により開発効率を大きく向上させてくれます。しかし、AI による提案を適用した際に、意図しない大量の変更が混入したり、差分表示が崩れてレビューが困難になったりする経験はないでしょうか。
本記事では、Cursor で差分トラブルが発生した際の 復旧手順 と 予防策 を体系的にまとめたプレイブックを提供します。エラーが起きたときに慌てず対処できるよう、段階的な解決策をご紹介しますね。
背景
Cursor は VSCode をベースに AI 機能を強化したエディタで、コードの自動補完や Chat 機能を通じたコード生成が可能です。これらの機能により、開発者は効率的にコードを記述できますが、一方で AI が生成するコードには以下のような特性があります。
Cursor における AI コード生成の特性
| # | 項目 | 説明 | 
|---|---|---|
| 1 | 複数ファイル同時編集 | Chat 機能で複数ファイルを一度に変更できる | 
| 2 | コンテキスト依存の提案 | プロジェクト全体の文脈から変更を提案する | 
| 3 | フォーマット自動適用 | AI が独自のフォーマットルールを適用することがある | 
| 4 | インデント・改行の調整 | 既存コードのスタイルと異なる整形を行う場合がある | 
| 5 | 依存関係の自動追加 | import 文やパッケージを自動で追加・削除する | 
以下の図は、Cursor で AI によるコード変更が適用される基本的なフローを示しています。
mermaidflowchart TB
  dev["開発者"] -->|Chat で指示| cursor["Cursor AI"]
  cursor -->|コンテキスト解析| analyzer["プロジェクト<br/>解析エンジン"]
  analyzer -->|ファイル特定| files["対象ファイル群"]
  cursor -->|変更提案| diff["差分生成"]
  diff -->|適用| apply["ファイル書き込み"]
  apply -->|結果| result["変更済みファイル"]
  result -.->|予期しない変更| problem["差分崩れ"]
このフローから分かるように、AI は複数のファイルを横断的に解析して変更を加えるため、開発者が想定していない箇所まで編集される可能性があります。
課題
Cursor を使った開発で発生する差分関連の問題には、いくつかの典型的なパターンがあります。これらの課題を理解しておくことで、トラブル時の原因特定がスムーズになるでしょう。
主な課題パターン
フォーマット変更の大量混入
AI がコード生成時に独自のフォーマットルールを適用し、変更対象外のコードまで整形してしまうケースです。
typescript// 元のコード
function getData() {
  return { name: 'test', value: 123 };
}
typescript// AI 適用後(意図しないフォーマット変更)
function getData() {
  return {
    name: 'test',
    value: 123,
  };
}
上記の例では、関数の中身を変更するつもりが、スペースやインデント、改行が全体的に変更されてしまっています。
インデント・改行の不整合
タブとスペースの混在や、予期しない改行の挿入により、差分が肥大化します。
| # | 問題 | 例 | 
|---|---|---|
| 1 | タブ → スペース変換 | \tが 2 スペースや 4 スペースに置き換わる | 
| 2 | 改行コード変更 | LF⇔CRLFの変換が発生 | 
| 3 | 末尾空白追加 | 行末に不要な空白が追加される | 
| 4 | 空行の挿入・削除 | 関数間の空行が増減する | 
import 文の自動整理
AI が依存関係を解析して、使用されていない import を削除したり、新しい import を追加したりします。これ自体は便利な機能ですが、意図しないタイミングで発動すると混乱を招きます。
typescript// 元のコード
import { useState, useEffect, useMemo } from 'react';
import { Button } from '@/components/Button';
import { formatDate } from '@/utils/date';
typescript// AI による自動整理後
import { useState } from 'react';
import { formatDate } from '@/utils/date';
上記では、useEffect、useMemo、Button が未使用と判断され削除されています。しかし、これらを今後使う予定だった場合、意図しない削除となります。
コメントや空白行の削除
AI がコードを最適化する過程で、コメントや空白行を不要と判断して削除することがあります。
以下の図は、差分が崩れる主なパターンとその影響範囲を示しています。
mermaidflowchart LR
  ai["AI による変更"] --> format["フォーマット<br/>変更"]
  ai --> indent["インデント<br/>不整合"]
  ai --> imports["import 整理"]
  ai --> comments["コメント削除"]
  format --> largeDiff["差分肥大化"]
  indent --> largeDiff
  imports --> unexpectedChange["意図しない<br/>機能変更"]
  comments --> infoLoss["情報損失"]
  largeDiff --> reviewHard["レビュー困難"]
  unexpectedChange --> reviewHard
  infoLoss --> reviewHard
これらの課題が複合的に発生すると、本来確認したかった変更点が埋もれてしまい、コードレビューが非常に困難になってしまいます。
解決策
差分が崩れたり意図しない変更が入ったりした場合、慌てずに段階的に対処することが重要です。ここでは、即座に実行できる復旧手順と、今後同様の問題を防ぐための予防策をご紹介します。
即時復旧手順
トラブルが発生した際は、以下の手順で迅速に復旧しましょう。
ステップ 1: Git での差分確認
まず、実際にどのような変更が加わったのかを正確に把握します。
bash# 変更されたファイルの一覧を確認
git status
このコマンドにより、変更されたファイルが一覧表示されます。予期しないファイルが含まれていないかチェックしてください。
bash# 詳細な差分を確認
git diff
差分内容を確認し、意図した変更と意図しない変更を区別します。
bash# 特定ファイルのみの差分確認
git diff path/to/file.ts
問題のあるファイルに絞って差分を確認することで、復旧すべき内容を特定できます。
ステップ 2: 部分的な変更の取り消し
すべての変更を取り消すのではなく、意図しない変更のみを元に戻します。
bash# 特定ファイルの変更を取り消し
git checkout -- path/to/file.ts
このコマンドで、指定したファイルを最新のコミット状態に戻せます。
bash# 複数ファイルを一度に取り消し
git checkout -- file1.ts file2.ts file3.ts
意図しない変更が入った複数のファイルをまとめて復旧できます。
ステップ 3: 対話的なステージング
変更内容を細かく確認しながら、コミットに含める部分を選択します。
bash# パッチモードで対話的にステージング
git add -p
このコマンドを実行すると、変更箇所ごとに「ステージングするか」を選択できます。
各変更箇所に対して、以下のような選択肢が表示されます。
| # | コマンド | 説明 | 
|---|---|---|
| 1 | y | この変更をステージングする | 
| 2 | n | この変更をスキップする | 
| 3 | s | 変更をさらに小さな単位に分割する | 
| 4 | e | 手動で編集する | 
| 5 | q | 終了する | 
この方法により、必要な変更だけを選択的にコミットできます。
ステップ 4: Stash を活用した一時退避
変更内容を一時的に退避させて、後で整理する方法です。
bash# 現在の変更を退避
git stash push -m "AI による変更を一時退避"
作業ディレクトリがクリーンな状態に戻ります。
bash# 退避した変更の一覧を確認
git stash list
複数の stash がある場合、どれがどの変更かを確認できます。
bash# 退避した変更を復元(stash は削除されない)
git stash apply stash@{0}
必要な変更を復元し、手動で整理してから再度コミットできます。
以下の図は、復旧手順の全体フローを示しています。
mermaidflowchart TD
  start["差分崩れ発生"] --> check["git status/diff<br/>で確認"]
  check --> judge{変更範囲は?}
  judge -->|全体的に問題| fullReset["git reset で<br/>全体リセット"]
  judge -->|部分的に問題| partialFix["git checkout で<br/>部分復旧"]
  judge -->|混在している| interactive["git add -p で<br/>対話的選択"]
  fullReset --> rework["必要な変更を<br/>再適用"]
  partialFix --> verify["git diff で<br/>最終確認"]
  interactive --> verify
  rework --> verify
  verify --> done["復旧完了"]
予防策
今後同様の問題を防ぐために、以下の設定や運用ルールを導入しましょう。
EditorConfig の設定
プロジェクトルートに .editorconfig ファイルを配置することで、エディタ間でフォーマットルールを統一できます。
ini# .editorconfig
root = true
ini# すべてのファイルに共通の設定
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
ini# TypeScript/JavaScript ファイルの設定
[*.{ts,tsx,js,jsx}]
indent_style = space
indent_size = 2
ini# JSON ファイルの設定
[*.json]
indent_style = space
indent_size = 2
この設定により、Cursor の AI も統一されたフォーマットルールに従うようになります。
Prettier の導入と設定
コードフォーマッターの Prettier を導入し、自動整形ルールを明示的に定義します。
bash# Prettier のインストール
yarn add -D prettier
json// .prettierrc
{
  "semi": true,
  "trailingComma": "es5",
  "singleQuote": true,
  "printWidth": 80,
  "tabWidth": 2,
  "useTabs": false
}
上記の設定ファイルにより、プロジェクト全体で統一されたフォーマットが適用されます。
json// .prettierignore
node_modules/
dist/
build/
.next/
coverage/
フォーマット対象外のディレクトリを指定し、不要な差分を防ぎます。
ESLint の設定最適化
ESLint を適切に設定することで、import の自動整理などの挙動を制御できます。
bash# ESLint と関連プラグインのインストール
yarn add -D eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin
json// .eslintrc.json(基本設定)
{
  "parser": "@typescript-eslint/parser",
  "extends": [
    "eslint:recommended",
    "plugin:@typescript-eslint/recommended"
  ],
  "parserOptions": {
    "ecmaVersion": 2022,
    "sourceType": "module"
  }
}
json// .eslintrc.json(import 整理ルール)
{
  "rules": {
    "no-unused-vars": "warn",
    "@typescript-eslint/no-unused-vars": [
      "warn",
      {
        "argsIgnorePattern": "^_",
        "varsIgnorePattern": "^_"
      }
    ]
  }
}
未使用の import を警告として扱い、自動削除されないようにします。_ で始まる変数は意図的な未使用として扱われます。
Cursor 固有の設定
Cursor の設定ファイルで AI の挙動を調整します。
プロジェクトルートに .cursorrules ファイルを作成します。
text# .cursorrules
- コードを生成する際は、既存のフォーマットルールを厳密に守ること
- import 文の自動削除は行わないこと
- コメントは保持すること
text- 変更は必要最小限にとどめ、フォーマット変更のみの差分を作らないこと
- インデントはスペース2つを使用すること
これらのルールにより、AI の挙動をプロジェクトのガイドラインに沿わせることができます。
Git フックの活用
コミット前に自動チェックを実行し、意図しない変更を検出します。
bash# Husky のインストール
yarn add -D husky lint-staged
bash# Husky の初期化
npx husky init
json// package.json
{
  "lint-staged": {
    "*.{ts,tsx,js,jsx}": [
      "eslint --fix",
      "prettier --write"
    ]
  }
}
ステージングされたファイルに対して、自動的に ESLint と Prettier を適用します。
bash#!/bin/sh
# .husky/pre-commit
. "$(dirname "$0")/_/husky.sh"
npx lint-staged
この pre-commit フックにより、コミット前に自動的にフォーマットとリントが実行され、統一されたコードスタイルが保たれます。
トラブル発生時のチェックリスト
問題が発生したときは、以下のチェックリストに従って確認しましょう。
| # | 確認項目 | コマンド例 | 
|---|---|---|
| 1 | 変更されたファイル数を確認 | git status | 
| 2 | 各ファイルの差分を確認 | git diff | 
| 3 | フォーマット変更のみの差分を特定 | git diff -w(空白無視) | 
| 4 | 意図した変更が含まれているか確認 | ファイルごとに git diff ファイル名 | 
| 5 | import 文の変更を確認 | 差分の先頭部分をチェック | 
| 6 | コメントの削除がないか確認 | git diffでコメント行を探す | 
| 7 | 設定ファイルの有無を確認 | .editorconfig、.prettierrcの存在確認 | 
このチェックリストを活用することで、体系的に問題の原因を特定できます。
具体例
実際のトラブルシナリオと、その復旧プロセスを具体的に見ていきましょう。
シナリオ: Chat 機能で新機能追加後に大量の差分が発生
あるプロジェクトで、Cursor の Chat 機能を使って新しい認証機能を追加しました。しかし、git status を確認すると、予想外に 15 ファイルが変更されていることが判明しました。
問題の発見
bash# 変更状況の確認
git status
実行結果として、以下のような出力が表示されます。
textChanges not staged for commit:
  modified:   src/auth/login.ts
  modified:   src/auth/register.ts
  modified:   src/components/Button.tsx
  modified:   src/components/Input.tsx
  modified:   src/utils/validation.ts
  modified:   src/utils/api.ts
  ... (他9ファイル)
Button.tsx や Input.tsx など、認証機能とは無関係に見えるファイルも変更されています。
bash# 詳細な差分確認
git diff src/components/Button.tsx
差分を確認すると、フォーマット変更のみで機能的な変更がないことが分かります。
diff- function Button({onClick,children}){
-   return <button onClick={onClick}>{children}</button>
+ function Button({ onClick, children }) {
+   return <button onClick={onClick}>{children}</button>;
 }
このように、スペースやセミコロンの追加のみで、実質的な変更はありません。
復旧手順の実践
まず、意図しないフォーマット変更が入ったファイルを元に戻します。
bash# フォーマット変更のみのファイルを復旧
git checkout -- src/components/Button.tsx src/components/Input.tsx
これらのファイルが最新のコミット状態に戻ります。
次に、本当に変更が必要なファイルだけを対話的に選択します。
bash# 認証関連ファイルを対話的にステージング
git add -p src/auth/login.ts
変更箇所ごとに y(ステージング)または n(スキップ)を選択します。
textStage this hunk [y,n,q,a,d,s,e,?]? y
必要な変更のみを選択してステージングします。
bash# 他の認証ファイルも同様に処理
git add -p src/auth/register.ts
git add -p src/utils/validation.ts
すべての必要なファイルを対話的に確認しながらステージングします。
最終確認として、ステージングされた変更を確認します。
bash# ステージングされた変更の確認
git diff --cached
意図した変更のみがステージングされていることを確認できます。
bash# コミット実行
git commit -m "feat: 認証機能の追加"
これで、意図した変更のみがコミットされました。
以下の図は、この復旧プロセスの流れを示しています。
mermaidsequenceDiagram
  participant Dev as 開発者
  participant Git as Git
  participant Files as ファイル群
  Dev->>Git: git status
  Git->>Dev: 15ファイル変更を検出
  Dev->>Git: git diff Button.tsx
  Git->>Dev: フォーマット変更のみ
  Dev->>Git: git checkout -- Button.tsx
  Git->>Files: ファイルを復旧
  Dev->>Git: git add -p login.ts
  Git->>Dev: 変更箇所を表示
  Dev->>Git: y(必要な変更を選択)
  Dev->>Git: git diff --cached
  Git->>Dev: 意図した変更のみ確認
  Dev->>Git: git commit
  Git->>Dev: 復旧完了
シナリオ: import 文の自動削除による機能破壊
次のシナリオでは、AI が未使用と判断した import を削除してしまい、実行時エラーが発生しました。
問題の発生
typescript// 元のコード(src/pages/dashboard.tsx)
import { useState, useEffect } from 'react';
import { fetchUserData } from '@/api/user';
import { Chart } from '@/components/Chart';
import { Table } from '@/components/Table';
export default function Dashboard() {
  const [data, setData] = useState(null);
  // 今後 Chart と Table を使う予定
  return <div>Dashboard</div>;
}
Cursor の Chat で「ローディング表示を追加して」と依頼したところ、以下のように変更されました。
typescript// AI による変更後
import { useState } from 'react';
import { fetchUserData } from '@/api/user';
export default function Dashboard() {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(false);
  return <div>{loading ? 'Loading...' : 'Dashboard'}</div>;
}
useEffect、Chart、Table の import が削除され、今後の実装に支障が出てしまいました。
復旧とその後の対策
差分を確認して、削除された import を特定します。
bash# 差分を確認
git diff src/pages/dashboard.tsx
削除された import 行が赤字で表示されます。
変更全体を一旦退避します。
bash# 現在の変更を退避
git stash push -m "ローディング表示の追加"
作業ディレクトリがクリーンな状態に戻ります。
元のファイルから必要な import を確認します。
bash# 退避前の状態を確認
git stash show -p stash@{0}
削除された import を特定し、手動で復元します。
退避した変更を復元します。
bash# 変更を復元
git stash apply
ファイルを手動で編集し、削除された import を追加します。
typescript// 手動で修正したコード
import { useState, useEffect } from 'react';
import { fetchUserData } from '@/api/user';
import { Chart } from '@/components/Chart';
import { Table } from '@/components/Table';
export default function Dashboard() {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(false);
  return <div>{loading ? 'Loading...' : 'Dashboard'}</div>;
}
必要な import を復元し、ローディング機能も追加された状態になります。
予防のための ESLint 設定
今後同様の問題を防ぐために、ESLint の設定を調整します。
json// .eslintrc.json
{
  "rules": {
    "@typescript-eslint/no-unused-vars": [
      "warn",
      {
        "varsIgnorePattern": "^_",
        "argsIgnorePattern": "^_",
        "ignoreRestSiblings": true
      }
    ]
  }
}
この設定により、未使用の変数は警告として表示されるだけで、自動削除はされません。
さらに、.cursorrules ファイルでルールを明示します。
text# .cursorrules
- import 文は、明示的に指示されない限り削除しないこと
- 未使用の import があっても、将来使用する可能性を考慮すること
これらの設定により、AI が過度に最適化することを防げます。
シナリオ: フォーマット設定の不統一による差分肥大化
最後のシナリオでは、チームメンバー間でフォーマット設定が異なり、コミットのたびに大量の差分が発生していました。
問題の状況
開発者 A は VSCode でタブ幅 2、開発者 B は Cursor でタブ幅 4 を使用していました。その結果、以下のような差分が頻繁に発生します。
diff function calculateTotal(items) {
-  return items.reduce((sum, item) => {
-    return sum + item.price;
-  }, 0);
+    return items.reduce((sum, item) => {
+        return sum + item.price;
+    }, 0);
 }
機能的な変更はないのに、インデントのみが変わっています。
統一設定の導入
プロジェクトルートに .editorconfig を作成します。
ini# .editorconfig
root = true
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
[*.{ts,tsx,js,jsx}]
indent_style = space
indent_size = 2
Prettier の設定を追加します。
bash# Prettier のインストール
yarn add -D prettier
json// .prettierrc
{
  "semi": true,
  "trailingComma": "es5",
  "singleQuote": true,
  "printWidth": 80,
  "tabWidth": 2,
  "useTabs": false,
  "endOfLine": "lf"
}
Git フックで自動フォーマットを設定します。
bash# Husky と lint-staged のインストール
yarn add -D husky lint-staged
npx husky init
json// package.json
{
  "lint-staged": {
    "*.{ts,tsx,js,jsx}": [
      "prettier --write",
      "eslint --fix"
    ]
  }
}
bash#!/bin/sh
# .husky/pre-commit
npx lint-staged
これにより、コミット前に自動的に統一されたフォーマットが適用されます。
フォーマット統一の実施
既存のすべてのファイルに統一フォーマットを適用します。
bash# プロジェクト全体をフォーマット
yarn prettier --write "src/**/*.{ts,tsx,js,jsx}"
すべてのファイルが統一されたフォーマットに変換されます。
bash# フォーマット変更をコミット
git add .
git commit -m "chore: コードフォーマットの統一"
この一度のコミットにより、以降はフォーマットによる差分が発生しなくなります。
以下の図は、フォーマット統一による効果を示しています。
mermaidflowchart LR
  before["統一前"] --> devA["開発者A<br/>(タブ幅2)"]
  before --> devB["開発者B<br/>(タブ幅4)"]
  devA --> conflict["差分衝突"]
  devB --> conflict
  after["統一後"] --> config["EditorConfig<br/>Prettier"]
  config --> allDev["全開発者<br/>(タブ幅2)"]
  allDev --> clean["差分クリーン"]
  style conflict fill:#ffcccc
  style clean fill:#ccffcc
まとめ
Cursor での差分トラブルは、AI の強力な機能ゆえに発生するものですが、適切な知識と対処法があれば恐れる必要はありません。
本記事でご紹介した復旧プレイブックのポイントをまとめます。
復旧の基本ステップ
| # | ステップ | キーコマンド | 
|---|---|---|
| 1 | 差分の確認 | git status、git diff | 
| 2 | 部分的な取り消し | git checkout -- ファイル名 | 
| 3 | 対話的な選択 | git add -p | 
| 4 | 一時退避 | git stash | 
予防策の実装
| # | 予防策 | ファイル/コマンド | 
|---|---|---|
| 1 | フォーマット統一 | .editorconfig、.prettierrc | 
| 2 | Lint ルール設定 | .eslintrc.json | 
| 3 | Cursor ルール定義 | .cursorrules | 
| 4 | Git フック活用 | Husky、lint-staged | 
これらの対策を実装することで、AI による意図しない変更を最小限に抑え、差分レビューを効率的に行えるようになります。トラブルが発生した際も、本記事のプレイブックに従って冷静に対処すれば、確実に復旧できるでしょう。
Cursor の AI 機能は非常に強力ですが、適切な設定とワークフローがあってこそ、その真価を発揮します。本記事が、より快適な Cursor での開発体験の一助となれば幸いです。
関連リンク
 article article- Cursor で差分が崩れる/意図しない大量変更が入るときの復旧プレイブック
 article article- Cursor × 生成 AI で変わる開発フロー:要件定義からレビューまでの新常識
 article article- Cursor の KPI 設計:リードタイム・欠陥率・レビュー時間を定量で追う
 article article- Cursor 前提の開発プロセス設計:要求 → 設計 → 実装 → 検証の短サイクル化
 article article- Cursor プロンプト定番 30:仕様化・分割統治・根拠提示・差分出力の句型集
 article article- Cursor を macOS で最短導入:設定同期・拡張機能共存・プロキシ対応まで
 article article- MySQL ERROR 1449 対策:DEFINER 不明でビューやトリガーが壊れた時の復旧手順
 article article- Cursor で差分が崩れる/意図しない大量変更が入るときの復旧プレイブック
 article article- Motion(旧 Framer Motion)で exit が発火しない/遅延する問題の原因切り分けガイド
 article article- JavaScript 時刻の落とし穴大全:タイムゾーン/DST/うるう秒の実務対策
 article article- Cline が差分を誤適用する時:改行コード・Prettier・改フォーマット問題の解決
 article article- htmx で二重送信が起きる/起きない問題の完全対処:trigger と disable パターン
 blog blog- iPhone 17シリーズの発表!全モデルiPhone 16から進化したポイントを見やすく整理
 blog blog- Googleストアから訂正案内!Pixel 10ポイント有効期限「1年」表示は誤りだった
 blog blog- 【2025年8月】Googleストア「ストアポイント」は1年表記はミス?2年ルールとの整合性を検証
 blog blog- Googleストアの注文キャンセルはなぜ起きる?Pixel 10購入前に知るべき注意点
 blog blog- Pixcel 10シリーズの発表!全モデル Pixcel 9 から進化したポイントを見やすく整理
 blog blog- フロントエンドエンジニアの成長戦略:コーチングで最速スキルアップする方法
 review review- 今の自分に満足していますか?『持たざる者の逆襲 まだ何者でもない君へ』溝口勇児
 review review- ついに語られた業界の裏側!『フジテレビの正体』堀江貴文が描くテレビ局の本当の姿
 review review- 愛する勇気を持てば人生が変わる!『幸せになる勇気』岸見一郎・古賀史健のアドラー実践編で真の幸福を手に入れる
 review review- 週末を変えれば年収も変わる!『世界の一流は「休日」に何をしているのか』越川慎司の一流週末メソッド
 review review- 新しい自分に会いに行こう!『自分の変え方』村岡大樹の認知科学コーチングで人生リセット
 review review- 科学革命から AI 時代へ!『サピエンス全史 下巻』ユヴァル・ノア・ハラリが予見する人類の未来