กลับไปที่บล็อก

Gitignore ไม่ทำงาน? นี่คือวิธีแก้ที่แท้จริง

17 กันยายน 2569

TL;DR: ถ้า .gitignore ไม่ทำงาน แทบทุกครั้งไฟล์นั้นถูก track ไปแล้ว Git จะ ignore เฉพาะไฟล์ที่ไม่เคยเห็น — ไฟล์ที่อยู่ใน index ลอยอยู่เหนือกฎทุกข้อที่คุณเขียน สืบความจริงด้วย git check-ignore -v <path>: ความเงียบแปลว่า path นั้นถูก track และไม่มีกฎมีผล แก้ด้วย git rm --cached <path> แล้ว commit กฎจึงเริ่มทำงานจาก commit นั้นเป็นต้นไป ลำดับกฎ กับดักเครื่องหมายปฏิเสธ และกับดักหลอกจาก VS Code อธิบายกรณีที่เหลือ

ทำไม .gitignore ถึงไม่ทำงาน?

กลไกหนึ่งตัวครอบคลุมกรณีส่วนใหญ่: ไฟล์ถูก commit ไปก่อนที่กฎจะมีอยู่ .gitignore ไม่ใช่ตัวกรองที่ซ่อนไฟล์จาก git — มันคือกฎเรื่องสิ่งที่ git add ควรหยิบจากสถานะ untracked เมื่อไฟล์เข้าไปอยู่ใน index แล้ว git จะตามการเปลี่ยนแปลงเนื้อหาของมันตลอดไปจนกว่าคุณจะเลิก track อย่างชัดเจน การแก้ .gitignore ภายหลังไม่เปลี่ยนอะไรกับไฟล์ที่ถูก track — นั่นคือเหตุผลที่ลำดับคลาสสิก “commit .env ตกใจ เพิ่ม .env ลง .gitignore commit อีกรอบ” ยังส่ง secret ออกไปทุกครั้งที่ push

เรื่องนี้กัดทุกคนเพราะ git ใกล้เคียง universal — สำรวจ Stack Overflow ปี 2022 พบการใช้ Git ในกว่า 93% ของนักพัฒนามืออาชีพ (survey.stackoverflow.co) — และทุกคนในนั้นต้องเขียนกฎกับไฟล์ที่ commit ไปเมื่อสัปดาห์ก่อนสักวันหนึ่ง

ที่เหลือของบทความคู่กับกรณีกลุ่มน้อย: ผิดลำดับกฎ กับดักปฏิเสธ edge case ของโฟลเดอร์ และคำถามเรื่อง IDE แต่รัน health check ในหัวข้อถัดไปก่อน — มันจบการสืบสวนมากกว่าเก้าจากสิบครั้ง

รู้ได้อย่างไรว่ากฎ gitignore ข้อไหนแมตช์ไฟล์อยู่?

git check-ignore คือเครื่องมือวินิจฉัย และความเงียบของมันก็คือการวินิจฉัย:

# พิมพ์กฎที่แมตช์ + ไฟล์ + เลขบรรทัดเมื่อมีกฎมีผล
git check-ignore -v debug.log
# .gitignore:3:*.log    debug.log

# ไม่พิมพ์อะไรเลยเมื่อไม่มีกฎมีผล — ไฟล์ถูก track (หรือยังไม่มีกฎ)
git check-ignore -v src/.env
# (เงียบ = .gitignore ไม่ได้ ignore path นี้ ไม่ว่าคุณจะเขียนอะไร)

# exit code: 0 = ถูก ignore, 1 = ไม่ — เขียนสคริปต์ต่อได้
git check-ignore -q debug.log && echo "ignored" || echo "tracked or unruly"

อ่านผลของ -v: แหล่งที่มาของการ ignore (.gitignore, .git/info/exclude หรือไฟล์ ignore ระดับ global), ตามด้วย เลขบรรทัด:pattern, ตามด้วย path ถ้ากฎตัวหลังทำให้คุณตกใจ จำลำดับความสำคัญไว้: กฎที่แมตช์เป็นตัวสุดท้ายชนะ ดังนั้น !important.log วางหลัง *.log จะดึงไฟล์นั้นกลับมาเฉพาะตัว

