[{"data":1,"prerenderedAt":896},["ShallowReactive",2],{"content-/gitkeep-explained":3,"all-pages-for-dir":894,"og-image-/gitkeep-explained":895},{"id":4,"title":5,"body":6,"category":877,"description":878,"extension":879,"meta":880,"navigation":231,"ogImage":881,"path":882,"project_name":881,"published":883,"publishedAt":884,"seo":885,"stem":886,"tags":887,"todo":881,"unpublished":883,"updatedAt":892,"__hash__":893},"pages/2026-01/2026-01-09/gitkeep-explained.md",".gitkeepとは？Gitで空ディレクトリを管理する方法と運用上の落とし穴",{"type":7,"value":8,"toc":855},"minimark",[9,13,17,30,46,49,53,63,66,128,151,154,198,200,204,208,211,290,294,297,328,335,339,342,350,356,400,402,406,459,465,470,472,476,526,528,531,539,585,609,616,622,634,642,674,677,715,717,721,727,766,772,774,777,812,814,817,851],[10,11,5],"h1",{"id":12},"gitkeepとはgitで空ディレクトリを管理する方法と運用上の落とし穴",[14,15,16],"h2",{"id":16},"結論",[18,19,20,24,25,29],"p",{},[21,22,23],"code",{},".gitkeep","は、",[26,27,28],"strong",{},"空のディレクトリをGitで追跡するためのダミーファイル","。Gitはファイル単位で変更を追跡する仕組みなので、ファイルが1つもないディレクトリを「存在するもの」として扱えない。そこに空のファイルを1つ置くことで、ディレクトリの存在をGitに認識させる。",[18,31,32,33,35,36,35,39,42,43,45],{},"ファイル名は ",[21,34,23],{}," でも ",[21,37,38],{},".keep",[21,40,41],{},".placeholder"," でも構わない。慣習として ",[21,44,23],{}," が広く使われている。",[47,48],"hr",{},[14,50,52],{"id":51},"gitが空ディレクトリを追跡できない理由内部構造から見る","Gitが空ディレクトリを追跡できない理由（内部構造から見る）",[18,54,55,56,59,60,62],{},"これは「Gitの設計上の都合」というより、",[26,57,58],{},"Gitのデータモデルそのものの帰結","。理屈を一度押さえると、",[21,61,23],{}," がなぜ必要なのか腑に落ちる。",[18,64,65],{},"Gitはリポジトリの状態を3種類のオブジェクトで表現する。",[67,68,69,85],"table",{},[70,71,72],"thead",{},[73,74,75,79,82],"tr",{},[76,77,78],"th",{},"オブジェクト",[76,80,81],{},"中身",[76,83,84],{},"役割",[86,87,88,102,115],"tbody",{},[73,89,90,96,99],{},[91,92,93],"td",{},[21,94,95],{},"blob",[91,97,98],{},"ファイルの中身（バイト列）",[91,100,101],{},"ファイル1つ分",[73,103,104,109,112],{},[91,105,106],{},[21,107,108],{},"tree",[91,110,111],{},"blob と他の tree への参照リスト",[91,113,114],{},"1ディレクトリ分のスナップショット",[73,116,117,122,125],{},[91,118,119],{},[21,120,121],{},"commit",[91,123,124],{},"1つの tree への参照 + 親 commit + メタ情報",[91,126,127],{},"履歴の1ノード",[18,129,130,131,133,134,136,137,140,141,143,144,147,148,150],{},"注目すべきは ",[21,132,108],{}," の中身。",[21,135,108],{}," は「",[26,138,139],{},"ファイル名 → blob/tree への参照","」のリストでしかない。つまり、ファイルが1つもないディレクトリを表す ",[21,142,108],{}," を作っても、それは「",[26,145,146],{},"中身ゼロのリスト","」になり、参照先がないので親 ",[21,149,108],{}," には何も載せようがない。",[18,152,153],{},"これがGitが空ディレクトリを「保持できない」理由。SVNやMercurialのように「ディレクトリそのもの」を一級オブジェクトとして持つVCSとは設計が違う。",[155,156,161],"pre",{"className":157,"code":158,"language":159,"meta":160,"style":160},"language-bash shiki shiki-themes vitesse-light vitesse-light","# 空ディレクトリを作っても git は何も検出しない\nmkdir uploads\ngit status\n# → uploads/ は何も表示されない（\"untracked files\" にも出てこない）\n","bash","",[21,162,163,172,183,192],{"__ignoreMap":160},[164,165,168],"span",{"class":166,"line":167},"line",1,[164,169,171],{"class":170},"sxvE3","# 空ディレクトリを作っても git は何も検出しない\n",[164,173,175,179],{"class":166,"line":174},2,[164,176,178],{"class":177},"senZ8","mkdir",[164,180,182],{"class":181},"sdGka"," uploads\n",[164,184,186,189],{"class":166,"line":185},3,[164,187,188],{"class":177},"git",[164,190,191],{"class":181}," status\n",[164,193,195],{"class":166,"line":194},4,[164,196,197],{"class":170},"# → uploads/ は何も表示されない（\"untracked files\" にも出てこない）\n",[47,199],{},[14,201,203],{"id":202},"gitkeepの使い方",".gitkeepの使い方",[205,206,207],"h3",{"id":207},"基本",[18,209,210],{},"空にしたいディレクトリに、中身ゼロのファイルを1つ置く。",[155,212,214],{"className":157,"code":213,"language":159,"meta":160,"style":160},"# uploadsディレクトリを作成\nmkdir uploads\n\n# .gitkeepを作成（中身は空でOK）\ntouch uploads/.gitkeep\n\n# これで git add できる\ngit add uploads/.gitkeep\ngit commit -m \"Add uploads directory\"\n",[21,215,216,221,227,233,238,247,252,258,268],{"__ignoreMap":160},[164,217,218],{"class":166,"line":167},[164,219,220],{"class":170},"# uploadsディレクトリを作成\n",[164,222,223,225],{"class":166,"line":174},[164,224,178],{"class":177},[164,226,182],{"class":181},[164,228,229],{"class":166,"line":185},[164,230,232],{"emptyLinePlaceholder":231},true,"\n",[164,234,235],{"class":166,"line":194},[164,236,237],{"class":170},"# .gitkeepを作成（中身は空でOK）\n",[164,239,241,244],{"class":166,"line":240},5,[164,242,243],{"class":177},"touch",[164,245,246],{"class":181}," uploads/.gitkeep\n",[164,248,250],{"class":166,"line":249},6,[164,251,232],{"emptyLinePlaceholder":231},[164,253,255],{"class":166,"line":254},7,[164,256,257],{"class":170},"# これで git add できる\n",[164,259,261,263,266],{"class":166,"line":260},8,[164,262,188],{"class":177},[164,264,265],{"class":181}," add",[164,267,246],{"class":181},[164,269,271,273,276,280,284,287],{"class":166,"line":270},9,[164,272,188],{"class":177},[164,274,275],{"class":181}," commit",[164,277,279],{"class":278},"snbK4"," -m",[164,281,283],{"class":282},"sMJiu"," \"",[164,285,286],{"class":181},"Add uploads directory",[164,288,289],{"class":282},"\"\n",[205,291,293],{"id":292},"gitignoreとの組み合わせ実務で最も多いパターン",".gitignoreとの組み合わせ（実務で最も多いパターン）",[18,295,296],{},"「ディレクトリ構造は維持したいが、中身は除外したい」というケース。アップロード用フォルダ、キャッシュ、ログ出力先などで頻出する。",[155,298,302],{"className":299,"code":300,"language":301,"meta":160,"style":160},"language-gitignore shiki shiki-themes vitesse-light vitesse-light","# uploads ディレクトリの中身は除外\nuploads/*\n\n# ただし .gitkeep は追跡する\n!uploads/.gitkeep\n","gitignore",[21,303,304,309,314,318,323],{"__ignoreMap":160},[164,305,306],{"class":166,"line":167},[164,307,308],{},"# uploads ディレクトリの中身は除外\n",[164,310,311],{"class":166,"line":174},[164,312,313],{},"uploads/*\n",[164,315,316],{"class":166,"line":185},[164,317,232],{"emptyLinePlaceholder":231},[164,319,320],{"class":166,"line":194},[164,321,322],{},"# ただし .gitkeep は追跡する\n",[164,324,325],{"class":166,"line":240},[164,326,327],{},"!uploads/.gitkeep\n",[18,329,330,331,334],{},"この設定により、",[21,332,333],{},"uploads/"," 配下にどんなファイルを置いても無視されるが、ディレクトリ構造自体はリポジトリに含まれる。",[205,336,338],{"id":337},"実際の使用例顧問先データ分離","実際の使用例（顧問先データ分離）",[18,340,341],{},"プロジェクトで顧問先ごとにデータを分離する構造を作った際の例。",[155,343,348],{"className":344,"code":346,"language":347},[345],"language-text","tests/data/client_001/\n├── inbox/           # スキャン待ちレシート\n│   └── .gitkeep\n├── batches/         # 処理済みバッチ\n│   └── .gitkeep\n├── exports/         # 出力済みファイル\n│   └── .gitkeep\n└── receipts.db      # DBファイル（.gitignoreで除外）\n","text",[21,349,346],{"__ignoreMap":160},[18,351,352,355],{},[21,353,354],{},".gitignore"," の設定はこうなる。",[155,357,359],{"className":299,"code":358,"language":301,"meta":160,"style":160},"# データディレクトリ（中身は除外、構造は維持）\ntests/data/client_*/inbox/*\ntests/data/client_*/batches/*\ntests/data/client_*/exports/*\n!tests/data/client_*/**/.gitkeep\n\n# DBファイルは除外\n*.db\n",[21,360,361,366,371,376,381,386,390,395],{"__ignoreMap":160},[164,362,363],{"class":166,"line":167},[164,364,365],{},"# データディレクトリ（中身は除外、構造は維持）\n",[164,367,368],{"class":166,"line":174},[164,369,370],{},"tests/data/client_*/inbox/*\n",[164,372,373],{"class":166,"line":185},[164,374,375],{},"tests/data/client_*/batches/*\n",[164,377,378],{"class":166,"line":194},[164,379,380],{},"tests/data/client_*/exports/*\n",[164,382,383],{"class":166,"line":240},[164,384,385],{},"!tests/data/client_*/**/.gitkeep\n",[164,387,388],{"class":166,"line":249},[164,389,232],{"emptyLinePlaceholder":231},[164,391,392],{"class":166,"line":254},[164,393,394],{},"# DBファイルは除外\n",[164,396,397],{"class":166,"line":260},[164,398,399],{},"*.db\n",[47,401],{},[14,403,405],{"id":404},"gitkeep-keep-placeholder-の違い",".gitkeep / .keep / .placeholder の違い",[67,407,408,421],{},[70,409,410],{},[73,411,412,415,418],{},[76,413,414],{},"名前",[76,416,417],{},"使われる文脈",[76,419,420],{},"補足",[86,422,423,435,447],{},[73,424,425,429,432],{},[91,426,427],{},[21,428,23],{},[91,430,431],{},"個人プロジェクト、OSS全般で広く使用",[91,433,434],{},"Git公式の機能ではなく慣習",[73,436,437,441,444],{},[91,438,439],{},[21,440,38],{},[91,442,443],{},"Rails系のプロジェクトでよく見る",[91,445,446],{},"Railsジェネレータが自動生成する",[73,448,449,453,456],{},[91,450,451],{},[21,452,41],{},[91,454,455],{},"「中身が後で来る予定」のニュアンス",[91,457,458],{},"用途を伝える命名",[18,460,461,464],{},[26,462,463],{},"重要な点",": いずれも「Gitの公式機能」ではない。Gitから見れば、ただの「中身ゼロのファイル」。意図を読み手に伝える名前を選んでいるだけ。",[18,466,467,469],{},[21,468,23],{}," を使うと「Gitの都合でここに置いているダミーである」ことが名前から伝わる。レビュー時に「これ何？」と聞かれにくい利点がある。",[47,471],{},[14,473,475],{"id":474},"ideエディタでの扱い","IDE/エディタでの扱い",[477,478,479,497,503,516],"ul",{},[480,481,482,485,486,488,489,492,493,496],"li",{},[26,483,484],{},"VSCode",": ",[21,487,23],{}," は通常のファイルとして表示される。エクスプローラーで非表示にしたい場合は ",[21,490,491],{},"files.exclude"," 設定で ",[21,494,495],{},"**/.gitkeep"," を追加",[480,498,499,502],{},[26,500,501],{},"JetBrains系（IntelliJ / WebStorm 等）",": 同じく通常表示。Settings > Editor > File Types > Ignored Files で除外可能",[480,504,505,485,508,511,512,515],{},[26,506,507],{},"Vim/Neovim",[21,509,510],{},"."," で始まるファイルは隠しファイル扱い。",[21,513,514],{},":set list"," で確認可能",[480,517,518,521,522,525],{},[26,519,520],{},"macOS Finder / Windows Explorer",": デフォルトでは隠しファイルとして非表示。",[21,523,524],{},"Cmd+Shift+."," / 表示オプションで表示可能",[47,527],{},[14,529,530],{"id":530},"実務でハマる落とし穴",[205,532,534,535,538],{"id":533},"落とし穴-1-gitignore-の-パターンが-gitkeep-まで除外する","落とし穴 1: .gitignore の ",[21,536,537],{},"*"," パターンが .gitkeep まで除外する",[155,540,542],{"className":299,"code":541,"language":301,"meta":160,"style":160},"# NG: これだと .gitkeep も除外されてしまう\nuploads/\n\n# NG: 同じく除外される\nuploads/**\n\n# OK: 中身だけ除外して .gitkeep は残す\nuploads/*\n!uploads/.gitkeep\n",[21,543,544,549,554,558,563,568,572,577,581],{"__ignoreMap":160},[164,545,546],{"class":166,"line":167},[164,547,548],{},"# NG: これだと .gitkeep も除外されてしまう\n",[164,550,551],{"class":166,"line":174},[164,552,553],{},"uploads/\n",[164,555,556],{"class":166,"line":185},[164,557,232],{"emptyLinePlaceholder":231},[164,559,560],{"class":166,"line":194},[164,561,562],{},"# NG: 同じく除外される\n",[164,564,565],{"class":166,"line":240},[164,566,567],{},"uploads/**\n",[164,569,570],{"class":166,"line":249},[164,571,232],{"emptyLinePlaceholder":231},[164,573,574],{"class":166,"line":254},[164,575,576],{},"# OK: 中身だけ除外して .gitkeep は残す\n",[164,578,579],{"class":166,"line":260},[164,580,313],{},[164,582,583],{"class":166,"line":270},[164,584,327],{},[18,586,587,589,590,593,594,597,598,600,601,604,605,608],{},[21,588,333],{}," という末尾スラッシュ付きや ",[21,591,592],{},"uploads/**"," は ",[26,595,596],{},"ディレクトリ全体"," を除外する。",[21,599,23],{}," も道連れになるため、",[21,602,603],{},"uploads/*"," + ",[21,606,607],{},"!uploads/.gitkeep"," のセットが正解。",[205,610,612,613,615],{"id":611},"落とし穴-2-ネストしたディレクトリで-gitkeep-が複数階層必要","落とし穴 2: ネストしたディレクトリで ",[21,614,23],{}," が複数階層必要",[155,617,620],{"className":618,"code":619,"language":347},[345],"data/\n├── 2026/\n│   ├── Q1/\n│   │   └── .gitkeep   # 必要\n│   ├── Q2/\n│   │   └── .gitkeep   # 必要\n│   └── .gitkeep       # data/2026/ が空のことはほぼ無いが念のため\n└── .gitkeep\n",[21,621,619],{"__ignoreMap":160},[18,623,624,627,628,630,631,633],{},[21,625,626],{},"data/"," 配下の各四半期ディレクトリを維持したいなら、それぞれに ",[21,629,23],{}," を置く必要がある。1階層下の ",[21,632,23],{}," で上位ディレクトリが「保持される」と誤解しがち。",[205,635,637,638,641],{"id":636},"落とし穴-3-git-rm-で消すと-gitkeep-ごと消える","落とし穴 3: ",[21,639,640],{},"git rm"," で消すと .gitkeep ごと消える",[155,643,645],{"className":157,"code":644,"language":159,"meta":160,"style":160},"# ディレクトリの中身を整理しようとして\ngit rm -r uploads/\n\n# uploads/.gitkeep も消えてディレクトリが消滅\n",[21,646,647,652,665,669],{"__ignoreMap":160},[164,648,649],{"class":166,"line":167},[164,650,651],{"class":170},"# ディレクトリの中身を整理しようとして\n",[164,653,654,656,659,662],{"class":166,"line":174},[164,655,188],{"class":177},[164,657,658],{"class":181}," rm",[164,660,661],{"class":278}," -r",[164,663,664],{"class":181}," uploads/\n",[164,666,667],{"class":166,"line":185},[164,668,232],{"emptyLinePlaceholder":231},[164,670,671],{"class":166,"line":194},[164,672,673],{"class":170},"# uploads/.gitkeep も消えてディレクトリが消滅\n",[18,675,676],{},"明示的に残す場合:",[155,678,680],{"className":157,"code":679,"language":159,"meta":160,"style":160},"git rm -r uploads/\nmkdir -p uploads/\ntouch uploads/.gitkeep\ngit add uploads/.gitkeep\n",[21,681,682,692,701,707],{"__ignoreMap":160},[164,683,684,686,688,690],{"class":166,"line":167},[164,685,188],{"class":177},[164,687,658],{"class":181},[164,689,661],{"class":278},[164,691,664],{"class":181},[164,693,694,696,699],{"class":166,"line":174},[164,695,178],{"class":177},[164,697,698],{"class":278}," -p",[164,700,664],{"class":181},[164,702,703,705],{"class":166,"line":185},[164,704,243],{"class":177},[164,706,246],{"class":181},[164,708,709,711,713],{"class":166,"line":194},[164,710,188],{"class":177},[164,712,265],{"class":181},[164,714,246],{"class":181},[47,716],{},[14,718,720],{"id":719},"gitkeep-を使わずに済むケース",".gitkeep を使わずに済むケース",[18,722,723,724,726],{},"実は、運用次第で ",[21,725,23],{}," を使わずに済むこともある。",[477,728,729,748,754],{},[480,730,731,485,734,737,738,737,741,744,745,747],{},[26,732,733],{},"ビルド時に生成されるディレクトリ",[21,735,736],{},"dist/"," ",[21,739,740],{},".nuxt/",[21,742,743],{},"node_modules/"," 等は ",[21,746,354],{}," で完全に除外し、起動時にツールが自動作成するので不要",[480,749,750,753],{},[26,751,752],{},"README.md を入れる",": 「このディレクトリは何のため？」を兼ねた説明書きを置けば、それが空ディレクトリ問題も解決する",[480,755,756,765],{},[26,757,758,737,761,764],{},[21,759,760],{},".npmrc",[21,762,763],{},".envrc"," などの設定ファイル",": 機能ファイル自体が常に存在するなら、ダミーは不要",[18,767,768,769,771],{},"「",[21,770,23],{}," を置く前に、本当に空でいいのか」を一度考える価値はある。",[47,773],{},[14,775,776],{"id":776},"まとめ",[477,778,779,785,793,803,809],{},[480,780,781,782,784],{},"Gitは ",[21,783,108],{}," オブジェクトの仕様上、空ディレクトリを保持できない",[480,786,787,593,789,792],{},[21,788,23],{},[26,790,791],{},"慣習的なダミーファイル","。中身ゼロのファイルを置けばいい",[480,794,795,797,798,604,800,802],{},[21,796,354],{}," と組み合わせるなら ",[21,799,603],{},[21,801,607],{}," のパターン",[480,804,805,806,808],{},"ネストしたディレクトリは階層ごとに ",[21,807,23],{}," が必要",[480,810,811],{},"実は使わずに済むケースもある（ビルド生成物・README 兼用）",[47,813],{},[14,815,816],{"id":816},"関連記事",[477,818,819,830,837,844],{},[480,820,821,826,827,829],{},[822,823,825],"a",{"href":824},"/claude-state-files-gitignore-cleanup","Claude Code のセッション状態ファイルを .gitignore で整理する"," — ",[21,828,354],{}," パターンの実例集",[480,831,832,836],{},[822,833,835],{"href":834},"/git-worktree-wtp-guide","Git worktree と wtp（worktree-plus）の運用"," — 複数ブランチを並行作業する際のディレクトリ構成",[480,838,839,843],{},[822,840,842],{"href":841},"/git-history-meaningful-commits","Git history を意味のあるコミットで残す"," — gitkeep のような細かいコミットをどうまとめるか",[480,845,846,850],{},[822,847,849],{"href":848},"/github-rulesets-guide","GitHub Rulesets 設定ガイド"," — リポジトリ全体の運用ルール",[852,853,854],"style",{},"html pre.shiki code .sxvE3, html code.shiki .sxvE3{--shiki-default:#A0ADA0;--shiki-dark:#A0ADA0}html pre.shiki code .senZ8, html code.shiki .senZ8{--shiki-default:#59873A;--shiki-dark:#59873A}html pre.shiki code .sdGka, html code.shiki .sdGka{--shiki-default:#B56959;--shiki-dark:#B56959}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .snbK4, html code.shiki .snbK4{--shiki-default:#A65E2B;--shiki-dark:#A65E2B}html pre.shiki code .sMJiu, html code.shiki .sMJiu{--shiki-default:#B5695977;--shiki-dark:#B5695977}",{"title":160,"searchDepth":174,"depth":174,"links":856},[857,858,859,864,865,866,874,875,876],{"id":16,"depth":174,"text":16},{"id":51,"depth":174,"text":52},{"id":202,"depth":174,"text":203,"children":860},[861,862,863],{"id":207,"depth":185,"text":207},{"id":292,"depth":185,"text":293},{"id":337,"depth":185,"text":338},{"id":404,"depth":174,"text":405},{"id":474,"depth":174,"text":475},{"id":530,"depth":174,"text":530,"children":867},[868,870,872],{"id":533,"depth":185,"text":869},"落とし穴 1: .gitignore の * パターンが .gitkeep まで除外する",{"id":611,"depth":185,"text":871},"落とし穴 2: ネストしたディレクトリで .gitkeep が複数階層必要",{"id":636,"depth":185,"text":873},"落とし穴 3: git rm で消すと .gitkeep ごと消える",{"id":719,"depth":174,"text":720},{"id":776,"depth":174,"text":776},{"id":816,"depth":174,"text":816},"dev",".gitkeepはGitで空ディレクトリを追跡するためのダミーファイル。なぜGitは空ディレクトリを追跡できないのか、.keepや.placeholderとの違い、.gitignoreとの組み合わせ、IDE/エディタでの扱い、実務でハマる落とし穴まで具体例つきで解説。","md",{},null,"/gitkeep-explained",false,"2026-01-09T00:00:00.000Z",{"title":5,"description":878},"2026-01/2026-01-09/gitkeep-explained",[888,889,301,890,891],"Git","gitkeep","ディレクトリ構造","開発環境","2026-06-09T00:00:00.000Z","Wvd5ztMxyS99hgRqdv08YQXSpkW3QOjykrBDNoiOxTU",[],"https://log.eurekapu.com/og/blog/gitkeep-explained.png?v=2026-06-09T00%3A00%3A00.000Z&title=.gitkeep%E3%81%A8%E3%81%AF%EF%BC%9FGit%E3%81%A7%E7%A9%BA%E3%83%87%E3%82%A3%E3%83%AC%E3%82%AF%E3%83%88%E3%83%AA%E3%82%92%E7%AE%A1%E7%90%86%E3%81%99%E3%82%8B%E6%96%B9%E6%B3%95%E3%81%A8%E9%81%8B%E7%94%A8%E4%B8%8A%E3%81%AE%E8%90%BD%E3%81%A8%E3%81%97%E7%A9%B4&author=Kei%20Komatsu&sig=96924d08e777624b",1782528791506]