🧪 D-studyLabブログ
🧩 開発のつまずき

GitHubが差分を表示しなくなった——原因は文字コードではなかった

2026年7月31日

公開用のリポジトリにJavaScriptのファイルを1つ足したら、GitHubが差分を出さなくなりました。 画面に出るのはこの1行だけです。

static/app.js | Bin 138544 -> 142619 bytes

Bin ——バイナリ扱いです。テキストのはずのJavaScriptが、 画像や実行ファイルと同じ扱いになっている。差分が読めないので、レビューもできません。

最初は文字コードか改行コードを疑いました。どちらも違いました。 原因が分かるまでの手順と、実際の原因を書きます。

1. 何が起きているかを確かめる

まず、そのファイルが本当にテキストとして読めるのかを見ます。

file static/app.js

返ってきたのは data でした。同じ種類の別ファイルは UTF-8 Unicode text と出ます。 つまりgitの気まぐれではなく、ファイルの中身がテキストとして扱えない状態だと分かります。

ここで文字コードの破損を疑いましたが、Pythonで decode('utf-8') は通りました。 UTF-8としては正しい。壊れているわけではありません。

2. gitが「バイナリ」と判定する条件

gitは、ファイルの先頭のかたまりを見てNULバイト(0x00)が含まれていればバイナリと判定します。 拡張子は見ていません。.js でも .txt でも、NULが1つ入っていればバイナリです。

つまり探すべきものは「文字化け」ではなく、本来テキストに入らないはずの制御文字でした。

3. 制御文字を探す

ファイルを1バイトずつ見て、制御文字の位置を出します。

bad = [(i, c) for i, c in enumerate(open('app.js','rb').read()) if c < 9 or 13 < c < 32]

結果は2バイトだけ。142,619バイト中の2バイトです。しかも隣り合っていました。 0x000x1F。前後の文字列を出すと、こう書かれていました。

if (p.length > 3 && !/[■-■]/.test(p) && ...
※ ■ の位置に入っていたのが問題のバイト。画面上は何も見えません(幅ゼロで表示されるため、 ここでは便宜的に ■ と書いています)

正規表現です。「制御文字を除外する」という意図のコードに、制御文字が生で入っていた。 そして見えないので、コードを目で追っても気づけません。

本来は /[\x00-\x1f]/4文字の文字列として書くべきところが、 \x00\x1f実際のバイトそのものとして保存されていました。 正規表現としては同じ意味なので、コードは正しく動きます。エラーも出ません。 動くのに、ファイルがバイナリになる——これが厄介なところでした。

4. 直す

生のバイトを、エスケープした文字列に置き換えます。意味は変わりません。

置換したら file の結果は UTF-8 Unicode text に戻り、 差分も Bin ではなく 104 insertions(+), 17 deletions(-) と、 ふつうの行単位の表示になりました。

なぜ混入したのか

はっきりした原因は、エディタやツールを経由するあいだに エスケープ表記が「解釈されて」実体のバイトになったことだと考えています。 たとえば文字列としてこの正規表現を組み立てて、どこかで一度展開してからファイルに書けば、こうなります。

実際、この記事を書くために調査していたときも、シェル経由でバックスラッシュを渡そうとして 同じ取り違えを起こしました\\x00 と書いたつもりが \x00 になって届き、 「置換したのに何も変わらない」という状態になった。バックスラッシュは、経路の数だけ消えます。

この症状のときに見る順番

ちなみに .gitattributes*.js text と書けば、 gitに「これはテキストだ」と強制することもできます。 ただしそれは表示を変えるだけで、混入した制御文字は残ります。 症状を隠すことになるので、まず中身を直したほうがいいと思います。

まとめ

※Windows 11・Git for Windows の環境で実際に踏んだものです。バイト数や差分の行数は実測値です。

© D-studyLab / ブログ一覧運営者情報プライバシーポリシー