ทำไม gitignore ถึงไม่เวิร์กกับไฟล์ที่ commit ไปแล้ว?

เลิก track ไฟล์ เก็บไว้บนดิสก์ แล้ว commit การถอน:

# เลิก track หนึ่งไฟล์ (ไฟล์ในเครื่องรอด — --cached แตะแค่ index)
git rm --cached .env
git commit -m "stop tracking .env"

# ยืนยันว่ากฎ ignore มีผลแล้ว
git check-ignore -v .env

จาก commit นี้เป็นต้นไป .gitignore เป็นเจ้าของ path นั้น: การแก้ไขไม่โผล่ใน git status อีก และ git add . จะไม่หยิบมันกลับ แต่ไฟล์ยังอยู่ในประวัติ — ถ้ามันคือ secret การเอาออกจาก commit ล่าสุดไม่พอ การหมุน credential คือวิธีแก้เดียวที่จริงจัง การแก้ประวัติเป็นเพียงการแต่งหน้า (และย้อนคอมมิตล่าสุดช่วยได้เฉพาะตอนที่คอมมิตแย่ยังเป็นยอดสุดอยู่)

สำหรับ repo ที่เต็มไปด้วยขยะที่ commit ไว้ก่อนหน้า — output ของการ build ขยะจาก editor node_modules ที่ซึมเข้ามาแต่แรก — การเลิก track ทีเดียวทั้งชุดคือสองบรรทัด:

git rm -r --cached .
git add .
git commit -m "apply .gitignore to tracked files"

คำสั่งนี้สร้าง index ใหม่ตามกฎปัจจุบัน: path ที่ถูก ignore ร่วงออก ที่เหลือถูกเพิ่มกลับเหมือนเดิม ดิฟจะดูอลังการ (ลบหลายพันบรรทัด) แต่ไม่ได้ลบอะไรออกจากดิสก์เลย ถ้าเป้าหมายคือการลบไฟล์เหล่านั้นจริง ๆ ไม่ใช่แค่เลิก track นั่นคือดินแดนของ git clean -fdx — ดูลบไฟล์ untracked อย่างปลอดภัย

Git จะ ignore เฉพาะไฟล์ที่ไม่เคยเห็น ไฟล์ที่อยู่ใน index แล้วจะไม่อยู่ใต้อิทธิพลของ .gitignore — ไม่มีกฎไหนทำให้มัน “ลืม” ได้

ทำไม gitignore ถึงไม่เวิร์กกับทั้งโฟลเดอร์?

สามกับดักเฉพาะโฟลเดอร์:

1. ทับท้ายเกี่ยวกับเจตนา ไม่ใช่กลไกแมตช์ build กับ build/ ต่างแมตช์โฟลเดอร์ได้ แต่ build/ บันทึกว่าคุณหมายถึงโฟลเดอร์เท่านั้น — ไฟล์ชื่อ build จะรอดพ้นมันไป แต่ความสมมาตรแตกที่การใส่กลับ (กับดักถัดไป)

2. เครื่องหมายปฏิเสธช่วยไฟล์ในโฟลเดอร์ที่ถูกยกเว้นไม่ได้ เอกสาร git พูดตรง ๆ: “It is not possible to re-include a file if a parent directory of that file is excluded” (git-scm.com/docs/gitignore) นี่คือการตัดสินใจเพื่อประสิทธิภาพ — git ข้ามโฟลเดอร์ที่ถูกยกเว้นทั้งก้อนแทนการเดินเข้าไปไล่ทีละไฟล์ ดังนั้นแบบนี้ไม่ทำงาน:

build/
!build/keep.me        # กฎตาย — git ไม่เคยมองเข้าไปใน build/ เลย

วิธีแก้คือยกเว้นเนื้อหา ไม่ใช่โฟลเดอร์:

