2026年8月14日 金曜日
AI時短ラボ
活用· 約28

Vercel CLIはgitではなく作業ツリーを直接デプロイする──git statusが本番の中身を表していない状態の見分け方

Vercel公式ドキュメントは「.vercelignoreで除外を指定していなければ、既定の除外を除きプロジェクト内の全ファイルがVercelにアップロードされる」と明記している。アップロードの単位はコミットではなくディレクトリである。当サイト(Next.js 16.2.7+Vercel CLI 53.2.0、git remote が空)では2026年8月14日16時18分時点でHEADのコミットが15時58分29秒、最新の本番デプロイが15時35分41秒と22分ずれており、16時17分時点のローカル記事362本に対し本番サイトマップは357本、差5本のうち3本が本番でHTTP 404を返した。

執筆・編集:
目次

Vercel CLIからvercel --prodでデプロイしている構成では、Vercelにアップロードされるのは「gitのコミット」ではなく「そのディレクトリのファイル」である。Vercel公式は.vercelignoreのページで「既定の除外を除き、プロジェクト内の全ファイルがアップロードされる」と書いており、判定に使われるのは.vercelignoreであってgitの管理状態ではない。したがって未コミット・未追跡のファイルもそのまま本番に載り、逆にコミット済みでもデプロイしていなければ本番には存在しない。自分のリポジトリがこの状態かどうかはgit remote -v.vercel/project.jsonの有無・vercel project inspectの3点で判定でき、本番と作業ツリーのズレは「最新デプロイの作成時刻とHEADのコミット時刻」「ローカルのファイル一覧と本番サイトマップの差分」「個別URLのHTTPステータス」で突き合わせられる。

  1. Vercel公式は「Aside from the default exclusions, all files within your project are uploaded to Vercel if no source path is specified to be excluded in a .vercelignore configuration file」と書いている。アップロードの判定条件にgitの追跡状態は出てこない
  2. 既定で除外されるファイル一覧には.git.gitignoreが含まれ、同ページは「these ignored files are only relevant when using Vercel CLI」と注記している。gitの履歴そのものはアップロードされない
  3. 2026年8月14日16時18分(日本時間)の当サイトの実測では、HEADのコミットは15時58分29秒、最新の本番デプロイは15時35分41秒。gitと本番は別々の時刻で進んでいた

症状1:git remote -v が何も返さないのに本番は動いている

2026年8月14日16時14分(日本時間)、当サイトのリポジトリで実行した結果をそのまま貼る。

$ git remote -v
$ echo $?
0

出力は1行も無く、終了コードは0である。リモートが1つも登録されていない(エラーではない)。この状態ではGitHubにpushする先が無いので、GitHub連携による自動デプロイは成立しない。それでも本番サイトは更新され続けている。更新しているのはVercel CLIである。

同じディレクトリには.vercelディレクトリがある。

$ ls -a .vercel
.
..
README.txt
project.json

.vercel/project.jsonのキーはorgIdprojectIdprojectNameの3つだった(値は識別子なので伏せる)。.vercel/README.txtはVercelが自動生成したもので、原文は次のとおりである。

> Why do I have a folder named ".vercel" in my project?
The ".vercel" folder is created when you link a directory to a Vercel project.

(なぜプロジェクトに「.vercel」という名前のフォルダがあるのか? 「.vercel」フォルダは、ディレクトリをVercelプロジェクトにリンクしたときに作成される)

リンクされているのはディレクトリであって、ブランチでもコミットでもない。

症状2:ローカルにあるファイルが本番で404を返す

同じ日の16時17分に、ローカルの記事ファイル一覧と本番サイトマップを突き合わせた。

$ ls content/news/*.md | wc -l
     362
$ curl -s https://www.ai-jitan-hub.com/sitemap.xml -o /tmp/sm.xml -w "HTTP=%{http_code}\n"
HTTP=200
$ grep -o '<loc>[^<]*</loc>' /tmp/sm.xml | sed 's|<loc>||;s|</loc>||' \
    | grep '/news/' | sed 's|.*/news/||' | sort -u | wc -l
     357

