Git LFSのclone後に実ファイルではなくpointerが残るときの確認

Git LFSでclone後に実ファイルではなくpointerが残るとき、LFS設定とファイル取得の状態を確認する手順を解説します。

AIものさし編集部

Git LFS対象の画像やアーカイブをcloneしたのに、中身の代わりにversion https://git-lfs.github.com/spec/v1、oid sha256:、sizeから始まる短いテキストが見えることがあります。これはLFS pointerの形式と一致する場合があり、ただちにファイル破損とは限りません。原因は、Git LFSのfilterが使われなかった、smudgeを意図的にskipした、LFS objectを取得する権限がない、GitHubのsource archiveを展開した、または対象をsubmodule側で扱っているなど複数あります。pointerの中のobject IDを残し、どの取得経路で何が起きたかを分けてから直します。

まず作業場所と変更状態を確認します。LFS取得コマンドはファイル内容を更新する可能性があるため、別の未commit作業があるリポジトリでいきなりpullしないでください。対象ファイルのpath、branchまたはcommit、取得方法(git clone、GitHubのZIP、release archive、submodule)、GitとGit LFSの版、実行したcommand、標準エラーを記録します。ファイルが短いテキストなら先頭数行を確認します。token、private URL、社内情報を含むログは共有前に伏せます。

1. pointerか通常ファイルかを確認する

GitHubの説明例と同じく、LFS pointerにはversion、oid sha256:<hash>、size <bytes>が含まれます。この形式のテキストが見つかった場合、pointerがGit管理下に残っている可能性を確認します。元ファイルを取れていないことは示しますが、それだけでは「cloneが失敗した」「objectがサーバーから消えた」「LFSが壊れた」のどれかまでは決まりません。git lfs pointer --check --file=<path>を使えば、ファイルが有効なpointer形式かを確認できます。コマンドの成否を記録し、対象がLFSで管理されるべきpathかも次の手順で照合します。

テキストがpointer形式でなければ、LFS以外の原因も調べます。たとえばGitHubのHTML画面を保存した、ダウンロード中にエラーページが返った、アーカイブの展開に失敗した場合は、pointerの3行とは異なる本文やHTMLが入っていることがあります。拡張子だけで中身を決めつけず、file <path>の結果やサイズ、発生した取得経路を記録します。oidとsizeが見えるpointerなら、その情報をobject取得・権限確認に引き継ぎます。

2. .gitattributesとGit LFS filterを照合する

対象pathに対してLFS属性が有効かを確認します。リポジトリの.gitattributesで、たとえば*.psd filter=lfs diff=lfs merge=lfs -textのような規則が対象ファイルに当たるかを調べます。Git属性はpathごとに適用範囲が異なり、より下層にある.gitattributesが追加・上書きすることもあります。git check-attr filter -- <path>で対象pathのfilter属性を確認し、結果がlfsか、unspecified等かを記録します。大文字小文字、ディレクトリ、拡張子、ruleの位置も見ます。

属性がLFSを指していても、Git LFSのclean/smudge filterがその環境で使われているとは限りません。git lfs versionでCLIの有無とversionを確認し、git lfs envで設定状況を調べます。git lfs installはGit設定にfilterを設定します。既存のglobal設定を意図せず変えないため、repo限定の確認・設定で足りるならgit lfs install --localを検討します。実行前にrepository pathを確認し、共有端末のglobal設定やhook設定へ不用意に変更を加えないでください。コマンドを打っただけで既存checkoutのpointerが自動的に全て実ファイルへ置き換わるとは限らないため、取得とcheckoutは分けて考えます。

3. clone時のsmudge skipを切り分ける

Git LFS filterはcheckout時にpointerを元の内容へ置き換えるために使われます。clone時に環境変数や設定でsmudgeをskipしている場合、Git本体のcloneは成功しても作業ツリーにpointerが残ります。例としてGIT_LFS_SKIP_SMUDGE=1やgit lfs install --skip-smudgeが設定されていないか、shell、CI job、開発環境設定、clone scriptを確認します。skipは大きなobjectを後から必要な分だけ取得する運用では意図的な選択です。設定が意図的なら、対象ファイルをいつ取得する契約かを確認してから次へ進みます。

既にclone済みでLFS CLIが使える場合、現在checkoutされているrefのLFS内容を取得して作業ツリーへ反映するには、git lfs pullが候補です。Git LFSの公式CLI referenceでは、pullは現在checkout中refのobjectを取得し、必要に応じてworking copyを更新する処理として説明されています。git lfs fetchは指定refのobjectをダウンロードしますが、working copyは更新しません。fetch後に別途checkout処理が必要なため、「fetchが成功したのにファイルはpointerのまま」という場合、単に展開まで実行していない可能性があります。作業内容を保全し、どのrefを取得するか確かめてから実行します。