build/*
!build/keep.me        # ทำงาน — ตัว build/ เองยังเปิดให้ส่องดูได้

3. ไฟล์ .gitignore ที่ซ้อนกันอยู่ชนะในขอบเขตของมัน กฎใน subdir/.gitignore ทับไฟล์รากสำหรับ path ใต้ subdir เมื่อ git check-ignore -v เรียกชื่อแหล่ง ignore ที่คุณไม่ได้คาดหวัง โดยมากเพราะเหตุนี้

ทำไม gitignore ถึงไม่ทำงานใน VS Code?

แทบไม่เคยเป็นเพราะ VS Code หน้าจอ Source Control ของ editor อ่าน index ชุดเดียวกับที่ git อ่าน อาการจึงเหมือนกันเป๊ะ: ไฟล์ถูก commit ไปก่อนแล้ว และการรีสตาร์ต IDE เปลี่ยน index ไม่ได้ สองข้อเท็จจริงใกล้เคียง VS Code ที่ควรรู้:

  • จางใน Explorer = ถูก ignore; ส้ม/เหลือง = ถูก track และมีการเปลี่ยนแปลง ไฟล์ที่ยังโชว์ว่า modifiedหลังคุณ ignore มันคือการยืนยันว่ามันถูก track — รันวิธีแก้ git rm --cached ด้านบน
  • .gitignore ไม่โผล่ในรายการchanged ของ Explorer แปลว่ากฎกำลังทำงาน — มันไม่เคยแสดงเป็น untracked ตั้งแต่แรก คนมักรายงานว่า “VS Code ไล่ gitignore ของผม” ทั้งที่ git status บน CLI ขัดกับหน้า SCM ที่ stale เพียงเพราะไม่ได้โหลดใหม่; รีโหลดหน้าต่าง (Cmd/Ctrl+Shift+P → “Reload Window”) ก่อนโทษ git

.gitignore vs .git/info/exclude vs global: ใช้ตัวไหนเมื่อไร?

ไฟล์ขอบเขตถูก commit?ใช้กับ
.gitignore (repo)ทุกคนที่ cloneใช่output การ build, dependencies, .env — กฎที่ใช้ร่วมกัน
.git/info/excludeเฉพาะ clone ของคุณไม่ขยะส่วนตัว: .scratch/, ขยะจาก editor
core.excludesFile (global)ทุก repo ของคุณไม่ขยะระบบปฏิบัติการ: .DS_Store, Thumbs.db, *.swp
.gitignore + ปฏิเสธrepoใช่ใส่ข้อยกเว้นกลับเข้ามาให้ไฟล์ config ที่ถูก track

ไฟล์ global คือสิ่งที่นักพัฒนาส่วนใหญ่ไม่เคยตั้งแต่ควรตั้ง:

git config --global core.excludesFile ~/.gitignore_global
printf '.DS_Store\nThumbs.db\n*.swp\n' >> ~/.gitignore_global

กฎบ้านที่ควรลอก: กฎที่เป็นประโยชน์กับทั้งทีมควรอยู่ใน repo กฎที่เป็นประโยชน์กับคุณคนเดียวควรอยู่ใน exclude หรือไฟล์ global การ commit กฎ ignore ส่วนตัวคือทางที่ทำให้ไฟล์ .gitignore ยาวถึง 300 บรรทัดโดยไม่มีใครรู้ว่าครึ่งไหนยังมีชีวิต

จะไม่สนใจการเปลี่ยนแปลงของไฟล์ที่ถูก track ทำอย่างไร?

บางทีคุณต้องการไฟล์ที่ถูก track (template ของ config, ไฟล์ตั้งค่า IDE) แต่อยากให้การแก้ในเครื่องหยุดโผล่ใน git status สอง flag ของ git update-index ซึ่งไม่มีตัวไหนควรอยู่ใน workflow ของทีม:

git update-index --skip-worktree config/local.dev   # การแก้ในเครื่องเงียบลง
git update-index --no-skip-worktree config/local.dev  # ...และกลับมา

--skip-worktree คือตัวที่ป้องกันได้ — มันบอกว่า “เวอร์ชันในเครื่องของฉันแตกต่างโดยเจตนา” ญาติของมัน --assume-unchanged เป็นคำสัญญาเรื่องประสิทธิภาพ (“ไฟล์นี้จะไม่เปลี่ยน”) ไม่ใช่กลไก ignore และ git อาจผิดคำสัญญาแบบเงียบ ๆ ทั้งสอง flag ล้มดัง ๆ ตอน pull ถ้า upstream แก้ไฟล์เดียวกันด้วย คำตอบที่ยั่งยืนคือ config เฉพาะเครื่องผ่าน exclude ตั้งแต่ตอนสร้าง หรือไฟล์ template (config.example) ที่ git track แล้วคุณ copy ใช้เอง

Health check gitignore ใน 30 วินาที

git check-ignore -v <path>        # กฎไหน? (เงียบ = ถูก track, ไม่มีกฎ)
git ls-files --error-unmatch <path>  # มันถูก track อยู่จริงไหม?
git rm --cached <path>            # เลิก track เก็บไฟล์ไว้บนดิสก์
git commit -m "stop tracking <path>"
git check-ignore -v <path>        # กฎโชว์ตัวแล้ว

วินิจฉัย เลิก track ตรวจสอบ รูปแบบที่ซ่อนหลังรายงาน “gitignore not working” ทุกฉบับคือไฟล์เดียวกันสวมหมวกสองใบ — ถูก track ฝั่งหนึ่งของ index แต่ถูก ignore อีกฝั่ง — และ flag --cached หนึ่งตัวถอดหมวกใบที่สองออก

FAQ

ทำไม .gitignore ถึงไม่ทำงาน?

ในกรณีส่วนใหญ่ไฟล์ถูก track ไปแล้ว Git จะ ignore เฉพาะไฟล์ที่ไม่เคยเห็นเท่านั้น ไฟล์ใน index จึงไม่อยู่ใต้อิทธิพลของ .gitignore จนกว่าคุณจะเลิก track มันด้วย git rm --cached แล้ว commit

เช็กได้อย่างไรว่ากฎ gitignore ข้อไหนแมตช์ไฟล์หนึ่ง?

รัน git check-ignore -v <path> มันจะพิมพ์บรรทัด .gitignore กับเลขกฎที่แม่นยำ หรือเงียบไปเลยเมื่อไฟล์ถูก track และไม่มีกฎใดมีผล

git rm --cached ลบไฟล์ในเครื่องผมหรือไม่?

ไม่ลบ --cached เอาไฟล์ออกจาก index เท่านั้น ไฟล์บนดิสก์อยู่ครบ พอ commit การถอนแล้วไฟล์กลายเป็น untracked จากนั้น .gitignore จึงมีผล

ทำไม gitignore ไม่สามารถใส่ไฟล์กลับเข้ามาในโฟลเดอร์ที่ถูก ignore?

เพื่อประสิทธิภาพ Git ข้ามโฟลเดอร์ที่ถูกยกเว้นทั้งก้อน ตามเอกสาร git จะเป็นไปไม่ได้ที่จะกลับมารวมไฟล์ใหม่ถ้าโฟลเดอร์แม่ของไฟล์นั้นถูกยกเว้น

— mrsaynothing

— mrsaynothing

บันทึกหน้างานเรื่อง AI, Linux และ self-hosted

คุยต่อโพสต์นี้บน dev.to dev.to ↗

รับวิธีแก้ฉบับถัดไปทางอีเมล

อีเมลหนึ่งฉบับต่อหนึ่งโพสต์ แก้เสร็จแล้วไปต่อ

self-hosted · ไม่มีบุคคลที่สาม · ยกเลิกได้ในคลิกเดียว

นี่คืออะไร?

Ollama ไม่ใช้ GPU? วิธีแก้บน Linux, Windows และ WSL

ถ้าอ่านแล้วชอบ — ผมสร้างงานแบบนี้เป็นอาชีพ จ้างผม