差は5本だった。ただしこの5という数字をそのまま「未デプロイ本数」と読むと誤る。5本それぞれに本番でHTTPステータスを取ると、内訳が割れた。

ローカルにあるslug 本番のHTTPステータス gitの追跡状態
deepfake-voice-clone-25m-hong-kong-fraud 200 追跡済み(最終更新コミット3497961・11:12)
spacex-cursor-anysphere-acquisition 200 追跡済み(最終更新コミット3497961・11:12)
http-200-empty-page-cloudflare-js 404 未追跡
indexnow-google-not-participating 404 未追跡
youtube-rss-feed-user-agent-404 404 未追跡

上2本はfrontmatterにcanonicalSlugが入っており、当サイトの仕様でサイトマップから意図的に外している(本番には存在してHTTP 200を返す)。サイトマップ差分5本のうち、本当に本番に無かったのは3本である。 差分の数え方を1回で確定させず、URLごとにステータスを取り直して初めて内訳が分かった。

$ curl -s -o /dev/null -w "%{http_code}\n" \
    https://www.ai-jitan-hub.com/news/youtube-rss-feed-user-agent-404
404

症状3:最新デプロイの時刻がHEADのコミット時刻より古い

2026年8月14日16時16分〜16時18分17秒(日本時間)に取得した2つの時刻である。

$ git log -1 --format="%h %ad %s" --date=iso
1f53d5a 2026-08-14 15:58:29 +0900 判定:被リンク0でも新規記事は2週間以内にほぼ100%インデックスされる
$ npx vercel inspect https://ai-jitan-ktqk33e1a-ai-jitan-lab.vercel.app
  General
    name	ai-jitan-hub
    target	production
    status	● Ready
    created	Fri Aug 14 2026 15:35:41 GMT+0900 (日本標準時)

HEADのコミットは15時58分29秒、本番デプロイの作成は15時35分41秒。HEADのほうが22分48秒新しい。 この状態では「gitに入っている=本番に載っている」は成り立たない。逆向き(本番のほうが新しい=未コミットのものが載っている)も同じ理屈で起こりうる。

なおvercel inspectの出力はGeneral/Aliases/Buildsの3セクションで、コミットハッシュやブランチ名を示す行は含まれていなかった。vercel project inspect <プロジェクト名>の出力も同様で、grep -i "git\|repo"のヒットは0件だった(2026年8月14日16時16分実行)。

原因:Vercelは「ディレクトリ」を送り、gitは判定に使われない

1. アップロード対象は.vercelignoreで決まる

Vercel公式ドキュメント「Exclude Files from Deployments with .vercelignore」(last_updated 2025年3月12日・2026年8月14日確認)の「Uploaded Files」節はこう書いている。

Aside from the default exclusions, all files within your project are uploaded to Vercel if no source path is specified to be excluded in a .vercelignore configuration file

(既定の除外を別にすれば、.vercelignore設定ファイルで除外対象のパスを指定していない限り、プロジェクト内の全ファイルがVercelにアップロードされる)

同ページは.vercelignoreについて「This file works similarly to a .gitignore file, but it is specific to Vercel.(このファイルは.gitignoreと似た働きをするが、Vercel専用である)」とも書く。似ているだけで、同じものではない。

当サイトの.vercelignoreは1行だけだった。

og_candidates

つまりog_candidates以外は、gitで追跡されていようがいまいが、アップロード対象になる。

2. 既定で除外されるものに.gitが入っている

「Build Features for Customizing Deployments」(last_updated 2026年6月9日・2026年8月14日確認)の「Ignored files and folders」節はこう始まる。

Vercel ignores certain files and folders by default and prevents them from being uploaded during the deployment process for security and performance reasons. Please note that these ignored files are only relevant when using Vercel CLI.

(Vercelは特定のファイルとフォルダを既定で無視し、セキュリティとパフォーマンス上の理由からデプロイ処理中にアップロードされないようにする。これらの無視されるファイルはVercel CLIを使うときにのみ関係する点に注意)

同ページが挙げる一覧には次が含まれる(抜粋)。