実行例:現在のbranchの対象objectを取る場合は、作業ディレクトリとremoteを確認してgit lfs pullを実行します。特定remoteを明示する必要がある構成では、repositoryの設定と権限を確認してから指定します。全branchのobjectを無条件に取るgit lfs fetch --allは容量と帯域を大きく使う場合があるので、単一ファイルが必要な通常の復旧手順として使わず、バックアップや移行など全refを集める理由があるときに限定します。取得量が分からない場合は対象refとinclude/exclude条件を先に確認します。

4. object取得の失敗と権限を調べる

git lfs pullが失敗した場合は、最後のエラー部分だけでなく、対象object、HTTP status、remote、認証方式を記録します。403や認証失敗なら、repositoryへのread accessだけでなくLFS objectを取得するためのアカウント・organization・fork権限が足りているか、期限切れcredentialやCI token scopeがないかを管理者と確認します。credential自体を記事やissueへ貼らず、秘密を除いたstatusとrequest contextだけを共有します。ネットワークproxyや企業環境がある場合は、通常Git fetchとLFS object downloadが別経路で失敗していないかを分けます。

取得成功とされてもobjectがない、またはサイズが期待と違う場合は、pointerのoidとsizeを保管し、対象commitがどのremoteにあるか、objectがそのrepo/organizationのLFS storageに実際に残っているかをownerに確認します。GitHub公式docsは、Git LFSを使わないcollaboratorではclone後にpointerのみを取得し元ファイルへアクセスできないケースを説明しています。したがって、LFS CLI未導入と権限・object問題を同じ「cloneやり直し」でまとめません。object欠落を示す根拠があれば、該当path、commit、oid、size、remote、エラーをそろえてrepository ownerへ渡します。

submoduleの場合、親repositoryのcloneが成功していても、LFS fileはsubmodule repository側の属性・filter・remote・権限に従います。git submodule statusで対象commitとpathを確認し、submodule内へ移動してGit LFS状態を調べます。親repoのLFS objectとして扱ったり、親の.gitattributesだけを直したりしないでください。submodule URLへアクセスできない場合は、Git submodule権限とLFS object権限の両方を分けて依頼します。社内submoduleや別organizationにあるobjectでは、親repoと異なる認証が必要な場合がありますが、実際の構成を確認するまでは断定しません。

5. GitHubのZIP・source archiveは別経路

ブラウザからGitHubのZIPやtarballをダウンロードした場合、通常のgit cloneとは挙動が異なります。GitHub公式docsではsource code archiveにLFS objectを含めるかをrepository管理者が選べ、初期状態ではarchiveに実ファイルではなくpointerだけが含まれると説明されています。archive側でpointerが見えたら、LFS filterのローカル設定を直す前に、取得物がGitHub source archiveか確認します。archiveにobjectを含める設定であっても、.gitattributesのtracking rulesや外部LFS serverの条件が関係します。archiveだけから特定のrepo設定が有効だと推測せず、ownerまたは管理者に確認します。

archiveにLFS objectを含める変更はrepository管理設定であり、一般ユーザーが手元のcloneで行う修復ではありません。必要ならownerへ設定確認を依頼します。archiveの再ダウンロードを何度も繰り返しても、pointerだけを含む設定のままなら実体は得られません。ビルドやデータ処理に実体が必要なら、可能な範囲で正式なGit cloneとLFS pullを使うか、ownerが指定する配布経路を利用してください。source archiveに実体を含めることで帯域利用が発生する点も、管理者が判断すべき事項です。

6. 症状から分岐する診断表

pointer形式で、git check-attrがfilter=lfsを返し、Git LFS CLIがない場合は、正規のGit LFSインストールとfilter設定を行い、現在のrefを取得します。filter=lfsだがGIT_LFS_SKIP_SMUDGE等が有効なら、skipの意図を確認し、必要なpathだけを後からpullします。filterがLFSを指さず、Git上のblobがpointerなら、正しい.gitattributesがcheckout・indexに含まれていたか、attributeがそのpathへ適用されるかを確認します。設定を直すだけで今の作業ツリーを再構築できるかは状態によるので、cleanな別worktree等で検証するのが安全です。

Git LFS CLIが正常でもpullが403・authentication failedで止まる場合は、objectを再生成するのではなくremoteとread権限を確認します。404、object missing、検証エラーが出た場合は、該当するcommitとoidがremoteに存在するかownerへ確認し、objectをpushできる権限のある担当者へ引き継ぎます。source archiveなら管理者設定を確認します。submoduleなら親repoとsubmoduleの両方を調べます。こうして「filter未設定」「skip」「archive」「権限」「object欠落」「submodule」を別の失敗経路として扱うと、無関係な設定変更を避けられます。