.hg
.git
.gitmodules
.svn
.cache
.next
.now
.vercel
.npmignore
.dockerignore
.gitignore

.gitが除外されている以上、Vercel側にはコミット履歴もブランチも渡っていない(CLIデプロイの場合)。渡っているのはファイルの中身だけである。ここが「gitの状態が本番に反映される」という直感が壊れる地点になる。

3. GitデプロイとCLIデプロイは別の経路

「Deploying to Vercel」(last_updated 2026年6月17日・2026年8月14日確認)は、デプロイの作り方を5つ(Git/Vercel Drop/Vercel CLI/Deploy Hooks/Vercel REST API)に分けている。Gitについての記述は次のとおりである。

The most common way to create a deployment is by pushing code to a connected Git repository. When you import a Git repository to Vercel, each commit or pull request (on supported Git providers) automatically triggers a new deployment.

(デプロイを作る最も一般的な方法は、接続されたGitリポジトリにコードをpushすることである。GitリポジトリをVercelにインポートすると、コミットやプルリクエストのたびに新しいデプロイが自動的にトリガーされる)

CLIについてはこう書いている。

You can deploy your Projects directly from the command line using Vercel CLI. This method works whether your project is connected to Git or not.

(Vercel CLIを使えば、コマンドラインから直接プロジェクトをデプロイできる。この方法は、プロジェクトがGitに接続されていてもいなくても機能する)

「コミットがデプロイを引き起こす」のはGit経路の性質であって、CLI経路には無い。 同ページはCLIの初回デプロイについて「This links your local directory to your Vercel Project and creates a Production Deployment. A .vercel directory is added to store Project and Organization IDs.(これによりローカルディレクトリがVercelプロジェクトにリンクされ、本番デプロイが作られる。プロジェクトIDと組織IDを保存するために.vercelディレクトリが追加される)」と書く。リンクされる単位は繰り返しディレクトリである。

4. Git連携を張るにはローカルにremoteが要る

「vercel git」(last_updated 2026年3月17日・2026年8月14日確認)はvercel git connectの動作をこう説明する。

When run, Vercel CLI searches for a local .git config file containing at least one remote URL. If found, you can connect it to the Vercel Project linked to your directory.

(実行すると、Vercel CLIはremote URLを少なくとも1つ含むローカルの.git設定ファイルを探す。見つかれば、それをディレクトリにリンクされたVercelプロジェクトに接続できる)

当サイトはgit remote -vが空なので、この条件を満たしていない。「連携を張り忘れた」のではなく、張る前提そのものが無い状態である。

なお「Linking Projects with Vercel CLI」(last_updated 2026年6月9日・2026年8月14日確認)は、リンクの中身をこう定義している。

Once set up, a new .vercel directory is added to your directory. The .vercel/project.json file contains the orgId and projectId of your Vercel Project. To unlink your directory, remove the .vercel directory.

(セットアップが済むと、ディレクトリに新しく.vercelディレクトリが追加される。.vercel/project.jsonファイルにはVercelプロジェクトのorgIdprojectIdが入っている。ディレクトリのリンクを解除するには.vercelディレクトリを削除する)

対処

自分のリポジトリがこの状態かを判定する(3コマンド)

# 1) gitのリモートがあるか
git remote -v

# 2) CLIリンクが張られているか(キー名だけ見る。値は識別子なので表示しない)
python3 -c "import json;print(sorted(json.load(open('.vercel/project.json')).keys()))"

# 3) Vercel側のプロジェクト設定にGitリポジトリが出るか
npx vercel project inspect <プロジェクト名>

1が空で2にorgIdprojectIdが入っているなら、そのディレクトリはCLIデプロイ専用の構成である。この場合、本番の中身を決めているのは作業ツリーであって、コミット履歴ではない。

本番と作業ツリーを突き合わせる

コンテンツ主体のサイトなら、この3つで十分に差が出る。

  1. 時刻を比べる。 git log -1 --date=isoのコミット時刻と、npx vercel inspect <本番デプロイURL>createdを並べる。後者が古ければ、その差分の中身は本番に載っていない
  2. 一覧の差分を取る。 ローカルのファイル一覧と本番のsitemap.xmlcommで突き合わせる。ただしサイトマップから意図的に外している記事があると差分は膨らむので、差分の数をそのまま結論にしない
  3. URLごとにHTTPステータスを取る。 差分に出たslugを1本ずつcurl -o /dev/null -w "%{http_code}"で叩く。404なら本番に無い、200ならサイトマップ側の都合である

巻き戻りを避ける運用

アップロード対象が作業ツリーである以上、作業ツリーを巻き戻せば、次のデプロイの中身も巻き戻る。 git checkoutgit stashで手元のファイルを戻した直後にデプロイすれば、戻した内容がそのまま本番になる。この因果はドキュメントの記述から導けるが、当サイトはこの事故を実際に起こした記録を持っていない(後述の但し書きを参照)。実務上の手当ては次の3つである。

  • デプロイ前にgit statusを見る。ただし**git statusが綺麗=本番と一致、ではない**。クリーンでも「コミット済みだが未デプロイ」は残る
  • 手元のファイルを巻き戻す操作の直後は、デプロイの前に上の突き合わせを1回挟む
  • 恒久的に直すなら、リモートを追加してvercel git connectでGit経路に寄せる。Git経路ならデプロイの単位がコミットになる

.vercelignore.gitignoreの代わりにならない

公式が「アップロードするかどうか」の判定条件として書いているのは.vercelignoreだけである。.gitignoreは「既定でアップロードされないファイル」の一覧に名前が載っているだけで、.gitignoreに書いたパターンがアップロード対象の判定に使われる、とは書かれていない。 本番に出したくないファイルがあるなら.vercelignore側に書く。当サイトはこの挙動を実測で検証していない。

観測条件(2026年8月14日・当サイト1件)

計測環境はNext.js 16.2.7+Vercel CLI 53.2.0、macOS(Darwin 24.1.0)、対象は/Users/koshitakayuta/projects/ai-jitan-hub。Vercelプロジェクトの作成日は2026年6月11日、Root Directoryは.、Framework PresetはNext.jsである(vercel project inspectで確認)。