7. 取得後に実体を検証する

取得できたように見えても、それだけで期待する内容と一致するとは限りません。LFS pointerに記録されたsizeと実ファイルのサイズ、object IDの対応を確認し、ファイル形式を適切なツールで開けるかを確認します。Git LFSのpointer formatは元データのSHA-256 OIDとsizeを表します。手元の実ファイルについて検証方法を選ぶときは、利用するGit LFS CLIの公式コマンドを確認し、単純に拡張子やファイル名だけで内容を保証しません。必要ならownerから信頼できるchecksumや元データの生成元を取得します。

検証後に、対象pathがpointerでなく期待する実体になっているか、git status --shortで意図しない変更が生じていないかを確認します。正常なsmudge checkoutで実体が作業ツリーに見えても、Git上のindexにはpointerが保持される構成です。実体を取り込んだことだけを理由にGitのtracked diffが発生したと誤認しないでください。逆に、内容を編集した場合は意図した変更か、LFS追跡ルールが有効かを確認し、無関係なバイナリをcommit対象にしません。

よくある誤対応

・pointerの数行をバイナリ実体と勘違いして編集し、元objectと無関係なテキストをcommitする。

・source archiveを再ダウンロードし続け、LFS objectがarchiveに含まれない設定を見落とす。

・git lfs install --forceを実行し、既存filter設定を理由なく上書きする。

・原因を問わずgit lfs fetch --allを実行し、大量のobjectを取得する。

・403やobject missingをfilter設定だけの問題と決めつける。

・親repoのLFS設定だけを確認し、実際にはsubmoduleにあるファイルを見落とす。

・取得後にサイズ・形式・対象commitを照合せず、違う版のファイルを使う。

問題が解消しない場合、管理者に渡す再現パッケージは「Articleではなくrepositoryの障害票」として整理します。repository URL(共有範囲に注意)、commit SHA、path、取得経路、pointerのoid/size、git lfs version、属性確認結果、実行コマンド、secretを除いたエラーを含めます。これにより、管理者はobjectの存在・storage quota・repository access・archive settingを確認しやすくなります。アカウント情報や署名付きURL、Authorization headerは含めません。

まとめ:まずこの順で確認する

  1. 対象path、commit、取得経路、未commit変更を控える。

  2. ファイル先頭がLFS pointer形式かを確認し、oidとsizeを保存する。

  3. .gitattributesとgit check-attr filter -- <path>でLFS属性を確認する。

  4. Git LFS CLI、filter、skip-smudge設定、submoduleを確認する。

  5. GitHub archiveなら、LFS objectを含めるrepository設定をownerに確認する。

  6. cleanな状態を保全し、現在refに対するgit lfs pullなど最小取得を試す。

  7. 失敗したらHTTP status・権限・object存在を切り分け、ownerへ再現情報を渡す。

  8. 取得後にsize・形式・commit・作業ツリーを確認する。

判断のポイントは、pointerが見える場所をまず確認することです。clone済みのGit working treeならfilter・skip-smudge・object accessを調べ、ブラウザから取得したZIPならarchiveの設定を確認します。対象がsubmoduleなら、親repoとは別にそのrepositoryを確認します。どの経路でも、pointerのoidとsizeは診断に役立ちますが、実ファイルを自動で復元するものではありません。objectがremoteにない場合やread権限がない場合は、ownerまたは管理者が確認するまで待ちます。権限回避やpointerの手編集で復旧したことにせず、取得できた実体を期待するcommit・size・形式と突き合わせてから作業へ進みます。

参考資料

確認日:2026-10-15。GitHub DocsとGit LFS公式CLI referenceを確認。特定repositoryの属性・設定・object・権限は実地確認していません。

GitHub:About Git Large File Storage: https://docs.github.com/en/repositories/working-with-files/managing-large-files/about-git-large-file-storage(新しいタブで開く)

GitHub:Collaboration with Git LFS: https://docs.github.com/en/repositories/working-with-files/managing-large-files/collaboration-with-git-large-file-storage(新しいタブで開く)

GitHub:LFS objects in repository archives: https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/managing-repository-settings/managing-git-lfs-objects-in-archives-of-your-repository(新しいタブで開く)

git-lfs install: https://github.com/git-lfs/git-lfs/blob/main/docs/man/git-lfs-install.adoc(新しいタブで開く)

git-lfs pull: https://github.com/git-lfs/git-lfs/blob/main/docs/man/git-lfs-pull.adoc(新しいタブで開く)

git-lfs fetch: https://github.com/git-lfs/git-lfs/blob/main/docs/man/git-lfs-fetch.adoc(新しいタブで開く)

git-lfs pointer: https://github.com/git-lfs/git-lfs/blob/main/docs/man/git-lfs-pointer.adoc(新しいタブで開く)