時刻(JST) 取得した内容
16:14:07 git remote -vが空・終了コード0。.vercel/project.jsonのキーは3つ。.vercelignoreは1行(og_candidates)。未追跡ファイルは1本
16:16 vercel project inspect実行。出力にGit/repositoryを含む行は0件
16:16 vercel inspectで最新本番デプロイの作成時刻=2026-08-14 15:35:41 JSTを確認
16:17:05 ローカルcontent/news/*.md=362本、本番サイトマップの/news/=357本、差5本
16:17:54 差5本のHTTPステータスを個別取得。200が2本(canonicalSlugあり)、404が3本
16:18:17 HEAD=1f53d5a(2026-08-14 15:58:29 +0900)。未追跡ファイルは5本に増加

なおこの記事のファイル自体は16時18分より後に作成したため、上の362本・357本の集計には含まれていない。同じコマンドを今から実行すれば数字は変わる。

16時14分から16時18分までの4分間で、未追跡ファイルが1本から5本に増えている。 同じリポジトリに対して別のプロセスが同時に書き込んでいたためである。作業ツリーは「今この瞬間の状態」であって、記録した数字はすべて取得時刻とセットでしか意味を持たない。

正直な但し書き

git checkoutgit stashで本番が巻き戻る事故を、当サイトは実際には踏んでいない。 記事中でそう書けるのは「アップロード対象が作業ツリーである」というVercel公式の記述からの推論までである。当サイトが観測したのは「作業ツリーにしか無いファイルは本番で404を返す」(3本)という一方向のズレだけで、逆方向(本番にあって作業ツリーに無い)は今回のスナップショットでは0件だった。

「未コミットのコードが本番で動いていた」という当日の記録は、いま再検証できない。 当サイトのセッションログには、関連記事の表示件数を8件にする変更が未コミットのまま本番で有効だった、という申し送りが残っている。git履歴で確認できるのは、getRelatedPosts(post, 8)がコミット26033ef(2026年8月14日05時10分03秒 +0900)で入り、それ以前はgetRelatedPosts(post, 3)だった(a2ae3ea・2026年7月10日20時36分18秒 +0900で導入)という点までである。コミット前の本番デプロイの中身を後から取り出す手段を当サイトは持っていないため、本記事はこの件を証拠として使っていない。

vercel project inspectにGitの行が無いことは、Git連携が無いことの証明にはならない。 CLIが連携時に必ずGit関連の行を出力する、という記載を公式ドキュメントで確認できていない。当サイトが確認したのは「出力にヒットが0件だった」という事実までである。判定の根拠として強いのはgit remote -vが空である点(vercel git connectの前提条件を満たさない)のほうである。

サイトマップ差分は最初「5本」と出た。 そのまま「未デプロイ5本」と書けば誤りだった。当サイトには重複記事をcanonicalSlugでまとめる仕組みがあり、2本はそれによってサイトマップから外れていた。差分を数えた直後に個別のHTTPステータスを取らなければ、この2本を混ぜたまま結論を書いていた。

.gitignoreのパターンがVercel CLIのアップロード判定に使われるかどうかは未検証である。 公式は.vercelignoreについてしか書いていない。当サイトは.gitignoreに書いたファイルが本番にアップロードされるかを直接確かめていないので、ここは「公式に記載が無い」以上のことを言えない。

Vercel公式ドキュメントの引用は英語版の原文を採り、訳は当サイトで付けた。 訳の責任は当サイトにある。またドキュメントのlast_updatedは取得時点でページに記載されていた値をそのまま書いており、当サイトが独立に検証したものではない。

出典

シェア: ポスト はてブ

出典・参照資料

AIニュースの解説を動画でも

YouTubeでは注目ニュースの背景を解説し、Xでは新着記事をお知らせしています。

コメント

まだコメントはありません。最初のコメントを書いてみませんか?

AIについて聞きたいことはありますか?

質問箱で無料で受け付けています。回答は公開され、他の方の参考にもなります。

質問箱を見る →

新しい記事をメールで受け取る

AIの新しい発表を、出典付きで整理して届けます。

関連記事

AI時短ラボ
活用08.14読了21

curlに-Lを付けずに308を叩くと15バイトの「Redirecting...」が返る──sitemapが0件・サイトが壊れたと誤診する罠

出典 ─ 308 Permanent Redirect(HTTP response status codes)
AI時短ラボ
活用08.14読了32

HTTP 200・79,525バイトなのに可視テキストは416文字──「取得できた」を200で数えてはいけない

出典 ─ RFC 9110: HTTP Semanti
AI時短ラボ
活用08.14読了42

IndexNowにGoogleは参加していない──418URL送ってもGoogleには渡らない仕組みと、鍵が無効なときに返る403 UserForbiddedToAccessSite

出典 ─ IndexNow - Home(indexn
AI時短ラボ
活用08.14読了26

出典URLの死活チェックで404だけ見てはいけない──1,517件を全数検査したら、HEADに404を返す公式ドキュメントと403の一次資料が出てきた

出典 ─ RFC 9110: HTTP Semanti
AI時短ラボ
活用08.14読了36

Vercelの自動生成ドメインがGSCの参照元に117本入っていた──*.vercel.appと「Duplicate, Google chose different canonical than user」を全351記事で実測

出典 ─ Accessing Deployments
AI時短ラボ
活用08.14読了32

Wayback CDX APIは「該当なし」を200・0バイトで返す──既定が完全一致なのを知らずに『誰も保存していない』と結論した話

出典 ─ Wayback CDX Server API
Vercelデプロイのエラーを減らす個人開発チェックリスト──ローカルビルドから本番200確認までの記事画像
活用07.16読了7

Vercelデプロイのエラーを減らす個人開発チェックリスト──ローカルビルドから本番200確認まで

出典 ─ Vercel Docs: Troublesh
「AIっぽい見た目」を卒業する──デザイントークンをAIに渡す方法の記事画像
活用06.01読了11

「AIっぽい見た目」を卒業する──デザイントークンをAIに渡す方法

出典 ─ Design Tokens Communit